私はシェルコードを別の方法で書くことになりました。戻る方法がわからなかったので、ユーザーランドに戻る際に、カーネルに面倒な作業を任せました。アイデアは、特権エスカレーション ビットを実行し、レジスタとスタックを修正して、脆弱な関数が戻るはずだった場所に戻ることでした。
カーネルが脆弱な関数から戻った直後 (オーバーフローしていないとき)、 gdb
経由で何かに気づきました . (アドレスは想像上のものですが、とにかく概念を説明しています。)
(gdb) x/i $eip
0xadd1: ret
(gdb) x/xw $esp
0xadd1: 0xadd2
(gdb) x/6i 0xadd2
0xadd2: add esp,0x40
0xadd3: pop ...
0xadd4: pop ...
0xadd5: pop ...
0xadd6: pop ...
0xadd7: ret
したがって、リターンの直後、スタックの 0x40 バイトは使用されていません add esp
で単純に消えます 命令。したがって、この事実を利用して、24 バイトの長さの ROP チェーンを構築しました (この質問を書いている間に既に構築していました。その仕事は、SMEP を無効にして、ユーザーランドのシェルコードにジャンプすることでした)。リターン時に 4 バイトが EIP を上書きし、残りの 20 (0x14
) は、未使用のスタックに続きます。これで 0x2c
が残ります まだ未使用のスタックのバイト。
しかし、もし 0xadd2
に戻ったら 、さらに 0x14
を失うリスクがあります スタックから数バイトの貴重なレジスタ情報を取得し、無効なデータでレジスタをいっぱいにします。 add 0x2c
できました esp
まで ユーザーランドのシェルコードで、直接 0xadd3
にジャンプします 、実際の add
をスキップします
また、デバッグセッションから、eax
を除くすべてに注意してください と ebx
正常に復元されていました。私のオーバーフローはそれらの両方を破壊したので、関数がクリーンなリターンを行った場合と同様の値でそれらを復元する必要がありました. (これを行うのは簡単でした:0xadd2
にブレークポイントを設定しました 、および info reg
から値を抽出しました )
したがって、私の最終的なユーザーランド シェルコードは次のとおりです。
Execute privesc payload -> add esp,0x2c -> register fixup -> jump to 0xadd3
これを行うと、コード パスは完全にクリーンに戻り、カーネルはユーザー モードに戻るタスクを実行しました。