pony686
- 关注
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
一、Apache Shiro介绍
Apache Shiro 是一个功能强大且易于使用的 Java 安全框架,它用于处理身份验证,授权,加密和会话管理
在默认情况下 , Apache Shiro 使用 CookieRememberMeManager 对用户身份进行序列化/反序列化 , 加密/解密和编码/解码 , 以供以后检索 .
因此 , 当 Apache Shiro 接收到未经身份验证的用户请求时 , 会执行以下操作来寻找他们被记住的身份.
从请求数据包中提取 Cookie 中 rememberMe 字段的值,对提取的 Cookie 值进行 Base64 解码,对 Base64 解码后的值进行 AES 解密,对解密后的字节数组调用 ObjectInputStream.readObject() 方法来反序列化.
但是默认AES加密密钥是 “硬编码” 在代码中的 . 因此 , 如果服务端采用默认加密密钥 , 那么攻击者就可以构造一个恶意对象 , 并对其进行序列化 , AES加密 , Base64编码 , 将其作为 Cookie 中 rememberMe 字段值发送 . Apache Shiro 在接收到请求时会反序列化恶意对象 , 从而执行攻击者指定的任意代码 .
Shiro 550 反序列化漏洞存在版本:shiro <1.2.4,产生原因是因为shiro接受了Cookie里面rememberMe的值,然后去进行Base64解密后,再使用aes密钥解密后的数据,进行反序列化。
构造该值为一个利用链序列化后的值进行该密钥aes加密后进行base64加密,反序列化payload内容后就可以命令执行。
二、漏洞分析
环境下载 https://github.com/yyhuni/shiroMemshell/tree/master/shirodemo
测试环境 apache-tomcat-8.5.50 jdk 1.8 202 shiro 1.2.24
下载到本地用idea打开
账号 root 密码 secret
设置好 访问
登录之后抓包 成功后 访问网站发现 在cookie多了一个 rememberMe 字段
在 org/apache/shiro/web/mgt/CookieRememberMeManager.java 下设置一个断点 调试打开网站
protected byte[] getRememberedSerializedIdentity(SubjectContext subjectContext) {
if (!WebUtils.isHttp(subjectContext)) {
if (log.isDebugEnabled()) {
String msg = "SubjectContext argument is not an HTTP-aware instance. This is required to obtain a " +
"servlet request and response in order to retrieve the rememberMe cookie. Returning " +
"immediately and ignoring rememberMe operation.";
log.debug(msg);
}
return null;
}
WebSubjectContext wsc = (WebSubjectContext) subjectContext;
if (isIdentityRemoved(wsc)) {
return null;
}
HttpServletRequest request = WebUtils.getHttpRequest(wsc);
HttpServletResponse response = WebUtils.getHttpResponse(wsc);
String base64 = getCookie().readValue(request, response);
// Browsers do not always remove cookies immediately (SHIRO-183)
// ignore cookies that are scheduled for removal
if (Cookie.DELETED_COOKIE_VALUE.equals(base64)) return null;
if (base64 != null) {
base64 = ensurePadding(base64);
if (log.isTraceEnabled()) {
log.trace("Acquired Base64 encoded identity [" + base64 + "]");
}
byte[] decoded = Base64.decode(base64);
if (log.isTraceEnabled()) {
log.trace("Base64 decoded byte array length: " + (decoded != null ? decoded.length : 0) + " bytes.");
}
return decoded;
} else {
//no cookie set - new site visitor?
return null;
}
}
提交包 把cookie session那个去掉 让程序读取 rememberMe这段
GET /shirodemo_war/ HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/112.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://localhost:8081/shirodemo_war/login.jsp
Connection: close
Cookie: rememberMe=ka1Ha+W9LDXInz0tXOyTFrFfm/M/SbsiUPeYxQGPs+IqjiGn4T9DBb4qLiZSvbLfOB9zkqIPfRKMcuWt4OvwymzOKFJkhmebyUJ7vVvttHo201UXGyjYxuYkdgX1JDTDgKOINOyb1K/soto/Rft8PON7POeVfj7EzL5Q7Ji/FIXxBvhetg4VfBs5jZW+ZKwo1I9VrYte71He1gi39pIOEzIHq78xfuIF1m3Iju8R6oKn47cpYnxGLa61xf4DE6sftCR7b0yGFqQU+hWmfXd+qxoacLYfo8MMwV8B+MIsStyGK7yGqvNwRO7l44KVH890gtS1BO84zX9vIji2JmJzWEtwpZ1kxQXqU2fx++5qwnfWovlW7wrEly6H1qCl93C1Udf4b6QWlctFvj7xDOTNw558WfSKB3Oe+N2bSSf/MtuIrbvAK+Uezl3wXnKmX94nEzJn8FsX7UZANqC8OOIqf54vL2b+BZcnnkTDkfLkNNeVstWNWt6LrwxiVCDeHhLD; Hm_lvt_42e5492fd27f48fc8becc94219516005=1678517471; _ga_WNLDH1S58P=GS1.1.1678517472.1.0.1678517484.0.0.0; _ga=GA1.1.1743648385.1678517472; __bid_n=1876a8763dc4f81b144207; Hm_lvt_1040d081eea13b44d84a4af639640d51=1681119471; FPTOKEN=CBYwY1vqboLVYjyDBqLlG6mLeS2dp0pfN+q78Dahs8zM1hQU2X8EUH0vILRgbn5C5a2G9rJOFQnpwtT+WKRF1ZweRsDFtkEc+i5KZI9DyVZpwV2Elnfrlh3ALJloXfEVtPtpJM6hhuzHlK4NY9J+jsZrMot2o5vOc6dSiKPacVyNFzIz+vvto3NgvXd/dtOXEr7cgjeul0qJM02VGtpmVtckCuIdB3Rmz/s2cj84LBRhLpP4WkiFTrdaE23Grdu84DwcziDuOWk+4iDehqlRZZQYYPghRzuGCxPeO/19d34T35r/Cok028cVe4sKLxtGvw/oUdzPKCzFABTtTk4HBt/QRviZS45E+TKD8DUgAYOd12SezamFFBLh6tW1kPshshu5hqwDFz5ZuyKakyt61A==|3uAMcHU7rpdn+Lq3u6Oy9wzSKQK2ztvNVV5V2pM18R4=|10|06b25955e8fbbbd80dff787def184fbd
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
判断传入的cookie是否为空
byte[] decoded = Base64.decode(base64); 解码
执行返回到 org/apache/shiro/mgt/AbstractRememberMeManager.java
public PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext) {
PrincipalCollection principals = null;
try {
byte[] bytes = getRememberedSerializedIdentity(subjectContext);
//SHIRO-138 - only call convertBytesToPrincipals if bytes exist:
if (bytes != null && bytes.length > 0) {
principals = convertBytesToPrincipals(bytes, subjectContext);
}
} catch (RuntimeException re) {
principals = onRememberedPrincipalFailure(re, subjectContext);
}
return principals;
}
判断 bytes != null && bytes.length > 0 进入 convertBytesToPrincipals
convertBytesToPrincipals 解密 处理
protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext) {
if (getCipherService() != null) {
bytes = decrypt(bytes);
}
return deserialize(bytes);
}
可以看到 在getCipherService() 存在一些 加密信息 modename cbc
解密函数 encrypted解密内容 getDecryptionCipherKey() 这个是获取加密的参数 也就是硬编码
protected byte[] decrypt(byte[] encrypted) {
byte[] serialized = encrypted;
CipherService cipherService = getCipherService();
if (cipherService != null) {
ByteSource byteSource = cipherService.decrypt(encrypted, getDecryptionCipherKey());
serialized = byteSource.getBytes();
}
return serialized;
}
跟进 getDecryptionCipherKey() 发现在 org/apache/shiro/mgt/AbstractRememberMeManager.java
解密完成后返回 serialized
完成解密后被 deserialize调用
畅读付费文章
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
