欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 产业 > 攻防世界 - Web - Level 3 | simple_js

攻防世界 - Web - Level 3 | simple_js

2025/5/2 3:29:06 来源:https://blog.csdn.net/m0_73360524/article/details/144805534  浏览:    关键词:攻防世界 - Web - Level 3 | simple_js

关注这个靶场的其它相关笔记:攻防世界(XCTF) —— 靶场笔记合集-CSDN博客

0x01:考点速览

本题考察的是 JS 的代码审计,以下是你需要了解的知识点:

  • String.fromCharCode() -> 将接收的 Unicode 编码转换成字符或字符串

0x02:Write UP

从题目来看,本题考查的就是一个前端 JS 的代码审计:

进入靶场,直接弹出一个框框,让我们输入 password,这里我们随便输入:

提示我们是假的密码,这里我们忽略,查看目标代码:

好了,接下来就是紧张刺激的代码审计了,下面是 1.0 版本的审计结果:

 <script>function dechiffre(pass_enc) {var pass = "70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65"; // 一串神秘的数字var tab = pass_enc.split(','); // 把输入的密文转换成数组var tab2 = pass.split(',');    // 把 pass 转换成数组var i, j, k, l = 0,m, n, o, p = "";i = 0;j = tab.length;         // j = 输入的密文数组的长度k = j + (l) + (n = 0);  // k = j + l + ( n = 0) = j + 0 + 0 = j -> 狗的,k 的大小和也等于输入密文的长度n = tab2.length;        // n = pass 数组的长度for (i = (o = 0); i < (k = j = n); i++) {   // o 转换为了 number 型,k,j 的长度也重新变为了 pass 数组的长度o = tab[i - l];     // o = tab[i - l] -> l 的值就没变过 -> o = tab[i] -> 依次取出我们输入的密文数组中的元素p += String.fromCharCode((o = tab2[i])); // o = tab2[i] -> 依次取出 pass 数组中的元素,并转换为字符,追加到 p 后if (i == 5) break;  // 取 5 个元素后,跳出循环}for (i = (o = 0); i < (k = j = n); i++) {   // k,j 的长度依旧为 pass 数组的长度o = tab[i - l]; // 同上个循环一样if (i > 5 && i < k - 1) // k 为 pass 数组的长度, k-1 为 pass 数组最后一个元素的位置 -> < k -1 -> 对方根本没想过要取出最后一个元素p += String.fromCharCode((o = tab2[i]));    // 取出 pass 数组中下标从6到倒数第二个元素,并转换为字符,追加到 p 后}p += String.fromCharCode(tab2[17]); // 单独取一个 17 ? -> 纯纯恶心人,你数数 pass 中有 18 组数字,最后一个的下标就是 17pass = p;return pass;    // 返回 pass 密文}// String["fromCharCode"]() -> String.fromCharCode() 的另一种写法,该方法接收一个或多个 Unicode 字符编码,返回一个字符串。String["fromCharCode"](dechiffre("\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30"));h = window.prompt('Enter password'); // window.prompt() 获取用户的输入赋值给 halert(dechiffre(h)); // 调用 dechiffre() 函数并传入 h 作为参数 -> 将最终结果弹出</script>

通过 1.0 的代码审计,我们发现了,其最终的输出和我们的输入没有任何关系,另外,它返回的结果也只是把预定的 pass 中的数组转换为字符后的结果,所以上面的代码完全可以简化为下面的样子:

 <script>function decode(pass) {var pass_array = pass.split(",");   // 将 pass 按 , 号隔开var result = "";for (i = 0; i < pass_array.length; i++) {result += String.fromCharCode(pass_array[i]);}console.log(result);}var pass = "70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65";decode(pass);</script>

其输出的值,和嘲讽我们的值是一样,其实动动脑也能发现,pass 后面的两个数字为 “72,65”,72 可能没法一下子反应,但 65 不就是 A 的 ASCII 码值嘛。

Ok,分析完目标狗的很的加密函数,下面问题就是正确的密钥在哪里?我们可以发现,目标中存在一大串神秘的以 “\x” 开头的字符,并且也同样调用了 dechiffre () 函数,只不过没输出出来。

看到 “\x” 其实很简单就能联想到十六进制字符,那我们就尝试把十六进制字符转成字符串看看是啥(这里我使用的还是开发者工具里的 Console,里面会自动转换):

可以发现,其转换完成后也是一串数字,还是一串用 “,” 号分隔的数字,直接传入我们编写的解密函数中:

其实已经拿到 Flag 了,别忘了题目一开始的提示(Flag 的格式为 Cyberpeace {xxxxxxxxx})。

不知道为啥,写这道题目让我有一个感慨:一个东西,外围防御做的越好,说明他的内部越薄弱。本题用花里胡哨的代码扰乱我们的思路,但其实只做了两件事,把密码转成 ASCII 码值,再把 ASCII 码值转成十六进制,对 Pwn 熟悉的小伙伴估计不用看 JS 代码,凭借对密码格式的熟悉,两部就解出来了(上面写函数的过程完全可以省略的)。

0x03:参考资料

  • JavaScript fromCharCode () 方法

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词