maze5@maze:~$ /maze/maze5
X----------------
Username: user
Key: pass
Wrong length you!
It asks for username and password that must have a proper length.
The section that checks the lenght of the passw rd is the following:
0x0804863a <+83>: call 0x80483f0 <strlen@plt> 0x0804863f <+88>: add $0x4,%esp 0x08048642 <+91>: cmp $0x8,%eax 0x08048645 <+94>: jne 0x8048658 <main+113> 0x0804864b <+100>: call 0x80483f0 <strlen@plt> 0x08048650 <+105>: add $0x4,%esp 0x08048653 <+108>: cmp $0x8,%eax 0x08048656 <+111>: je 0x804866c <main+133>
So the required length is 8 bytes for both username and password.
If we input 8 byte username and password and trace it with ltrace:
maze5@maze:~$ ltrace /maze/maze5 __libc_start_main(0x80485e7, 1, 0xffffd7a4, 0x80486e0 <unfinished ...> puts("X----------------"X---------------- ) = 18 printf(" Username: ") = 11 __isoc99_scanf(0x804877e, 0xffffd6ff, 0x804821c, 0x80486e9 Username: username ) = 1 printf(" Key: ") = 11 __isoc99_scanf(0x804877e, 0xffffd6f6, 0x804821c, 0x80486e9 Key: password ) = 1 strlen("username") = 8 strlen("password") = 8 ptrace(0, 0, 0, 0) = 0xffffffff puts("\nnahnah..."
The interesting code in main section is the following:
0x0804866c <+133>: push $0x0 0x0804866e <+135>: push $0x0 0x08048670 <+137>: push $0x0 0x08048672 <+139>: push $0x0 0x08048674 <+141>: call 0x8048420 <ptrace@plt> 0x08048679 <+146>: add $0x10,%esp 0x0804867c <+149>: test %eax,%eax 0x0804867e <+151>: je 0x8048694 <main+173> 0x08048680 <+153>: push $0x80487a0 0x08048685 <+158>: call 0x80483c0 <puts@plt> 0x0804868a <+163>: add $0x4,%esp 0x0804868d <+166>: mov $0x0,%eax 0x08048692 <+171>: jmp 0x80486d6 <main+239> 0x08048694 <+173>: lea -0x12(%ebp),%eax 0x08048697 <+176>: push %eax 0x08048698 <+177>: lea -0x9(%ebp),%eax 0x0804869b <+180>: push %eax 0x0804869c <+181>: call 0x804853b <foo>
The bad part is that call to ptrace(0,0,0,0) which don't help when it comes to analyzing the behavior in gdb.
Well, not that bad...
We can get past that ptrace with a combination of break and jump.
Let's place a break on the ptrace call when we start gdb, at run time, when the flow stops at the break we jump after the puts call:
# at gdb start
(gdb) break *0x08048674
# during execution
(gdb) jump *0x08048694
This helps to get past the ptrace call in gdb.
After the ptrace() and a call to puts() that shows "nahnah", we have other code that pushes username and password address then calls the function foo().
Let's see the foo() code:
0x0804853b <+0>: push %ebp 0x0804853c <+1>: mov %esp,%ebp 0x0804853e <+3>: sub $0x14,%esp 0x08048541 <+6>: movl $0x6e697270,-0x11(%ebp) 0x08048548 <+13>: movl $0x6c6f6c74,-0xd(%ebp) 0x0804854f <+20>: movb $0x0,-0x9(%ebp) 0x08048553 <+24>: movl $0x0,-0x4(%ebp) 0x0804855a <+31>: jmp 0x804859c <foo+97> 0x0804855c <+33>: mov -0x4(%ebp),%edx 0x0804855f <+36>: mov 0x8(%ebp),%eax 0x08048562 <+39>: add %edx,%eax 0x08048564 <+41>: movzbl (%eax),%eax 0x08048567 <+44>: movsbl %al,%eax 0x0804856a <+47>: sub $0x41,%eax 0x0804856d <+50>: mov %eax,-0x8(%ebp) 0x08048570 <+53>: lea -0x11(%ebp),%edx 0x08048573 <+56>: mov -0x4(%ebp),%eax 0x08048576 <+59>: add %edx,%eax 0x08048578 <+61>: movzbl (%eax),%eax 0x0804857b <+64>: mov %eax,%ecx 0x0804857d <+66>: mov -0x4(%ebp),%eax 0x08048580 <+69>: lea (%eax,%eax,1),%edx 0x08048583 <+72>: mov -0x8(%ebp),%eax 0x08048586 <+75>: add %edx,%eax 0x08048588 <+77>: sub %eax,%ecx 0x0804858a <+79>: mov %ecx,%eax 0x0804858c <+81>: mov %eax,%ecx 0x0804858e <+83>: lea -0x11(%ebp),%edx 0x08048591 <+86>: mov -0x4(%ebp),%eax 0x08048594 <+89>: add %edx,%eax 0x08048596 <+91>: mov %cl,(%eax) 0x08048598 <+93>: addl $0x1,-0x4(%ebp) 0x0804859c <+97>: pushl 0x8(%ebp) 0x0804859f <+100>: call 0x80483f0 <strlen@plt> 0x080485a4 <+105>: add $0x4,%esp 0x080485a7 <+108>: mov %eax,%edx 0x080485a9 <+110>: mov -0x4(%ebp),%eax 0x080485ac <+113>: cmp %eax,%edx 0x080485ae <+115>: ja 0x804855c <foo+33> 0x080485b0 <+117>: jmp 0x80485d3 <foo+152> 0x080485b2 <+119>: lea -0x11(%ebp),%edx 0x080485b5 <+122>: mov -0x4(%ebp),%eax 0x080485b8 <+125>: add %edx,%eax 0x080485ba <+127>: movzbl (%eax),%edx 0x080485bd <+130>: mov -0x4(%ebp),%ecx 0x080485c0 <+133>: mov 0xc(%ebp),%eax 0x080485c3 <+136>: add %ecx,%eax 0x080485c5 <+138>: movzbl (%eax),%eax 0x080485c8 <+141>: cmp %al,%dl 0x080485ca <+143>: je 0x80485d3 <foo+152> 0x080485cc <+145>: mov $0x0,%eax 0x080485d1 <+150>: jmp 0x80485e5 <foo+170> 0x080485d3 <+152>: mov -0x4(%ebp),%eax 0x080485d6 <+155>: lea -0x1(%eax),%edx 0x080485d9 <+158>: mov %edx,-0x4(%ebp) 0x080485dc <+161>: test %eax,%eax 0x080485de <+163>: jne 0x80485b2 <foo+119> 0x080485e0 <+165>: mov $0x1,%eax 0x080485e5 <+170>: leave 0x080485e6 <+171>: ret
Quite long and complex with some jumps.
The interesting part is at line 0x080485c8.
It compares byte values in eax and edx.
If they are not equal, it puts 0 in eax and then exit.
Otherwise it jumps to another section, it loads data from memory location and jumps back to the beginning of foo().
It looks like it is checking something between the username and password byte-by-byte.
We can put a break at the line of the comparison and see what happens.
So now let's summarize the approach:
- choose a standard username: AAAAAAAA
- choose a standard password: BCDEFGHI
- open gdb
- put a break at the ptrace() call
- put a break at the comparison in foo()
- use jump to reach foo() and the break inside it
- verify register eax and edx that needs to be compared
maze5@maze:~$ gdb /maze/maze5 ... (gdb) break *0x08048674 Breakpoint 1 at 0x8048674: file maze5.c, line 48.
(gdb) break *0x080485c8
Breakpoint 2 at 0x80485c8: file maze5.c, line 32.
(gdb) r Starting program: /maze/maze5 X---------------- Username: AAAAAAAA Key: BCDEFGHI Breakpoint 1, 0x08048674 in main () at maze5.c:48 48 maze5.c: No such file or directory.
(gdb) jump *0x08048694 Continuing at 0x8048694. Breakpoint 2, 0x080485c8 in foo (s=0xffffd6ef "AAAAAAAA", a=0xffffd6e6 "BCDEFGHI") at maze5.c:32 32 in maze5.c
(gdb) info reg eax edx eax 0x49 73 edx 0x5e 94 (gdb)
It seems that eax contains the last byte of the password (0x49 is hex for letter I)
Edx has another value, so comparison will fail.
Let's run again with a different username, let's say we change last byte of the username to AAAAAAAB
(gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /maze/maze5 X---------------- Username: AAAAAAAB Key: BCDEFGHI Breakpoint 1, 0x08048674 in main () at maze5.c:48 48 in maze5.c (gdb) jump *0x08048694 Continuing at 0x8048694. Breakpoint 2, 0x080485c8 in foo (s=0xffffd6ef "AAAAAAAB", a=0xffffd6e6 "BCDEFGHI") at maze5.c:32 32 in maze5.c (gdb) info reg eax edx eax 0x49 73 edx 0x5d 93
Interesting... the edx value is decreased by 1, while the 8th byte of username was change from A to B (se increased by 1)
Let's then adjust the last byte of username to V and retry
Explanation: A gives 0x5e, B gives 0x5d ... so V should give 0x49
(gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /maze/maze5 X---------------- Username: AAAAAAAV Key: BCDEFGHI Breakpoint 1, 0x08048674 in main () at maze5.c:48 48 in maze5.c (gdb) jump *0x08048694 Continuing at 0x8048694. Breakpoint 2, 0x080485c8 in foo (s=0xffffd6ef "AAAAAAAV", a=0xffffd6e6 "BCDEFGHI") at maze5.c:32 32 in maze5.c (gdb) info reg eax edx eax 0x49 73 edx 0x49 73
Yes... eax and edx are equal...
If we continue the execution we should reach the break again
(gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /maze/maze5 X---------------- Username: AAAAAAAV Key: BCDEFGHI Breakpoint 1, 0x08048674 in main () at maze5.c:48 48 in maze5.c (gdb) jump *0x08048694 Continuing at 0x8048694. Breakpoint 2, 0x080485c8 in foo (s=0xffffd6ef "AAAAAAAV", a=0xffffd6e6 "BCDEFGHI") at maze5.c:32 32 in maze5.c (gdb) info reg eax edx eax 0x49 73 edx 0x49 73 (gdb) c Continuing. Breakpoint 2, 0x080485c8 in foo (s=0xffffd6ef "AAAAAAAV", a=0xffffd6e6 "BCDEFGHI") at maze5.c:32 32 in maze5.c (gdb) info reg eax edx eax 0x48 72 edx 0x63 99
OK, we got past the first jump and then the code is now comparing the 7th byte of the password (H which is 0x48) with another value 0x63
Let's use the same approach: with some tries, we discover that changing 7th byte of the username to \ will give eax = edx = 0x48
(gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /maze/maze5 X---------------- Username: AAAAAA\V Key: BCDEFGHI Breakpoint 1, 0x08048674 in main () at maze5.c:48 48 in maze5.c (gdb) jump *0x08048694 Continuing at 0x8048694. Breakpoint 2, 0x080485c8 in foo (s=0xffffd6ef "AAAAAA\\V", a=0xffffd6e6 "BCDEFGHI") at maze5.c:32 32 in maze5.c (gdb) info reg eax edx eax 0x49 73 edx 0x49 73 (gdb) c Continuing. Breakpoint 2, 0x080485c8 in foo (s=0xffffd6ef "AAAAAA\\V", a=0xffffd6e6 "BCDEFGHI") at maze5.c:32 32 in maze5.c (gdb) info reg eax edx eax 0x48 72 edx 0x48 72 (gdb) c Continuing. Breakpoint 2, 0x080485c8 in foo (s=0xffffd6ef "AAAAAA\\V", a=0xffffd6e6 "BCDEFGHI") at maze5.c:32 32 in maze5.c (gdb) info reg eax edx eax 0x47 71 edx 0x62 98
We got past the second iteration and now we have to guess the 6th byte...
If we use the same approach for the 6th byte, etc.... we discover that a possibile combination of username and password are: onbdg\\V and BCDEFGHI
Note that with a different password, also username must be different: the base fact is that foo() function compares the password byte by byte with a shifted value of the correspondent byte of username.
Finally we can use the username and password to get our privileged shell
maze5@maze:~$ /maze/maze5 X---------------- Username: onbdg\\V Key: BCDEFGHI Yeh, here's your shell $ id uid=15005(maze5) gid=15005(maze5) euid=15006(maze6) groups=15005(maze5) $ cat /etc/maze_pass/maze5 cat: /etc/maze_pass/maze5: Permission denied $ cat /etc/maze_pass/maze6 e*******i
$
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.