SECCON Beginners CTF 2021 初心者writeup
CTFにはwriteupというものを書く文化があるみたいなので書きましたが力尽きました。
welcome合わせて16問で88位/943位。
解けたやつ
welcome
simple_RSA
何もわからないので「RSA ctf decrypt」ググるとこのStackOverFlowの質問が出てくる。
p,n,uncipherが分かっているとRsaCtfToolというので解けるみたい。
python3 ./RsaCtfTool.py -n 略 -e 3 --uncipher 略
- 日本語の記事があまりなくて使うのに躊躇した。(仮想環境で解いてたのでインストール)
- 他人のwrite upを見てるとgmpy2というので解いてる。
Logical_SEESAW
とりあえず与えられたスクリプトのFLAG部分を0b11110000に変えて試すと以下の対応になることがわかる。
確実に1が出力される桁 | 元の桁は1 |
確実に0が出力される桁 | 元の桁は0 |
0と1が不確定に出力される桁 | 元の桁は1 |
くっつけて終了
from Crypto.Util.number import * from collections import Counter cipher = [] #略 ans=['0','b'] for i in range(287): print(i) counter=Counter() counter.update([cip[i] for cip in cipher]) print(counter) if len(counter)==1: if counter['1']==16: ans.append('1') else: ans.append('0') else: ans.append('1') x=int(''.join(ans),2) print(long_to_bytes(x))
GFM
謎の拡張子が出てきたのでググるとsageMath というものらしい。
ソースを読むとフラグが仕込まれた行列Mが他の行列keyでかけられている(key*M*key)ので両側から逆行列をかけることを考える。
p=331941721759386740446055265418196301559 key = Matrix(GF(p),[[]])#略 enc = Matrix(GF(p),[[]])#略 SIZE=8 key_inv=key^(-1) ans=key_inv*enc*key_inv for i in range(SIZE): for j in range(SIZE): n = i * SIZE + j print(ans[i,j])
で出力される数字を文字になおして終了。
- GFM(atrix)
imaginary
何もわからないのでこことかここを見た。
AESのECBモードはブロック単位で並べ替えても復号には成功するという話だと思われる。
うまいことサーバー側で
{"hoge + hogei":[hoge, hoge], "1337i":[hoge, hoge]}
みたいな文字列に復号(化)される暗号文を作る。
|------128bit平文-------|1337i":[hoge, hoge] ...適当な複素数...hoge] "|------128bit平文-------|
みたいな二文を暗号化してくっつける。
- うまいことブロック長を合わせるとこがミソのはず。
- 選択平文攻撃(CPA)をやってるんだと思ったけど鍵自体は求めてないので違う...?
only_read
objdump -d chall.out
でcmpが連続してる所が怪しかったので文字に直したらフラグ出た。
children
ps fで見てpidをコピペ。
複数回増えた場合、pidの大きい方入れたら出来た。
- 子プロセスの追加の子プロセスの区別つけかたがわからない。
weresolf
開発者ツールでhtmlに以下を追加してsubmit
<input class="input" type="text" name="_Player__role" value="WEREWOLF">
json
まず、ヘッダー:X-Forwarded-Forを192.168.11.*に設定してリクエスト元を偽る。(このアドオンを入れた。)
後は以下実行
xhr = new XMLHttpRequest(); xhr.open("POST", "/"); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onload = () => { if (xhr.status === 200) { console.log( '<article class="message is-success"><div class="message-header"><p>Success</p></div><div class="message-body">' + JSON.parse(xhr.response).result + "</div></article>"); } else { console.log( '<article class="message is-danger"><div class="message-header"><p>Error</p></div><div class="message-body">' + JSON.parse(xhr.response).error + "</div></article>"); } }; data = JSON.stringify({ id: 2, ID:1 }); xhr.send(data);
ソースコード見てJsonキーの小文字、大文字の区別が聞かれた気がしたけどdata = "{\"id\":2,\"id\":1}"でも行けるらしい。
cant_use_db
データベースの完全性とか基本情報で見たなと懐かしい気持ちになった。
複数ブラウザ立ち上げてーセッションIDを一緒にしてーとかやったが、普通にクリックで行けた。
git-leak
git reflogとgit resetで手動線形探索
Mail_Address_validator
確か同じ文字で正規表現やると遅くなるんだよな...で行けた。
depixelization
import cv2 import numpy as np # flag = list("cpaw")+[chr(x) for x in range(ord('a'),ord('z')+1)]+[chr(x) for x in range(ord('A'),ord('Z')+1)]+[str(x) for x in range(1,10))] def spaced(s): if len(s)<25: s=s+"x"*25 s=s[:25] return s def cpawed(s): return 'cpaw{'+s+"}" letters=[chr(x) for x in range(ord('a'),ord('z')+1)]+[chr(x) for x in range(ord('A'),ord('Z')+1)]+[str(x) for x in range(1,10)]+['_','!','?'] flag_img=cv2.imread('output.png') ans=[] #31 for i in range(5,30): mi=None diff_min=10000000000000000000000000000000000000000000000 candidate=None for let in letters: ans_tmp=ans[:] ans_tmp.append(let) #print('ans_tmp:',ans_tmp) #print('"".join(ans_tmp):',''.join(ans_tmp)) #print('spaced("".join(ans_tmp)):',spaced(''.join(ans_tmp))) flag=cpawed(spaced(''.join(ans_tmp))) print("FLAG",flag) # char2img images = np.full((100, 85, 3), (255,255,255), dtype=np.uint8) for c in flag: img = np.full((100, 85, 3), (255,255,255), dtype=np.uint8) cv2.putText(img, c, (0, 80), cv2.FONT_HERSHEY_PLAIN, 8, (0, 0, 0), 5, cv2.LINE_AA) # pixelization cv2.putText(img, "P", (0, 90), cv2.FONT_HERSHEY_PLAIN, 7, (0, 0, 0), 5, cv2.LINE_AA) cv2.putText(img, "I", (0, 90), cv2.FONT_HERSHEY_PLAIN, 8, (0, 0, 0), 5, cv2.LINE_AA) cv2.putText(img, "X", (0, 90), cv2.FONT_HERSHEY_PLAIN, 9, (0, 0, 0), 5, cv2.LINE_AA) simg = cv2.resize(img, None, fx=0.1, fy=0.1, interpolation=cv2.INTER_NEAREST) # WTF :-o img = cv2.resize(simg, img.shape[:2][::-1], interpolation=cv2.INTER_NEAREST) # concat if images.all(): images = img else: images = cv2.hconcat([images, img]) ## complete image im_diff=flag_img.astype(int)-images.astype(int) im_diff_abs=np.abs(im_diff) diff_size=len(list(zip(*np.where(im_diff_abs>100)))) if diff_min>diff_size: print('update!') print(f'diff_min:{diff_min}->{diff_size}') print(f'candidate:{candidate}->{let}') diff_min=diff_size candidate=let ans.append(candidate) print(i,'no moji ha "'+candidate+'"')
解けなかったやつやる(随時追記
please_not_trace_me
そのまま実行すると"flag decrypted. bye."と出る。ghidraで逆コンパイルする。
ここを参考にbreakpointを入れる。
(gdb) b main (gdb) run (gdb) i proc mapping process 3176 Mapped address spaces: Start Addr End Addr Size Offset objfile 0x555555554000 0x555555555000 0x1000 0x0 (gdb) x/16xb 0x555555554000 #確認のため 0x555555554000: 0x7f 0x45 0x4c 0x46 0x02 0x01 0x01 0x00 #ここがelfのヘッダーになってる (gdb) b *0x55555555526c (gdb) n
mainの逆コンパイルしたものを見ると以下で良い事がわかる。
適宜jumpで飛んでいく。
(gdb) b main (gdb) run #break point張る (gdb) b *0x555555555494 (gdb) b *0x555555555270 (gdb) b *0x555555555289 (gdb) b *0x55555555564e (gdb) b *0x55555555526e (gdb) n Single stepping until exit from function main, which has no line number information. Breakpoint 6, 0x000055555555526e in main () (gdb) jump *0x555555555494 Continuing at 0x555555555494. Breakpoint 2, 0x0000555555555494 in main () (gdb) n Single stepping until exit from function main, which has no line number information. Breakpoint 6, 0x000055555555526e in main () (gdb) jump *0x555555555270 Continuing at 0x555555555270. Breakpoint 3, 0x0000555555555270 in main () (gdb) n Single stepping until exit from function main, which has no line number information. Breakpoint 6, 0x000055555555526e in main () (gdb) jump *0x555555555289 Continuing at 0x555555555289. Breakpoint 4, 0x0000555555555289 in main () (gdb) s Single stepping until exit from function main, which has no line number information. Breakpoint 5, 0x000055555555564e in rc4 () (gdb) s Single stepping until exit from function rc4, which has no line number information. __GI___libc_malloc (bytes=27) at malloc.c:3023 3023 malloc.c: そのようなファイルやディレクトリはありません. (gdb) fin Run till exit from #0 __GI___libc_malloc (bytes=27) at malloc.c:3023 0x0000555555555653 in rc4 () Value returned is $1 = (void *) 0x5555555592c0 (gdb) fin Run till exit from #0 0x0000555555555653 in rc4 () 0x000055555555529c in main () (gdb) x/27bc ((char [])*0x5555555592c0) 0x5555555592c0: 99 'c' 116 't' 102 'f' 52 '4' 98 'b' 123 '{' 100 'd' 49 '1' 0x5555555592c8: 100 'd' 95 '_' 121 'y' 48 '0' 117 'u' 95 '_' 100 'd' 51 '3' 0x5555555592d0: 99 'c' 114 'r' 121 'y' 112 'p' 116 't' 95 '_' 114 'r' 99 'c' 0x5555555592d8: 52 '4' 63 '?' 125 '}'
- ghidraはJVMがjava11じゃないと変なエラーが出る。(11以降の16とかでもだめだった)