本文是高级前端加解密与验签实战的第8篇文章,本系列文章实验靶场为Yakit里自带的Vulinbox靶场,本文讲述的是绕过RSA与AES加密来爆破登录。
由于RSA加解密有长度限制,以及解密速度等问题,所以如https等协议都是用非对称加密对称加密的密钥,然后用对称加密算法来加密数据。本关卡就是用RSA来加密AES的key和iv,用AES来加密表单数据。
分析
直接Submit,观察数据包发现请求包和响应包AES加密的key和iv都被加密了。
查看源码,RSA的key是通过请求/crypto/js/rsa/generator
路径获取的
AES的加密方法为AES-GCM
流程图如下:
graph TD; A[开始] --> B(加载页面); B --> C{获取RSA密钥对}; C -- 是 --> D(从服务器获取公钥和私钥); D --> E(将PEM格式的公钥和私钥转换为CryptoKey对象); E --> F(生成随机AES密钥与IV); F --> G(使用RSA-OAEP加密AES密钥与IV); G --> H(使用Encrypt函数用AES-GCM方式加密提交的数据); H --> I(发送加密数据到服务器); I --> J(接收服务器响应); J --> K(使用Decrypt函数用RSA与AES-GCM解密接收的数据); K --> L(显示解密后的数据);
序列+热加载
本文和上文遇到一样的问题,本来打算用第三个请求来解密响应包的,最后选择了使用mirrorHTTPFlow
函数来解密。
上文中只能看到登陆成功,但不知道账号密码是什么。这次写了个解密函数解密请求包,不管怎么说,能跑就行。
热加载代码如下:
// RSA-OAEP 加密 rsaEncrypt = (pem, data) => { data = codec.RSAEncryptWithOAEP(pem, data)~ data = codec.EncodeBase64(data) return data } // AES-GCM 加密 aesEncrypt = (key, iv, data) => { encryptedData = codec.AESGCMEncryptWithNonceSize12(key, data, iv)~ encryptedData = codec.EncodeBase64(encryptedData) return encryptedData } // 分割参数的函数 splitParams = (params) => { pairs := params.SplitN("|", 2) return pairs } // 主函数 encrypt = (params) => { pairs := splitParams(params) key = randstr(16) iv = randstr(12) data = aesEncrypt(key, iv, pairs[1]) encryptIV = rsaEncrypt(pairs[0], iv) encryptKey = rsaEncrypt(pairs[0], key) body = f`{"data":"${data}","iv":"${iv}","encryptedIV":"${encryptIV}","encryptedKey":"${encryptKey}"}` return body } // 解密函数 mirrorHTTPFlow = (req, rsp, params) => { // 获取私钥 pem = params.privateKey // 切割响应中的数据,作为 JSON 加载 body = json.loads(poc.GetHTTPPacketBody(rsp)) // 提取 IV、KEY 和 DATA data = body.data iv = body.encryptedIV key = body.encryptedKey // 使用 RSA-OAEP 解密 IV 和 KEY iv = codec.RSADecryptWithOAEP(pem, codec.DecodeBase64(iv)~)~ key = codec.RSADecryptWithOAEP(pem, codec.DecodeBase64(key)~)~ // 使用 AES-GCM 解密 data = codec.AESGCMDecryptWithNonceSize12(key, codec.DecodeBase64(data)~, iv)~ return string(data) }
使用Yakit的序列功能,效果如下,在提取数据中显示了未加密的请求和响应的内容:
爆破效果: