抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

DokeyC2

已完成第一阶段demo开发, 目前实现了客户端到服务端之间的通信, 向服务端发送命令、获取监听器信息、获取被控端信息、向所有客户端推送信息; 被控端到服务端之间的通信, 向服务端获取命令执行、向服务端发送心跳包保持活跃, 向服务端发送系统信息等

运行展示

DockeyC2通信架构

通信协议

  • C2服务端客户端选择使用HTTPS通信,使用TLS(传输层安全协议加密通信内容);服务端和客户端之前长连接机制来保证连接复用,在多次请求/响应周期中复用连接,避免频繁建立和断开连接的开销,提高系统性能和响应速度。
  • C2服务端被控端目前支持HTTP协议通信,因为这块后续需要根据监听器(HTTP、HTTPS、DNS)来选择并创建不同的Beacon,所以目前只实现了HTTP逻辑。

服务端(Server)

服务端主要功能概述

监听连接

  • 监听4433端口,并使用线程处理每个验证通过的客户端,可以多个客户端连接到服务端
  • 可以通过客户端在服务端创建监听器,被控端(Beacon)通过监听器连接至服务端,支持多个被控端连接到该监听器并正确处理请求

命令分发与执行

  • 在服务端通过工厂模式来模块化处理客户端、被控端的请求,不同的请求对应不同的处理逻辑
  • 客户端通过HTTPS请求将命令传递到服务端,服务端根据beacon_id将命令存储到对应命令队列中,并等待被控端通过请求来获取需要执行的命令
  • 被控端通过/CommandRetrieval接口请求需要执行的命令,当获取命令后根据command_id将命令状态置为正在执行状态,当执行完并返回结果后根据command_id将命令状态置为已执行状态;保证获取命令时不会获取已执行的命令;此处将命令执行完成将命令状态置为已完成状态,暂未实现将命令删除或转存,后续可能需要获取命令执行历史记录

信息存储

  • 客户端连接存储:使用std::unordered_map <std::string, std::shared_ptr<ClientSession>>来存储客户端session,以client_id为键,值为std::shared_ptr<ClientSession>;首先std::unordered_map提供了高效查找,可以通过std::mutex来实现线程安全;其次使用std::shared_ptr<ClientSession>可以动态管理内存,简化对象的共享和传递,避免重复传递对象;另外std::unordered_map支持快速扩展,可以轻松地添加或删除客户端会话,确保性能稳定
  • 被控端连接存储:使用std::unordered_map<std::string, std::shared_ptr<Beacon>>来存储被控端session,以beacon_id为键,值为std::shared_ptr<Beacon>,使用特性和客户端连接存储同理
  • 监听器存储:使用 std::unordered_map <std::string, std::shared_ptr<IListener>>来存储监听器信息,使用特性和客户端连接存储同理

服务端架构与代码结构

服务端结构概述

  • C2服务端:作为指挥中心,负责接收来自客户端的命令、验证客户端身份、管理客户端连接以及向被控端下发指令。
  • C2客户端:用于与服务端通信的用户界面,通常负责显示当前连接的状态、管理连接、发送命令、接收执行结果等。
  • 被控端(Beacon):被控制的目标机器,通过 C2 服务端下发的命令执行操作,并将结果反馈给服务端。

通信流程

客户端连接与认证
  1. 建立连接:C2客户端发起 HTTPS 请求,尝试与服务端建立连接。
  2. 身份验证:
    • 客户端在请求中附带加密的身份验证信息(如用户名/密码或密钥)。
    • 服务端接收到请求后,解密并验证身份。
    • 如果身份验证通过,服务端生成一个会话令牌(目前为client_id)并发送给客户端,表示身份验证成功。客户端保存此令牌并在后续的请求中使用。
    • 如果身份验证失败,服务端返回错误响应,客户端需要重新进行身份验证。
