【JS逆向】前端加密对抗练习靶场encrypt-labs

admin 2025年1月14日08:26:39评论29 views字数 8833阅读29分26秒阅读模式

前言

文章首发于先知社区:https://xz.aliyun.com/t/16919

项目地址:https://github.com/SwagXz/encrypt-labs

作者:SwagXz

现在日子越来越不好过了,无论攻防、企业src还是渗透项目,总能看到大量的存在加密的网站,XZ师傅的前端加密靶场还是很值得做一做的,环境很贴合实战会遇到的一些情况,本人web小菜鸡练完之后反正是收获颇丰,推荐给各位师傅。

之前自己在学习前端加解密经常遇到加密解不了的情况;之后慢慢看师傅们的文章,也学到了很多绕过技术,于是写了个简单的靶场,为之后的师傅们铺路学习,加密方式列出了我经常见的8种方式包含非对称加密、对称加密、加签以及禁止重放的测试场景,比如AES、DES、RSA,用于渗透测试加解密练习。希望可以帮助到最近在学习这块知识的师傅,靶场函数.很简单,和实战相比还是差的有点多,不过应该够入门了

默认密码:admin/123456

http://82.156.57.228:43899 (混淆)

http://82.156.57.228:43899/easy.php (无混淆)

加解密插件/工具

burp自动加解密插件autoDeceder:https://github.com/f0ng/autoDecoder

这个插件可以帮忙处理常见的AES、DES、SM4、SM2、RSA等加密,灰常好用

【JS逆向】前端加密对抗练习靶场encrypt-labs

还有个浏览器插件:Ctool 程序开发常用工具:https://ctool.dev/

直接在谷歌商店或者火狐商店即可下载

也是可以对常见的加密方式进行加密解密,这个适用的加解密方式更多,如果只是用于验证加解密情况的话,这个插件会方便很多

【JS逆向】前端加密对抗练习靶场encrypt-labs

encrypt-labs

使用无混淆的进行测试说明

http://82.156.57.228:43899/easy.php (无混淆)

admin/123456

【第一关】AES固定key

抓包发现数据包被加密了:加密参数为encryptedData

【JS逆向】前端加密对抗练习靶场encrypt-labs

直接跟进js查看,直接在进入位置下断点,再次抓包

【JS逆向】前端加密对抗练习靶场encrypt-labs

一个断点直接找到加密后的数据和加密前的数据,向上查找,发现是用AES加密

