RASP技术相关内容DEMO实现

RaspAgent

public class RaspAgent {


    public static void premain(String agentArgs, Instrumentation inst) throws UnmodifiableClassException, IOException {
        readVersion();
        inst.addTransformer(new DefineTransformer(), true);
    }

    public static void agentmain(String agentArgs, Instrumentation inst) throws UnmodifiableClassException, IOException {
        readVersion();
        inst.addTransformer(new DefineTransformer(), true);
        Class<?>[] classes = inst.getAllLoadedClasses();
        for (Class<?> cls : classes){
            if(cls.getName().equals(agentArgs)){
                inst.retransformClasses(cls);
            }
       }
    }

    public static void readVersion() throws IOException {
        Class clazz = RaspAgent.class;
        String className = clazz.getSimpleName() + ".class";
        String classPath = clazz.getResource(className).toString();
        String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1)  + "/META-INF/MANIFEST.MF";
        Manifest manifest = new Manifest(new URL(manifestPath).openStream());
        Attributes attr = manifest.getMainAttributes();
        String projectVersion = attr.getValue("Project-Version");
        String buildTime = attr.getValue("Build-Time");
        System.out.print("---- xxx-RASP ---- \n" +
                "--- version:" + projectVersion + "---\n" +
                "-- buildTime: "+ buildTime + "--\n");
    }

    static class DefineTransformer implements ClassFileTransformer{

        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            Set<Class> hooks = AnnotationScanner.getClassWithAnnotation("com.zte.rasp", HookAnnotation.class);
            for(Class hook: hooks){
                try {
                    Object object = hook.newInstance();
                    if (object instanceof AbstractClassHook) {
                        AbstractClassHook item =(AbstractClassHook) object;
                        if(item.isClassMatched(className)){
                            CtClass clazz = null;
                            try {
                                final ClassPool classPool = ClassPool.getDefault();
                                clazz = classPool.get(className.replace("/","."));
                                return item.hookMethod(clazz, className);
                            } catch (NotFoundException e) {
                                e.printStackTrace();
                            } catch (CannotCompileException e) {
                                e.printStackTrace();
                            } catch (IOException e) {
                                e.printStackTrace();
                            } catch (Throwable e) {
                                e.printStackTrace();
                            }
                        }
                    }
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

            }
            return classfileBuffer;
        }
    }
}

hook 流程

public class PreMainTraceAgent {

    public static void premain(String agentArgs, Instrumentation inst) throws UnmodifiableClassException {
        System.out.println("agentArgs : " + agentArgs);
        inst.addTransformer(new DefineTransformer(), true);
    }

    public static void agentmain(String agentArgs, Instrumentation inst) {
        System.out.println("这是agentmain");
        inst.addTransformer(new DefineTransformer(), true);
    }

    static class DefineTransformer implements ClassFileTransformer{

        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            if(SQLPreparedStatementHook.isClassMatched(className)){
                CtClass clazz = null;
                try {
                    final ClassPool classPool = ClassPool.getDefault();
                    clazz = classPool.get(className.replace("/","."));
                    return SQLPreparedStatementHook.hookMethod(clazz, className);
                } catch (NotFoundException e) {
                    e.printStackTrace();
                } catch (CannotCompileException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            }
            if(XXEHook.isClassMatched(className)){
                CtClass clazz = null;
                try {
                    final ClassPool classPool = ClassPool.getDefault();
                    clazz = classPool.get(className.replace("/","."));
                    return XXEHook.hookMethod(clazz, className);
                } catch (NotFoundException e) {
                    e.printStackTrace();
                } catch (CannotCompileException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return classfileBuffer;
        }
    }
}

Attach main文件

import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;

import java.util.List;

public class AttachMain {
    public static void main(String[] args) throws Exception {
//        VirtualMachine vm = VirtualMachine.attach(args[0]);
        //System.out.print(args[0]);
        List<VirtualMachineDescriptor> list = VirtualMachine.list();
        String pid = "";
        for (VirtualMachineDescriptor vmd : list)
        {
            if(vmd.displayName().equals(args[0])){
                pid = vmd.id();
                break;
            }
        }
        VirtualMachine vm = VirtualMachine.attach(pid);
        try {
            vm.loadAgent(args[1], args[0]);
        } finally {
            vm.detach();
        }
    }
}

Annotation代码

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HookAnnotation {
}

Annotation Scanner代码

import java.io.File;
import java.io.FileFilter;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @description: 扫描特定包名下的含有特定的注解的类
 * @author: anyang
 * @create: 2018/08/29 21:04
 */
public class AnnotationScanner {

