├── README.md ├── hot_ip.py ├── image └── run.png └── iplist.txt /README.md: -------------------------------------------------------------------------------- 1 | # check_ip 2 | check_ip结合开源威胁情报,判断数据包中IP地址或者域名地址或者IP清单中的IP地址恶意性   3 | 4 | ### 运行条件 5 | **威胁情报查询接口** 6 | AlienVault网址:https://otx.alienvault.com/api 7 | 需要注册一个账号以得到API_KEY,添加到hot_ip.py开头的对应字段 8 | ``` 9 | url = 'http://ip.taobao.com/service/getIpInfo.php?ip=' 10 | API_KEY = '' #add API_key 11 | OTX_SERVER = 'https://otx.alienvault.com/' 12 | ``` 13 | 14 | **程序依赖包** 15 | ``` 16 | pip install OTXv2 pandas dpkt 17 | ``` 18 | 19 | **运行环境** 20 | ``` 21 | ubuntu 16.04 64bit; ubuntu 18.04 64bit 22 | ``` 23 | 24 | ### 运行事例 25 | usage: hot_ip.py --pcapfile=./out.pcap –d -c #数据包解析模式,对目的IP地址的恶意性进行排查 26 | usage: hot_ip.py --IPfile=./iplist.txt -c #IP清单文件解析模式,排查清单中的IP地址的恶意性 27 | usage: hot_ip.py --pcapf=./out.pcap -p #数据包解析模式,对域名地址的恶意性进行排查 28 | ![Image test](https://github.com/scu-igroup/check_ip/blob/master/image/run.png) 29 | 30 | ### 其他项 31 | **中间文件** 32 | ``` 33 | out_IP.txt #解析网络数据包时产生,源/目的IP列表 34 | ip_location.txt #解析IP地址地理信息 35 | malicious_results.txt #可疑IP地址信息 36 | out_DNS.txt #解析网络数据包时产生,域名地址列表 37 | maliciousDNS.txt #可疑域名地址信息 38 | ``` 39 | **查看结果** 40 | ``` 41 | f117@ubuntu:~/Downloads/check_ip$ cat malicious_results.txt 42 | 117.18.237.29 potentially malicious 台湾-台湾-台北 https://otx.alienvault.com/indicator/ip/117.18.237.29 43 | 52.230.80.159 potentially malicious 新加坡-XX-XX https://otx.alienvault.com/indicator/ip/52.230.80.159 44 | 40.77.226.249 potentially malicious 爱尔兰-Dublin-XX https://otx.alienvault.com/indicator/ip/40.77.226.249 45 | 46 | fxx@fxx-X450LD:~/myprojects/check_ip$ cat maliciousDNS.txt 47 | graph.facebook.com https://otx.alienvault.com/indicator/hostname/graph.facebook.com 48 | data.flurry.com https://otx.alienvault.com/indicator/hostname/data.flurry.com 49 | alog.umeng.com https://otx.alienvault.com/indicator/hostname/alog.umeng.com 50 | ``` 51 | **参考文章** 52 | http://www.freebuf.com/system/171987.html 53 | 54 | 55 | -------------------------------------------------------------------------------- /hot_ip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #coding:utf-8 3 | import urllib2 4 | import json 5 | import time 6 | import sys 7 | import dpkt 8 | import socket 9 | from optparse import OptionParser 10 | from OTXv2 import OTXv2 11 | import IndicatorTypes 12 | import argparse 13 | #import get_malicious 14 | import hashlib 15 | #!/usr/bin/env python 16 | 17 | reload(sys); 18 | sys.setdefaultencoding('utf-8'); 19 | 20 | url = 'http://ip.taobao.com/service/getIpInfo.php?ip=' 21 | API_KEY = '9facf92fec62a521c595aece7d587c954db42f7d0e05bc92ccf04c489a125944' #change your API_Key 22 | OTX_SERVER = 'https://otx.alienvault.com/' 23 | 24 | def getValue(results, keys): 25 | if type(keys) is list and len(keys) > 0: 26 | 27 | if type(results) is dict: 28 | key = keys.pop(0) 29 | if key in results: 30 | return getValue(results[key], keys) 31 | else: 32 | return None 33 | else: 34 | if type(results) is list and len(results) > 0: 35 | return getValue(results[0], keys) 36 | else: 37 | return results 38 | else: 39 | return results 40 | 41 | def CheckIp(otx, ip): 42 | alerts = [] 43 | result = otx.get_indicator_details_by_section(IndicatorTypes.IPv4, ip, 'general') 44 | 45 | # Return nothing if it's in the whitelist 46 | validation = getValue(result, ['validation']) 47 | if not validation: 48 | pulses = getValue(result, ['pulse_info', 'pulses']) 49 | if pulses: 50 | for pulse in pulses: 51 | if 'name' in pulse: 52 | alerts.append('In pulse: ' + pulse['name']) 53 | 54 | return alerts 55 | 56 | def checkhostname(otx, hostname): 57 | alerts = [] 58 | result = otx.get_indicator_details_by_section(IndicatorTypes.HOSTNAME, hostname, 'general') 59 | 60 | # Return nothing if it's in the whitelist 61 | validation = getValue(result, ['validation']) 62 | if not validation: 63 | pulses = getValue(result, ['pulse_info', 'pulses']) 64 | if pulses: 65 | for pulse in pulses: 66 | if 'name' in pulse: 67 | alerts.append('In pulse: ' + pulse['name']) 68 | 69 | result = otx.get_indicator_details_by_section(IndicatorTypes.DOMAIN, hostname, 'general') 70 | # Return nothing if it's in the whitelist 71 | validation = getValue(result, ['validation']) 72 | if not validation: 73 | pulses = getValue(result, ['pulse_info', 'pulses']) 74 | if pulses: 75 | for pulse in pulses: 76 | if 'name' in pulse: 77 | alerts.append('In pulse: ' + pulse['name']) 78 | 79 | 80 | return alerts 81 | 82 | def IsMalicious(Lfile): 83 | otx = OTXv2(API_KEY, server=OTX_SERVER) 84 | fin = open(Lfile,'r') 85 | fout = open('malicious_results.txt','w') 86 | count = 0 87 | for line in fin: 88 | ip= line.strip('\n').split(":")[0].strip() 89 | location = line.strip('\n').split(":")[1].strip() 90 | alerts = CheckIp(otx,ip) 91 | if len(alerts) > 0: 92 | reference = 'https://otx.alienvault.com/indicator/ip/'+ip 93 | print 'potentially malicious:'+line+"reference:"+reference 94 | print>>fout,"%s"%line 95 | count=count+1 96 | print "-----There are %d malicious IP-----" % count 97 | fin.close() 98 | fout.close() 99 | 100 | def IsDNSMalicious(Lfile): 101 | otx = OTXv2(API_KEY, server=OTX_SERVER) 102 | fin = open(Lfile,'r') 103 | fout = open('maliciousDNS.txt','w') 104 | count=0 105 | for line in fin: 106 | hostname=line.strip('\n').strip() 107 | alerts=checkhostname(otx,hostname) 108 | if len(alerts)>0: 109 | reference='https://otx.alienvault.com/indicator/hostname/'+hostname 110 | print 'potentially malicious:'+line+'reference:'+reference 111 | print>>fout,"%s"%(line+' '+reference) 112 | count=count+1 113 | print "-----There are %d malicious hostname-----"%count 114 | fin.close() 115 | fout.close() 116 | 117 | def checkTaobaoIP(ip, fout1): 118 | try: 119 | response = urllib2.urlopen(url + ip, timeout=10) 120 | result = response.readlines() 121 | data = json.loads(result[0]) 122 | #sys.exit(1) 123 | if data['data']['city'] == "内网IP": 124 | return 125 | return "%15s: %s-%s-%s" % (ip,data['data']['country'],data['data']['region'],data['data']['city']) 126 | except Exception,err: 127 | print "[error] %s" % err 128 | print >>fout1, "%s" %ip 129 | return "%15s: time out" % ip 130 | 131 | def parseIPlistLocation(IPfile): 132 | try: 133 | f1 = open(IPfile, "r+") 134 | ips = f1.readlines() 135 | f1.close() 136 | fout1 = open("out_error.txt", "wb") 137 | f2 = open('ip_location.txt', 'w') 138 | for ip in ips: 139 | line = checkTaobaoIP(ip.strip(), fout1) 140 | if line: 141 | print line.encode('utf-8') 142 | f2.write(line.encode('utf-8')+'\n') 143 | else: 144 | continue 145 | #print line 146 | f2.close() 147 | fout1.close() 148 | #print "---------IP Location Result---------" 149 | except Exception,err: 150 | print "[error] %s" % err 151 | 152 | def parseDNS(pcap): 153 | lines_seen=set() 154 | 155 | fout1=open("text1.txt","wb") 156 | fout2=open("out_DNS.txt","wb") 157 | for(ts,buf) in pcap: 158 | try: 159 | 160 | eth = dpkt.ethernet.Ethernet(buf) 161 | ip=eth.data 162 | udp=ip.data 163 | if udp.dport!=53 and udp.sport!=53: 164 | continue 165 | dns=dpkt.dns.DNS(udp.data) 166 | if dns.qr!=dpkt.dns.DNS_R: 167 | continue 168 | if dns.opcode!=dpkt.dns.DNS_QUERY: 169 | continue 170 | if dns.rcode!=dpkt.dns.DNS_RCODE_NOERR: 171 | continue 172 | if len(dns.an)<1: 173 | continue 174 | for answer in dns.an: 175 | #print answer.name 176 | result=answer.name+"\n" 177 | fout1.write(result) 178 | 179 | except: 180 | pass 181 | fout1.close() 182 | f=open("text1.txt","r") 183 | 184 | for line in f: 185 | 186 | if line not in lines_seen: 187 | print line 188 | fout2.write(line) 189 | lines_seen.add(line) 190 | 191 | f.close 192 | fout2.close 193 | 194 | def printPcap(pcap, if_srcIp, if_dstIP): 195 | flowList = [[] for i in range(20000)] 196 | counts = 0 197 | countFlow = [0]*20000 198 | isFlag = 0 199 | fout = open("out_IP.txt", "wb") 200 | for (ts,buf) in pcap: 201 | try: 202 | eth = dpkt.ethernet.Ethernet(buf) 203 | if not isinstance(eth.data, dpkt.ip.IP): 204 | #print('Non IP Packet type not supported %s' % eth.data.__class__.__name__) 205 | continue 206 | ip = eth.data 207 | if isinstance(ip.data, dpkt.icmp.ICMP): 208 | continue 209 | if isinstance(ip.data, dpkt.igmp.IGMP): 210 | continue #filter tcp packets 211 | src = socket.inet_ntoa(ip.src) 212 | dst = socket.inet_ntoa(ip.dst) 213 | udp = ip.data 214 | if counts == 0 : 215 | flowList[0].append(src) 216 | flowList[0].append(udp.sport) 217 | flowList[0].append(dst) 218 | flowList[0].append(udp.dport) 219 | counts = counts + 1 220 | countFlow[0] = 1 221 | '''if flowList[0][2] == '119.23.18.179':''' 222 | if if_srcIp == True: 223 | print >>fout, "%s"% (flowList[0][0]) 224 | print "%s"% (flowList[0][0]) 225 | if if_dstIP == True: 226 | print >>fout, "%s"% (flowList[0][2]) 227 | print "%s"% (flowList[0][2]) 228 | continue 229 | 230 | if if_srcIp == True: 231 | for i in range(0, counts): 232 | if flowList[i][0] == src: 233 | countFlow[i] = countFlow[i] + 1 234 | isFlag = 1 235 | break 236 | else: 237 | isFlag = 0 238 | continue 239 | 240 | if if_dstIP == True: 241 | for i in range(0, counts): 242 | if flowList[i][2] == dst: 243 | countFlow[i] = countFlow[i] + 1 244 | isFlag = 1 245 | break 246 | else: 247 | isFlag = 0 248 | continue 249 | 250 | if i == counts - 1 and isFlag == 0: 251 | flowList[counts].append(src) 252 | flowList[counts].append(udp.sport) 253 | flowList[counts].append(dst) 254 | flowList[counts].append(udp.dport) 255 | '''if flowList[counts][2] == '119.23.18.179':''' #filter some packets relying on dstIP 256 | if if_srcIp == True: 257 | print >>fout, "%s"% (flowList[counts][0]) 258 | print "%s"% (flowList[counts][0]) 259 | if if_dstIP == True: 260 | print >>fout, "%s"% (flowList[counts][2]) 261 | print "%s"% (flowList[counts][2]) 262 | 263 | countFlow[counts] = 1 264 | counts = counts + 1 265 | 266 | isFlag = 0 267 | except Exception,err: 268 | print "[error] %s" % err 269 | 270 | fout.close 271 | 272 | if __name__ == "__main__": 273 | usage = "usage: hot_ip.py --pcapfile=./out.pcap -d -c |--OR--| hot_ip.py --IPfile=./iplist.txt -c|--OR--|hot_ip.py --pcapf=./out.pcap -p" 274 | parser = OptionParser(usage=usage) 275 | parser.add_option( 276 | "--pcapfile", dest="pcapfile", 277 | action='store', type='string', 278 | help="special the pcap file path", 279 | default=None 280 | ) 281 | 282 | parser.add_option( 283 | "--pcapf",dest="pcapf", 284 | action='store',type='string', 285 | help="special the pcap file path", 286 | default=None) 287 | 288 | parser.add_option( 289 | "--IPfile", dest="IPfile", 290 | action='store', type='string', 291 | help="special the IP list file path", 292 | default=None 293 | ) 294 | 295 | parser.add_option( 296 | "-s", "--srcIP", action="store_true", 297 | help="parse pcapfile srcIP location", 298 | dest="srcIP", default=False 299 | ) 300 | 301 | parser.add_option( 302 | "-d", "--dstIP", action="store_true", 303 | help="parse pcapfile dstIP location", 304 | dest="dstIP", default=False 305 | ) 306 | 307 | parser.add_option( 308 | "-c", "--check", action="store_true", 309 | help="check whether IP is malicious", 310 | dest="checkIP", default=False 311 | ) 312 | 313 | parser.add_option( 314 | "-p", "--checkhostname",action='store_true', 315 | help="check whether hostname is malicious", 316 | dest="checkhostname",default=False) 317 | 318 | (options, args) = parser.parse_args() 319 | 320 | if (options.pcapfile is None) and (options.IPfile is None)and(options.pcapf is None): 321 | print usage 322 | sys.exit(0) 323 | 324 | if options.srcIP == True and options.dstIP == True: 325 | print "either -s or -d, can not both" 326 | sys.exit(0) 327 | 328 | print "Let's start!" 329 | print "------------------------------------" 330 | 331 | if options.IPfile is not None: 332 | parseIPlistLocation(options.IPfile) 333 | if options.checkIP == True: 334 | print "-------------check ip--------------" 335 | IsMalicious("./ip_location.txt") 336 | sys.exit(0) 337 | 338 | if options.pcapf is not None: 339 | f=open(options.pcapf) 340 | try: 341 | pcap = dpkt.pcapng.Reader(f) 342 | except: 343 | print "it is not pcapng format..." 344 | f.close() 345 | finally: 346 | f = open(options.pcapf) 347 | pcap = dpkt.pcap.Reader(f) 348 | parseDNS(pcap) 349 | 350 | if options.checkhostname==True: 351 | print "-----------check hostname-----------" 352 | IsDNSMalicious("./out_DNS.txt") 353 | sys.exit(0) 354 | 355 | if options.pcapfile is not None: 356 | if (options.srcIP or options.dstIP) == False: 357 | print "choose -s or -d" 358 | sys.exit(0) 359 | f = open(options.pcapfile) 360 | try: 361 | pcap = dpkt.pcapng.Reader(f) 362 | except: 363 | print "it is not pcapng format..." 364 | f.close() 365 | finally: 366 | f = open(options.pcapfile) 367 | pcap = dpkt.pcap.Reader(f) 368 | printPcap(pcap, options.srcIP, options.dstIP) 369 | parseIPlistLocation("./out_IP.txt") 370 | 371 | if options.checkIP == True: 372 | print "-------------check ip--------------" 373 | IsMalicious("./ip_location.txt") 374 | sys.exit(0) 375 | -------------------------------------------------------------------------------- /image/run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NewBee119/check_ip/628fd801731342c4759e1f4988e76cf939844f5a/image/run.png -------------------------------------------------------------------------------- /iplist.txt: -------------------------------------------------------------------------------- 1 | 79.166.237.196 2 | 151.237.76.4 3 | 86.115.8.114 4 | 117.18.237.29 5 | 39.90.172.72 6 | 58.40.80.48 7 | 61.170.136.100 8 | 175.146.13.86 9 | 88.250.25.192 10 | 182.151.200.18 11 | 111.9.44.141 12 | 52.230.80.159 13 | 119.4.113.79 14 | 183.220.184.107 15 | 40.77.226.249 16 | --------------------------------------------------------------------------------