前期准备
通过扫描端口快速发现目标薄弱点,在这之前,我们得准备好端口扫描工具。
我接触的扫描工具也不多,常用的端口扫描工具有:
nmap+masscan、御剑端口扫描工具2020
御剑端口扫描似乎不会显示协议,仅凭端口号判断不够精准,这时我们可以再用nmap的-sV识别具体服务。该工具主要用于快速扫描发现
在挖掘SRC的过程中,往往需要对整个C段进行端口扫描,这时我们对扫描速度的要求没那么高,可以追求更高的精确度。我们可以结合masscan的扫描速度和nmap的端口识别功能写一个脚本,采用同步多线程,线程数默认为5,masscan扫描速度默认为100(可自行调整),我个人觉得速度100就基本不会出现遗漏的端口,速度越快越容易遗漏,我自己写了个demo,大佬轻喷:
# -*- coding: UTF-8 -*-
import queue
import nmap
import datetime
import threading
import json
import os
import urllib3
import subprocess
import sys
import click
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
lock = threading.Lock()
final_domains = set()
insert = set()
ports = []
bad_ips = []
class PortScan(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self._queue = queue
def run(self):
while not self._queue.empty():
scan_ip = self._queue.get()
try:
portscan(scan_ip)
except Exception as e:
print('run:', str(e))
pass
# 调用masscan
def portscan(scan_ip):
global count
temp_ports = [] # 设定一个临时端口列表
name = scan_ip + '.json'
command = 'masscan.exe ' + scan_ip +' -p21,22,23,25,53,67,68,80,81,82,83,84,85,86,87,88,89,110,139,143,161,300,' \
'389,443,445,465,512,513,514,591,593,832,837,873,888,901,981,993,1010,1080,1100,1241,1311,1352,1433,1434,' \
'1521,1527,1582,1583,1723,1944,2049,2082,2082,2086,2087,2095,2096,2181,2222,2301,2375,2480,3000,3128,3306,' \
'3333,3389,4000,4001,4002,4100,4125,4243,4443,4444,4567,4711,4712,4848,4849,4993,5000,5104,5108,5432,5555,' \
'5632,5800,5801,5802,5900,5901,5984,5985,5986,6082,6225,6346,6347,6379,6443,6480,6543,6789,6984,7000,7001,' \
'7002,7396,7474,7674,7675,7777,7778,8000,8001,8002,8003,8004,8005,8006,8008,8009,8010,8014,8042,8069,8075,' \
'8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8095,8016,8118,8123,8161,8172,8181,' \
'8200,8222,8243,8280,8281,8333,8384,8403,8443,8500,8530,8531,8800,8806,8834,8880,8881,8887,8888,8910,8983,' \
'8989,8990,8991,9000,9043,9060,9080,9090,9091,9200,9294,9295,9300,9443,9444,9800,9981,9988,9990,9999,10000,' \
'10880,11211,11371,12043,12046,12443,15672,16225,16080,18091,18092,20000,20720,24465,27017,27018,28017,28080,' \
'30821,43110,50070,61600 -oJ ' + name + ' --rate 100'
child = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
child.wait() # 等待任务完成
# 提取json文件中的端口
if os.path.exists(name):
with open(name, 'r') as f:
for line in f:
if line.startswith('{ '):
temp = json.loads(line[:-2])
temp1 = temp["ports"][0]
temp_ports.append(str(temp1["port"]))
else:
print('文件不存在')
sys.exit()
if len(temp_ports) > 40:
count += 1
print(scan_ip + ' 疑似存在waf')
bad_ips.append(scan_ip)
temp_ports.clear() # 如果端口数量大于40,说明可能存在防火墙,属于误报,清空列表
else:
ports.append(temp_ports) # 小于40则放到总端口列表里
ips[scan_ip] = temp_ports
if os.path.exists(name):
os.remove(name)
print('file detele')
if ips.get(scan_ip):
Scan(scan_ip)
# 调用nmap识别服务
def Scan(scan_ip):
global count
open_ports_list = ips[scan_ip]
open_ports = ",".join(open_ports_list)
nm = nmap.PortScanner()
lock.acquire()
click.secho(f'[*] 开始nmap扫描 ip: {scan_ip} => 端口: {open_ports}', fg='red')
count += 1
print('当前是第', count, '个目标')
lock.release()
try:
ret = nm.scan(scan_ip, open_ports, arguments=nmap_arguments)
try:
output_item = ret['scan'][scan_ip]['tcp']
except Exception:
pass
else: # try语句无异常时执行else语句
for port, port_info in output_item.items(): # 返回可遍历的(键, 值) 元组数组
save_item = f"[+] {scan_ip} {port} {port_info['name']} {port_info['product']} {port_info['version']}"
insert.add(scan_ip + '\t' + str(port) + '\t' + port_info['name'] + '\t' + port_info['product'] + ' '
+ port_info['version'] + '\n')
lock.acquire()
print(save_item)
lock.release()
fw = open('ports3.txt', 'w+', encoding='utf-8')
fw.writelines(insert)
fw.close()
except Exception as e:
print(str(e))
pass
def main():
que = queue.Queue()
try:
# 要扫描的ip列表,一行一个
f = open(r'ips.txt', 'r')
for line in f.readlines():
final_ip = line.strip()
que.put(final_ip)
f.close()
threads = []
thread_count = 5
for i in range(thread_count):
threads.append(PortScan(que))
for t in threads:
t.start()
for t in threads:
t.join()
except Exception as e:
print('Main:', e)
pass
spend_time = (datetime.datetime.now() - start_time).seconds
print("疑似存在waf的IP:")
print(bad_ips)
print('程序共运行了: ' + str(spend_time) + '秒')
if __name__ == '__main__':
ips = {}
index = 1
count = 0
start_time = datetime.datetime.now()
path = r"D:\Security-Tools\masscan1.0.4\x64" # masscan所在路径,可自行修改
os.chdir(path)
nmap_arguments = "-sV -Pn"
main()
扫描端口的脚本有了,接下来我们得清楚各种端口的利用方式
存在未授权风险的端口服务:
redis、svn、Hadoop、vnc、mongodb、memcached、docker、zookeeper、rsync、ldap、ftp、couchdb
存在爆破风险的端口服务:
rdp、vnc、redis、ssh、mongodb、postgresql、mysql、oracle、ms-sql-s、socks5、ldap、smtp、ftp、zebra、snmp、netbios
那么多风险点记不住咋办?像对我这种没有什么经验的菜鸟来说,可以把以上风险点写入脚本中:
对于未授权的端口服务,有能力的话最好能复现一遍,然后平时要收集一些利用脚本,下面这个未授权查询脚本是在github上看到的,可以再自行添加
import socket
import pymongo
import requests
import ftplib
from tqdm import tqdm
import sys
from concurrent.futures import ThreadPoolExecutor
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1464.0 Safari/537.36'}
def redis(ip):
try:
socket.setdefaulttimeout(5)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, 6379))
s.send(bytes("INFO\r\n", 'UTF-8'))
result = s.recv(1024).decode()
if "redis_version" in result:
print(ip + ":6379 redis未授权")
s.close()
except Exception as e:
pass
finally:
bar.update(1)
def mongodb(ip):
try:
conn = pymongo.MongoClient(ip, 27017, socketTimeoutMS=4000)
dbname = conn.list_database_names()
print(ip + ":27017 mongodb未授权")
conn.close()
except Exception as e:
pass
finally:
bar.update(1)
def memcached(ip):
try:
socket.setdefaulttimeout(5)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, 11211))
s.send(bytes('stats\r\n', 'UTF-8'))
if 'version' in s.recv(1024).decode():
print(ip + ":11211 memcached未授权")
s.close()
except Exception as e:
pass
finally:
bar.update(1)
def elasticsearch(ip):
try:
url = 'http://' + ip + ':9200/_cat'
r = requests.get(url, headers=headers, timeout=15)
if '/_cat/master' in r.content.decode():
print(ip + ":9200 elasticsearch未授权")
except Exception as e:
pass
finally:
bar.update(1)
def zookeeper(ip):
try:
socket.setdefaulttimeout(5)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, 2181))
s.send(bytes('envi', 'UTF-8'))
data = s.recv(1024).decode()
s.close()
if 'Environment' in data:
print(ip + ":2181 zookeeper未授权")
except:
pass
finally:
bar.update(1)
def ftp(ip):
try:
ftp = ftplib.FTP.connect(ip,21,timeout=15)
ftp.login('anonymous', 'Aa@12345678') # 匿名访问,用户名为anonymous,密码为空或任意邮箱
print(ip + ":21 FTP未授权") # 弱口令,username:FTP password:FTP或空
except Exception as e: # username: USET password: pass
pass
finally:
bar.update(1)
def CouchDB(ip):
try:
url = 'http://' + ip + ':5984'+'/_utils/'
r = requests.get(url, headers=headers, timeout=15)
if 'couchdb-logo' in r.content.decode():
print(ip + ":5984 CouchDB未授权")
except Exception as e:
pass
finally:
bar.update(1)
def docker(ip):
try:
url = 'http://' + ip + ':2375'+'/version'
r = requests.get(url, headers=headers, timeout=15)
if 'ApiVersion' in r.content.decode():
print(ip + ":2375 docker api未授权")
except Exception as e:
pass
finally:
bar.update(1)
def Hadoop(ip):
try:
url = 'http://' + ip + ':50070'+'/dfshealth.html'
r = requests.get(url, headers=headers, timeout=15)
if 'hadoop.css' in r.content.decode():
print(ip + ":50070 Hadoop未授权")
except Exception as e:
pass
finally:
bar.update(1)
def Jenkins(ip):
try:
url = 'http://' + ip + ':8080' + '/manage'
r = requests.get(url, headers=headers, timeout=15)
if 'Jenkins' in r.content.decode():
print(ip + ":8080 Jenkins api未授权")
except Exception as e:
pass
finally:
bar.update(1)
if __name__ == '__main__':
if len(sys.argv) == 1:
print("Usage:python3 unauthorized-check.py url.txt")
file = sys.argv[1]
with open(file, "r", encoding='UTF-8') as f:
line = [i for i in f.readlines()]
bar = tqdm(total=len(line)*9)
with ThreadPoolExecutor(max_workers=20) as pool:
for target in line:
target=target.strip()
pool.submit(redis, target)
pool.submit(Hadoop, target)
pool.submit(docker, target)
pool.submit(CouchDB, target)
pool.submit(ftp, target)
pool.submit(zookeeper, target)
pool.submit(elasticsearch, target)
pool.submit(memcached, target)
pool.submit(mongodb, target)
pool.submit(Jenkins, target)
有时候端口扫描会发现一些中间件服务,比如下图:
针对这种情况,平时可以收集一些中间件漏洞总结的文章,或者在零组漏洞库中查找相应的漏洞
网友评论