day1
pwn
verifmt
a powerful verifier bro,that’s the Verifmt
analysis
保護は全開、glibc2.39環境、問題はソースコードが与えられている | |
% の後に数字が続くと直接エラーになるが、しかし
*を使ってオフセットを増やす必要があり、また符号なしなので前方へのリークはできない*は引数を取り出すプレースホルダであり、適切なペイロードを使えば、argsの引数を合理的に消費しつつ、% と読み取る引数の数を制限数字内に収めることができる
\0文字を注入してverifyをバイパスし、hhnで\0を上書きできる
問題は、
%*だけでパラメータをポップすると、後続のr9 r10レジスタがnilになり、formatの制限によってリークされるアドレスもnilになること。そのため%*cを使って6個の引数を消費し、最後の%pでスタック上のアドレスポインタを出力できるようにする必要がある
これに基づいて書き込み用の関数write_byte(addr,val)を作成できる: | |
最後に書き込む際は、リークしたスタックポインタを使ってリターンアドレスを指し、1バイトずつ書き込む。whileがあるので十分な書き込み機会がある
exp
なぜかローカルのdockerでは接続できない。libcはコンテナのものを取ってきているはずなのに
| |
StackPrelude
前奏曲(Prelude)、不可能なスタックオーバーフローチャレンジに備えよう
analysis
ソースコードを分析する
| |
この問題の鍵は、リモートで大量のデータをリークできる状態でもインタラクションを継続できるようにすること。ここで構築されたソケットサーバは一度に1つのリクエストしか処理できず、サーバはデータを受信した後にsendで送り返す
コンピュータネットワーク
を簡単に参照できる
1つ目のアイデアはTCPプロトコルの半閉鎖特性を利用すること。4ウェイハンドシェイクの際に最初にクライアントの半閉鎖FINパケットを送る。このとき正常に接続を閉鎖せず、サーバがMSG_WAITALLによって0を返しcfdを閉じるようにする。しかしこの条件下では入力パケットを閉じることになり、データをリークできてもインタラクションを継続できないよって却下
2つ目のアイデアは割り込み信号を利用すること。ここではOOB(OUT-OF-BAND)を使用する。sendのときにurgent byteを使うことでサーバ側でSIGURGシグナルを発生させる。これは非同期シグナルであり、recv関数を中断するが、sendは同じ長さのデータを返し、しかもインタラクションを継続できる!
インタラクションとexp
まず問題とのインタラクションのデバッグ方法を説明する
| |
上記は私のスクリプト。ローカルデバッグではまずプログラムとpwndbgを起動しioを作成し、その後新しいプロセスを開いて接続とインタラクションを行う
| |
その後、pwndbgを開いたインターフェースでインタラクションできる
デバッグはデータを送信した後、recvを開いたときに2番目のステップで以下の状況が見られる
socketはfd 4を固定のインタラクションハンドルとして使用する。シェルを開いた後に正常なターゲットとのインタラクションを行うには、dup2を使ってプログラムのインタラクションハンドルをstdout(または他のstdin、stderr)に複製する必要がある。そうしないとエコーが得られず、実行した
system("/bin/sh")は直接 | |
tips:
注意すべき点として、dockerのターゲットとインタラクションする際には、ループバックアドレスloではなく、ホストにマッピングされたdocker内部IPを使用する必要がある。理由はOOBを使用するため
dockerがポートをマッピングするとき、通常ホスト上で2つのことを行う:
- **
iptablesルールの設定**: ホストポートからのトラフィックをDockerブリッジネットワークに導く。 - **
docker-proxyプロセスの起動**:docker-proxyまたは類似のコンポーネント(userland-proxy)がホスト上で稼働し、ホストポートをリスンし、トラフィックをコンテナの内部IPとポートに転送する。 ループバックアドレスloを使うと、カーネルは宛先アドレスが127.0.0.1であることを検出し、TCPフレームに対してチェックサムなどをスキップする最適化を行う可能性がある。
データは通常のトラフィックのようにアプリケーション→トランスポート→ネットワーク→リンクを経由せず、ネットワーク層でキャプチャされる。docker-proxyはループバックトラフィックを処理する際にTCPフレームとフラグを完全に維持しないため、URGポインタを設定したOOB攻撃がTCPフレームフラグの最適化によって失敗し、recvの割り込みが発生せずデータリークに失敗する。したがってDockerのブリッジネットワークインターフェースを使わなければならない
day2
pwn
Stack_Impromptu
The world impossible is not in my dictionary
analysis
保護は全開まずソースコードを監査する
main.cpp:
| |
新しい接続cfdが生成されたとき、正常にpthread_createが呼び出され、使用後にpthread_detachで回収される
明らかにマルチスレッドのチャレンジであり、1つのスレッドでメモリの内容を直接リークすることは不可能。鍵はserver_mainとserver_readである
| |
server_readにはスタックオーバーフローがあり、繰り返し呼び出される。しかし前問とは異なり、この問題にはMSG_WAITALLがないため割り込み信号を使って情報をリークできない
pthreadマルチスレッドでは、すべてのスレッドが共通のプロセス空間内で実行され、グローバル変数、ヒープ空間、ファイル記述子を共有する。各スレッドは独立したスタック(Stack)のみを持つ!
この問題は大会中に全くアイデアが浮かばなかった。厳格な入力フォーマットとデータサイズの制限によりリークを実現する方法がわからず、AIもデータを使って他のスレッドのメモリ空間を上書きできないか試すようアドバイスするだけだった。またマルチスレッドのデバッグは簡単ではなく、主にタイミングに関するインタラクションの問題を考慮する必要がある
幸い大会後、discordで唯一解いた方のヒントを見た:
ではswap fdを具体的にどう実現するのか..?—>pthreadがあるじゃないか!
もしマルチスレッドを利用して特定のスレッドのfdを上書きし、fdをハイジャックできれば、fdをコピーして出力をリダイレクトし、最終的にメモリ情報をリークできるのではないか?
fdハイジャック
まず2つのスレッドを使って本当にfdをハイジャックできるか確認する。2つのスレッドを作成する
スレッドスタック2でスタックの底を確認すると、実際の空間は上の図の通りで、fdを上書きするのに十分なスペースがある
[rbp-0x68]のqwordポインタであり、それをさらにデリファレンスして格納されたfdを取得する。したがって実際には0x7cの0を書き込んで最後にfdを上書きするこれにより、最後のcloseでは実際にはプロセス1のfdが閉じられることになる
ブロッキングの回避
実際にはスレッド2を閉じているため、次にfdを割り当てるときもスレッド2用のfdが割り当てられ、再利用された4ではない よってリークデータを試みる前に4つのスレッドを作成する必要があり、最終的に2つのスレッドがfdを再利用する状況を作り出す
leak
ここでTCPリセット攻撃
の方法を使用する必要がある
再びfd=4を取得したが、実際には最初に作成したスレッドはまだ閉じられていない。そのため直接相手にRSTパケットを送信して強制的に接続を切断する。このとき4番目のスレッドが再びfd=4を受け取る(極短時間)。これにより実現されるのは
ROP
その後、オーバーフローを利用してROPをどのように行い、インタラクションをどのように行うか?アイデアは上記と似ている:
- スレッドを作成し、上書きを使って1つのfdを閉じる
- 再度スレッドを作成し、必要なfdと対応するスレッドの制御を取得する
- 標準入出力を介してインタラクションするため、基本的にリモートシェルを取得できる
exp
| |


















