专栏名称: FreeBuf
国内关注度最高的全球互联网安全新媒体
目录
相关文章推荐
飞鲸投研  ·  4000亿科技巨头,走投无路! ·  4 天前  
飞鲸投研  ·  4000亿科技巨头,走投无路! ·  4 天前  
FreeBuf  ·  安全什么时候才能被看见? ·  5 天前  
安天集团  ·  安天AVL ... ·  5 天前  
一亩三分地Warald  ·  逆天wlb!还能远程办公的公司有哪些? ·  5 天前  
一亩三分地Warald  ·  逆天wlb!还能远程办公的公司有哪些? ·  5 天前  
安天集团  ·  人民网 | ... ·  6 天前  
51好读  ›  专栏  ›  FreeBuf

如何使用CODASM编码Payload并降低熵值

FreeBuf  · 公众号  · 互联网安全  · 2024-09-17 09:30

正文


关于CODASM


CODASM是一款针对Payload的编码工具,该工具针对红蓝队研究人员设计,可以帮助我们对Payload执行编码操作并显著降低Payload的熵值。




该工具允许您将任意数据编码为伪 ASM 指令,并将其编译到二进制文件的 .text 部分。


开销为 80-120%(例如 380KB CS shellcode => 870KB CODASM 有效负载)。


功能介绍

Payload(尤其是 shellcode)具有相当高的熵,在编译后的二进制文件中的大多数位置看起来都不合适。CODASM 旨在将Payload隐藏在已经具有高熵的位置:.text包含二进制文件编译代码的部分。为此,CODASM 将生成良性 shellcode,并支持嵌入任意Payload。


CODASM 是一个 Python 脚本,可以生成以下内容:

1、良性的 shellcode,用于任意Payload;

2、一个 C 头文件,你可以在程序中使用它将 shellcode 嵌入到二进制文件中,并在运行时检索它;


对于编码Payload,CODASM 执行以下操作:

1、生成有效 x86_64 函数;

2、将Payload字节嵌入到指令操作数中(例如mov eax, <4 bytes of payload>);

3、对嵌入的Payload字节进行异或加密;


为了解码Payload,生成的 C 头文件执行以下操作:

1、解析单个指令,直到检索到所需数量的Payload字节:

2、检测单个指令,确定它们是否包含Payload字节;

3、如果指令包含Payload字节,则提取并解密它们;


工具要求


Python 3


工具安装


由于该工具基于Python 3开发,因此我们首先需要在本地设备上安装并配置好最新版本的Python 3环境。


接下来,广大研究人员可以直接使用下列命令将该项目源码克隆至本地:

git clone https://github.com/NVISOsecurity/codasm


工具使用


usage: codasm.py [-h] -i INPUT [-oa OUT_ASM] [-ob OUT_BIN] [-oc OUT_C] [-op OUT_P] [--rng RNG] [-vbmin VAL_BYTES_MIN] [-vbmax VAL_BYTES_MAX] [-vbch VAL_BYTES_CHANCE] [-v]

CODASM encoding utility

options:
-h, --help 显示此帮助消息并退出
-i INPUT, --input INPUT
要编码为ASM/二进制指令的输入文件的路径
-oa OUT_ASM, --out-asm OUT_ASM
将生成的ASM指令写入的路径
-ob OUT_BIN, --out-bin OUT_BIN
将生成的二进制指令写入的路径
-oc OUT_C, --out-c OUT_C
将生成的CODASM解码器写入的路径
-op OUT_P, --out-p OUT_P
将嵌入式Payload写入的路径
--rng RNG 用于随机化的rng种子(异或密钥、有效载荷指令顺序、解码操作顺序)
-vbmin VAL_BYTES_MIN, --val-bytes-min VAL_BYTES_MIN
编码到单个方法中的最小字节数(默认值64)
-vbmax VAL_BYTES_MAX, --val-bytes-max VAL_BYTES_MAX
编码到单个方法中的最大字节数(默认256)
-vbch VAL_BYTES_CHANCE, --val-bytes-chance VAL_BYTES_CHANCE
操作成为编码数据而不是虚拟数据的机会(0.1-0.9,默认值0.1)
-v, --verbose 输出详细程度(0-3,默认值0)

Note: ASM output is meant to be used for manual reference, not for compiling!


使用样例


1、准备 shellcode (例如 CS/BR shellcode) 并另存为shellcode.bin;

2、使用 CODASM 对 shellcode 进行编码:

./codasm.py --i shellcode.bin -oc codasmloader.h


3、复制codasmloader.h到您最喜欢的加载器或独立的最小加载器中(例如/demo/codasm.c);


4、确保decode在调用 shellcode 之前调用 CODASM:

/* ~snip CODASM decode~*/

#include

// "INTEXT" macro ensures that the payload is pleced into the `.text` section
INTEXT uint8_t payload[5978] = {
0x50, 0x53, 0x51, 0x52, 0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41, 0x53, /* ... */
};

