EnemyBot简单分析
○ 简介
○ 样本粗略分析
○ hide.c
○ enemy.c
◇ 初始化部分
◇ 命令架构processCmd
◇ Util_性能比较
◇ 代码来源分析
○ 编译调试
◇ 文件方面
◇ 进程方面
◇ 网络方面
◇ 代码逻辑混乱
○ 声明
关注 天虞实验室 官方微信公众号,发送 “EnemyBot” 获得本文隐藏内容~
简介
NO.1
Enemy的设计思路沿用的是Qbot的扫描器及其编程结构,融合了Mirai的扫描器和Zbot的一个可扩展的僵尸网络病毒。
其传播思路除了Telnet,也包括ssh爆破。支持一定程度上的驻留手段。
在CNC网络的运营方面,enemy采用了Tor网络,也是可能是未来的主流。
公布在网上可能都是粗糙的框架,投入实战的可能才是相关人员值得深入研究的。
因此,相关人员可以关注enemy的变种,看看其往哪个方面变形了。
样本粗略分析
NO.2
直接拖到IDA里面进行分析。
测试环境:gdbserver in Ubuntu 22.04 + IDA in Windows 10
首先给虚拟机加快照。
首先是它启动显示打印一个假装的 core dumped ,也许是想让人误以为是出错了
然后就是简单的反调试,取父进程的uid之后得到执行路径,然后检查是否等于 gdb 、 lldb 、strace 、 ltrace 。
其实这里面相关人员用的是gdbserver,如果在第24行里面用strstr来判断更加合理,因为除了用gdb调试外,gdbserver也是可以调试的
反调试执行完之后,在系统里面打印一个被KEKSEC运营商黑进去的提示。
system("echo ENEMEYBOT V3.1-ALCAPONE hail KEKSEC > /tmp/.pwned");
打印完之后,它会进入starup函数里面尝试写自启。分别在 /etc/rc.local 和 /etc/crontab 里面写入启动目录。在打开这个目录的时候,它会先在全局变量decode里面解压字符串,然后分别得到这两个文件路径。
因为我的电脑没有 /etc/rc.local ,因此样本只在我的电脑里面的 /etc/crontab 写入成功了。
运行完stratup之后,就是开始感染文件了,首先其先通过环境变量USER获取当前用户名,然后用getcwd获取当前工作路径。
中间有一个stat函数获取文件状态,不是很清楚为什么在第二个参数当中没有任何引用。
感染文件从for循环里面的find_target里面开始。
比较奇怪的是,原来的样本是递交了一个二级指针。
hide.c
NO.3
字符串解密算法性能——这个是一个简单的映射密码模块,总的来说性能是比较优越的,是以空间换时间的一个典型密码,这里面的映射表可以随时更换,在迭代过程中是比较方便的。
enemy.c
NO.4
初始化部分
● 单一实例——这里面的执行思路跟相关人员一样,也是使用套接字来实现,但是是创建一个本地套接字实现的。实现上,比相关人员更好。
● 随机种子
● 清空启动参数
● 随机化进程名
● 切入后台为服务
● 父进程退出,子进程继续
● 写入启动列表尝试自启动——在这边是rc.local
● 关闭看门狗——沿用了Mirai相关的
● 获取本地地址——沿用了Mirai相关的
● coil_xywz
□ REBIND——TELNET、ssh、http、1~65535 rebind
□ 获取文件执行路径——正常情况下二进制文件会被删掉,因此从/proc/$pid/exe路径中获取真实路径
□ 子进程主循环
□ 尝试打开/proc/路径
□ 读取当前路径下的所有pid文件夹
□ 获取exe_path和status_path
□ 删除不存在落地文件的进程——排除当前进程和父进程删除已知bot端的进程——
□ d8ds(exe_path)
□读取当前进程是否存在含有已知bot端的特殊字段
□ 清空查找的特殊字段
□ 有的话返回1
□ 删除含有特殊字段的进程
□ 销毁检查过的exe_path和status_path
● 主循环
□ 连接成功后
□返回bot端架构——在编译的时候就可以确定了
□ 有子进程退出后更新子进程列表——在实现上用一个数组来记录所有子进程的pid,有一个退出,就重新malloc+1个,舍弃掉之前的那个。
□ 获取参数——这当中用到了strtok函数,用法可以学习一下
□ 执行参数
□system()直接执行
□ processCmd——核心执行指令。
□ 连接不成功
□继续连接——while循环里面的_time是没有增加的。也就是说是一个无限循环的。
单一实例
里面有一个sun_path,查了一下。
原文是这么说的,会自定义一个socket套接字命名空间,前提是调用者必须有相关的写权限,而创建的文件也必须由调用者删除,而AF_UNIX类型的套接字可以被unlink()删除。
In the UNIX domain,a connection is composed of (usually) one or two path names.【Linux万物皆文件】
进程间通信的一种方式是使用UNIX套接字,人们在使用这种方式时往往用的不是网络套接字,而是一种称为本地套接字的方式。这样做可以避免为黑客留下后门。不同于网络套接字的绑定,本地套接字的绑定的是struct sockaddr_un结构。
struct sockaddr_un结构有两个参数:un_family、sun_path。
sun_family只能是AF_LOCAL或AF_UNIX,而sun_path是本地文件的路径。通常将文件放在/tmp目录下。
enemy采用了跟有的框架相同模式的基于锁定相同套接字的来保证单一实例运行的方法。
可以将两方算法性能进行对比。
对比之后不难发现,enemy的代码在单一实例上更加精简,相关人员的代码(沿用的是Mirai的)。
小结:enemy在单一实例代码的功能、性能上比现成强太多了。
隐匿行踪部分
给出的代码里面,统计了所有参数,然后尝试清零,然后顺便把进程名也给清空了。
然后在fork出的子进程在操作上有点迷惑,简单做了一个逻辑的分析。
驻留部分
在这里面多了一个uid,目前已知的样本当中我并没有看到过这个函数。可以简单看看。
一般在编写具 setuid root的程序时,为减少此类程序带来的系统安全风险,在使用完root权限后建议马上执行setuid(getuid());来抛弃root权限。setuid()用来重新设置执行目前进程的用户识别码。
不过,要让此函数有作用,其有效的用户识别码必须为0(root)。在Linux 下,当root使用setuid()来变换成其他用户识别码时,root权限会被抛弃,完全转换成该用户身份。
也就是说,该进程往后将不再具有可etuid()的权利,如果只是向暂时抛弃root权限,稍后想重新取回权限,则必须使用seteuid()。
通过检查rc.local里面是否有自己的痕迹。
在代码里面,发现很多细节发现作者可能是拼凑而成的,比如一些缩进的问题(虽然也不一定)。
接着在驻留方面,选择沿用了Mirai里面使用的关闭看门狗ioctl-5704操作。
同时也用了Mirai的全局变量LOCAL_ADDR。(在框架里面也有这个)
coil_xywz
REBIND
在驻留部分,还有一个就是在coil_xywz开启一个新子进程之后进行对常用端口进行REBIND操作,具体表现在SSH/22、TELNET/23,HTTP/80【更多的端口可能会接着其他开发者需求进行】。
然后出现了一段比较有意思的代码。
代码里面某些函数相对来说有点复杂,作者一开始都有些看不太明白。
获取文件执行路径
在这里面用coil_exe用来标记当前进程的执行路径【虽然运行起来会被删掉,但是通过pid文件夹里面的exe可以获知真正的执行路径】
子进程主循环
这个就是coil_xywz作为子进程运行的核心所在,这里面的主要功能是保证当前进程的稳定性,说白了就是杀掉一切已知进程,杀掉一切含有特殊字段的进程。
这也是我认为enemy作为一个新的样本源代码比较特别的地方。
守护进程的功能模式也还是第一次见到,对于Linux的结构来说是比较熟悉的。主循环当中,采用时间戳检查的方式来保证不会占线太长时间。
这里面主循环当中,会尝试打开 /proc/ 路径,检查里面所有pid文件夹,获取 exe_path 和status_path ,检查是否存在落地文件,删掉不存在落地文件的进程,删掉已知bot端的进程【使用d8ds 函数完成】,之后销毁检查过的exe_path和status_path。
这里面比较有意思的是 d8ds(exe_path) 这个函数。
这里面我初步猜测,通过16进制映射表转换之后,应该是特殊字符串。等有时间动调一下试试看。
存疑:为啥里面将杀其他进程的操作叫做 j83j ?还有一个是 d8ds 操作是检查当前exe_path下是否含有已知的特殊字段。
命令架构processCmd
enemy的核心部分,它定义了一个名为 processCmd 的函数,它接受两个参数:argc 和 argv。它根据argv[0] 的值来判断执行哪个命令。
LDSERVER——植入、驻留当 argv[0] 的值为 "~-6mvgmv" 时,它会将 argv[1] 的值复制到 ldserver 数组中,直译过来是Loader Server,在全文上下,ldserver作为一个全局变量,真正赋值的地方是在processCmd函数当中进行的,而真正调用的地方在别处。
TCPON——监听
当 argv[0] 的值为 "TCPON" 时,它会启动一个名为 tcpkernel 的函数作为子进程,其会申请一个 RAW类型的套接字,用于统计所有流经内核的tcp包,目前只统计
HTTP/80,FTP/21,SMTP/25,Doom Id Software/666,JSWebDev/1337,Omirr/808 相关的。
TCPOFF——监听
当 argv[0] 的值为 "TCPOFF" 时,它会终止之前启动的 tcpkernel 函数子进程。
UDP——DDoS
当 argv[0] 的值为 "1-|"【UDP】 时,它会执行一个名为 sendUDP 的函数,它会向给定的 IP 地址和端口发送 UDP 数据包,用于执行UDP DDoS攻击。该代码可以向目标主机发送大量的UDP数据包,从而导致目标主机资源耗尽并无法响应正常请求,从而实现DDoS攻击。
攻击执行的具体操作包括:建立套接字、生成随机数据包、不断发送UDP数据包到目标主机,以达到堵塞目标主机的目的。
TCP——DDoS
当 argv[0] 的值为 "cD|"【TCP] 时,它会执行一个名为 ftcp 的函数,它会向给定的 IP 地址和端口发送 TCP 数据包。
OVERTCP——DDoS
当 argv[0] 的值为 "x09x1cx9dx36xb0xdax1e" 【OVERTCP】时,它会执行和上面类似的ftcp 函数。它实现了一个网络层攻击,即TCP洪水攻击。它使用了原始套接字来构造和发送TCP数据包。
代码首先声明了一个“ftcp”函数,它接收了关于目标主机,端口,攻击时间,伪造IP,TCP标志,数据包大小和轮询间隔的参数。接下来,它创建了一个原始套接字并进行了一些设置。
然后,它构造了一个TCP数据包,其中包含了IP首部和TCP首部。代码还处理了TCP标志,允许用户选择特定的标志,例如SYN,RST,FIN等。最后,代码在指定的攻击时间内不断发送数据包,其中随机地修改了IP和TCP头中的一些字段。
OVTCP——TCP洪水攻击
这是一个实现TCP洪水攻击的C函数。该函数接受七个参数:
1. target:指向保存目标IP地址的无符号字符的指针。
2. port:目标端口号。
3. timeEnd:攻击应持续的秒数。
4. spoofit:如果设置为0,源IP将被随机化。如果设置为大于0的值,则源IP的第一个“欺骗”位
将保持不变,而其余位将随机化。
5. flags:指向无符号字符的指针,该字符保存要在数据包的TCP报头中设置的标志(SYN、
RST、FIN、ACK、PSH)。
6. packetsize:数据包有效负载的大小。
7. pollinterval:函数轮询攻击结束的时间间隔。
该函数首先设置目标地址信息,打开TCP的原始套接字,并设置要包含在套接字中的IP头。然后,它生成一个包含IP报头和TCP报头的数据包,其中包含指定的标志和有效负载。
然后,它重复发送数据包,直到攻击时间结束,随机更改每个数据包的源IP地址和TCP报头信息。
HTTP——DDoS
该函数接受五个参数:
1. method:HTTP 请求方法,例如 GET 或 POST。
2. host:请求目标主机的主机名或 IP 地址。
3. port:目标主机的端口号。
4. path:请求的路径。
5. timeFoo:请求的持续时间。
6. power:请求的数量。
该函数首先定义了一个字符串数组 connections,包含三个字符串:"close"、"keep-alive" 和"accept"。
然后,通过使用 sprintf 函数,将 HTTP 请求存储到字符串 request 中。请求的格式遵循HTTP/1.1 协议,并包含两个自定义的字段:
一个是 Connection,值从 connections 数组中随机选择;
另一个是 "eika(xbcx03x51xe7xdcxa4xf9x51xffx39)",值从用户代理数组中随机选择。
最后,通过使用循环语句和 fork 函数,并在循环内使用 socket_connect、write 和 close 函数,发送指定数量的请求。请求的发送循环会在指定时间到达后停止。
HOLD——http DDoS攻击
这段代码实现了一个名为sendHLD的函数,该函数用于发送高并发连接(HLD)攻击。它接受三个参数:IP地址,端口号和结束时间。首先,它利用getdtablesize() / 2来计算可以同时打开的最大连接数。
然后它使用struct sockaddr_in结构体定义了目标地址,并通过getHost函数获取IP地址。接下来,它使用state_t结构体数组定义了最多的连接,并为每个连接分配状态(0:未连接,1:连接中,2:已连接)。它循环检查所有连接的状态,根据状态做不同的操作。
如果状态为0,它会创建一个新的套接字并尝试连接;如果状态为1,它会通过select函数检查连接是否完成;如果状态为2,它会通过select函数检查是否有故障。当循环达到结束时间时,该函数将终止。
JUNK——DDoS
主要功能是实现一个网络攻击,叫做JNK攻击。首先定义了一个最大连接数,用来限制打开的套接字数量。然后定义了一个sockaddr_in结构体,存储目标地址的信息(IP地址和端口)。
接着定义了一个状态结构体state_t,包含了套接字的描述符和状态信息。然后定义了一个循环,循环持续的时间为end_time,在循环内部执行网络攻击。在每次循环中,使用switch语句控制套接字的状态:
如果状态为0,则创建一个新的套接字,并连接到目标地址。
如果状态为1,则使用select函数等待连接成功或出错。
如果状态为2,则发送一个随机长度的数据包。
循环结束后,程序结束执行。
TLS——DDoS
执行了一个“TLS攻击”。代码通过创建许多socket来实现一种类型的DDoS攻击,这些socket尝试与目标主机(通过传递的IP地址和端口)建立连接。在这个代码中,连接将不断尝试,直到它们成功与目标主机建立连接,或者当前时间超过预定的结束时间。代码使用了一些技巧来提高执行效率,例如使用非阻塞I/O和多路复用等。
STD——DDoS
这是一个用于发送数据包的函数,可以将随机生成的字符串作为数据包发送到指定的主机和端口。
函数以三个参数作为输入:
1. host:指定的主机地址。
2. port:指定的端口号。
3. secs:发送数据包的时间长度。
在函数中,首先创建了一个套接字,并设置了主机的地址信息。然后,在一个无限循环中不断地发送数据包。发送的数据包是存储在一个随机字符串数组中的数据包,随机选择一个字符串发送。如果在给定的时间内发送了50个数据包,则会暂停并重置计数器。如果给定的时间到期,则退出函数。
DNS——DDoS
这段代码是一个DNS flood攻击程序,通过发送大量的DNS请求包给目标主机,以达到拒绝服务的目的。代码中使用了socket API实现网络通信,并通过设置不同的端口和随机生成的DNS请求数据来模拟DNS查询。由于这段代码没有对服务器的正常使用进行考虑,并且消耗了大量的资源。
SCANNER——开发者还没有写,估计是内网扫描器。
ON
OFF
OVH——DDoS
程序首先创建了一个UDP套接字,然后设置了IP和UDP头,并将其发送到指定的目标地址(td参数)和端口(port参数)。然后,程序生成了一定量的随机数据,并在每个数据包的UDP头中填充随机的端口和数据,然后一直循环发送这些数据包,直到到达timeEnd时间。但是好像并没有看到退出条件,且通过num_threads指定并发数。
BLACKNURSE——DDoS
一个在给定主机上发送 ICMP 数据包的功能。首先定义了一个字节数组 pkt_template,存储了待发送的 ICMP 数据包的数据。然后定义了一个结构体 sockaddr_in 用于存储地址信息,以及一个结构体 pollfd 用于 poll 调用。
使用 socket 函数创建了一个原始套接字,指定为 ICMP 协议。如果创建失败,则直接退出。然后,使用 getHost 函数获取主机的 IP 地址,并将它存储到sockaddr_in 结构中。然后是一个无限循环,循环内部调用 sendto 函数,向目标地址发送 ICMP数据包,如果 sendto 失败且errno 等于 ENOBUFS,则调用 poll 函数,否则跳出循环。最后关闭套接字,退出。
ARK——DDoS
此代码定义了一个函数sendARK,该函数通过UDP(用户数据报协议)连接将数据发送到指定的服务器。该函数接受三个参数:
1. host是指向包含服务器的主机名或IP地址的无符号字符数组(即字符串)的指针。
2. port是一个整数,指定数据应发送到的服务器上的端口号。
3. secs是连接超时前等待的秒数。
4. 该函数首先使用套接字函数创建套接字,并检查套接字创建是否成功。如果不是,函数将返回。
5. 接下来,该函数使用参数中提供的信息和getHost函数的结果来设置服务器地址结构(serveraddr),getHost函数将主机名映射到IP地址。
6. 最后,该函数使用sendto函数和serveraddr结构将消息发送到服务器。
值得注意的是,发送的消息似乎包含随机数据,并且可能不会以对收件人有意义的方式进行格式化。
ADNS——DDoS
同上文,对一直的DNS服务器发起攻击。
STOP——终止所有攻击,杀死所有pid。
Uyil_性能比较
随机种子
在随机化字符串上,对双方代码进行对比研究,先上enemy的代码。
看过代码后发现递归到randNum函数里面可能性能上就变得非常差了,因此,这一步需要着重分析调整。
pid列表维护
对于恶意代码来说,有些任务是需要fork出来的。因此对于子进程pids的创建、维护与回收尤为重要。
其复杂度来说,相关人员的时间复杂度更快。相关人员的原理是这样的,相关人员采用的是本地全局定长的pids数组,如果是采用gdb调试的话,直接访问相关人员框架的进程即可调试所有的子进程,且子进程的进程数是限制住的,这对于嵌入式设备上来说资源的使用是有限的。
再看看enemy的原理,其是通过malloc动态的维护一个子进程pids列表,其总大小总是pids进程数+1。当有一个进程创建的时候,其就将原来的pids列表复制过来。回收的时候,原理也是一样,malloc一个更小的然后将原本的复制过去。从安全性的角度来说,enemy在全局维护一个堆块指针更安全,能更安全的进行木马运行。
最后,这里还有一个相关人员可以改进的地方,就是enemy存在pids列表的维护,而相关人员没有。而enemy里面维护的代码在main的主循环里面,其每一次主循环都有可能会有一个子进程fork出来。
CMWC算法
这段代码是在send_UDP函数当中看到的,搜了一下。
实现了一种随机数生成算法,称为 "Complementary-Multiply-With-Carry"(CMWC,互补乘法携带)算法。它的主要思路是使用一个长度为 4095 的数组 Q 存储生成的随机数,并使用变量 a 、 c 和 i 记录生成随机数的状态。为了生成下一个随机数,它先使用 a 和 Q[i] 算出一个中间值 t,再根据 t 算出 c 的值。最后,函数返回 Q[i] = r - x 的结果,其中 r 是一个固定的值 0xfffffffe。
CMWC (Complementary-Multiply-With-Carry)算法的优点是:
1. 随机数序列具有较长的周期。
2. 生成的随机数具有很高的熵,也就是具有很好的随机性。
3. 算法简单,代码实现简单,效率高。
CMWC算法的劣势是:
1. 如果种子选择不当,随机数序列的周期可能较短,这会影响随机性。
2. 在64位计算机上可能有精度损失。
3. CMWC算法不是很广为人知,不是所有的计算机语言都有相应的库函数支持。
代码来源分析
在源代码中,原文提到了几点功能模块。
相关人员可以看到这基本上是有一些代码来源的,也就是说,enemy的代码是目前已知几个物联网威胁的集合:Mirai、Qbot、Zbot、lightaidra。
Mirai
这个就不做过多的研究,因为之前是已经分析过的,原文提到的是SYN扫描器,相关人员大致分析了一下,enemy在Mirai的基础上取消了2323端口的爆破以及增加了一下硬编码的凭证,其他大致没有改变。
Lightaidra
Aidra 恶性代码在 2012 年 2 月初首次被发现,据推测该恶性代码在 2011 年末完成制作,是最早的物联网恶性代码。横跨MIPS 、MIPSEL、PowerPC、SuperH 等多种架构。
这个代码里面的字符串自定义映射规则是enemy所采用的,这部分会猜测为什么采用这种方式来进行字符串加密。其工作逻辑很简单。
其SYN攻击和NGSYN攻击两个函数当中的不一样之处,到目前为止还是没弄明白为什么会怎么安排,ACK与NGACK也是如此。
关于IRC部分的暂时没有分析,因为IRC目前只有在国外的一些高校还在使用,IRC聊天是一个比较纯粹的互联网精神的载体。
在独立进程这方面,其采用了文件锁,是相关人员一开始开发框架里面所采用的方法,而现在采用自监听的方法。
Lightaidra的命令系统是围绕IRC服务器展开的,也就是说Lightaidra是以IRC服务器为星形网络的中心网络,涵盖以下几个方面:
登录命令——登录、登出到僵尸结点
信息命令——直接执行命令、返回当前bot端版本、当前bot状态
扫描命令——用设定好的账号密码尝试登陆23端口/D-Link路由器、设置好账号密码递归扫描本地
网络的23端口/D-Link设备,对IP地址(A.B.C.D)进行A.B或者C.D的范围扫描。
DDoS命令——伪造好攻击源IP、指定攻击的IP、支持SYN、NGSYN、ACK、NGACK攻击。
IRC信息维护命令——维护IRC服务器等。
Qbot
Qbot在github上最早是2016年公布的。其文件结构非常简单,核心文件只有是三个:注入脚本(cc7.py)、服务端(server.c)、受控端(client.c)。
简单分析了一下,可以看到enemy是基于Qbot魔改的,其很多是跟Qbot很相像的,其整体思路都是沿用了Qbot的思路:
所有代码写在一个文件里面,没有 .h 文件里面。
自己写很多常用的函数而不用系统函数。
其支持的命令种类:
DDoS攻击——UDP/TCP/HTTP/JUNK/HOLD:指定端口、持续时间
CNC网络维护
僵尸网络计数
其传播途径都是Telnet-23爆破。有预配置好的弱口令。
CNC网络是星型网络。没有驻留手段,只有简单的混淆进程名称,没有删除自身。
目前还有不断的扩展与变种出现。
Zbot
又是一个基于IRC聊天网络进行的僵尸网络,最早在github是2017年公布的。
DDoS的种类更多,细粒度更小, RAW UDP/TCP SYN/TCP FIN/TCP PSH/TCP ACK/TCP URG/TCP RST/TCP ECE/TCP SEW/TCP XMAS 。
命令支持DDoS、终止攻击、命令执行。
但是有趣的是,在zbot.c当中也是没有找到完成命令执行代码的相关函数system【也有可能编程方法,不止用system一种方式】。
但是分析了整个逻辑,好像没有看到关于执行命令的相关代码,可能被删改了。
里面看到了一些方法调用,命名有点奇怪。
它定义了多个回调函数,用于处理在网络通信中收到的不同类型的数据。其主要是IRC协议工作的代码。
具体而言:
_376 函数执行模式设置,加入频道和查询某个用户信息的操作。
_PING 函数回复PING数据。
_352 函数检查接收到的数据是否与当前用户的昵称匹配,如果匹配,则解析并保存与该用户相关的主机信息。
Zbot主体还是沿用了Qbot的框架,在Qbot的基础进行扩充的。
分析小结
Mirai与Qbot是两个体系的僵尸网络——其在代码结构上、扫描器实现方式上存在区别。
但是其主体的传播思路也都是利用弱口令来实现的。
编译调试
NO.5
测试环境:物理机Win10 + 虚拟机Ubuntu18.04、IDA+Linux Debugger。
文件方面
在main函数当中,创建了一个名为 enemyv2.1.lock 的socket文件作为单一实例的本地套接字锁。如果当前工作目录下创建本地套接字失败则退出。
【代码逻辑混乱】对 /etc/rc.local 文件的启动操作:将最后一个文件夹作为文件名然后复制到rc.local当中。
当我在 /home/u/Downloads 下运行的时候,
其生成的路径是 /home/u/Downloads/Downloads ;
当我在 /home/u/Downloads/radare2 下运行的时候,
其写入 /etc/rc.local 的路径是/home/u/Downloads/radare2/radare2 。
就算相关人员对写入rc.local文件里面的路径进行启动,也是不行的。
进程方面
修改了进程名为随机的字符串,影响的相关地方:/proc/self/comm 和 /proc/self/exe 里面。
会有两重fork,第一重等待子进程结束后才退出,第二重fork的父进程没什么用,第二重fork的子进程才有用。
网络方面
一开始,并没有发现有很好的连接行为,分析发现,在这里出现了一个解析的问题。
当运行到getaddrinfo函数的时候,就是有问题了。
对于到这个函数调用链是:main-connectTimeout-getHost-getaddrinfo ,对于一开始的main函数里面的代码申请。
对于 eika("xfcx86xadx74x20xadxe1x9ax52xadx86x20") ,返回的结果是一个乱七八糟的东西。
然后尝试修改一个可靠的IP来进行调试,我编译了 servertor.c 。
修改bot端代码为server端的IP,同时修改连接的端口是5200,bot端源码里面直连的端口是7。也就是说,端口7一般是root权限运行的。
这之间TCP连接,配置是5200端口。server与bot之间的用明文传输,一开始上线,server对bot说:NSCANNER ON ,网络扫描器打开,然后bot返回一个 x01 。
紧接着,bot端对server发送 arch x86_64 。而这一块的代码,实测发现工作效率非常高。打算可以引用,其内置了20种CPU指令集,其利用的是编译器内置的声明,是非常方便的,首先查看测试环境中的定义。
紧接着就是呼吸指令,bot对server:PING,server回应bot:PONG。PING-PONG呼吸包,呼吸频率7s。但是我修改代码之后发现都是没用的。查看函数一开始的定义。
再查看其对这个函数的调用。
我对最后一个值进行修改并没有用,都还是7s。结果发现是只有连接不上的时候timeout才会生效。
server.c 的健壮性不行,server的命令格式 Usage: ./servertor [Bot-Port] [Threads] [CncPort] ,最后一个是CNC网络部分,相关人员选用的是5201端口,于是我用 nc 192.168.150.1285201 然后就下图了,直接段错误。
CNC网络部分的健壮性也不行,server下线之后,bot端竟然没有重连。
第2行和第8行是我添加的调试语句,结果发现运行在读取文件的时候出错了,如下图所示。
可以看到在上图主动提出FIN标记的是server端。
从上面的代码逻辑相关人员可以推出,这里面的工作逻辑相对来说就进入了自定义的步骤了。
代码逻辑混乱
写入启动路径
要注意这是在非DARWIN系统【OSX,苹果系统】的其他Linux发行版本当中执行。
直接运行:其整段代码将enemy的执行路径的最后一个文件夹作为文件名然后复制到rc.local当中,而这种情况下是在ubuntu中直接运行,不论是否是root权限。
就算相关人员对写入rc.local文件里面的路径进行启动,也是不行的。在IDA当中进行调试运行:
在这里面相关人员一开始对于rc.local是没有办法进行写成功的,使用chmod之后就可以了。
在上文第20行代码这里,相关人员可以看到引用了 argv[0] ,而在前文当中,存在 rand_str(pname,12);strncpy(argv[0], pname, space - 1); 这样的初始化代码,导致了原本替换了argv[0]里面的内容,但是这里又引用了这个变量,就造成了逻辑混乱。
改进思路
如果需要改进的话,可以取用 /proc/self/exe 或者 /proc/self/comm 的返回值作为启动参数。
可以直接运行类似的命令 system("echo $(pwd)/$(cat /proc/self/comm) >>/etc/rc.local") 或者相类似的系列指令当中。
coil_xywz
该逻辑混乱。看代码后发现这里面的代码其实是一个死代码。根据第18行代码的判定条件,只有当前检查的pid是父进程或者当前进程的文件,才会跳出第18行的判定语句(判定为假),当进入到第46行,那么就会直接continue,也就是永远不会进入第50行代码。
改进思路
将第18行的不等号换成等号。
Server端
后面又继续分析发现是server端的工作模式与相关人员设想的不一样。
看完代码后还是不得不吐槽一句,为什么这个代码里面连个基本的文件指针是否为空都没有的判断,就很奇怪。
改进思路
添加文件指针的判断。
声明
NO.6
本文作者:LovenSar
本文编辑:Yusa
感谢 LovenSar 师傅 (๑•̀ㅂ•́)و✧
往期回顾
扫码关注我们
天虞实验室为赛宁网安旗下专业技术团队,重点攻关公司业务相关信息安全前沿技术。
原文始发于微信公众号(天虞实验室):EnemyBot简单分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论