由于最近写c2感觉自己有闭门造车之势,于是读了一遍AHXR/ghost的源码,发现自己很多想法还不成熟,打算先把周遭的c2读一遍,开一个《安全工具月月读》专题,提升一下自己的眼界。我会尽量把blog写得简单易懂,有问题的地方持续改进
0x01 文件结构
作者把很多函数实现都写在头文件中,这操作让代码混乱,不过可以方便引用,c++中的hpp就是这样
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
| ├─Encryption (加密逻辑的实现) │ encrypt.h # 提供加密算法函数 │ ├─ghostdll(动态链接库,将server和zombie共同的结构体和函数提出,减少代码量,方便调用) │ AssemblyInfo.cpp # 文件属性、信息的添加 │ ghostlib.h # 定义公有结构体,声明公有函数 │ ghostlib.cpp # 全局变量声明、公有函数的实现 │ resource.h # 资源宏定义 │ ├─server (服务端) │ │ callbacks.h # 回调函数 │ │ config.h # 主要是菜单、日志函数的宏定义 │ │ console.h # 命令控制逻辑 │ │ gui.h # 图形界面类,继承System::Windows::Forms::Form │ │ info.h # 基本信息获取的实现(获取真实IP等) │ │ resource.h # 资源宏定义 │ │ server.cpp # 界面主逻辑,调用 │ │ │ └─ini (配置文件的读取,使用第三方库INIReader,本篇不会详细描述) │ AssemblyInfo.cpp # 同上 │ ini.h │ ini.cpp │ INIReader.cpp │ INIReader.h │ └─zombie (客户端,植入被控主机) info.h # 声明工具函数 info.cpp # 工具函数的实现 resource.h # 资源宏定义 zombie.cpp # 交互主逻辑
|
0x02 数据结构
数据结构并不复杂,大多数都是直接全局声明的数值和字符串,这里仅介绍结构体和类
struct _clientData
是zombie的主要结构体
1 2 3 4 5
| struct _clientData { SOCKET socketRef; struct addrinfo * clientInfo; char * system_data; };
|
class gui
是server的界面类,继承了windows系统表格基类
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| public ref class gui : public System::Windows::Forms::Form { public: gui(void) { InitializeComponent(); }
protected: ~gui() { if (components) { delete components; } } public: static System::Windows::Forms::NotifyIcon^ taskbarIcon; private: System::ComponentModel::IContainer^ components;
private:
#pragma region Windows Form Designer generated code void InitializeComponent(void) { this->components = (gcnew System::ComponentModel::Container()); System::ComponentModel::ComponentResourceManager^ resources = (gcnew System::ComponentModel::ComponentResourceManager(gui::typeid)); this->taskbarIcon = (gcnew System::Windows::Forms::NotifyIcon(this->components)); this->SuspendLayout(); this->taskbarIcon->BalloonTipIcon = System::Windows::Forms::ToolTipIcon::Info; this->taskbarIcon->BalloonTipText = L"New Client"; this->taskbarIcon->BalloonTipTitle = L"ghost"; this->taskbarIcon->Icon = (cli::safe_cast<System::Drawing::Icon^>(resources->GetObject(L"taskbarIcon.Icon"))); this->taskbarIcon->Text = L"ghost"; this->taskbarIcon->Visible = true;
this->taskbarIcon->MouseDoubleClick += gcnew System::Windows::Forms::MouseEventHandler(this, &gui::taskbarIcon_MouseDoubleClick);
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->ClientSize = System::Drawing::Size(164, 19); this->Icon = (cli::safe_cast<System::Drawing::Icon^>(resources->GetObject(L"$this.Icon"))); this->Name = L"gui"; this->ShowIcon = false; this->ShowInTaskbar = false; this->Text = L"#ghost (github.com/AHXR)"; this->WindowState = System::Windows::Forms::FormWindowState::Minimized; this->Load += gcnew System::EventHandler(this, &gui::gui_Load); this->ResumeLayout(false);
} #pragma endregion private: System::Void gui_Load(System::Object^ sender, System::EventArgs^ e) { } private: System::Void taskbarIcon_MouseDoubleClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) { if (b_hidden) { SHOW_CONSOLE(); freopen("conin$", "r", stdin); freopen("conout$", "w", stdout); freopen("conout$", "w", stderr); HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE); ahxrlogger_handle(hstdout); WARNING("Refreshing clients... Please wait."); refreshClients(); SHOW_GHOST(); } } };
|
对上面的GUI编程暂时不是很了解,就是把参数填到对应的入口中,比如程序图标注册、双击回调函数注册,在构造函数那里还能进行一些扩展操作。
0x03 主要函数及关系
0x31 ghostdll
其中采用vector维护全局clientData集合
std::vector < GHOSTLIB > client_data;
0x32 encrypt
其中存在两个函数接口及其实现
1 2 3 4 5 6
| std::string encryptCMD(std::string text); std::string unencryptCMD(std::string text);
char c_original[ENCRYPT_CMD_LEN] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890,./<>?;:\'\"[]\\{}|=-+_)(*&^%$#@!~` ";
char c_encrypt [ENCRYPT_CMD_LEN] = "yr(GRMJ1#\'Fme]Kc3<0`{QCBa,$pDx2[h;g8_./unU|+fELqYN~}7l>=dzX?WkjTVH%@b6s9viIo4:v5Aw&O*tP!\\S^)Z\" -";
|
原理很简单,密码和明文下标相对应转换,通信全程用这种方法加解密
0x33 zombie
- 先说info.h中的三个工具函数的功能
1 2 3 4 5 6 7 8 9
|
std::string getAntivirus();
std::string real_ip();
DWORD FindProcessId(const std::wstring& processName);
|
- zombie.cpp中除main外的函数功能
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
| DWORD WINAPI t_ping(LPVOID lpParams);
DWORD WINAPI t_payloads(LPVOID lpParams);
void onClientConnect();
void onClientRecData(char* data);
|
- zombie.cpp中main函数逻辑
几个要点:
- wget构建
- 将zombie程序移动到系统目录下
- 杀死taskmgr的策略
- 注册表接口编程
0x34 server
server端的主要逻辑在console.h中
大体的逻辑结构:先设置端口、超时时间,保存刚才的配置,然后在一个while(1)循环内读取命令or选项
代码略丑就不放出来,单纯通过堆砌while和SwitchCase完成逻辑,下面是zombie和server的执行效果
下面用简单的伪代码演示server逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| while(1) switch(input) 1) 显示所有zombie的ID、IP、Port 输入想要继续操作的zombie_id,查看USER、VERSION、AV switch(input_a) 0) 回主界面 1) 执行命令 2) 下载和执行程序 3) 杀死taskmgr开关
2) 配置保存,保存刚才设置的端口和web页面中查询的IP 保存完后下次会读取配置 3) 刷新,如果zombie断连则数据会从全局容器中删除 4) 最小化 选择后我们便可以关闭console,图标会保留在右下角的任务窗口中,双击任务窗口console又会显示
5) 退出
|
0x04 功能设计要点
- 命令传输、执行
- 文件下载、执行
- 操作注册表,自启动
- 心跳探活
- 通信加密
- GUI
- 定时杀死任务管理器
- 真实IP获取
- 获取Antivirus信息
- zombie数据保存
0x05 关键语法及技术
- 宏的使用
#pragma once
:让文件编译一次,防止多次引入,有别于#ifndef
[8]
#pragma comment (lib, "lib_name")
:链接对应的库,这样就不用再手动设置工程settings
#pragma region(endregion)
:pragma region (endregion) 是一个Visio Studio Code Editor中的命令,来定义可以扩展和收缩的代码区域的开头和结尾,可以用来收缩或者展开一段代码。
cin.ignore()
:吃掉一个字符,比如最后的\n
,防止干扰下一次输入
- 这里的
^
并非c#扩展,而是c++/cli中的语法,c++/cli是标准c++的超集,该语法拥有垃圾回收特性
- gcnew(garbage collection new),带有gc特性的new,配合上述3使用
- 注册表函数接口(后续开一篇blog写)
0x06 总结
- 虽然头文件会被包含进cpp文件,但函数实现写在头文件里似乎不是一个好的习惯
- 代码逻辑略为混乱,尤其在命令执行模式和下载执行模式那一块,如果软件定位是一项目一重写那也未尝不可,但如果不是则这么写后期的维护工作量会很大
- 加密是必要的,可以采用不同的对称加密方式进行加密(AES、DES等)
- 通过注册表操作属于持久化的一种形式,持久化是后渗透的另一个大项
- 多线程编程思想,不阻塞主逻辑,在t_ping接收server返回消息和b_payload杀死taskmgr中体现
- 良好的开关设计思想,体现在杀死taskmgr的逻辑中
- 隐藏方式(不弹窗)执行命令
- json和ini都运用到库,有效提高开发效率
- 参数的合理判断,多处对于输入参数的判断采用string输入后转int判断的方式,增强容错性
- 配置存储,下一次运行直接读取先前保存的配置
0x07 参考资料
[1] awesome rat
[2] AHXR/ghost
[3] AHXR/ahxrlogger
[4] AHXR/ahxrwinsocket
[5] MITRE ATT&CK
[6] Why is implementation code in header files?
[7] windows中常见后门持久化方法总结
[8] #pragma once用法总结