    public static Set<Class> getClassWithAnnotation(String packageName, Class annotationClass) {
        Set<Class> classes = new LinkedHashSet<Class>();
        Set<Class> res = new LinkedHashSet<Class>();
        String newPackageName = packageName.replace('.', '/');
        Enumeration<URL> urls;
        try {
            urls = AnnotationScanner.class.getClassLoader().getResources(newPackageName);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                String protocol = url.getProtocol();
                if ("file".equals(protocol)) {
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    findAndAddClass(packageName, filePath, classes);
                } else if ("jar".equals(protocol)) {
                    JarFile jar = ((JarURLConnection) url.openConnection()).getJarFile();
                    if (jar != null) {
                        Enumeration<JarEntry> entries = jar.entries();
                        while (entries.hasMoreElements()) {
                            JarEntry entry = entries.nextElement();
                            String name = entry.getName();
                            if (name.startsWith("/")) {
                                name = name.substring(1);
                            }
                            if (name.startsWith(newPackageName)) {
                                int index = name.lastIndexOf('/');
                                if ((index != -1) && name.endsWith(".class") && !entry.isDirectory()) {
                                    packageName = name.substring(0, index).replace('/', '.');
                                    String className = name.substring(packageName.length() + 1, name.length() - 6);
                                    classes.add(Class.forName(packageName + '.' + className));
                                }
                            }
                        }
                    }

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        for (Class clazz : classes) {
            if (clazz.getAnnotation(annotationClass) != null) {
                res.add(clazz);
            }
        }
        return res;

    }

    private static void findAndAddClass(String packageName, String packagePath, Set<Class> classes) {
        File baseDir = new File(packagePath);
        if (!baseDir.exists() || !baseDir.isDirectory()) {
            return;
        }
        File[] fileList = baseDir.listFiles(new FileFilter() {
            public boolean accept(File file) {
                return (file.isDirectory()) || (file.getName().endsWith(".class"));
            }
        });
        if (fileList != null && fileList.length > 0) {
            for (File file : fileList) {
                if (file.isDirectory()) {
                    findAndAddClass(packageName + "." + file.getName(), file.getAbsolutePath(), classes);
                } else {
                    String className = file.getName().substring(0,
                            file.getName().length() - 6);
                    try {
                        classes.add(AnnotationScanner.class.getClassLoader().loadClass(packageName + '.' + className));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

抽象hook类

public abstract class AbstractClassHook {

    public abstract Boolean isClassMatched(String className);

    public abstract byte[] hookMethod(CtClass clazz, String className) throws CannotCompileException, NotFoundException, IOException;

}

检测sql注入的具体hook类

@HookAnnotation
public class SQLPreparedStatementHook extends AbstractClassHook {

    private String className;

    //类匹配函数
    public Boolean isClassMatched(String className){
        if(className.equals("org/hsqldb/jdbc/JDBCStatement")||
            className.equals("com/alibaba/druid/pool/DruidPooledPreparedStatement")
        ){
            System.out.print("加载到SQL-prepared-JDBC类");
            return true;
        }
        return false;
    }

    //字节码修改方法注入
    public byte[] hookMethod(CtClass clazz, String className) throws CannotCompileException, NotFoundException, IOException {
        clazz.addMethod(CtMethod.make(getIsValidSqlOrigCode(), clazz));
        String originSql = getOriginalSql(className);
        String hookCode = originSql + "if(isValSql(sql)==1){System.out.print(\"有效sql\");}else{System.out.print(\"可能有sql注入\");}";
        CtMethod ctMethod_query = clazz.getDeclaredMethod("executeQuery");
        CtMethod ctMethod_execute = clazz.getDeclaredMethod("execute");
        CtMethod ctMethod_update = clazz.getDeclaredMethod("executeUpdate");
        ctMethod_query.insertBefore(hookCode);
        ctMethod_execute.insertBefore(hookCode);
        ctMethod_update.insertBefore(hookCode);
        byte[] byteCode = clazz.toBytecode();
        clazz.detach();
        return byteCode;
    }

    //获取各类数据库的执行sql参数
    public static String getOriginalSql(String className){
        String originalSql = "";
        if (className.equals("org/hsqldb/jdbc/JDBCStatement")){
            originalSql = "String sql = $1;";
        }
        return originalSql;
    }

    public static String getIsValidSqlOrigCode(){
        String hookCode = "public static int isValSql(String sql){\n" +
                "                       System.out.println(sql);" +
                "                       if(sql.contains(\" or \")){" +
                "                             String[] sql_list = sql.split(\" or \");" +
                "                             String sqlChar = sql_list[1];" +
                "                             String[] sql_char_list = sqlChar.split(\"=\");" +
                "                             String sqlChar0 = sql_char_list[0].replace(\" \",\"\");" +
                "                             String sqlChar1 = sql_char_list[1].replace(\" \",\"\").replace(\";\",\"\");" +
                "                             if(sqlChar0.equals(sqlChar1)){" +
                "                                   System.out.println(\"同义反复\");" +
                "                                   return 0; } " +
                "                                }" +
                "                        for(int i=0; i<sql.length()-1; i++){ " +
                "                            if(sql.charAt(i)==\';\'){" +
                "                                  System.out.println(\"联合查询\");" +
                "                                  return 0;}" +
                "                            if(sql.charAt(i)==\'0\' && sql.charAt(i+1)==\'x\'){" +
                "                                  System.out.println(\"检测到16进制值\");" +
                "                                  return 0;}" +
                "                          }"+
                "                        try {\n" +
                "                            net.sf.jsqlparser.parser.CCJSqlParserUtil.parseStatements(sql);\n" +
                "                            return 1;\n" +
                "                        } catch (net.sf.jsqlparser.JSQLParserException e) {\n" +
                "                            System.out.println(\"SQL语法解析错误\");" +
                "                            return 0;\n" +
                "                        } catch (Throwable t) {\n" +
                "                            t.printStackTrace();\n" +
                "                            return 0;\n" +
                "                        }\n" +
                "                    }";
        return hookCode;
    }
}

依赖:

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

相关内容:
JSqlParser是一个SQL语句解析器。它将SQL转换为Java类的可遍历层次结构。
支持Oracle,SqlServer,MySQL,PostgreSQL等常用数据库。但各种数据库系统的SQL语法都在动态变化,可以解析某些(不是全部)。

        <dependency>
            <groupId>com.github.jsqlparser</groupId>
            <artifactId>jsqlparser</artifactId>
            <version>3.2</version>
        </dependency>
        
Select select = (Select) CCJSqlParserUtil.parse("SELECT username,age,sex FROM user or 1=1");
SelectBody selectBody = select.getSelectBody();
System.out.print(select);
System.err.println(selectBody);

sql注入案例语句:

select department from employees where first_name = 'Bob'
select department from employees where first_name = 'Bob' and;
select department from employees where first_name = 'Bob' or 1 = 1;
select department from employees where first_name = 'Bob'; select * from employees;
select department from employees where first_name = '0x'
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值