gdb
is useful for us to understand how a given binary works.
Let's see what this program does...
$> gdb ./level03
(gdb) disassemble main
Dump of assembler code for function main:
0x0804885a <+0>: push %ebp
0x0804885b <+1>: mov %esp,%ebp
0x0804885d <+3>: and $0xfffffff0,%esp
0x08048860 <+6>: sub $0x20,%esp
0x08048863 <+9>: push %eax
0x08048864 <+10>: xor %eax,%eax
0x08048866 <+12>: je 0x804886b <main+17>
0x08048868 <+14>: add $0x4,%esp
0x0804886b <+17>: pop %eax
0x0804886c <+18>: movl $0x0,(%esp)
0x08048873 <+25>: call 0x80484b0 <time@plt>
0x08048878 <+30>: mov %eax,(%esp)
0x0804887b <+33>: call 0x8048500 <srand@plt>
0x08048880 <+38>: movl $0x8048a48,(%esp)
0x08048887 <+45>: call 0x80484d0 <puts@plt>
0x0804888c <+50>: movl $0x8048a6c,(%esp)
0x08048893 <+57>: call 0x80484d0 <puts@plt>
0x08048898 <+62>: movl $0x8048a48,(%esp)
0x0804889f <+69>: call 0x80484d0 <puts@plt>
0x080488a4 <+74>: mov $0x8048a7b,%eax
0x080488a9 <+79>: mov %eax,(%esp)
0x080488ac <+82>: call 0x8048480 <printf@plt>
0x080488b1 <+87>: mov $0x8048a85,%eax
0x080488b6 <+92>: lea 0x1c(%esp),%edx
0x080488ba <+96>: mov %edx,0x4(%esp)
0x080488be <+100>: mov %eax,(%esp)
0x080488c1 <+103>: call 0x8048530 <__isoc99_scanf@plt>
0x080488c6 <+108>: mov 0x1c(%esp),%eax
0x080488ca <+112>: movl $0x1337d00d,0x4(%esp)
0x080488d2 <+120>: mov %eax,(%esp)
0x080488d5 <+123>: call 0x8048747 <test>
0x080488da <+128>: mov $0x0,%eax
0x080488df <+133>: leave
0x080488e0 <+134>: ret
End of assembler dump.
It uses
time()
,srand()
,puts()
,printf()
,scanf()
and calls a custom functiontest()
.
All we know at this point is that we input something interpreted as a number and that 322424845 is loaded a second argument of function test()
.
Let's get a closer look to the test()
function...
$> gdb ./level03
(gdb) disassemble test
Dump of assembler code for function test:
0x08048747 <+0>: push %ebp
0x08048748 <+1>: mov %esp,%ebp
0x0804874a <+3>: sub $0x28,%esp
0x0804874d <+6>: mov 0x8(%ebp),%eax
0x08048750 <+9>: mov 0xc(%ebp),%edx
0x08048753 <+12>: mov %edx,%ecx
0x08048755 <+14>: sub %eax,%ecx
0x08048757 <+16>: mov %ecx,%eax
0x08048759 <+18>: mov %eax,-0xc(%ebp)
0x0804875c <+21>: cmpl $0x15,-0xc(%ebp)
0x08048760 <+25>: ja 0x804884a <test+259>
0x08048766 <+31>: mov -0xc(%ebp),%eax
0x08048769 <+34>: shl $0x2,%eax
0x0804876c <+37>: add $0x80489f0,%eax
0x08048771 <+42>: mov (%eax),%eax
0x08048773 <+44>: jmp *%eax
0x08048775 <+46>: mov -0xc(%ebp),%eax
0x08048778 <+49>: mov %eax,(%esp)
0x0804877b <+52>: call 0x8048660 <decrypt>
0x08048780 <+57>: jmp 0x8048858 <test+273>
0x08048785 <+62>: mov -0xc(%ebp),%eax
0x08048788 <+65>: mov %eax,(%esp)
0x0804878b <+68>: call 0x8048660 <decrypt>
0x08048790 <+73>: jmp 0x8048858 <test+273>
0x08048795 <+78>: mov -0xc(%ebp),%eax
0x08048798 <+81>: mov %eax,(%esp)
0x0804879b <+84>: call 0x8048660 <decrypt>
0x080487a0 <+89>: jmp 0x8048858 <test+273>
0x080487a5 <+94>: mov -0xc(%ebp),%eax
0x080487a8 <+97>: mov %eax,(%esp)
0x080487ab <+100>: call 0x8048660 <decrypt>
0x080487b0 <+105>: jmp 0x8048858 <test+273>
0x080487b5 <+110>: mov -0xc(%ebp),%eax
0x080487b8 <+113>: mov %eax,(%esp)
0x080487bb <+116>: call 0x8048660 <decrypt>
0x080487c0 <+121>: jmp 0x8048858 <test+273>
0x080487c5 <+126>: mov -0xc(%ebp),%eax
0x080487c8 <+129>: mov %eax,(%esp)
0x080487cb <+132>: call 0x8048660 <decrypt>
0x080487d0 <+137>: jmp 0x8048858 <test+273>
0x080487d5 <+142>: mov -0xc(%ebp),%eax
0x080487d8 <+145>: mov %eax,(%esp)
0x080487db <+148>: call 0x8048660 <decrypt>
0x080487e0 <+153>: jmp 0x8048858 <test+273>
0x080487e2 <+155>: mov -0xc(%ebp),%eax
0x080487e5 <+158>: mov %eax,(%esp)
0x080487e8 <+161>: call 0x8048660 <decrypt>
0x080487ed <+166>: jmp 0x8048858 <test+273>
0x080487ef <+168>: mov -0xc(%ebp),%eax
0x080487f2 <+171>: mov %eax,(%esp)
0x080487f5 <+174>: call 0x8048660 <decrypt>
0x080487fa <+179>: jmp 0x8048858 <test+273>
0x080487fc <+181>: mov -0xc(%ebp),%eax
0x080487ff <+184>: mov %eax,(%esp)
0x08048802 <+187>: call 0x8048660 <decrypt>
0x08048807 <+192>: jmp 0x8048858 <test+273>
0x08048809 <+194>: mov -0xc(%ebp),%eax
0x0804880c <+197>: mov %eax,(%esp)
0x0804880f <+200>: call 0x8048660 <decrypt>
0x08048814 <+205>: jmp 0x8048858 <test+273>
0x08048816 <+207>: mov -0xc(%ebp),%eax
0x08048819 <+210>: mov %eax,(%esp)
0x0804881c <+213>: call 0x8048660 <decrypt>
0x08048821 <+218>: jmp 0x8048858 <test+273>
0x08048823 <+220>: mov -0xc(%ebp),%eax
0x08048826 <+223>: mov %eax,(%esp)
0x08048829 <+226>: call 0x8048660 <decrypt>
0x0804882e <+231>: jmp 0x8048858 <test+273>
0x08048830 <+233>: mov -0xc(%ebp),%eax
0x08048833 <+236>: mov %eax,(%esp)
0x08048836 <+239>: call 0x8048660 <decrypt>
0x0804883b <+244>: jmp 0x8048858 <test+273>
0x0804883d <+246>: mov -0xc(%ebp),%eax
0x08048840 <+249>: mov %eax,(%esp)
0x08048843 <+252>: call 0x8048660 <decrypt>
0x08048848 <+257>: jmp 0x8048858 <test+273>
0x0804884a <+259>: call 0x8048520 <rand@plt>
0x0804884f <+264>: mov %eax,(%esp)
0x08048852 <+267>: call 0x8048660 <decrypt>
0x08048857 <+272>: nop
0x08048858 <+273>: leave
0x08048859 <+274>: ret
End of assembler dump.
It uses
rand()
and yet another custom functiondecrypt()
.
This function substract its two arguments and evaluate the result... if it is between 1 and 9 or 16 and 21 this value is used as input to decrypt()
otherwise rand()
pick a value by itself for the same purpose.
Finally let's check on that decrypt()
thing.
$> gdb ./level03
(gdb) disassemble decrypt
Dump of assembler code for function decrypt:
0x08048660 <+0>: push %ebp
0x08048661 <+1>: mov %esp,%ebp
0x08048663 <+3>: push %edi
0x08048664 <+4>: push %esi
0x08048665 <+5>: sub $0x40,%esp
0x08048668 <+8>: mov %gs:0x14,%eax
0x0804866e <+14>: mov %eax,-0xc(%ebp)
0x08048671 <+17>: xor %eax,%eax
0x08048673 <+19>: movl $0x757c7d51,-0x1d(%ebp)
0x0804867a <+26>: movl $0x67667360,-0x19(%ebp)
0x08048681 <+33>: movl $0x7b66737e,-0x15(%ebp)
0x08048688 <+40>: movl $0x33617c7d,-0x11(%ebp)
0x0804868f <+47>: movb $0x0,-0xd(%ebp)
0x08048693 <+51>: push %eax
0x08048694 <+52>: xor %eax,%eax
0x08048696 <+54>: je 0x804869b <decrypt+59>
0x08048698 <+56>: add $0x4,%esp
0x0804869b <+59>: pop %eax
0x0804869c <+60>: lea -0x1d(%ebp),%eax
0x0804869f <+63>: movl $0xffffffff,-0x2c(%ebp)
0x080486a6 <+70>: mov %eax,%edx
0x080486a8 <+72>: mov $0x0,%eax
0x080486ad <+77>: mov -0x2c(%ebp),%ecx
0x080486b0 <+80>: mov %edx,%edi
0x080486b2 <+82>: repnz scas %es:(%edi),%al
0x080486b4 <+84>: mov %ecx,%eax
0x080486b6 <+86>: not %eax
0x080486b8 <+88>: sub $0x1,%eax
0x080486bb <+91>: mov %eax,-0x24(%ebp)
0x080486be <+94>: movl $0x0,-0x28(%ebp)
0x080486c5 <+101>: jmp 0x80486e5 <decrypt+133>
0x080486c7 <+103>: lea -0x1d(%ebp),%eax
0x080486ca <+106>: add -0x28(%ebp),%eax
0x080486cd <+109>: movzbl (%eax),%eax
0x080486d0 <+112>: mov %eax,%edx
0x080486d2 <+114>: mov 0x8(%ebp),%eax
0x080486d5 <+117>: xor %edx,%eax
0x080486d7 <+119>: mov %eax,%edx
0x080486d9 <+121>: lea -0x1d(%ebp),%eax
0x080486dc <+124>: add -0x28(%ebp),%eax
0x080486df <+127>: mov %dl,(%eax)
0x080486e1 <+129>: addl $0x1,-0x28(%ebp)
0x080486e5 <+133>: mov -0x28(%ebp),%eax
0x080486e8 <+136>: cmp -0x24(%ebp),%eax
0x080486eb <+139>: jb 0x80486c7 <decrypt+103>
0x080486ed <+141>: lea -0x1d(%ebp),%eax
0x080486f0 <+144>: mov %eax,%edx
0x080486f2 <+146>: mov $0x80489c3,%eax
0x080486f7 <+151>: mov $0x11,%ecx
0x080486fc <+156>: mov %edx,%esi
0x080486fe <+158>: mov %eax,%edi
0x08048700 <+160>: repz cmpsb %es:(%edi),%ds:(%esi)
0x08048702 <+162>: seta %dl
0x08048705 <+165>: setb %al
0x08048708 <+168>: mov %edx,%ecx
0x0804870a <+170>: sub %al,%cl
0x0804870c <+172>: mov %ecx,%eax
0x0804870e <+174>: movsbl %al,%eax
0x08048711 <+177>: test %eax,%eax
0x08048713 <+179>: jne 0x8048723 <decrypt+195>
0x08048715 <+181>: movl $0x80489d4,(%esp)
0x0804871c <+188>: call 0x80484e0 <system@plt>
0x08048721 <+193>: jmp 0x804872f <decrypt+207>
0x08048723 <+195>: movl $0x80489dc,(%esp)
0x0804872a <+202>: call 0x80484d0 <puts@plt>
0x0804872f <+207>: mov -0xc(%ebp),%esi
0x08048732 <+210>: xor %gs:0x14,%esi
0x08048739 <+217>: je 0x8048740 <decrypt+224>
0x0804873b <+219>: call 0x80484c0 <__stack_chk_fail@plt>
0x08048740 <+224>: add $0x40,%esp
0x08048743 <+227>: pop %esi
0x08048744 <+228>: pop %edi
0x08048745 <+229>: pop %ebp
0x08048746 <+230>: ret
End of assembler dump.
What this function does is, using the argument sent from test()
it XOR this value to every single character of a string and then compare it to another one. So the goal is to find which value outputs the expected string.
0x08048673 <+19>: movl $0x757c7d51,-0x1d(%ebp)
0x0804867a <+26>: movl $0x67667360,-0x19(%ebp)
0x08048681 <+33>: movl $0x7b66737e,-0x15(%ebp)
0x08048688 <+40>: movl $0x33617c7d,-0x11(%ebp)
These are hardcoded, not stored in memory somewhere, we need to reconstruct it as we did for previous level password.
$> python -c "print '757c7d51'.decode('hex')[::-1] + '67667360'.decode('hex')[::-1] + '7b66737e'.decode('hex')[::-1] + '33617c7d'.decode('hex')[::-1]"
Q}|u`sfg~sf{}|a3
The string Q}|u`sfg~sf{}|a3
is used as the base for the XOR process.
0x080486f2 <+146>: mov $0x80489c3,%eax
(gdb) x/s 0x80489c3
0x80489c3: "Congratulations!"
The string Congratulations!
is located at address 0x80489c3
. It is the target string of the XOR process.