最近论坛发布了CS4.8的破解版,抱着学习的心态准备重新分析分析一遍相关内容,本文章将会是一个系列文章,围绕 CS马的生成流程、逆向分析、常见特征检测和对抗分析,由于本人不擅长流量侧的分析,因此没有分析流量侧相关的内容。
CS 客户端页面的相关操作都可以在\aggressor\dialogs
中找到,本文接下来将基于 windows 可执行 stager 进行分析,所以首先会找到对应WindowsExecutableDialog
,其页面和对应的操作如下所示:
跟进dialog/DialogManager.java
的 action函数
,记住这边传递过来的 str 是 Generate
继续跟进 action_noclose 函数:
在点击按钮之后新建了线程然后调用了 dialogAction 方法,而 WindowsExecutableDialog 实现了 DialogListener,同时 override 了 dialogAction 方法,其关键代码如下,其中生成 payload 的函数就是 getPayloadStager。
继续往下跟进 ScListener.java 查看 getPayloadStager 函数:
查看 Stagers.java 的 shellcode 函数:
有三个参数,第一个是上层传递下来的 listener,第二个是 payload 名称,第三个是上层传递下来的参数,标识了 payload 的位数。 调用 GenericStager 的 create() 方法生成对应的 shellcode,GenericStager 是一个抽象函数,其不同的 listener 和架构的实现均存在 stagers 目录下,这里以windows/beacon_http/reverse_http
,模式的listener为例,其对应的实现是 BeaconHTTPStagerX64:
同时 BeaconHTTPStagerX64 继承 GenericHTTPStagerX64
而 GenericHTTPStagerX64 又继承 GenericHTTPStager,GenericHTTPStager 实现了 generate() 方法,这也是 payload 生成的位置:
这段代码,首先从资源文件中取出对应的 shellcode,这里是resources/httpstager.bin
,然后拼接了一些特定的偏移量信息,包括 teamserver ip,端口号,SSL标志,连接标志等,最后 函数执行完返回 shellcode,回到了 dialogAction 函数,然后基于扩展名调用 savefile 保存,跟进 savefile 函数
在最后调用了 post 方法:
创建一个新的线程,调用 dialogResult 方法,该方法位于 WindowsExecutableDialog.java
由于选择的是64位 windows exe,因此调用图中的 patchArtifact 进行 patch
跟进另一个 patchArtifact
调用 _patchArtifact
,这里就是最终处理的位置,第一个参数是写入的 payload 信息,第二个参数则是传递过来的字符串 artifact32.exe
这里就以 beacon 类型生成为例说明, 函数调用顺序如下所示
其中beacon config生成的位置在
仔细观察 exportBeaconStage
:
在后面会调用beacon_obfuscate 混淆 beacon config
public void show() {
/
/
设置windows executable对话框对应信息
this.dialog
=
DialogUtils.dialog(
"Windows Executable"
,
640
,
480
);
/
/
窗口管理对象
DialogManager dialogManager
=
new DialogManager(this.dialog);
/
/
加入对应DialogListener链表
dialogManager.addDialogListener(this);
/
/
Listener选择
dialogManager.sc_listener_stagers(
"listener"
,
"Listener:"
, this.client);
/
/
输出类型选择
dialogManager.combobox(
"output"
,
"Output:"
, CommonUtils.toArray(
"Windows EXE, Windows Service EXE, Windows DLL"
));
/
/
单选,是否生成
64
位beacon
dialogManager.
set
(
"x64"
,
"true"
);
dialogManager.checkbox_add(
"x64"
,
"x64:"
,
"Use x64 payload"
);
/
/
单选,签名
dialogManager.checkbox_add(
"sign"
,
"Sign:"
,
"Sign executable file"
, DataUtils.getSigner(this.client.getData()).available());
/
/
调用 action方法生产对应payload
JButton action
=
dialogManager.action(
"Generate"
);
/
/
help
将会打开帮助文档
JButton
help
=
dialogManager.
help
(
"https://www.cobaltstrike.com/help-windows-executable"
);
this.dialog.add(DialogUtils.description(
"This dialog generates a Windows executable. Use Cobalt Strike Arsenal scripts (Help -> Arsenal) to customize this process."
,
DialogUtils.getDescription3LineDimension()),
"North"
);
this.dialog.add(dialogManager.layout(),
"Center"
);
this.dialog.add(DialogUtils.center(action,
help
),
"South"
);
this.dialog.pack();
this.dialog.setVisible(true);
}
public void show() {
/
/
设置windows executable对话框对应信息
this.dialog
=
DialogUtils.dialog(
"Windows Executable"
,
640
,
480
);
/
/
窗口管理对象
DialogManager dialogManager
=
new DialogManager(this.dialog);
/
/
加入对应DialogListener链表
dialogManager.addDialogListener(this);
/
/
Listener选择
dialogManager.sc_listener_stagers(
"listener"
,
"Listener:"
, this.client);
/
/
输出类型选择
dialogManager.combobox(
"output"
,
"Output:"
, CommonUtils.toArray(
"Windows EXE, Windows Service EXE, Windows DLL"
));
/
/
单选,是否生成
64
位beacon
dialogManager.
set
(
"x64"
,
"true"
);
dialogManager.checkbox_add(
"x64"
,
"x64:"
,
"Use x64 payload"
);
/
/
单选,签名
dialogManager.checkbox_add(
"sign"
,
"Sign:"
,
"Sign executable file"
, DataUtils.getSigner(this.client.getData()).available());
/
/
调用 action方法生产对应payload
JButton action
=
dialogManager.action(
"Generate"
);
/
/
help
将会打开帮助文档
JButton
help
=
dialogManager.
help
(
"https://www.cobaltstrike.com/help-windows-executable"
);
this.dialog.add(DialogUtils.description(
"This dialog generates a Windows executable. Use Cobalt Strike Arsenal scripts (Help -> Arsenal) to customize this process."
,
DialogUtils.getDescription3LineDimension()),
"North"
);
this.dialog.add(dialogManager.layout(),
"Center"
);
this.dialog.add(DialogUtils.center(action,
help
),
"South"
);
this.dialog.pack();
this.dialog.setVisible(true);
}
@Override
/
/
stagers.GenericStager
public byte[] generate() {
try
{
/
/
resources
/
httpstager.
bin
InputStream resource
=
CommonUtils.resource(getStagerFile());
byte[] readAll
=
CommonUtils.readAll(resource);
String bString
=
CommonUtils.bString(readAll);
resource.close();
String
str
=
bString
+
getListener().getStagerHost()
+
(char)
0
;
Packer packer
=
new Packer();
packer.little();
packer.addShort(getListener().getPort());
/
/
teamserver ip
+
port
AssertUtils.TestPatchS(readAll,
4444
, getPortOffset());
String replaceAt
=
CommonUtils.replaceAt(
str
, CommonUtils.bString(packer.getBytes()), getPortOffset());
Packer packer2
=
new Packer();
packer2.little();
packer2.addInt(ReflectiveDLL.EXIT_FUNK_PROCESS);
AssertUtils.TestPatchI(readAll, ReflectiveDLL.EXIT_FUNK_PROCESS, getExitOffset());
String replaceAt2
=
CommonUtils.replaceAt(replaceAt, CommonUtils.bString(packer2.getBytes()), getExitOffset());
Packer packer3
=
new Packer();
packer3.little();
packer3.addShort(getStagePreamble());
AssertUtils.TestPatchS(readAll,
5555
, getSkipOffset());
String replaceAt3
=
CommonUtils.replaceAt(replaceAt2, CommonUtils.bString(packer3.getBytes()), getSkipOffset());
Packer packer4
=
new Packer();
packer4.little();
packer4.addInt(getConnectionFlags());
AssertUtils.TestPatchI(readAll, isSSL() ?
-
2069876224
:
-
2074082816
, getFlagsOffset());
String replaceAt4
=
CommonUtils.replaceAt(replaceAt3, CommonUtils.bString(packer4.getBytes()), getFlagsOffset());
if
(CommonUtils.isin(CommonUtils.repeat(
"X"
,
Property
.VOLUME), replaceAt4)) {
replaceAt4
=
CommonUtils.replaceAt(replaceAt4, getConfig().pad(getHeaders()
+
(char)
0
,
Property
.VOLUME), replaceAt4.indexOf(CommonUtils.repeat(
"X"
,
127
)));
}
return
CommonUtils.toBytes(CommonUtils.replaceAt(replaceAt4, getConfig().pad(getURI()
+
(char)
0
,
79
), replaceAt4.indexOf(CommonUtils.repeat(
"Y"
,
79
),
0
))
+
getConfig().getWatermark());
} catch (IOException e) {
MudgeSanity.logException(
"HttpStagerGeneric: "
+
getStagerFile(), e, false);
return
new byte[
0
];
}
}
@Override
/
/
stagers.GenericStager
public byte[] generate() {
try
{
/
/
resources
/
httpstager.
bin
InputStream resource
=
CommonUtils.resource(getStagerFile());
byte[] readAll
=
CommonUtils.readAll(resource);
String bString
=
CommonUtils.bString(readAll);
resource.close();
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!