命令交互
  1. 客户端发送命令:
    +客户端通过 HTTPS 向服务端发送执行命令请求(如执行 Beacon 任务、查询 Beacon 状态等)。请求中会附带会话令牌作为身份验证。
  2. 服务端处理命令:
    • 服务端接收到请求后,验证会话令牌的有效性。
    • 如果令牌有效,服务端根据请求内容查找对应的 Beacon,并将命令下发给目标 Beacon。
    • 服务端等待 Beacon 返回执行结果。
Beacon执行与反馈
  1. Beacon 接收命令:
    • Beacon 定期向服务端发送心跳包或 Beacon 信息,表明自己在线,并接收下发的命令。
  2. Beacon 执行命令:
    • eacon 根据服务端下发的命令执行相应操作,并将结果返回给服务端(例如执行 Shell 命令、获取系统信息等)。
  3. 服务端反馈结果:
    • 服务端接收到 Beacon 返回的结果后,整理并通过 HTTPS 返回给客户端。
    • 客户端收到执行结果并显示给用户,更新客户端的状态。

代码结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
DokeyC2
├── Commands (命令处理模块)
│ ├── Include (接口文件)
| | ├── ICommand.hpp (命令基类实现)
| | └── ICommandFactory.hpp (工厂模式基类实现)
│ ├── ListenerCommand (监听器命令处理模块)
| | └── HttpCommand.hpp / HttpCommand.cpp (http命令处理头文件 / 具体处理函数实现)
│ ├── Command.hpp / Command.cpp (客户端命令处理头文件 / 具体处理函数实现)
│ └── CommandFactory.hpp / CommandFactory.cpp (命令工厂模式头文件 / 具体工厂模式函数实现)
├── Networks (网络通信处理模块)
│ ├── Beacons (被控端)
│ │ └── Beacon.hpp / Beacon.cpp (被控端头文件 / 被控端逻辑实现)
│ ├── Clients (客户端)
│ │ └── Client.hpp / Client.cpp (客户端头文件 / 客户端逻辑实现)
│ ├── Include (接口文件)
│ │ ├── IListener.hpp (监听器基类实现)
│ │ └── INetworkManager.hpp (网络通信管理基类实现)
│ ├── Listeners (监听器)
│ │ └── HttpListener.hpp / HttpListener.cpp (http监听器头文件 / http监听器处理函数实现)
│ ├── Manager (网络通信管理)
│ │ ├── AuthManager.hpp (客户端身份验证头文件, 逻辑实现)
│ │ └── NetworkManager.hpp / NetworkManager.cpp (网络通信管理头文件 / 网络通信管理处理函数实现)
│ └── Sessions (连接处理)
│ │ ├── ClientSession.hpp (客户端session处理逻辑头文件)
│ │ ├── HttpSession.hpp / HttpSession.cpp (http session处理逻辑头文件 / http session处理函数实现)
│ | └── ListenerSession.hpp (监听器session处理逻辑头文件)
└── Tools (工具模块)
└── Serialization.hpp / Serialization.cpp (处理通信序列化头文件 / 处理通信序列化逻辑实现)

服务端与客户端、Beacon通信数据验证

服务端与客户端(HTTPS)

客户端发送的请求以及服务端的回应都会使用 SSL/TLS 进行加密,保证数据传输的安全性。

服务端与被控端(HTTP)

当前支持HTTP监听器, 后续添加HTTPS、DNS类型监听器

客户端(Client)

客户端使用 MFC 单文档开发,界面参考 cobalt strike ;自实现 C2 上版本使用 MFC基于对话框 进行客户端界面开发,在视觉效果上总觉得差强人意,在视图、数据的展示会存在很多隐藏的bug,所以选用MFC单文档进行开发,也一边学习 MFC 单文档开发模式

MFC单文档概述

MFC(Microsoft Foundation Class)是一个基于 C++ 的类库,提供了丰富的界面和功能来帮助开发 Windows 应用程序。MFC 支持多种应用程序架构,其中 单文档界面(SDI) 是最常用的一种。

  • 单文档界面 (SDI):在单文档界面应用程序中,通常只有一个主窗口(即主框架窗口),并且该窗口的内容或数据通过一个文档对象来管理。SDI 应用程序的特点是每次只能打开一个文档实例。
  • 特点:简单、直观、适用于需要操作单一数据集的应用,用户界面清晰。