function sendDataAes(url) { const formData = {  username: document.getElementById("username")   .value,  password: document.getElementById("password")   .value }; const jsonData = JSON.stringify(formData); const key = CryptoJS.enc.Utf8.parse("1234567890123456"); const iv = CryptoJS.enc.Utf8.parse("1234567890123456"); const encrypted = CryptoJS.AES.encrypt(jsonData, key, {   iv: iv,   mode: CryptoJS.mode.CBC,   padding: CryptoJS.pad.Pkcs7  })  .toString();    const params = `encryptedData=${encodeURIComponent(encrypted)}

根据断点信息可知:

AES加密,CBC模式,PKCS5Padding

key:1234567890123456   /  iv:1234567890123456

autoDecoder

配置数据包自动加解密

输入key /iv,设置正则表达式,正确设置正则表达式之后才可以实现自动解密【JS逆向】前端加密对抗练习靶场encrypt-labs

配置需要加解密的域名

【JS逆向】前端加密对抗练习靶场encrypt-labs

尝试重放

【JS逆向】前端加密对抗练习靶场encrypt-labs

【第二关】AES服务端获取Key

点击第二关抓包,可以获取到两个数据包,一个是服务端返回的keyiv,一个是登录数据包

【JS逆向】前端加密对抗练习靶场encrypt-labs

经过测试发现,重发数据包该keyiv,发现keyiv短时间内不会发生变化,应该是服务端和客户端断连之前,keyiv都会保持不变

【JS逆向】前端加密对抗练习靶场encrypt-labs
{"aes_key":"OUd4SEqDsA1GP2l8WszZnQ==","aes_iv":"RQenJ2Hszn1p7Q6poVngFQ=="}

查看js数据,确定为AES加密

【JS逆向】前端加密对抗练习靶场encrypt-labs
autoDecoder
【JS逆向】前端加密对抗练习靶场encrypt-labs
【JS逆向】前端加密对抗练习靶场encrypt-labs

【第三关】RSA加密

抓包查看:加密参数是data

【JS逆向】前端加密对抗练习靶场encrypt-labs

进入eazy.js下断点,往上查看,很容易获取到了publickey

-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRvA7giwinEkaTYllDYCkzujviNH+up0XAKXQot8RixKGpB7nr8AdidEvuo+wVCxZwDK3hlcRGrrqt0Gxqwc11btlMDSj92Mr3xSaJcshZU8kfj325L8DRh9jpruphHBfh955ihvbednGAvOHOrz3Qy3CbocDbsNeCwNpRxwjIdQIDAQAB-----END PUBLIC KEY-----
【JS逆向】前端加密对抗练习靶场encrypt-labs

经确认为RSA加密,RSA加密需一个公钥,解密需要私钥,没有私钥,只能尝试加密

autoDecoder
【JS逆向】前端加密对抗练习靶场encrypt-labs
【JS逆向】前端加密对抗练习靶场encrypt-labs

【第四关】AES+Rsa加密

抓包查看【JS逆向】前端加密对抗练习靶场encrypt-labs下断点往上查看

【JS逆向】前端加密对抗练习靶场encrypt-labs
function sendDataAesRsa(url) { const formData = {  username: document.getElementById("username")   .value,  password: document.getElementById("password")   .value }; const jsonData = JSON.stringify(formData); const key = CryptoJS.lib.WordArray.random(16); const iv = CryptoJS.lib.WordArray.random(16); const encryptedData = CryptoJS.AES.encrypt(jsonData, key, {   iv: iv,   mode: CryptoJS.mode.CBC,   padding: CryptoJS.pad.Pkcs7  })  .toString(); const rsa = new JSEncrypt(); rsa.setPublicKey(`-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRvA7giwinEkaTYllDYCkzujviNH+up0XAKXQot8RixKGpB7nr8AdidEvuo+wVCxZwDK3hlcRGrrqt0Gxqwc11btlMDSj92Mr3xSaJcshZU8kfj325L8DRh9jpruphHBfh955ihvbednGAvOHOrz3Qy3CbocDbsNeCwNpRxwjIdQIDAQAB-----END PUBLIC KEY-----`); const encryptedKey = rsa.encrypt(key.toString(CryptoJS.enc.Base64)); const encryptedIv = rsa.encrypt(iv.toString(CryptoJS.enc.Base64));

被加密的参数是formData也就是"{"username":"admin","password":"123456"}",经过AES加密,且加密使用的keyiv是16位随机数、得到encryptedData

之后对keyiv进行rsa加密得到encryptedKeyencryptedIv

再将这三个参数传入数据包中,发包进行验证

现在想办法将随机16位的keyiv进行固定,右键选择替换内容,使用本地替换的方式将keyiv固定下来,就选择之前第一关的keyiv即可

【JS逆向】前端加密对抗练习靶场encrypt-labs
【JS逆向】前端加密对抗练习靶场encrypt-labs

再次下断点,查看是否修改成功,可以看到已经修改成功,keyiv变成了1234567890123456

【JS逆向】前端加密对抗练习靶场encrypt-labs
【JS逆向】前端加密对抗练习靶场encrypt-labs

成功替换encryptedData,其中加密的keyiv经过测试似乎不用替换也能通过,就不进行加解密操作了

【第五关】Des规律Key

抓包查看,可以看到只对password进行了加密

【JS逆向】前端加密对抗练习靶场encrypt-labs进入js下断点抓包

【JS逆向】前端加密对抗练习靶场encrypt-labs可以看到就是简单的DES加密,keyiv都使用了username的值

key是八位,如果username不满8位,则用6补满

iv是八位,9999+username的前四位

key:admin666iv:9999admi
autoDecode

【JS逆向】前端加密对抗练习靶场encrypt-labs成功解密【JS逆向】前端加密对抗练习靶场encrypt-labs

【第六关】明文加签

依旧抓包

【JS逆向】前端加密对抗练习靶场encrypt-labs

可以看到有两个参数不清楚是啥,分别是noncesignature,还有个时间戳,分析下js看看,依旧是js中下断点,发包

【JS逆向】前端加密对抗练习靶场encrypt-labs
function sendDataWithNonce(url) { const username = document.getElementById("username")  .value; const password = document.getElementById("password")  .value; const nonce = Math.random()  .toString(36)  .substring(2); const timestamp = Math.floor(Date.now() / 1000); const secretKey = "be56e057f20f883e"; const dataToSign = username + password + nonce + timestamp; const signature = CryptoJS.HmacSHA256(dataToSign, secretKey)  .toString(CryptoJS.enc.Hex);

nonce:由0-9 a-z生成的10位随机数

dataToSign:username + password + nonce + timestamp

signature:由dataToSignSHA256加密生成,secretKey为固定值be56e057f20f883e

SHA256autoDecoer中没有,尝试自写发包器,其中nonce可以随机生成也可以固定

import requestsimport timeimport hashlibimport hmacdef generate_signature(username, password, nonce, timestamp, secret_key):    data_to_sign = username + password + nonce + str(timestamp)    h = hmac.new(secret_key.encode('utf-8'), digestmod=hashlib.sha256)    h.update(data_to_sign.encode('utf-8'))return h.hexdigest()url = "http://82.156.57.228:43899/encrypt/signdata.php"username = "admin"password = "123456"nonce = "dq7kos6hzy"secret_key = "be56e057f20f883e"while True:    timestamp = int(time.time())    signature = generate_signature(username, password, nonce, timestamp, secret_key)    headers = {"Host""82.156.57.228:43899","Content-Length""163","User-Agent""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36","Content-Type""application/json","Accept""*/*","Origin""http://82.156.57.228:43899","Referer""http://82.156.57.228:43899/easy.php","Accept-Encoding""gzip, deflate","Accept-Language""zh-CN,zh;q=0.9","Cookie""PHPSESSID=q3nlpgst4h9kpdiklq2rcbrnc1","Connection""close"    }    data = {"username": username,"password": password,"nonce": nonce,"timestamp": timestamp,"signature": signature    }    response = requests.post(url, json=data, headers=headers)print(response.status_code)print(response.text)    time.sleep(1)  # 发包间隔
【JS逆向】前端加密对抗练习靶场encrypt-labs

【第七关】加签key在服务端

依旧抓包,发送了俩数据包

【JS逆向】前端加密对抗练习靶场encrypt-labs
【JS逆向】前端加密对抗练习靶场encrypt-labs

通过第一个数据包获取signature,第二个数据包发包时加上这个,达到加签key在服务端的效果

emmm测试了下,如果要做密码爆破操作的话,需要发第一个包

获取对应的signature值,丢到第二个包中,依旧是自写脚本即可,不难,这里不演示了。

【第八关】禁止重放

还是抓包 账号密码还是明文的,多次重放发现返回No Repeater

【JS逆向】前端加密对抗练习靶场encrypt-labs

其中加密参数为random,分析js看看

依旧是断点,查看

【JS逆向】前端加密对抗练习靶场encrypt-labs
functiongenerateRequestData() {    const username = document.getElementById("username").value;    const password = document.getElementById("password").value;    const timestamp = Date.now();    const publicKey = `-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRvA7giwinEkaTYllDYCkzujviNH+up0XAKXQot8RixKGpB7nr8AdidEvuo+wVCxZwDK3hlcRGrrqt0Gxqwc11btlMDSj92Mr3xSaJcshZU8kfj325L8DRh9jpruphHBfh955ihvbednGAvOHOrz3Qy3CbocDbsNeCwNpRxwjIdQIDAQAB-----END PUBLIC KEY-----`;function rsaEncrypt(data, publicKey) {        const jsEncrypt = new JSEncrypt();         jsEncrypt.setPublicKey(publicKey);        const encrypted = jsEncrypt.encrypt(data.toString());if (!encrypted) {            throw new Error("RSA encryption failed.");        }return encrypted;    }    // Encrypt the timestamplet encryptedTimestamp;    try {        encryptedTimestamp = rsaEncrypt(timestamp, publicKey);    } catch (error) {        console.error("Encryption error:", error);return null;    }    const dataToSend = {        username: username,        password: password,        random: encryptedTimestamp // Replace timestamp with encrypted version    };return dataToSend;}function sendLoginRequest(url) { const dataToSend = generateRequestData();unction sendLoginRequest(url) { const dataToSend = generateRequestData();functiongenerateRequestData() {    const username = document.getElementById("username").value;    const password = document.getElementById("password").value;    const timestamp = Date.now();

现在是要寻找random参数怎么来的,根据上面js可知是通过encryptedTimestamp来的,encryptedTimestamp是通过时间戳经过RSA加密来的

依旧写一个发包器来实现

import requestsimport jsonfrom Crypto.PublicKey import RSAfrom Crypto.Cipher import PKCS1_v1_5from Crypto.Cipher import AESfrom Crypto.Util.Padding import padfrom base64 import b64encode, b64decodeimport timedef rsa_encrypt(data, public_key):"""    RSA加密,Base64格式    """    key = RSA.import_key(public_key)    cipher = PKCS1_v1_5.new(key)    encrypted_data = cipher.encrypt(data.encode('utf-8'))return b64encode(encrypted_data).decode('utf-8')def generate_request_data():"""    生成random字段    """    username = "admin"    password = "123456"    timestamp = str(int(round(time.time() * 1000)))  # 时间戳print(timestamp)    public_key = """-----BEGIN PUBLIC KEY-----nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRvA7giwinEkaTYllDYCkzujviNH+up0XAKXQot8RixKGpB7nr8AdidEvuo+wVCxZwDK3hlcRGrrqt0Gxqwc11btlMDSj92Mr3xSaJcshZU8kfj325L8DRh9jpruphHBfh955ihvbednGAvOHOrz3Qy3CbocDbsNeCwNpRxwjIdQIDAQABn-----END PUBLIC KEY-----"""    encrypted_timestamp = rsa_encrypt(timestamp, public_key)    data_to_send = {"username": username,"password": password,"random": encrypted_timestamp    }print(data_to_send)return data_to_senddef send_request():    url = "http://82.156.57.228:43899/encrypt/norepeater.php"    headers = {"Host""82.156.57.228:43899","Content-Length""224","User-Agent""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36","Content-Type""application/json; charset=utf-8","Accept""*/*","Origin""http://82.156.57.228:43899","Referer""http://82.156.57.228:43899/easy.php","Accept-Encoding""gzip, deflate","Accept-Language""zh-CN,zh;q=0.9","Connection""close"    }    data = generate_request_data()    response = requests.post(url, headers=headers, data=json.dumps(data))print(response.text)if __name__ == "__main__":while True:          send_request()        time.sleep(5)  

原文始发于微信公众号(momo安全):【JS逆向】前端加密对抗练习靶场encrypt-labs

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年1月14日08:26:39
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【JS逆向】前端加密对抗练习靶场encrypt-labshttps://cn-sec.com/archives/3625667.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息