├── images ├── 20230420143607.jpg └── 20230420143625.jpg ├── README.md └── HostInfoScan.py /images/20230420143607.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y0-kan/HostInfoScan/HEAD/images/20230420143607.jpg -------------------------------------------------------------------------------- /images/20230420143625.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y0-kan/HostInfoScan/HEAD/images/20230420143625.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HostInfoScan 2 | 红队小工具 | 利用DCERPC协议,无需认证获取Windows机器主机信息和多网卡信息 3 | ## 简介 4 | 分析和部分代码参考了倾旋师傅的[文章](https://payloads.online/archivers/2020-07-16/1/) 5 | 6 | **本工具主要用于探测内网中Windows机器的操作系统信息、域名、主机名以及多网卡信息,可以辅助红队快速定位多网卡主机,以及判断机器是否在域内。** 7 | 8 | **优点: 9 | 无需认证,只要目标的135端口开放即可获得信息** 10 | 11 | 12 | ## 效果 13 | 域内机器: 14 | ![image](images/20230420143625.jpg) 15 | 16 | 工作组机器: 17 | ![image](images/20230420143607.jpg) 18 | 19 | ## 使用 20 | ``` 21 | usage: HostInfoScan.py [-h] -i IP [-t THREADS] [-a ATTACK] [-o OUTPUT] 22 | 23 | optional arguments: 24 | -h, --help show this help message and exit 25 | -i IP, --ip IP IP Address,expample:192.168.0.1, 192.168.0.1-100, 192.168.0.1/24, ip.txt 26 | -t THREADS, --threads THREADS 27 | threads, default 20 28 | -a ATTACK, --attack ATTACK 29 | choose attack: 0:all 1:OSInfo 2:NetWorkInfo, defualt 0 30 | -o OUTPUT, --output OUTPUT 31 | Output result, default: log.txt 32 | ``` 33 | 34 | 常规使用 35 | ``` 36 | python3 HostInfoScan.py -i 192.168.0.1/24 37 | python3 HostInfoScan.py -i 192.168.0.1/24 -a 1 #只进行主机信息探测(操作系统、域名、主机名) 38 | python3 HostInfoScan.py -i 192.168.0.1/24 -a 2 #只进行多网卡信息探测 39 | python3 HostInfoScan.py -i 192.168.0.1/24 -a 0 -t 5 -o result.txt 40 | ``` 41 | ip支持多种输入形式 42 | ``` 43 | python3 HostInfoScan.py -i 192.168.0.100 44 | python3 HostInfoScan.py -i 192.168.0.1-50 45 | python3 HostInfoScan.py -i 192.168.0.1/24 46 | python3 HostInfoScan.py -i ip.txt 47 | ``` 48 | 49 | ## 免责声明 50 | 本工具仅面向合法授权的企业安全建设行为,如您需要测试本工具的可用性,请自行搭建靶机环境。 51 | 52 | 在使用本工具进行检测时,您应确保该行为符合当地的法律法规,并且已经取得了足够的授权。请勿对非授权目标进行扫描。 53 | 54 | 如您在使用本工具的过程中存在任何非法行为,您需自行承担相应后果,我们将不承担任何法律及连带责任。 55 | 56 | 在安装并使用本工具前,请您务必审慎阅读、充分理解各条款内容。 除非您已充分阅读、完全理解并接受本协议所有条款,否则,请您不要安装并使用本工具。您的使用行为或者您以其他任何明示或者默示方式表示接受本协议的,即视为您已阅读并同意本协议的约束。 57 | -------------------------------------------------------------------------------- /HostInfoScan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from base64 import b64encode 5 | from argparse import ArgumentParser, FileType 6 | from queue import Queue 7 | from threading import Thread 8 | from threading import RLock 9 | import sys 10 | import socket 11 | import ipaddress 12 | import logging 13 | import binascii, time 14 | 15 | TIME_OUT = 3 16 | RESULT_LIST1 = [] 17 | RESULT_LIST2 = [] 18 | length = 0 19 | 20 | 21 | def get_ip_list(ip_str) -> list: 22 | ip_list = [] 23 | if '.txt' in ip_str: 24 | with open(ip_str, 'r') as f: 25 | for ip in f.readlines(): 26 | ip_list.extend(get_ip_list(ip.strip('\n'))) 27 | else: 28 | if '-' in ip_str: 29 | for i in range(int(ip_str.split('-')[0].split('.')[3]), int(ip_str.split('-')[1]) + 1): 30 | ip_list.append(ip_str.split('.')[0] + '.' + ip_str.split('.')[1] + '.' + ip_str.split('.')[2] + '.' + str(i)) 31 | elif '/' in ip_str: 32 | try: 33 | for ip in ipaddress.IPv4Network(ip_str).hosts(): 34 | ip_list.append(str(ip)) 35 | except ValueError: 36 | ip_str = ip_str.split('.')[0] + '.' + ip_str.split('.')[1] +'.' + ip_str.split('.')[2] + '.' + '0' + '/24' 37 | for ip in ipaddress.IPv4Network(ip_str).hosts(): 38 | ip_list.append(str(ip)) 39 | else: 40 | ip_list.append(ip_str.strip()) 41 | return ip_list 42 | 43 | 44 | def attribute_name(Target_Info_bytes): 45 | global length 46 | att_name_length = int.from_bytes(Target_Info_bytes[length + 2:length + 4], byteorder='little') 47 | att_name = Target_Info_bytes[length + 4:length + 4 + att_name_length].replace(b"\x00", b"").decode( 48 | encoding="unicode_escape") 49 | length = length + 4 + att_name_length 50 | return att_name 51 | 52 | 53 | def send_packet(ip): 54 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 55 | try: 56 | sock.settimeout(TIME_OUT) 57 | sock.connect((ip, 135)) 58 | buffer_v1 = b"\x05\x00\x0b\x03\x10\x00\x00\x00\x48\x00\x00\x00\x01\x00\x00\x00\xb8\x10\xb8\x10\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x08\x83\xaf\xe1\x1f\x5d\xc9\x11\x91\xa4\x08\x00\x2b\x14\xa0\xfa\x03\x00\x00\x00\x33\x05\x71\x71\xba\xbe\x37\x49\x83\x19\xb5\xdb\xef\x9c\xcc\x36\x01\x00\x00\x00" 59 | sock.send(buffer_v1) 60 | packet1 = sock.recv(1024) 61 | digit = "x86" 62 | if b"\x33\x05\x71\x71\xBA\xBE\x37\x49\x83\x19\xB5\xDB\xEF\x9C\xCC\x36" in packet1: 63 | digit = "x64" 64 | return digit 65 | except Exception as e: 66 | # print(e) 67 | return -1 68 | finally: 69 | sock.close() 70 | 71 | 72 | def get_osinfo(ip): 73 | global length 74 | lock = RLock() 75 | 76 | osinfo = { 77 | "NetBIOS_domain_name": "", 78 | "NetBIOS_computer_name": "", 79 | "DNS_domain_name": "", 80 | "DNS_computer_name": "", 81 | } 82 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 83 | try: 84 | sock.settimeout(TIME_OUT) 85 | sock.connect((ip, 135)) 86 | buffer_v2 = b"\x05\x00\x0b\x03\x10\x00\x00\x00\x78\x00\x28\x00\x03\x00\x00\x00\xb8\x10\xb8\x10\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x01\x00\xa0\x01\x00\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\x46\x00\x00\x00\x00\x04\x5d\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00\x2b\x10\x48\x60\x02\x00\x00\x00\x0a\x02\x00\x00\x00\x00\x00\x00\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00\x07\x82\x08\xa2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x01\xb1\x1d\x00\x00\x00\x0f" 87 | sock.send(buffer_v2) 88 | packet2 = sock.recv(4096) 89 | #print(packet2) 90 | digit = send_packet(ip) 91 | OS_Version_bytes = packet2[int('0xa0', 16) - 54 + 10:int('0xa0', 16) - 54 + 18] 92 | Major_Version = int.from_bytes(OS_Version_bytes[0:1], byteorder='little') 93 | Minor_Version = int.from_bytes(OS_Version_bytes[1:2], byteorder='little') 94 | Build_Number = int.from_bytes(OS_Version_bytes[2:4], byteorder='little') 95 | NTLM_Current_Reversion = int.from_bytes(OS_Version_bytes[7:8], byteorder='little') 96 | OS_Verison = "Windows Version {0}.{1} Build {2} {3}".format(Major_Version, Minor_Version, Build_Number, digit) 97 | 98 | Target_Info_Length_bytes = packet2[int('0xa0', 16) - 54 + 2:int('0xa0', 16) - 54 + 4] 99 | Target_Info_Length = int.from_bytes(Target_Info_Length_bytes, byteorder='little') 100 | Target_Info_bytes = packet2[-Target_Info_Length:-4] # 最后四个0x00000000 101 | lock.acquire() # 上锁 102 | print("[*] " + ip + ' OS Info :') 103 | print("\t[->]", "OS_Verison :", OS_Verison) 104 | #print(Target_Info_bytes) 105 | for k in osinfo.keys(): 106 | osinfo[k] = attribute_name(Target_Info_bytes) 107 | print("\t[->]", k, ":", osinfo[k]) 108 | lock.release() # 开锁 109 | length = 0 110 | osinfo["OS_Verison"] = OS_Verison 111 | result = {ip: osinfo} 112 | return result 113 | except Exception as e: 114 | return -1 115 | finally: 116 | sock.close() 117 | 118 | def get_addres(ip): 119 | lock = RLock() 120 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 121 | try: 122 | sock.settimeout(TIME_OUT) 123 | sock.connect((ip,135)) 124 | buffer_v1 = b"\x05\x00\x0b\x03\x10\x00\x00\x00\x48\x00\x00\x00\x01\x00\x00\x00\xb8\x10\xb8\x10\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\xc4\xfe\xfc\x99\x60\x52\x1b\x10\xbb\xcb\x00\xaa\x00\x21\x34\x7a\x00\x00\x00\x00\x04\x5d\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00\x2b\x10\x48\x60\x02\x00\x00\x00" 125 | buffer_v2 = b"\x05\x00\x00\x03\x10\x00\x00\x00\x18\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00" 126 | sock.send(buffer_v1) 127 | packet = sock.recv(1024) 128 | sock.send(buffer_v2) 129 | packet = sock.recv(4096) 130 | #print(packet) 131 | packet_v2 = packet[42:] 132 | packet_v2_end = packet_v2.find(b"\x09\x00\xff\xff\x00\x00") 133 | packet_v2 = packet_v2[:packet_v2_end] 134 | hostname_list = packet_v2.split(b"\x00\x00") 135 | #print(hostname_list) 136 | result = {ip:[]} 137 | lock.acquire() # 上锁 138 | print("[*] " + ip + ' Network Info :') 139 | for h in hostname_list: 140 | h = h.replace(b'\x07\x00',b'') 141 | h = h.replace(b'\x00',b'') 142 | if h == '': 143 | continue 144 | if h.decode() != '': 145 | print("\t[->]" + h.decode()) 146 | result[ip].append(h) 147 | lock.release() # 开锁 148 | #print(result) 149 | return result 150 | except Exception as e: 151 | return -1 152 | finally: 153 | sock.close() 154 | 155 | def worker(q,a): 156 | while True: 157 | try: 158 | data = q.get() 159 | if a == 1: 160 | result1 = get_osinfo(data) 161 | if result1 != -1 : 162 | RESULT_LIST1.append(result1) 163 | elif a == 2: 164 | result2 = get_addres(data) 165 | if result2 != -1: 166 | RESULT_LIST2.append(result2) 167 | else: 168 | result1 = get_osinfo(data) 169 | result2 = get_addres(data) 170 | if result1 != -1 and result2 != -1: 171 | RESULT_LIST1.append(result1) 172 | RESULT_LIST2.append(result2) 173 | except Exception as e: 174 | sys.stderr.write(str(e)) 175 | finally: 176 | q.task_done() 177 | 178 | 179 | def main(): 180 | parser = ArgumentParser() 181 | parser.add_argument('-i', '--ip', help=u'IP Address,expample:192.168.0.1, 192.168.0.1-100, 192.168.0.1/24, ip.txt', required=True,type=str) 182 | parser.add_argument('-t', '--threads', help=u'threads, default 20', default=20, type=int) 183 | parser.add_argument('-a', '--attack', help=u'choose attack: 0:all 1:OSInfo 2:NetWorkInfo, defualt 0', default=0, type=int) 184 | parser.add_argument('-o', '--output', help=u'Output result, default: log.txt', default='log.txt', type=FileType('a+')) 185 | 186 | args = parser.parse_args() 187 | if args.ip is None: 188 | print("Some Wrong.") 189 | q = Queue(args.threads) 190 | 191 | for _ in range(args.threads): 192 | t = Thread(target=worker, args=(q,args.attack)) 193 | t.daemon = True 194 | t.start() 195 | 196 | ip_list = get_ip_list(args.ip) 197 | 198 | for i in ip_list: 199 | q.put(i) 200 | q.join() 201 | 202 | for osinfo_dict in RESULT_LIST1: 203 | for ip in osinfo_dict.keys(): 204 | args.output.write("[*] " + ip + "\n") 205 | for k, v in osinfo_dict[ip].items(): 206 | args.output.write("\t[->] " + k + ":" + v + "\n") 207 | # print(osinfo_dict) 208 | for host in RESULT_LIST2: 209 | for ip in host.keys(): 210 | args.output.write("[*] " + ip + "\n") 211 | for other_ip in host[ip]: 212 | if other_ip.decode() != '': 213 | args.output.write("\t[->] " + other_ip.decode() + "\n") 214 | 215 | if __name__ == '__main__': 216 | main() 217 | --------------------------------------------------------------------------------