文章总结: 本文详细介绍了使用yakit工具进行加解密热加载的进阶技术,包括AES服务端获取key、RSA加密、AES+RSA组合加密、DES规律key、明文加签名、防重放以及加签key在服务端等多种加密场景的处理方法。文章通过实例展示了如何编写加解密脚本,提供了完整的代码示例,并针对每种加密场景给出了具体的解决方案,帮助安全研究人员在渗透测试和漏洞挖掘中更有效地处理各种加密数据。 综合评分: 88 文章分类: WEB安全,漏洞分析,安全工具,实战经验,渗透测试
【加解密】yakit热加载新手进阶
赤弋安全团队
2025年12月17日 22:54 陕西
以下文章来源于Al1onym0us安全 ,作者1ucky st0r
Al1onym0us安全 .
千里之堤,溃于蚁穴
免责声明:本公众号所发的内容仅供学习用途使用,由于传播、利用本公众号所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责!
相信看完这篇入门文章的师傅已经对yakit的热加载有一定了解,并且也能编写自己的加解密脚本了。
https://mp.weixin.qq.com/s/qcK3tLIroAsSBIszBn5JBw
本篇公众号将会涉及到靶场所有加解密,以及每一关脚本的编写,内容较长,希望师傅们耐心看完,相信师傅们看完并自己动手实践一遍,对于挖洞或者工作中遇到的加密肯定能得心应手、如鱼得水、手拿把掐。
第二关:AES服务端获取key
登陆抓包会抓到两个数据包,其中server_generate_key.php会返回密钥和偏移量。定位到算法部分我们可以注意到,只要服务器的session不过期,那么我们获取到的key和iv就是固定的。
查看数据包发现已经把key和iv返回了。
在编写解密代码的时候只需要多一步将aes_key和aes_iv进行base64解密就行了,其他代码一样,解密代码如下:
key = codec.DecodeBase64("2v8Frzfj5zZyC0MInRzvmw==")~iv = codec.DecodeBase64("dym/dMwWBKm4X1qiEn/tng==")~
decryptData = func(packet) { body = poc.GetHTTPPacketBody(packet) params = json.loads(body) raw = codec.DecodeBase64(params.encryptedData)~ result = codec.AESCBCDecrypt(key /*type: []byte*/, raw, iv /*type: []byte*/)~ return string(poc.ReplaceBody(packet /*type: []byte*/, string(result) /*type: []byte*/, false /*type: bool*/))}
hijackSaveHTTPFlow = func(flow /* *yakit.HTTPFlow */, modify /* func(modified *yakit.HTTPFlow) */, drop/* func() */) { request = codec.StrconvUnquote(flow.Request)~ newRequest = decryptData(request) flow.Request = codec.StrconvQuote(newRequest) modify(flow)}
这里需要注意数据包为json格式,需要使用json.loads()拿到json数据,再通过params.encryptedData的方式获取到json里面encryptedData的值。
加密代码如下:
key = codec.DecodeBase64("2v8Frzfj5zZyC0MInRzvmw==")~iv = codec.DecodeBase64("dym/dMwWBKm4X1qiEn/tng==")~
encryptData = func(packet) { //拿到明文请求体 body = poc.GetHTTPPacketBody(packet) //加密,需要去除body多余的换行 rawData = codec.AESCBCEncrypt(key /*type: []byte*/, body.Trim("\n\n\n"), iv /*type: []byte*/)~ //重新拼接好请求体 body = { "encryptedData": codec.EncodeBase64(rawData) } body = json.dumps(body)//返回替换好的请求体 return poc.ReplaceBody(packet /*type: []byte*/, body /*type: []byte*/, false /*type: bool*/)}
beforeRequest = func (req) { return encryptData(req)}
这里需要注意数据包为json格式,需要使用json.dumps()把拼接好的json数据转换成json字符串。
hijackSaveHTTPFlow和beforeRequest这两个内置函数的作用已经在入门篇解释过,这里不做赘述。
第三关:RSA加密
这里可能有些师傅可能不知道什么是RSA加密,这里简单解释一下,RSA 是一种非对称加密算法,RSA有两个不同的密钥,公钥和私钥,公钥用于加密一般是公开的,私钥用于解密一般是不公开的。
这里通过下XHR断点可以定位到关键函数sendEncryptedDataRSA()
这里可以看到请求体body参数为:”data=” + “RSA加密账号密码”。因为这里我们只有公钥,没有私钥,所以只能加密数据不能解密数据。
那如果我们在真实环境里,挖src的时候发现是rsa加密同时只有公钥该怎么办呢?这里我给出我自己的解决办法。比如需要在repeater模块进行改参数然后发包测越权,可以下XHR断点,断点调试找到未加密的数据,然后把明文数据包复制到repeater模块,再配合热加载就可以了。
回到正题,这我们通过调试拿到了明文数据包
{ "username": "admin", "password": "111111" }
这样我们在repeater模块就可以编写我们的加密脚本了。
encryptData = func (packet) { publicKey = `-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRvA7giwinEkaTYllDYCkzujviNH+up0XAKXQot8RixKGpB7nr8AdidEvuo+wVCxZwDK3hlcRGrrqt0Gxqwc11btlMDSj92Mr3xSaJcshZU8kfj325L8DRh9jpruphHBfh955ihvbednGAvOHOrz3Qy3CbocDbsNeCwNpRxwjIdQIDAQAB-----END PUBLIC KEY-----`; body = poc.GetHTTPPacketBody(packet) rawData = codec.RSAEncryptWithPKCS1v15(publicKey, body.Trim("\n\n\n"))~ body = "data="+ codec.EncodeUrl(codec.EncodeBase64(rawData)) return poc.ReplaceBody(packet /*type: []byte*/, body /*type: []byte*/, false /*type: bool*/)}beforeRequest = func (req) { return encryptData(req)}
验证一下,发现可以正常修改明文数据了。
第四关:AES+RSA加密
这种加密一般在金融业务里面出现得比较多,相信经常挖金融的师傅肯定不会陌生。
看一下数据包,一般这种格式的都是对称+非对称组合加密,每次请求包的key都会变化,然后我们去看加密方法,一样的思路,下XHR断点定位关键函数sendDataAesRsa()
可以看到,key和iv是随机生成的16位字符,AES加密数据,RSA加密AES的密钥,服务端用RSA私钥解密AES的key和iv,这里前端只能获取到公钥,不能对数据进行解密。
这里使用yakit的规则配置固定AES的随机密钥,方便后续编写代码。
CryptoJS\.lib\.WordArray\.random\(\d{2}\)//使用正则匹配到生成key和iv的代码,替换成下面固定16位字符,可以是16位任意字符CryptoJS.enc.Utf8.parse("1234567890654321")
同时作用范围选择响应和body。
然后清空浏览器缓存,刷新页面,让yakit重新抓取到js文件。
这里可以看见已经替换成功了。
如果没能替换成功的师傅也不用着急,可以使用原始办法,让yakit抓取js数据包修改响应再放包是一样的效果。
回到正题,这里我们已经让密钥固定住了,接下来开始写加密代码,同样的,通过下XHR断点找到为加密的数据包。
为加密数据包如下:
'{"username":"admin","password":"Aa011612"}'
有了之前RSA加密代码,只需要在之前的基础上加上AES加密的代码就行了。
AesRsaEncryptData = func (packet) { publicKey = `-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRvA7giwinEkaTYllDYCkzujviNH+up0XAKXQot8RixKGpB7nr8AdidEvuo+wVCxZwDK3hlcRGrrqt0Gxqwc11btlMDSj92Mr3xSaJcshZU8kfj325L8DRh9jpruphHBfh955ihvbednGAvOHOrz3Qy3CbocDbsNeCwNpRxwjIdQIDAQAB-----END PUBLIC KEY-----`; aes_key = "1234567890654321" aes_iv = "1234567890654321" body = poc.GetHTTPPacketBody(packet) encryptedData = codec.AESCBCEncrypt(aes_key /*type: []byte*/, body.Trim("\n\n\n"), aes_iv /*type: []byte*/)~ encryptedKey = codec.RSAEncryptWithPKCS1v15(publicKey, codec.EncodeBase64(aes_key))~ encryptedIv = codec.RSAEncryptWithPKCS1v15(publicKey, codec.EncodeBase64(aes_iv))~ //重新拼接好json数据 data = { "encryptedData": codec.EncodeBase64(encryptedData), "encryptedKey": codec.EncodeBase64(encryptedKey), "encryptedIv": codec.EncodeBase64(encryptedIv) } body = json.dumps(data) return poc.ReplaceBody(packet /*type: []byte*/, body /*type: []byte*/, false /*type: bool*/)}beforeRequest = func (req) { return AesRsaEncryptData(req)}
这里需要注意一下,数据包里的key和iv是经过两次base64加密后的。
这里正常修改数据已经可以了。
第五关:DES规律key
可能有些师傅没见过DES,我简单介绍一下,DES类是于AES,简单理解就是DES是阉割版AES。
这里还是一样的思路,定位关键函数encryptAndSendDataDES()
这里可以看见key和iv的生成逻辑(key:从username取前8个字符,不足8字符就补'6'; iv:'9999'+用户名前4个字符,不足4个字符补'9'),假如用户名为admin, 那么key就为admin666, iv为9999admi。对pawwword先进行des加密再进行hex加密,知道生成逻辑后就开始编码了。
decryptData = func (packet) { body = poc.GetHTTPPacketBody(packet) params = json.loads(body) //key和iv的生成逻辑,师傅们可以不做了解 key := params.usernameif len(key) > 8 { key = key[:8] }for len(key) < 8 { key += "6" } p := params.usernameif len(p) > 4 { p = p[:4] }for len(p) < 4 { p += "9" } iv := "9999" + p //对password进行des解密 password = codec.DESDecrypt(key /*type: []byte*/, codec.DecodeHex(params.password,)~, iv /*type: []byte*/)~ //重新组装请求体 body = { "username": params.username, "password": password.Trim("\b") } body = json.dumps(body) return poc.ReplaceBody(packet, body /*type: []byte*/, false /*type: bool*/)}hijackSaveHTTPFlow = func(flow /* *yakit.HTTPFlow */, modify /* func(modified *yakit.HTTPFlow) */, drop/* func() */) { request = codec.StrconvUnquote(flow.Request)~ newRequest = decryptData(request) flow.Request = codec.StrconvQuote(newRequest) modify(flow)}
接下来是加密代码(仅作参考):
encryptData = func (packet) { body = poc.GetHTTPPacketBody(packet) params = json.loads(body) //key和iv的生成逻辑,师傅们可以不做了解 key := params.usernameif len(key) > 8 { key = key[:8] }for len(key) < 8 { key += "6" } p := params.usernameif len(p) > 4 { p = p[:4] }for len(p) < 4 { p += "9" } iv := "9999" + p //对password进行des解密 password = codec.DESEncrypt(key /*type: []byte*/, params.password, iv /*type: []byte*/)~ password = codec.EncodeToHex(codec.EncodeToHex(password)) //重新组装请求体 body = { "username": params.username, "password": password } body = json.dumps(body) println(password) return poc.ReplaceBody(packet, body /*type: []byte*/, false /*type: bool*/)}beforeRequest = func (req) { return encryptData(req)}
第六关:明文加签名
定位关键函数sendDataWithNonce()
可以看到signature是通过对dataToSign这个字段进行HmacSHA256进行加密的,因为有key,所以只要篡改dataToSign即可,可以看到dataToSign是由用户名+密码+随机字符串+时间戳拼接组成。可能有的师傅不清楚HmacSHA256是什么,我简单解释一下,HmacSHA256是用来生成防伪造的签名,如果没有密钥则无法伪造数据。
现在我们已经了解了大致的逻辑,接下来开始编码。
hmacSha256 = func (packet) { secretKey = "be56e057f20f883e"; body = poc.GetHTTPPacketBody(packet) params = json.loads(body) timestamps = timestamp() data = string(params.username) + string(params.password) + string(params.nonce) + string(timestamps) encrptData = codec.HmacSha256(secretKey, data) data = { "username": string(params.username), "password": string(params.password), "nonce": string(params.nonce), "timestamp": timestamps, "signature": codec.EncodeToHex(encrptData) } body = json.dumps(data) return poc.ReplaceBody(packet, body /*type: []byte*/, false /*type: bool*/)}beforeRequest = func (req) { return hmacSha256(req)}
需要在repeater模块加载脚本,每次修改参数后发包会重新计算签名和时间戳,这里需要注意,服务端会校验时间戳。
第七关:防重放
定位关键函数generateRequestData()
分析代码,发现账号密码是明文,使用RSA对时间戳进行加密生成参数random,这里不涉及解密,直接给出加密代码,相信看过第三关RSA加密的师傅已经不用我再解释代码了。
rsaEncryptWithNoRepeater = func (param) { publicKey = `-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRvA7giwinEkaTYllDYCkzujviNH+up0XAKXQot8RixKGpB7nr8AdidEvuo+wVCxZwDK3hlcRGrrqt0Gxqwc11btlMDSj92Mr3xSaJcshZU8kfj325L8DRh9jpruphHBfh955ihvbednGAvOHOrz3Qy3CbocDbsNeCwNpRxwjIdQIDAQAB-----END PUBLIC KEY-----`; result = codec.RSAEncryptWithPKCS1v15(publicKey /*type: []byte*/, param)~return codec.EncodeBase64(result)}
直接通过{{yak(rsaEncryptWithNoRepeater|{{timestamp(ms)}})}}
去调用rsaEncryptWithNoRepeater()函数就行了。
第八关:加签key在服务端
定位到关键函数sendDataWithNonceServer(),分析一下代码。
签名先去服务端获取,然后再对password、username、timestamp进行带签登录,加解密都由服务器端完成,因为拿不到密钥,所以无法伪造数据,分析完毕,非常安全。
总结:
encrypt-labs是一个非常优秀的加解密靶场,相信师傅们学完后一定会有不小的收获。
https://github.com/SwagXz/encrypt-labs
查看原文:《【加解密】yakit热加载新手进阶》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论