|
15 | 15 | import collections |
16 | 16 | import chardet |
17 | 17 | import requests |
18 | | -import ipaddress |
| 18 | +import urllib |
19 | 19 | from collections import OrderedDict |
20 | 20 | from functools import wraps |
21 | 21 | from ipaddress import ip_address, ip_network |
|
51 | 51 | collectionsAbc = collections |
52 | 52 |
|
53 | 53 |
|
| 54 | +def urlparse(address): |
| 55 | + # https://stackoverflow.com/questions/50499273/urlparse-fails-with-simple-url |
| 56 | + try: |
| 57 | + ip = ip_address(address) |
| 58 | + if ip.version == 4: |
| 59 | + return urllib.parse.urlparse(f'tcp://{address}') |
| 60 | + elif ip.version == 6: |
| 61 | + return urllib.parse.urlparse(f'tcp://[{address}]') |
| 62 | + except ValueError: |
| 63 | + pass |
| 64 | + |
| 65 | + if not re.search(r'^[A-Za-z0-9+.\-]+://', address): |
| 66 | + address = f'tcp://{address}' |
| 67 | + return urllib.parse.urlparse(address) |
| 68 | + |
| 69 | + |
54 | 70 | def read_binary(filename): |
55 | 71 | content = '' |
56 | 72 | with open(filename, 'rb') as f: |
@@ -242,7 +258,8 @@ def parse_target_url(url): |
242 | 258 | ret = "[" + ret + "]" |
243 | 259 |
|
244 | 260 | if not re.search("^http[s]*://", ret, re.I) and not re.search("^ws[s]*://", ret, re.I) and '://' not in ret: |
245 | | - if re.search(":443[/]*$", ret): |
| 261 | + port = urlparse(ret).port |
| 262 | + if port and str(port).endswith('443'): |
246 | 263 | ret = "https://" + ret |
247 | 264 | else: |
248 | 265 | ret = "http://" + ret |
@@ -343,11 +360,10 @@ def get_file_items(filename, comment_prefix='#', unicode_=True, lowercase=False, |
343 | 360 | try: |
344 | 361 | with open(filename, 'r') as f: |
345 | 362 | for line in f.readlines(): |
346 | | - # xreadlines doesn't return unicode strings when codecs.open() is used |
347 | | - if comment_prefix and line.find(comment_prefix) != -1: |
348 | | - line = line[:line.find(comment_prefix)] |
349 | | - |
350 | 363 | line = line.strip() |
| 364 | + # xreadlines doesn't return unicode strings when codecs.open() is used |
| 365 | + if comment_prefix and line.startswith(comment_prefix): |
| 366 | + continue |
351 | 367 |
|
352 | 368 | if not unicode_: |
353 | 369 | try: |
@@ -376,38 +392,41 @@ def get_file_items(filename, comment_prefix='#', unicode_=True, lowercase=False, |
376 | 392 | return ret if not unique else ret.keys() |
377 | 393 |
|
378 | 394 |
|
379 | | -def parse_target(address): |
380 | | - target = None |
381 | | - if is_domain_format(address) \ |
382 | | - or is_url_format(address) \ |
383 | | - or is_ip_address_with_port_format(address): |
384 | | - target = address |
| 395 | +def parse_target(address, additional_ports=[]): |
| 396 | + # parse IPv4/IPv6 CIDR |
| 397 | + targets = OrderedSet() |
| 398 | + try: |
| 399 | + for ip in ip_network(address, strict=False).hosts(): |
385 | 400 |
|
386 | | - elif is_ipv6_url_format(address): |
387 | | - conf.ipv6 = True |
388 | | - target = address |
| 401 | + if ip.version == 6: |
| 402 | + conf.ipv6 = True |
389 | 403 |
|
390 | | - elif is_ip_address_format(address): |
391 | | - try: |
392 | | - ip = ip_address(address) |
393 | | - target = ip.exploded |
394 | | - except ValueError: |
395 | | - pass |
396 | | - else: |
397 | | - if is_ipv6_address_format(address): |
| 404 | + targets.add(str(ip)) |
| 405 | + |
| 406 | + for port in additional_ports: |
| 407 | + targets.add(f'[{ip}]:{port}' if conf.ipv6 else f'{ip}:{port}') |
| 408 | + |
| 409 | + return targets |
| 410 | + |
| 411 | + except ValueError: |
| 412 | + pass |
| 413 | + |
| 414 | + # URL |
| 415 | + try: |
| 416 | + if ip_address(urlparse(address).hostname).version == 6: |
398 | 417 | conf.ipv6 = True |
399 | | - try: |
400 | | - ip = ip_address(address) |
401 | | - target = ip.exploded |
402 | | - except ValueError: |
403 | | - try: |
404 | | - network = ip_network(address, strict=False) |
405 | | - for host in network.hosts(): |
406 | | - target = host.exploded |
407 | | - except ValueError: |
408 | | - pass |
| 418 | + except ValueError: |
| 419 | + pass |
409 | 420 |
|
410 | | - return target |
| 421 | + targets.add(address) |
| 422 | + pr = urlparse(address) |
| 423 | + for port in additional_ports: |
| 424 | + netloc = f'[{pr.hostname}]:{port}' if conf.ipv6 else f'{pr.hostname}:{port}' |
| 425 | + t = pr._replace(netloc=netloc).geturl() |
| 426 | + if t.startswith('tcp://'): |
| 427 | + t = t.lstrip('tcp://') |
| 428 | + targets.add(t) |
| 429 | + return targets |
411 | 430 |
|
412 | 431 |
|
413 | 432 | def single_time_log_message(message, level=logging.INFO, flag=None): |
@@ -510,7 +529,7 @@ def get_host_ip(dst='8.8.8.8', check_private=True): |
510 | 529 | finally: |
511 | 530 | s.close() |
512 | 531 |
|
513 | | - if check_private and ipaddress.ip_address(ip).is_private: |
| 532 | + if check_private and ip_address(ip).is_private: |
514 | 533 | logger.warn( |
515 | 534 | f'your wan ip {mosaic(ip)} is a private ip, ' |
516 | 535 | 'there may be some issues in the next stages of exploitation' |
|
0 commit comments