发布于 2024-01-31  46 次阅读


37.前台js登陆加密分析

1|0开篇

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

或者这样:

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

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

2|0加密方式

2|1对称加密

1|0什么是对称加密

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

1|0对称加密的过程

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

1|0常见对称加密算法

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

1|0对称加密算法的不足

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

2|2非对称加密

1|0什么是非对称加密

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

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

1|0非对称加密的过程

  • 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发送消息也是一样。

1|0常见的非对称加密算法

RSA,ECC,DSA...

2|3单向散列函数

1|0单向散列函数的特点

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

1|0单向散列函数常见算法

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

2|4其他加密

1|0BASE64

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

1|0HEX

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

3|0如何使用chrome浏览器调试js

3|1断点调试

1|0代码断点

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

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

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

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

1|0条件断点

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

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

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

1|0管理代码断点

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

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

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

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

1|0DOM断点

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

1.切换到 Elements 选项卡

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

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

三种断点类型解释:

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

1|0XHR/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|0事件监听断点

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

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

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

1|0异常断点

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

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

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

3|2步进执行代码

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

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

1|0恢复执行

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

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

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

1|0跳过,跳入,跳出,逐步执行

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

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

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

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

3|3编辑脚本

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

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

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

3|4压缩脚本格式化

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

3|5查看当前执行上下文

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

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

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

3|6查看当前调用堆栈

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

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

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

3|7代码片段

 如果发现自己在控制台中反复运行相同的调试代码,就可以考虑使用 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

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

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

4|1F12一键定位

1|0F12查找处理登陆函数

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

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

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

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

1|0F12事件监听

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

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

4|2全局搜索定位

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

1|0使用加密函数名搜索

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

1|0使用特定字段搜索

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

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

先通过Network抓取登陆请求

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

成功定位到加密的地方

4|3XHR断点定位

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

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

切换到,控制台Source 选项卡

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

重新发送请求:

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

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

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

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

5|0简单例子实践

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

哦呦,还不错!

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

F12查看元素,事件监听

进去查看

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

全局search这个方法:

跳到这个md5.js代码

如何进行爆破

1|0使用大佬的 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!

1|0使用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()

尝试运行:

 菜鸡代码,大佬勿喷!

6|0真实环境测试

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

尝试使用错误的密码登陆,同时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抓取数据包,把上面结果粘贴进去,登陆成功!

7|0结尾

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

参考连接:

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

    http://travistidwell.com/jsencrypt/

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

__EOF__

本文作者:bmjoker
本文链接https://www.cnblogs.com/bmjoker/p/11784817.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!

分类: 技术杂谈

好文要顶关注我收藏该文

bmjoker
粉丝 - 652关注 - 21

+加关注

1

0

« 上一篇: 26.Apache Solr RCE
» 下一篇: 8. Android加载流程(打包与启动)

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

届ける言葉を今は育ててる
最后更新于 2024-01-31