#!/usr/bin/env python # -*- Mode: Python; tab-width: 4 -*- # # Boot Information Negotiation Layer - OpenSource Implementation # # Copyright (C) 2005-2006 Gianluigi Tiesi # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # ====================================================================== from socket import socket, AF_INET, SOCK_DGRAM, getfqdn from codecs import utf_16_le_decode, utf_16_le_encode, ascii_encode from struct import unpack, pack from sys import argv, exit as sys_exit from time import sleep, time from cPickle import load from os import chdir, getpid from datetime import datetime from getopt import getopt, error as getopt_error __version__ = '0.8' __usage__ = """Usage %s: [-h] [-d] [-l logfile] [-a address] [-p port] [devlist.cache] -h, --help : show this help -d, --daemon : daemonize, unix only [false] -l, --logfile= : logfile when used in daemon mode [/var/log/binlsrv.log] -a, --address= : ip address to bind to [all interfaces] -p, --port= : port to bind to [4011] devlist.cache : device list cache file [devlist.cache in current dir] """ OSC_NOTFOUND=""" Client Installation Wizard
[F3] restart computer


The requested file %s was not found on the server
""" ############# # Make sure there is the trailing / here BASEPATH = '/mnt/disk/ris/OSChooser/English/' WELCOME = 'welcome.osc' DUMPING = False # DUMPING = True ############# NTLM_NEGOTIATE = 1 NTLM_CHALLENGE = 2 NTLM_AUTHENTICATE = 3 NTLM_ANY = 0 #define NTLMSSP_NEGOTIATE_UNICODE 0x00000001 #define NTLMSSP_NEGOTIATE_OEM 0x00000002 #define NTLMSSP_REQUEST_TARGET 0x00000004 #define NTLMSSP_NEGOTIATE_SIGN 0x00000010 #define NTLMSSP_NEGOTIATE_SEAL 0x00000020 #define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080 #define NTLMSSP_NEGOTIATE_NTLM 0x00000200 #define NTLMSSP_NEGOTIATE_00001000 0x00001000 #define NTLMSSP_NEGOTIATE_00002000 0x00002000 #define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000 #define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000 #define NTLMSSP_TARGET_TYPE_SERVER 0x00020000 #define NTLMSSP_NEGOTIATE_NTLM2 0x00080000 #define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000 #define NTLMSSP_NEGOTIATE_128 0x20000000 #define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000 # NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_OEM | NTLMSSP_NEGOTIATE_UNICODE # NTLMSSP_NEGOTIATE_NTLM #0x00000000 # 2 5 #0x00018206 -> # X -> NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_OEM # X -> NTLMSSP_NEGOTIATE_NTLM # X -> NTLMSSP_NEGOTIATE_ALWAYS_SIGN # X -> NTLMSSP_TARGET_TYPE_DOMAIN #0x00808011 -> # X -> NTLMSSP_NEGOTIATE_UNICODE # X -> NTLMSSP_NEGOTIATE_SIGN # X -> NTLMSSP_NEGOTIATE_ALWAYS_SIGN # X -> NTLMSSP_NEGOTIATE_TARGET_INFO #0xa2898215 -> # X -> NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET # X -> NTLMSSP_NEGOTIATE_SIGN # X -> NTLMSSP_NEGOTIATE_NTLM # X -> NTLMSSP_NEGOTIATE_ALWAYS_SIGN # X -> NTLMSSP_NEGOTIATE_NTLM2 | NTLMSSP_TARGET_TYPE_DOMAIN # X -> ??? # X -> ??? #0xC000006FL The user is not allowed to log on at this time. #0xC0000070L The user is not allowed to log on from this workstation. #0xC0000071L The password of this user has expired. #0xC0000072L Account currently disabled. #0xC0000193L This user account has expired. #0xC0000224L The user.s password must be changed before logging on the first time. AUTH_OK = 0x00000000L SEC_E_LOGON_DENIED = 0x8009030cL MAGIC = 'KGS!@#$%' C = '\x81' S = '\x82' FILEREQ = C+'RQU' # 8152 5155 - OSC File request FILEREPLY = S+'RSU' # 8252 5355 - OSC File reply NEG = C+'NEG' # 814e 4547 - NTLM Negotiate CHL = S+'CHL' # 8243 484c - NTLM Sending Challenge AUT = C+'AUT' # 8141 5554 - NTLM Autorize RES = S+'RES' # 8252 4553 - NTLM Auth reply NCQ = C+'NCQ' # 814e 4351 - Network Card Query NCR = S+'NCR' # 824e 4352 - Network Card Reply REQ = C+'REQ' # 8152 4551 - Unknown :( RSP = S+'RSP' # 8252 5350 - Unknown :( OFF = C+'OFF' # 814f 4646 - Maybe like reboot and start new rom # Session expired, only works with code 0x1 UNR = S+'UNR' myfqdn = getfqdn() myhostinfo = myfqdn.split('.', 1) mydomain = myhostinfo.pop() # workaround if hosts files is broken try: myhostname = myhostinfo.pop() except: myhostname = mydomain server_data = { 'domain': mydomain.upper(), 'name' : myhostname.upper(), 'dnsdm' : mydomain, 'fqdn' : myfqdn } tr_table = { '%SERVERNAME%' : server_data['name'], '%SERVERDOMAIN%' : server_data['domain'], '%MACHINENAME%' : 'client', '%NTLMV2Enabled%' : '0', '%ServerUTCFileTime%' : str(int(time())) } devlist = None count = 0 regtype = [ 'REG_NONE', 'REG_SZ', 'REG_EXPAND_SZ', 'REG_BINARY', 'REG_DWORD', 'REG_MULTI_SZ' ] codes = [ 'NULL', 'NAME', 'DOMAIN', 'FQDN', 'DNSDM', 'DNSDM2' ] NULL = chr(0x0) AUTH_U1 = 'N\x11\x155F\r\xa6\xeb' # Challenge AUTH_U2 = '\x05\x02\xce\x0e\x00\x00\x00\x0f' NTLM = 'NTLMSSP\x00' ### Logger class wrapper class Log: """file like for writes with auto flush after each write to ensure that everything is logged, even during an unexpected exit.""" def __init__(self, f): self.f = f def write(self, s): self.f.write(s) self.f.flush() def dotted(data): res = '' for i in range(len(data)): if (ord(data[i]) < 32) or (ord(data[i]) > 127): res += '.' else: res += data[i] return res def hexdump(data): data_len = len(data) off = 0 base = 0 while 1: start = off end = off + 8 if end > data_len: end = data_len values1 = ' '.join([ '%02x' % ord(data[x]) for x in range(start, end) ]) data1 = dotted(data[off:off+8]) off += 8 start = off if start > data_len: start = data_len end = off + 8 if end > data_len: end = data_len values2 = ' '.join([ '%02x' % ord(data[x]) for x in range(start, end) ]) data2 = dotted(data[off:off+8]) off += 8 print '%08x %-23s %-23s |%-8s%-8s|' % (base, values1, values2, data1, data2) base += 16 if end - start < 8: break def utf2ascii(text): return ascii_encode(utf_16_le_decode(text, 'ignore')[0], 'ignore')[0] def ascii2utf(text): return utf_16_le_encode(text)[0] def get_packet(s): try: data, addr = s.recvfrom(1024) except KeyboardInterrupt: print 'Server terminated by user request' s.close() sys_exit(0) pktype = data[:4] if DUMPING: open('/tmp/' + pktype[1:] + '.hex', 'w').write(data) data = data[4:] l = unpack('', pkt[drv_off-8:].replace('\x00','.') data = data[4:] # 0x50 - offset to driver file, -8 from start of packet srv_off = unpack(' %d from start' % (srv_off, srv_off, srv_off-8) #print p, '--->', pkt[srv_off-8:] #print p, '--->', data[srv_off-32:] data = data[4:] # 0x6a - offset for unicode string to service name plen = unpack(' %d from start' % (p_off, p_off, p_off-8) #print p, '--->', pkt[p_off-8:].replace('\x00', '.') data = data[4:] # 0x76 - offset from start for params s1 = data.find('\x00\x00') hid = utf2ascii(data[:s1+1]) data = data[s1+3:] print p, 'hid: %s - Len 0x%x (%d)' % (hid, len(hid), len(hid)) s1 = data.find('\x00\x00') drv = utf2ascii(data[:s1+1]) data = data[s1+3:] print p, 'drv: %s - Len 0x%x (%d)' % (drv, len(drv), len(drv)) s1 = data.find('\x00\x00') srv = utf2ascii(data[:s1+1]) data = data[s1+3:] print p, 'srv: %s - Len 0x%x (%d)' % (srv, len(srv), len(srv)) sets = data.split(NULL) parms = 0 for i in range(0, len(sets), 3): if sets[i] == '': break if sets[i+2] == '': continue name = sets[i] try: t = int(sets[i+1]) except: t = 0 value = sets[i+2] print p, '%s (%s [%d]) = %s' % (name, regtype[t], t, value) parms = parms + 1 print p, 'Total Params:', parms def send_ncq(s, vid, pid, subsys, spath): #vid = 0x1022 #pid = 0x2000 #rev_u1 = 0x2 #rev_u2 = 0x0 #rev_u3 = 0x0 #rev = 0x10 #rev2 = 0x88 #subsys = 0x20001022 #spath = '\\\\Attila\\RemInst\\winpe' #vid = 0x10b7 #pid = 0x9200 rev_u1 = 0x2 rev_u2 = 0x0 rev_u3 = 0x0 #rev_u4 = 0x0 rev = 0x0 rev2 = 0x0 #subsys = 0x0 #spath = '\\\\Attila\RemInst\\Setup\\Italian\\IMAGES\\WINDOWS' data = pack('= 0xffff): print 'Port not in range 1-65534' sys_exit(-1) if len(args): devfile = args[0] try: devlist = load(open(devfile)) except: print 'Could not load %s as cache, build it with infparser.py' % devfile sys_exit(-1) if daemon: daemonize(logfile) thistime = datetime.now() print thistime.strftime("%Y-%m-%d %H:%M:%S") + ' Succesfully loaded %d devices' % len(devlist) s = socket(AF_INET, SOCK_DGRAM) s.bind((address, port)) thistime = datetime.now() print thistime.strftime("%Y-%m-%d %H:%M:%S") + ' Binlserver started... pid %d' % getpid() while 1: addr, t, data = get_packet(s) if t == FILEREQ: u1 = data[:7*4] data = data[7*4:] if data == '\n': send_file(s, addr, u1, BASEPATH, WELCOME) else: filename, params = data.split('\n', 1) filename = filename.lower() + '.osc' params = parse_arguments(params) print 'Client requested:', filename if len(params): print 'Arguments:', repr(params) ## TODO there are also other actions if filename.startswith('launch'): send_file(s, addr, u1, BASEPATH, 'warning.osc') else: send_file(s, addr, u1, BASEPATH, filename) elif t == NEG: decode_ntlm('[C]', data) print 'NEG request, sending CHALLENGE' send_challenge(s, addr, server_data) sleep(1) elif t == AUT: print 'AUT request, sending ok' decode_ntlm('[C]', data) send_res(s, addr, data) sleep(1) elif t == NCQ: print 'NCQ Driver request' if DUMPING: open('/tmp/ncq.hex','w').write(data) vid, pid, subsys, os = decode_ncq('[R]', data) send_ncr(s, addr, vid, pid, subsys, os) elif t == REQ: print 'REQ request, sending Session Expired (RSP not implemented)' decode_req('[C]', data) if DUMPING: open('/tmp/req.hex','w').write(REQ+pack('