int main() {
uint64_t xor_key = 0xFFFDA6803A51E3FB; // Generated by CODASM
uint8_t* input = (uint8_t*)payload;
uint8_t* output = (uint8_t*)malloc(sizeof(payload));
uint32_t output_length = 0xac4;
int32_t res = 0;
if ((res = decode(input, sizeof(payload), output, output_length, xor_key)) < 0)
return 1; // Some doo-doo happened, investigate value of res
// You successfully recovered the payload, do something fun with it here :)
return 0;
}

5、编译你的加载器;

6、确保生成的二进制文件删除所有调试信息;


工具运行演示


# Input file
$ cat test.txt
my secret message

# Encode & generate C file
$ ./codasm.py -i test.txt -ob test.bin -oa test.asm -oc test.c

# Generated mashine code
$ xxd test.bin
00000000: 5053 5152 4150 4151 4152 4153 4154 4155 PSQRAPAQARASATAU
00000010: 4156 4157 4883 ec28 751b 757f c705 5696 AVAWH..(u.u...V.
00000020: 44e9 6df6 61d6 84c0 488d 0d91 9e02 2640 D.m.a...H.....&@
00000030: 32ff b91b 8052 f975 8283 f99c 85c9 7513 2....R.u......u.
00000040: 4883 c428 415f 415e 415d 415c 415b 415a H..(A_A^A]A\A[AZ
00000050: 4159 4158 5a59 5b68 c3cc AYAXZY[X..

# Generated ASM
$ cat test.asm
push rax
...
push r15
sub RSP, 0x28
jnz 0x1B
jnz 0x7F
mov 0xE9449656,0xD661F66D
test al,al
lea 0x26029E91
xor dil,dil
mov ecx,0xF952801B
jnz 0x82
cmp ecx,0x9C
test ecx,ecx
jnz 0x13
add RSP, 0x28
pop r15
...
pop rax
retn
; Padding

# Prepare generated C file for compilation
$ sed -i "s/\/\* Generated.*//" test.c
$ sed -i "s/Sample usage://" test.c
$ sed -i "s/}\*\//}/" test.c

# Minimal program in C file:
$ tail -n 30 test.c

#endif // CODASM_DECODE




#include

INTEXT uint8_t payload[90] = {
0x50, 0x53, 0x51, 0x52, 0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41, 0x53, /* ... */

int main() {
uint64_t xor_key = 0xFBE38A21E5760676;
uint8_t* input = (uint8_t*)payload;
uint8_t* output = (uint8_t*)malloc(sizeof(payload));
uint32_t output_length = 0x11;
int32_t res = 0;
if ((res = decode(input, sizeof(payload), output, output_length, xor_key)) < 0)
return 1; // Some doo-doo happened, investigate value of res
// You successfully recovered the payload, do something fun with it here :)
return 0;
}

# Compile using MINGW
$ x86_64-w64-mingw32-gcc test.c -o test.exe

# Find generated ASM in compiled EXE:
$ objdump -d test.exe

...
0000000140002da0 :
140002da0: 50 push %rax
...
140002db2: 41 57 push %r15
140002db4: 48 83 ec 28 sub $0x28,%rsp
140002db8: 75 1b jne 140002dd5
140002dba: 75 7f jne 140002e3b

140002dbc: c7 05 56 96 44 e9 6d movl $0xd661f66d,-0x16bb69aa(%rip) # 12944c41c <__size_of_stack_reserve__+0x12924c41c>
140002dc3: f6 61 d6
140002dc6: 84 c0 test %al,%al
140002dc8: 48 8d 0d 91 9e 02 26 lea 0x26029e91(%rip),%rcx # 16602cc60 <.debug_ranges+0x25fde970>
140002dcf: 40 32 ff xor %dil,%dil
140002dd2: b9 1b 80 52 f9 mov $0xf952801b,%ecx
140002dd7: 75 82 jne 140002d5b
140002dd9: 83 f9 9c cmp $0xffffff9c,%ecx
140002ddc: 85 c9 test %ecx,%ecx
140002dde: 75 13 jne 140002df3
140002de0: 48 83 c4 28 add $0x28,%rsp
140002de4: 41 5f pop %r15
...
140002df7: 58 pop %rax
140002df8: c3 ret
140002df9: cc int3
...

嵌入的payload在IDA中如下所示:



检测


您可以使用以下 YARA 规则来潜在地检测嵌入 CODASM 生成的输出的 PECOFF 文件。您可以在代码块下方找到规则的简要说明:

import "pe"
import "math"


private rule IsPE
{
meta:
description = "Tests whether the file starts with the MZ header."
author = "Moritz Thomas"
date = "2024-07-24"

condition:
uint16(0) == 0x5A4D
}

private rule ExampleUsage
{
meta:
description = "Detects malloc and invoking the decode function, passing in references to the .data section"
author = "Moritz Thomas"
date = "2024-07-24"

strings:
$AllocDecode = {
8b 0d ?? ?? ?? ?? // MOV ECX, dword ptr [DAT_1400fb070]
89 4c 24 ?? // MOV dword ptr [RSP + 0x3c], ECX
e8 ?? ?? ?? ?? // CALL MSVCRT.DLL::malloc
// ECX = DAT_1400fb070
// [RSP + 0x3c] = ECX
// malloc()
8b 15 ?? ?? ?? ?? // MOV EDX, dword ptr [DAT_1400fb080]
44 8b 4c 24 ?? // MOV R9D, dword ptr [RSP + 0x3c]
// EDX = DAT_1400fb080
// R9D = [RSP + 0x3c]
48 8d 0d ?? ?? ?? ?? // LEA RCX, [FUN_140001460]
48 89 c3 // MOV RBX, RAX
48 8b 05 ?? ?? ?? ?? // MOV RAX, qword ptr [DAT_1400fb090]
49 89 d8 // MOV R8, RBX
48 89 44 24 ?? // MOV qword ptr [RSP + 0x20], RAX
// RCX = &FUN_140001460
// RBX = RAX
// RAX = DAT_1400fb090
// R8 = RBX
// [RSP + 0x20] = RAX
e8 ?? ?? ?? ?? // CALL FUN_1400f9cf0
// FUN_1400f9cf0()
89 c2 // MOV EDX, EAX
85 c0 // TEST EAX, EAX
79 13 // JNS LAB_1400fac7b
// EDX = EAX
// if (EAX >= 0) goto LAB_1400fac7b
48 8d 0d ?? ?? ?? ?? // LEA RCX, [LAB_1400fc0e3]
e8 ?? ?? ?? ?? // CALL FUN_1400faae0
// RCX = &LAB_1400fc0e3
// FUN_1400faae0()
b8 01 00 00 00 // MOV EAX, 0x1
eb ?? // JMP !AB_1400faca3
// EAX = 1
// goto LAB_1400faca3
83 f8 42 // CMP EAX, 0x42
75 ?? // JNZ LAB_1400fac8a
// if (EAX != 0x42) goto LAB_1400fac8a
b9 22 00 00 00 // MOV ECX, 0x22
e8 ?? ?? ?? ?? // CALL FUN_140001460
// ECX = 0x22
// FUN_140001460()
}
// if (iVar2 == 0x42) FUN_140001460(0x22, 0x42);
$PseudoCall= {
83 f8 42 // CMP EAX, 0x42
75 ?? // JNZ LAB_1400FACA3
b9 22 00 00 00 // MOV ECX, 0x22
e8 ?? ?? ?? ?? // CALL FUN_140001460
}

condition:
IsPE and $AllocDecode and $PseudoCall
}

private rule Decode
{
meta:
description = "Detects parameter validation (null-checks), returning -2 and performing a pseudo call RBX(22h)"
author = "Moritz Thomas"
date = "2024-07-24"

strings:
$NullTest = {
48 85 c9 // TEST param_1,param_1
74 ?? // JZ LAB_1400f9d76
4d 85 c0 // TEST param_3,param_3
74 ?? // JZ LAB_1400f9d76
}
$ReturnMinusTwo = {
b8 fe ff ff ff // MOV EAX,0xfffffffe
}
$PseudoCall = {
b9 22 00 00 00 // MOV ECX,0x22
ff d3 // CALL RBX
}

condition:
IsPE and $NullTest and $ReturnMinusTwo and $PseudoCall
}

private rule PEAnalysis
{
meta:
description = "Detects PE files with very large .text sections (>=90%) that have reasonable entropy (5.0 < e(.text) < 7.0)."
author = "Moritz Thomas"
date = "2024-07-24"

condition:
IsPE and // Check for MZ header
for any i in (0..pe.number_of_sections - 1) : (
pe.sections[i].name == ".text" and
pe.sections[i].raw_data_size > (filesize * 0.9) and
math.in_range(
math.entropy(pe.sections[i].raw_data_offset, pe.sections[i].raw_data_size),
5.0, 7.0
)

)
}

rule CODASMed
{
condition:
ExampleUsage or (Decode and PEAnalysis)
}

许可证协议


本项目的开发与发布遵循MIT开源许可协议。


项目地址


CODASM

https://github.com/NVISOsecurity/codasm


FreeBuf粉丝交流群招新啦!
在这里,拓宽网安边界
甲方安全建设干货;
乙方最新技术理念;
全球最新的网络安全资讯;
群内不定期开启各种抽奖活动;
FreeBuf盲盒、大象公仔......
扫码添加小蜜蜂微信回复「加群」,申请加入群聊

https://www.nviso.eu/