MFC单文档应用程序的基本结构

在 MFC 单文档应用程序中,主要有以下几个关键组成部分:

  • 文档类(CDocument):负责应用程序的数据管理和存储。文档类通常用于存储应用程序中的核心数据(如网络连接信息、命令队列等),并提供对这些数据的操作接口。
  • 视图类(CView):负责显示文档中的内容,并与用户进行交互。视图类的任务是将文档中的数据可视化,提供用户与应用程序交互的界面。
  • 框架类(CFrameWnd):负责窗口的布局、菜单、工具栏等界面的控制。框架窗口是应用程序的主要容器,包含视图和文档的显示界面。

C2客户端设计

网络连接与通信

客户端与 C2 服务端通过 HTTPS 建立连接并保持会话。使用 Boost.Beast 库来实现与服务端的通信。

  1. 建立连接:客户端在启动时通过 HTTPS 长连接与服务端进行通信,进行身份验证等。
  2. 命令发送与结果接收:客户端可以通过发送命令请求(如执行 Beacon 操作、查询 Beacon 状态等),等待服务端返回结果并显示。
关键代码
  1. 在 HttpsClient.h 中实现了对服务端的连接并进行响应解析,并且在 StartReceiveResponse 函数中对响应进行持续读取解析,并通过自定义消息(WM_HTTPS_RESPONSE)将响应发送到主框架处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    void StartReceiveResponse()
    {
    // 清空响应正文
    response_.body().clear();
    // 重置响应头部和状态码
    response_.clear();
    // 清空缓冲区
    buffer_.consume(buffer_.size());
    boost::beast::http::async_read(*stream_, buffer_, response_,
    [this](boost::system::error_code ec, std::size_t /*length*/) {
    if (ec) {
    HandleError(ec, "读取服务器响应失败");
    return;
    }
    std::vector<char> body_data(response_.body().begin(), response_.body().end());
    try {
    Command2Server deserializedServer;
    std::stringstream ss(std::string(body_data.begin(), body_data.end()));
    boost::archive::binary_iarchive ia(ss);
    ia >> deserializedServer;

    if (pMainWnd_) {
    Command2Server* pData = new Command2Server(deserializedServer);
    pMainWnd_->PostMessage(WM_HTTPS_RESPONSE, reinterpret_cast<WPARAM>(pData), 0);
    //手动处理消息循环, 让WM_HTTPS_RESPONSE先被处理
    MSG msg;
    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }
    }
    }
    catch (const std::exception& e) {
    CString msg;
    msg.Format(_T("解析响应失败: %S"), e.what());
    AfxOutputDebugString(msg);
    AfxMessageBox(msg);
    }
    // 你可以继续读取响应数据
    StartReceiveResponse();
    });
    }
  2. 在 MainFrm.cpp 中对响应进行细化处理,根据不同的命令类型解析出数据并传递到文档类(CDocument),通过文档类(CDocument)来通知视图类(CView)进行数据更新

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    LRESULT CMainFrame::OnHttpsResponse(WPARAM wParam, LPARAM lParam)
    {
    try
    {
    Command2Server* pData = reinterpret_cast<Command2Server*>(wParam);
    // 获取当前活动视图
    CView* pView = GetActiveView();
    if (pData && pView)
    {
    // 处理 pData 数据
    CString cstrMessage(pData->message.c_str()); // std::string 转 CString
    // 获取与当前视图关联的文档
    CDokeyC2Demov43ClientDoc* pDoc = dynamic_cast<CDokeyC2Demov43ClientDoc*>(pView->GetDocument());
    AfxOutputDebugString(cstrMessage);
    pDoc->AddLog(cstrMessage);
    if (pData->command == "INITIALIZE_CONNECTION")
    {
    client_id_ = pData->client_id;
    pDoc->UpdateClientID(pData->client_id);
    }
    else if(pData->command == "CREATE_LISTENER" || pData->command == "GET_LISTENERS")
    {
    // 反序列化 data 中的 ListenerInfo
    std::stringstream listenerDataStream(pData->data);
    boost::archive::binary_iarchive iaListener(listenerDataStream);
    ListenerInfo newListener;
    iaListener >> newListener; // 从 data 中恢复 ListenerInfo
    pDoc->UpdateListenerInfo(newListener);
    }
    else if (pData->command == "PUSH_BEACON_INFO" || pData->command == "GET_BEACONS")
    {
    std::stringstream beaconSystemInfoDataStream(pData->data);
    boost::archive::binary_iarchive iaBeaconSystemInfos(beaconSystemInfoDataStream);
    BeaconsInfo newBeacons;
    iaBeaconSystemInfos >> newBeacons;
    pDoc->UpdateBeaconInfo(newBeacons);
    }
    else if (pData->command == "COMMAND_RESULT_REPORT")
    {
    pDoc->UpdateCommandResult(*pData);
    }
    else if (pData->command == "UPDATE_DELAY")
    {
    pDoc->UpdateBeaconUpdateDelay(*pData);
    }
    }
    delete pData;
    }
    catch (const std::exception& e)
    {
    CString msg;
    msg.Format(_T("接收响应出现错误: %s"), CString(e.what()));
    AfxMessageBox(msg);
    }
    return LRESULT();
    }

