手把手带你打造一款适合自己的扫描工具
0x01 简言
作为一名安全从业人员,在平时的工作中,少不了进行一系列漏洞的挖掘验证以及修复。那么除了很多业务逻辑漏洞需要人为的识别和测试以外,一些常规类的漏洞挖掘是可以依据自动化扫描工具去实现的。当然有很多非常成熟且强大功能的安全扫描工具。但本篇文章主要以python编写一款轻便型的扫描脚本工具。
代码地址:https://github.com/get0shell/AScan.git
0x02 目标
1)轻量级且便携性高,python脚本支持随时执行
2)满足子域名猜解、目录爆破、漏洞扫描以及弱口令爆破等功能
3)字典支持根据不同需求进行增减调整优化
4)漏洞检测规则依托json文件,可依据poc复现进行编写
0x03 开发实战
该主动扫描器主要包括子域名扫描、目录扫描、漏洞扫描以及弱口令扫描等四大模块。
3.1 子域名扫描模块
子域名扫描,即对主域名的二级域名存活进行请求判断。具体实现逻辑如下:
1)打印需要输入主域名格式,并进行接受输入的域名内容
2)将接收到的主域名传入具体扫描的方法中
3)从子域名字典中便利二级域名的关键词,并和主域名进行拼接
4)对拼接完成的域名进行get请求,状态码为200则存在并进行打印输出
5)利用多线程进行子域名的扫描(线程数量默认为5)
具体代码如下:
# @Author : alan
# @File : subScan.py
import requests
import threading
def run(e,s):
header = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0",
}
s.acquire()
with open('./plug/sub.txt', 'rt', encoding='utf-8') as f:
m = f.readlines()
for i in m:
url = 'http://' + str(i.strip('\n')) + '.' + str(e)
try:
response = requests.get(url, headers=header)
if response.status_code == 200:
print('[-]存在域名:' + str(i.strip('\n')) + '.' + str(e))
else:
pass
except requests.exceptions.ConnectionError:
requests.status_codes = 'xxx'
def subScan():
print('======================================================')
print('域名格式:xxx.com')
print('======================================================')
domain = input('请输入域名:').strip()
mutex = threading.Lock()
for i in range(5):
t = threading.Thread(target=run,args=(domain,mutex))
t.start()
3.2 目录扫描模块
目录扫描,即对应用系统的url目录存活进行请求判断。目录扫描支持多域名目录扫描和单域名目录扫描,因此代码逻辑进行分开分析,具体如下:
1、单域名目录扫描
1)打印需要输出扫描模式参数规格,以及域名提交格式
2)接收到扫描模式为1时调用单个域名扫描方法
3)提示输入需要扫描的url,并获取传入的url
4)从目录字典中遍历目录关键词,并和传入的url进行拼接
5)将拼接完成的url进行get请求,状态码为200则存在并进行打印输出
2、多域名目录扫描
1)打印需要输出扫描模式参数规格,以及域名提交格式
2)接收到扫描模式为2时调用多个域名扫描方法
3)提示输入需要扫描的url目录,并获取传入的目录文件
4)便利目录文件中的url,且从目录字典中遍历目录关键词,并和便利出的url进行拼接
5)将拼接完成的url进行get请求,状态码为200则存在并进行打印输出
具体代码如下:
# @Author : alan
# @File : dirScan.py
import requests
def scan():
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0"
}
url = input('请输入url:').strip()
with open('./plug/dir.txt', 'rt', encoding='utf-8') as f:
m = f.readlines()
for i in m:
Url = url + i
response = requests.get(Url, headers=headers)
if response.status_code == 200:
print('[+]存在目录:' + i)
else:
pass
def scan1():
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0"
}
pwd = input('请输入文件路径:').strip()
with open(pwd, 'rt', encoding='utf-8') as f:
m = f.readlines()
for i in m:
with open('./plug/dir.txt', 'rt', encoding='utf-8') as f:
j = f.readlines()
for h in j:
Url = str(i.strip('\n')) + str(h)
response = requests.get(Url, headers=headers)
if response.status_code == 200:
print('[+]存在目录:' + Url)
else:
pass
def dirScan():
print('======================================================')
print('参数规格:')
print('1、单个域名扫描')
print('2、多域名文件扫描')
print('域名格式:http://www.xxx.com')
print('======================================================')
t = input('请输入扫描模式:').strip()
if t == '1':
scan()
elif t == '2':
scan1()
else:
print('参数错误!')
3.3 漏洞扫描模块
漏洞扫描,即对应用系统中存在的漏洞进行请求判断。该模块中扫描逻辑比较简单,主要是从poc文件中获取请求payload进行请求,然后通过响应体中关键内容进行判断是否漏洞存在。这块可以根据自己测试扫描的重点进行对应poc文件的编写,程序以json文件为固定文件格式。具体poc的json文件格式如下:
{
"name": "druid 监控页未授权访问",
"payload": "/druid/index.html",
"res": "(\\S)Druid Stat Index(\\S){2}title(\\S)"
}
具体代码逻辑如下:
1)打印需要输入url格式,并接收输入的url内容
2)将接收到的url传入具体扫描的方法中
3)从poc目录下便利出所有的poc文件,并对该文件中的内容进行读取
4)将读取到的payload和url进行拼接,
5)对拼接完成的url进行get请求,利用正在匹配响应内容中是否存在poc文件中的特征内容
6)如果匹配成功则打印存在该漏洞,以及存在的url+payload
7)利用多线程实现漏洞的扫描
具体代码如下:
# @Author : alan
# @File : vulScan.py
import json
import os
import threading
import requests
import re
def run(u, i):
header = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0",
}
i.acquire()
pathDir = os.listdir('./plug/vul/')
for allDir in pathDir:
p = os.path.join('./plug/vul/' + allDir)
try:
with open(p, 'rt', encoding='utf-8') as f:
json_data = json.load(f)
url = u + json_data['payload']
response = requests.get(url, headers=header)
i = re.search(json_data['res'], response.text)
if i:
print('[+] 存在漏洞:' + json_data['name'])
print('[+]'+url)
except Exception:
print('请求异常,检查url是否正确')
def vulScan():
print('url格式:http://www.xxx.com/ OR http://www.xxx.com/?id=')
domain = input('请输入域名:').strip()
mutex = threading.Lock()
for i in range(2):
t = threading.Thread(target=run, args=(domain, mutex))
t.start()
3.4 弱口令扫描模块
弱口令扫描,即对系统不同服务的账户密码进行猜解判断。该模块包括SSH弱口令、FTP弱口令、以及MYSQL弱口令等三大模块。
3.4.1 SSH弱口令扫描模块
SSH弱口令扫描模块主要是针对ssh服务的账户和密码进行猜解,这边主要调用paramiko库进行实现,因此需要提前安装该库。具体代码逻辑如下:
1)读取用户名和密码字典文件中的用户名和字典
2)提示输入ip以及端口,并获取输入的内容
3)将输入的ip和端口传入对应的连接方法
4)利用paramiko.SSHClient()进行连接测试
5)连接成功则打印对应用户名和密码
具体代码如下:
import paramiko
import threading
def sshScan(t, i, o, u, p):
try:
t.acquire()
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname=i, port=o, username=str(u), password=str(p), timeout=10)
print('[+]SSH连接成功,用户:%s,密码:%s' % (u, p))
client.close()
except:
t.release()
def ssh():
with open('./plug/passdict/username.txt', 'r')as f:
user = f.readlines()
with open('./plug/passdict/password.txt', 'r')as f:
passw = f.readlines()
ip = input("请输入IP:").strip()
port = input("请输入端口:").strip()
s = threading.Semaphore(10)
for i in user:
username = i.strip()
for j in passw:
password = j.strip()
t = threading.Thread(target=sshScan, args=(s, ip, port, username, password))
t.start()
3.4.2 FTP弱口令扫描模块
FTP弱口令扫描模块主要针对ftp服务的用户名和密码进行猜解,主要利用ftplib库进行连接测试,这块需要设计有账户密码登录以及无用户密码登录,具体代码逻辑如下:
1、无用户密码登录
1)提示输入ip以及端口,并获取输入的内容
2)将输入的ip和端口传入对应的连接方法
3)利用ftplib.FTP()进行连接测试
4)连接成功则打印成功信息
具体代码如下:
import ftplib
def ftp_anyScan(t, i, o):
try:
t.acquire()
ftp = ftplib.FTP()
ftp.connect(i, o, 2)
ftp.login()
ftp.quit()
print('[+]FTP连接成功,无用户名密码')
except:
t.release()
def ftp_any():
ip = input("请输入IP:").strip()
port = input("请输入端口:").strip()
s = threading.Semaphore(10)
t = threading.Thread(target=ftp_anyScan, args=(s, ip, port))
t.start()
1、用户密码登录
1)读取用户名和密码字典文件中的用户名和字典
2)提示输入ip以及端口,并获取输入的内容
3)将输入的ip和端口传入对应的连接方法
4)利用ftplib.FTP()进行连接测试
5)连接成功则打印对应用户名和密码
具体代码如下:
import ftplib
def ftpScan(t, i, o, u, p):
try:
t.acquire()
ftp = ftplib.FTP()
ftp.connect(i, o, 2)
ftp.login(u, p)
ftp.quit()
print('[+]FTP连接成功,用户:%s,密码:%s' % (u, p))
except:
t.release()
def ftp():
with open('./plug/passdict/username.txt', 'r')as f:
user = f.readlines()
with open('./plug/passdict/password.txt', 'r')as f:
passw = f.readlines()
ip = input("请输入IP:").strip()
port = input("请输入端口:").strip()
s = threading.Semaphore(10)
for i in user:
username = i.strip()
for j in passw:
password = j.strip()
t = threading.Thread(target=ftpScan, args=(s, ip, port, username, password))
t.start()
3.4.3 MYSQL弱口令扫描模块
MYSQL弱口令扫描模块主要是针对mysql服务的账户和密码进行猜解,这边主要调用pymysql库进行实现,因此需要提前安装该库。具体代码逻辑如下:
1)读取用户名和密码字典文件中的用户名和字典
2)提示输入ip以及端口,并获取输入的内容
3)将输入的ip和端口传入对应的连接方法
4)利用pymysql.connect()进行连接测试
5)连接成功则打印对应用户名和密码
具体代码如下:
import pymysql
def mysqlScan(t, i, u, p):
try:
t.acquire()
db = pymysql.connect(host=i, user=u, password=p)
print('[+]MYSQL连接成功,用户:%s,密码:%s' % (u, p))
db.close()
except:
t.release()
def mysql():
with open('./plug/passdict/username.txt', 'r')as f:
user = f.readlines()
with open('./plug/passdict/password.txt', 'r')as f:
passw = f.readlines()
ip = input("请输入IP:").strip()
s = threading.Semaphore(10)
for i in user:
username = i.strip()
for j in passw:
password = j.strip()
t = threading.Thread(target=mysqlScan, args=(s, ip, username, password))
t.start()
3.4.4弱口令扫描模块整合
在扫描工具调用的过程中,需要依据当前的需求进行不同模块的扫描代码,因此需要一个模块进行模块选择,将支持的功能模块打印输出,并获取用户输入的参数选择调用对应的扫描方法。具体代码如下:
def passScan():
print('''
支持弱口令扫描类型:
1、SSH弱口令扫描
2、FTP弱口令扫描
3、SMB弱口令扫描
4、MYSQL弱口令扫描
''')
m = input("请选择扫描的类型:").strip()
if m == '1':
ssh()
elif m == '2':
ftp()
ftp_any()
elif m == '3':
smb()
elif m == '4':
mysql()
else:
print('请输入正确的参数')
3.5 扫描模块整合
通过上述一系列代码的编写,成功完成了子域名扫描、目录扫描、漏洞扫描以及弱口令四大模块的扫描逻辑,但是在程序运行时需要实现统一运行,并依据不同的需求选择不同的扫描模块进行执行。因此再编写一个统一的整合模块,用来统一调用和选择不同扫描功能模块。具体代码如下:
# @Author : alan
# @File : Ascan.py
from scan import subScan,dirScan,vulScan
from scan import passScan
def Ascan():
print('============================================欢迎使用AScan==============================================')
print('''
= ==== === =
= = = = == = = = ==
==== = == == =
= = = ==== = = = == ==
=== = == == == =
= = ==== = = = ==
''')
print('=====================================================================================================')
print('''
扫描模块:
1、子域名扫描
2、目录扫描
3、漏洞扫描
4、弱口令扫描
请选择对应的序号进行扫描
''')
t = input('请输入对应扫描模式序号:').strip()
if t == '1':
subScan.subScan()
elif t == '2':
dirScan.dirScan()
elif t == '3':
vulScan.vulScan()
elif t == '4':
passScan.passScan()
else:
print('参数错误!')
if __name__ == '__main__':
Ascan()
0x04 结语
那么通过以上代码的编写,成功完成了一个基础的主动漏洞扫描工具。这样的好处就是扩展性高,小巧便携,可以根据自己的不同需求随时进行调整,同时还可以实现自动化的测试。当然主动扫描工具相对被动扫描工具来说还是有很大的弊端,有些漏洞可能因为没有参数之类的无法识别到。因此在后面也会再进行被动扫描器的编写以及利用web界面进行展示功能的优化。
- 本文作者: 阿蓝
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/1757
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!