前期準(zhǔn)備
通過掃描端口快速發(fā)現(xiàn)目標(biāo)薄弱點,在這之前饲嗽,我們得準(zhǔn)備好端口掃描工具炭玫。
我接觸的掃描工具也不多,常用的端口掃描工具有:
nmap+masscan貌虾、御劍端口掃描工具2020
御劍端口掃描似乎不會顯示協(xié)議吞加,僅憑端口號判斷不夠精準(zhǔn),這時我們可以再用nmap的-sV識別具體服務(wù)尽狠。該工具主要用于快速掃描發(fā)現(xiàn)
在挖掘SRC的過程中衔憨,往往需要對整個C段進(jìn)行端口掃描,這時我們對掃描速度的要求沒那么高袄膏,可以追求更高的精確度践图。我們可以結(jié)合masscan的掃描速度和nmap的端口識別功能寫一個腳本,采用同步多線程沉馆,線程數(shù)默認(rèn)為5码党,masscan掃描速度默認(rèn)為100(可自行調(diào)整),我個人覺得速度100就基本不會出現(xiàn)遺漏的端口斥黑,速度越快越容易遺漏揖盘,我自己寫了個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
# 調(diào)用masscan
def portscan(scan_ip):
global count
temp_ports = [] # 設(shè)定一個臨時端口列表
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() # 等待任務(wù)完成
# 提取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() # 如果端口數(shù)量大于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)
# 調(diào)用nmap識別服務(wù)
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('當(dāng)前是第', count, '個目標(biāo)')
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語句無異常時執(zhí)行else語句
for port, port_info in output_item.items(): # 返回可遍歷的(鍵, 值) 元組數(shù)組
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()
掃描端口的腳本有了,接下來我們得清楚各種端口的利用方式
存在未授權(quán)風(fēng)險的端口服務(wù):
redis耻姥、svn销钝、Hadoop、vnc琐簇、mongodb蒸健、memcached座享、docker、zookeeper似忧、rsync渣叛、ldap、ftp盯捌、couchdb
存在爆破風(fēng)險的端口服務(wù):
rdp淳衙、vnc、redis饺著、ssh箫攀、mongodb、postgresql幼衰、mysql靴跛、oracle、ms-sql-s渡嚣、socks5梢睛、ldap、smtp识椰、ftp绝葡、zebra、snmp裤唠、netbios
那么多風(fēng)險點記不住咋辦挤牛?像對我這種沒有什么經(jīng)驗的菜鳥來說,可以把以上風(fēng)險點寫入腳本中:
對于未授權(quán)的端口服務(wù)种蘸,有能力的話最好能復(fù)現(xiàn)一遍墓赴,然后平時要收集一些利用腳本,下面這個未授權(quán)查詢腳本是在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未授權(quán)")
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未授權(quán)")
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未授權(quán)")
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未授權(quán)")
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未授權(quán)")
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未授權(quán)") # 弱口令,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未授權(quán)")
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未授權(quán)")
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未授權(quán)")
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未授權(quán)")
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)
有時候端口掃描會發(fā)現(xiàn)一些中間件服務(wù)刊侯,比如下圖:
針對這種情況章办,平時可以收集一些中間件漏洞總結(jié)的文章,或者在零組漏洞庫中查找相應(yīng)的漏洞