攻防世界 - Web - Level 3 | simple_js

🌟 关注这个靶场的其它相关笔记:CTF 靶场笔记 —— 攻防世界(XCTF)· 过关思路合集

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 组数字,最后一个的下标就是 17
         pass = 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() 获取用户的输入赋值给 h
     alert(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:参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Blue17 :: Hack3rX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值