用户界面(UI)设计

在主界面上,使用 SDI 架构的主窗口展示核心功能和信息。主界面通常包括以下部分:

  • 列表控件:显示当前所有连接的 Beacon 信息,包括 Beacon ID、状态、IP 地址、用户名、计算机名、延迟等。
  • 标签页控件:通过动态创建标签来展现不同标签页内容,包括日志标签页、监听器信息标签页、Beacon 命令执行/执行结果展示标签页。
  • 日志视图:显示客户端与服务端的交互日志,帮助用户了解当前操作的执行情况。使用 CRichEdit 控件展示日志信息。属于动态标签页。
  • 监听器信息视图:属于动态标签页,显示通过客户端创建的监听器信息。
  • Beacon 命令操作视图:属于动态标签页,包括输入命令CRichEdit控件、执行结果展示CRichEdit控件;分别用于输入命令、展示执行结果。
界面详情

被控端(Beacon)

Beacon主要功能

定期通信(心跳和报告)

Beacon 定期与 C2 服务端保持连接,以确保与控制中心的联系始终畅通。这种通信通常以 心跳包 形式进行,目的是:

  • 确保 Beacon 仍然处于在线状态。
  • 使得 C2 服务端能够实时监控 Beacon 的状态。

获取命令

Beacon 通过 HTTP/HTTPS/DNS 连接到 C2 服务端并通过接口来获取命令指挥 Beacon 执行不同的任务。命令可能包括:

  • 执行系统命令或脚本。
  • 获取目标系统的信息(如硬件信息、操作系统版本等)。

执行命令

Beacon 收到服务端的指令后,执行相应的操作。根据命令类型,Beacon 可以:

  • 执行系统命令、shell 命令或自定义脚本。
  • 获取并返回系统的状态信息或日志。
  • 执行恶意操作(如删除文件、修改配置、植入后门等)。

反馈执行结果

执行完命令后,Beacon 会将结果反馈给 C2 服务端。这些结果可能包括:

  • 执行成功或失败的状态。
  • 执行过程中的错误信息或输出结果。
  • 执行时间、命令日志等。

TODO

后期实现功能清单

在客户端、服务端实现自动生成Beacon功能

重构Beacon代码结构, 使其结构化, 具备自动生成

服务端、客户端实现创建监听器保存至文件, 运行服务端自动加载并开启监听

支持更多监听器类型(HTTPS、DNS)

评论