---
# System prepended metadata

title: BambooFox Wargame Writeup
tags: [' BambooFox', NCTU]

---

---
tags: NCTU, BambooFox
robots: noindex, nofollow
lang: zh-TW
GA: UA-179809226-1
---
# BambooFox Wargame Writeup

## Flag0

[robots.txt](https://en.wikipedia.org/wiki/Robots_exclusion_standard#Examples)

```
User-agent: *
Disallow: /posts/
Disallow: /flag.*
```

可以知道有名為 `posts` 的目錄及開頭為 `flag.` 的檔案。

訪問 `/flag.html` 即可。

## Flag1

在 index 中，可以知道圖片的目錄為 `/images`，直接請求可以看到 Apache2 很貼心地把目錄下的所有檔案列出來了。

直接點 `secret.jpg` 可能看不到任何東西，可以嘗試把它 curl 下來觀察：

```shell
$ curl https://bamboofox.cs.nctu.edu.tw:15180/images/secret.jpg
BambooFox{#1_REDACTED}
```

## Flag2

在首頁閱讀文章時，文章的 id 是用 get parameter 傳進去的，而 id 只在 1~3，我們可以試著在 id 傳入其他數字，發現都會被 redirect 回前一個頁面。

**Solution 1**
回到 Flag0 得到的資訊，直接進入 `/posts` 會跟你說 `posts folder`。
試著直接請求 `/posts/1`，發現正是 id 為 1 的文章內容，可以再試著請求 id 不為 1~3 的頁面，例如：4、5...
id 為 4 時跟你說 `this is a garbage page`，而 5 以上就 404 了。

那麼 id 為 0 的頁面呢？沒錯，正是存有 flag 的那個頁面。

**Solution 2**
```
$ curl https://bamboofox.cs.nctu.edu.tw:15180/?post=0
```

## Flag3

從前一個 flag 得到的資訊可以得知此網站閱讀文章的功能是利用讀檔的方式把內文引入，這種方式如果沒處理好的話會造成 Local File Inclusion (LFI) 的漏洞。

LFI 允許他人獲取伺服器上的檔案，要驗證此方法是否可行，在 Linux 下可以 include 一些已知的檔案，例如：`/etc/passwd`, `/etc/hosts`...

在嘗試的時候由於當前目錄可能不是在根目錄，所以需要在檔案前加上 `../` 來達到目錄穿越的效果，嘗試的 payload 可能長這樣：

```
../etc/passwd
../../etc/passwd
../../../etc/passwd
```

待成功引入後，可以看到 `/etc/passwd` 的內容，而此題的 flag 也在其中。

> 註：此題的 LFI 需要透過 curl 完成

## Flag4

承上題，既然 LFI 存在，可以繼續利用此漏洞獲取 `index.php` 的 source code，這裡要小心一點：被 include 進來的 PHP 會被解析，也就是說拿不到其中的 PHP Code。我們需要借助 PHP 一些好用的功能將內文轉成不會被解析的格式，例如：base64...等。

PHP 提供了一個 Wrapper：`php://`，在他之下有一個 [php://filter](https://www.php.net/manual/en/filters.php)，提供了 base64 的功能，其用法大概如下：

```
php://filter/read=convert.base64-encode/read=file.php
php://filter/convert.base64-encode/read=file.php
```

可以利用他來獲得 base64 過後的 `index.php`，將結果 base64 decode 就可以拿到 flag 了。

## Flag5

掃一下目錄可以知道根目錄下有 `admin.php`，其內容是一個簡單的登入頁面，可以利用前面找到的 LFI 來取得 source code

`admin.php`
```php=
<?php
    session_start();

    $flag = shell_exec('cat /flag*');
    $action = @$_GET['action'];

    if ($action == 'login') {
        $username = @$_POST['username'];
        $password = @$_POST['password'];

        if (!$username) die('no username');
        if (!$password) die('no password');

        if (strcmp($password, md5($flag)) != 0)
            die('wrong password');

        $_SESSION['user'] = $username;
    }
    else if ($action == 'logout') {
        unset($_SESSION['user']);
    }
?>

<?php if (!isset($_SESSION['user'])): ?>
    <form action="?action=login" method="post">
        <b>Admin Login</b><br>
        <input type="text" name="username" value="admin"><br>
        <input type="password" name="password"><br>
        <input type="submit" value="Login">
    </form>
<?php else: ?>
    <form action="?action=logout">
        <h3>Welcome, <?= $_SESSION['user'] ?>, this admin panel is garbage.</h3>
        <b><?php $flag = shell_exec('cat /flag*'); ?></b><br>
        <input type="input" value="<?= htmlentities($flag) ?>">
        <input type="submit" value="Logout">
    </form>
<?php endif; ?>
```

要注意的大概這幾點：
- 在確認密碼的部分用了 `strcmp()`
- `$password = @$_POST['password'];`

接著是幾點 PHP 的特性：
- `@` 的用途是抑制（suppress）錯誤：假設後面的 expression 會噴錯誤，那 `@` 會讓它當作沒事一樣繼續執行下去
- PHP 可以透過 parameter 傳入陣列
- 在 function 內傳入非預期的型態會噴錯誤


把上述的結合起來，就差不多可以登入並拿到 flag 了。

POST `username=whatever&password[]=` 出去就會讓 `strcmp()` 噴錯誤，並成功在 `$_SESSION['user']` 寫入 POST 出去的 username，接著就會執行前端的 else 部分，flag 就在其中。

## Flag6

大多數的網站通常都會上版本控制，常用的工具是 git，有上 git 的專案在根目錄會有一個名為 `.git` 的資料夾，所以若是 Web Server 端的權限沒有設定好，那麼直接訪問 `/.git` 會列出所有檔案（如 Flag1）。

那既然知道他的 `/.git` 存在了，可以用工具把他 dump 下來。
dump 下來後：

```shell
$ git log # check all log
commit 953faac7d3a3474404646d0611b19a08306df466 (HEAD -> master)
Author: djosix <djosicks@gmail.com>
Date:   Sat Sep 5 07:02:59 2020 +0800

    Update

commit fe8b3e04a109b7946a709847a6d54bcbae73a471
Author: djosix <djosicks@gmail.com>
Date:   Sat Sep 5 07:01:53 2020 +0800

    Update

commit d2e7d86b4bd42975ee3c0266e44f5c075bb8a30c
Author: djosix <djosicks@gmail.com>
Date:   Sat Sep 5 07:01:27 2020 +0800

    Initial commit
    
$ git checkout d2e7d8 # revert back to commit d2e7d8
```

Flag 在 `flag.php` 裡。

## Flag7

承上題，git 有一個功能可以讓你把做到一半的東西先藏起來（stash），不要 commit 出去。

```
$ git stash list
stash@{0}: WIP on master: 953faac Update

$ git stash pop
HEAD detached at d2e7d86
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   note.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	flag.html
	robots.txt

Dropped refs/stash@{0} (109e34850c02a969abcdd108bbcc503b755e125f)
```

Flag 在 `note.txt` 裡。

## Flag8

先破個梗，這題的漏洞是 Session RCE + LFI 的組合技。
詳細的原理可以參考 Cyku 大大的 [透過 LFI 引入 PHP session 檔案觸發 RCE](https://cyku.tw/lfi-leads-to-rce-via-session-file/)。

總之這邊大致帶過這題的解題流程：

1. 透過 LFI 可以 include PHP 的 session file
2. `admin.php` 頁面會把我們 POST 出去的 username 存入 session file
3. 因為 1、2，我們可以嘗試在 username 中塞入 PHP，並透過 LFI include 當前 session 的 session file，進而達到 RCE 的目的。

## Flag9

承上題，因為已經可以 RCE 了，我們可以試著彈一個 Reverse Shell 回來。

經過一點資訊的收集後，我們可以知道以下資訊：
- 我們的身份是 `www-data`
- 這台機器上有一個叫 `djosix` 的使用者，並且他的密碼存放在其家目錄下的 `password.txt` 裡
- `djosix` 的家目錄裡有一個叫做 `secret` 的目錄，但是權限是 `rwx------`，亦即我們不能觀看

為了要知道 `secret` 內有什麼東西，我們可以用得到的使用者名稱及密碼登入看看。
Linux 下有一個指令叫 `su` 可以達到利用他人身份登入的目的，用法大概是 `su [username]`，並輸入該使用者的密碼即可。
然而在 Reverse Shell 下，你會發現無法輸入密碼（~~原因我也不知道~~），出題者的解法好像是塞另一種後門：[TSH](https://github.com/creaktive/tsh)，塞完就可以打密碼了。

而我的作法是直接 `echo [PASSWORD]` 再把他 pipe 到 `su` 裡
大概是這樣：
```shell
$ echo "PASSWORD" | su -c "whoami" djosix
echo "PASSWORD" | su -c "whoami" djosix
Password: djosix
```

接著就可以用 `djosix` 的身份做其他事情了。

