linux xorddos样本分析2
逆向分析
之后我们通过ida对该样本进行更深入的分析样本的main函数中,一开始会调用函数dec_conf对样本中的大量加密的字符串进行解密,如下图所示。
而函数dec_conf中实际调用了encrypt_code函数进行实际的解密,解密的操作为按位进行xor操作。
以此可以通过脚本对样本中的字符解密,解密之后部本分结果如下图代码中的注释所示,包含了样本运行中会使用到的目录。
此处为目标解密之后的目标域名。
解密之后的目标ip。
之后根据main传入的参数个数,样本分别运行三个状态
当argc参数为2,即2号流程,运行以下代码,在该代码中实现样本的删除。
当argv参数为1,即一号流程。
首先,将自身拷贝为/lib/libudev.so,之后获取当前样本的路径,判断是否为/usr/bin,/bin,/tmp,如果不是,则会依次往这三个目录拷贝样本文件,此处的拷贝只要有一个存在,就停止向后面的目录拷贝,重新拷贝的文件名按26个小写字母随机生成(具体在randstr函数中实现)。长度为10,其中lib目录下的文件名为libudev.so。
此处注意观察两个拷贝之后的文件
首先是原样本文件,此处记录了样本的大小和,md5值。
/usr/bin目录下被拷贝的样本文件的大小和md5值。
/lib目录下拷贝的样本文件libudev.so的大小和md5值。
可以发现,同样是拷贝,问什么样本的md5值发生了变化,处理lib目录之外的样本的大小也发生了变化,注意对上图可以发现,/usr/bin目录下的样本大小增加了11kb,因为通过查询md5值确定样本类型是现代杀软和一般扫描器中一项常规的方法,而样本随时变化的mda5值将导致常规的md5值扫描无效,通过代码的进一步分析可以发现每次样本拷贝实现之后,会运行函数randmd5函数。
在函数randmd5中,通过调用lseek在该文件的末尾写入了0xB(十进制为11)字节的内容,从而导致每次复制之后的文件md5值,大小不同,当然在/lib/udev.so的大小没有发生变化是因为在该拷贝结束之后并没有调用randmd5的操作(详见上图中的几处函数拷贝代码)。
拷贝结束之后调用函数linuxexec启动该拷贝样本,比如在我的主机上就是启动/usr/bin目录下的样本,这里需要注意一点就是linuxexec函数是样本作者对函数execvp函数的一个封装,在该函数中不仅调用了execvp,同时还调用了一个名为doublefork的函数。如下图所示,该函数会再次创建子进程,并在子进程游fork一次,这就导致每次linuxexec函数调用的时候会创建大量的子进程,当然此处的不止linuxexec一个函数,样本作者还写了一个名为LinuxExec_Argv2的函数,给函数与linuxexec实现一致,不过他的参数是两个,通过这两个启动函数,可以实现对拷贝样本的多种启动,从而使得样本运行之后进入不同的流程,因为上面提到,样本一共三个流程,分别靠传入的参数来决定进入其中的某一个。
比如此处doublefork的多个子进程。
此处因为调用了linuxexec,使得接下来的子进程启动时进入第一个流程(因为linuxecec调用时,产生的子进程的argc为1),此时/usr/bin中已经存在样本备份,故会进入1流程中的第二个分支,在该分支中一开始会调用InstallSYS函数来进行内核模块的安装。但是通过分析发现,在改函数的内核模块加载前会有一个判断,该判断会导致,该函数直接跳过内核模块的加载,因此此处猜测,该处代码并没有完成,只是作者预留了一个功能性的函数以备后面完善。
之后函数通过调用addservice函数实现自启动,在函数decrypt_remotestr解密服务器的ip地址,最后开启新的线程调用tcp_thread实现和服务器的通信。
通信
在函数tcp_thread函数中进行通信前,首先会调用函数getmagic,该函数首先会判断目录/var/run/gcc.pid是否存在,之后会生成一段key与实现和服务器的认证。
之后函数会依次获取计算机的内核版本,处理器,cpu核数,时钟频率,key,static,样本的版本,形成第一次的认证数据包,该数据包经过加密,算法与之前的解密算法互逆。
加密的数据包如下图所示。
通过之前的脚本,下图为还原之后的真实数据包内容,其中vsyd~vef的内容作为该样本驻留肉鸡的通信凭证,每次的通信包中都会在末尾附加上着一段字符。
与服务器远程通信认证完毕之后,会收到远程服务器的一个数据包,该数据包中包含的用于控制肉鸡进行ddos指令的命令等,通过分析发现数据包的其中一位用于决定进行攻击的类型
为4时,是dns攻击
为5时,为syn工具
为10时,为ack攻击
当然远端的控制还有其他的类型,与常见的木马类似,此处不做过多分析。
之后该进程会进入一个循环,在该循环中,会将自身一次尝试向/usr/bin,/tmp,/bin三个目录下再次拷贝,拷贝成功后会以这个备份的样本文件为执行文件进行启动,通过函数linuxexec_argc2(此处将导致启动的进程在main函数中进入3号流程)。
通过该函数启动时,会传入两个参数,入样本文件为xxx,则启动方式为xxx pid 2333(当前进程号),以此方式启动的子进程,在进入main函数式,argc为3,会进入3号流程。
在3号流程中会去判断/lib目录下的libedve.so是否存在,如果不存在,则会将当前进程的执行镜像文件拷贝到/usr/bin,/tmp,/bin三个目录,注意此时的拷贝文件源是在/usr/bin中,拷贝结束则通过调用linuxexec函数对该样本进行启动,此时无参数,无参数启动的样本会导致的后果就是在启动进程的main函数中,运行到1号流程,而一号流程的一开始就会将进程的进程拷贝到/lib目录下,形成libedve.so的文件。
至此我们就可以明白该样本为什会难以被删除,样本启动之后启动的主进程A会进入1号流程,在改流程中首先将自身拷贝为/lib/libedve.so,然后不断将/lib/libedve.so这个样本拷贝到/usr/bin,/tmp,/bin中的某一目录下,并通过函数linuxexec_argv2启动将其作为子进程启动,这些启动的子进程B会进入样本的3号流程,在该流程中同样完成完成将自身拷贝到/usr/bin,/tmp,/bin目录下任务,但是此时的启动函数变成了linuxexec,因而此时启动的子进程c将直接进入1号流程,结果就是libedve.so会被再次拷贝到/lib目录下,因此只要主进程不断创建子进程(尤其是通过doublefork增幅之后,子进程的创建会非常快,这就导致普通的脚本删除无法对机器中的样本进程删除,因为样本的增殖速度更快),大量的子进程就会反过来形成对主进程有效的保护。
关于查杀
关于样本的查杀,样本主进程和子进程的相互保护(这种机制很早起梆梆加壳的保护机制有些相似-_-!),通常的方法很难进行清理,但是可以通过以下方法实现。
首先找到父进程。
此处不使用kill把他杀死,如前面所分析的,这将导致子进程又重新启动生成新的进程,此时换一个参数,用stop将父进程挂起。
挂起的结果就是,所有的子进程就停止创建,并死亡,没有了子进程的保护,主进程又被挂起,此时就可以在主机上将所有的样本文件删除了。
有可能包括的目录为
/usr/bin
/tmp
/bin
/lib/libedev.so
启动目录
/etc/init.d
计划任务
/etc/cronhourly/gcc.sh
比如下面
最后再把孤立无援的父进程杀掉即可