失德帅哥 發表於 2020-8-23 00:06:00

CTF-WEB:Bugku-江湖魔头(Javascript 审计 + 逆向解密)

<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>这是个啥?</li><li>突破口</li><li>代码审计</li><li>构造变量并逆向封装</li><li>提交 cookie</li></ul></div><p></p>
<h1 id="这是个啥">这是个啥?</h1>
<p>打开网页,首先先了解下这个网页的正确打开方式。根据描述应该是个游戏,试着玩玩看,首先需要选择初始属性(有金庸群侠传内味了,哈哈)。<br>
<img src="https://img2020.cnblogs.com/blog/1774310/202008/1774310-20200821205608689-515300540.png" alt="" loading="lazy"><br>
接下来根据提示,我们应该是要把蒙老魔干掉,但是每一次练功或者赚钱都需要花 5 秒时间,这段时间会卡一下很影响体验。<br>
<img src="https://img2020.cnblogs.com/blog/1774310/202008/1774310-20200821205557149-2134207393.png" alt="" loading="lazy"><br>
打开商店,根据提示应该要学习如来神掌,但是这需要一笔巨款。同时学如来神掌之前要先把所有属性都刷满,可以练功也可以直接氪金,但是无论怎么搞都很费时间。<br>
<img src="https://img2020.cnblogs.com/blog/1774310/202008/1774310-20200821205953402-1176467652.png" alt="" loading="lazy"><br>
讨伐蒙老魔需要学会如来神掌,解决这个问题可以氪命,但是这并不是高效的方式。<br>
<img src="https://img2020.cnblogs.com/blog/1774310/202008/1774310-20200821210018416-757533517.png" alt="" loading="lazy"></p>
<h1 id="突破口">突破口</h1>
<p>首先还是先进行常规的操作,F12、抓包和后台扫描都没有什么有价值的信息,想要直接修改数值把钱搞上去也证明无效。不过现在看到的页面已经是经过 GET 方法传参之后的页面了,考虑在 url 把传参删掉,成功看到一个新页面。<br>
<img src="https://img2020.cnblogs.com/blog/1774310/202008/1774310-20200821211021949-1930205943.png" alt="" loading="lazy"><br>
F12 打开新页面的源码,看到了还有 3 个 JavaScript 文件。<br>
<img src="https://img2020.cnblogs.com/blog/1774310/202008/1774310-20200821211213590-1083481342.png" alt="" loading="lazy"><br>
先打开第一个文件,能看得出是代码,但是里面不知道是什么东西。</p>
<pre><code>eval(function(p,a,c,k,e,r){e=function(c){return(c&lt;62?'':e(parseInt(c/62)))+((c=c%62)&gt;35?String.fromCharCode(c+29):c.toString(36))};if('0'.replace(0,e)==0){while(c--)r=k;k=||e}];e=function(){return''};c=1};while(c--)if(k)p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k);return p}('7 s(t){5 m=t+"=";5 8=9.cookie.n(\';\');o(5 i=0;i&lt;8.d;i++){5 c=8.trim();u(c.v(m)==0)p c.substring(m.d,c.d)}p""}7 w(a){5 x=new Base64();5 q=x.decode(a);5 r="";o(i=0;i&lt;q.d;i++){5 b=q.charCodeAt();b=b^i;b=b-((i%10)+2);r+=String.fromCharCode(b)}p r}7 ertqwe(){5 y="user";5 a=s(y);a=decodeURIComponent(a);5 z=w(a);5 8=z.n(\';\');5 e="";o(i=0;i&lt;8.d;i++){u(-1&lt;8.v("A")){e=8.n(":")}}e=e.B(\'"\',"").B(\'"\',"");9.write(\'&lt;img id="f-1" g="h/1-1.k"&gt;\');j(7(){9.l("f-1").g="h/1-2.k"},1000);j(7(){9.l("f-1").g="h/1-3.k"},2000);j(7(){9.l("f-1").g="h/1-4.k"},3000);j(7(){9.l("f-1").g="h/6.png"},4000);j(7(){alert("你使用如来神掌打败了蒙老魔,但不知道是真身还是假身,提交试一下吧!A{"+md5(e)+"}")},5000)}',[],38,'|||||var||function|ca|document|temp|num||length|key|attack|src|image||setTimeout|jpg|getElementById|name|split|for|return|result|result3|getCookie|cname|if|indexOf|decode_create|base|temp_name|mingwen|flag|replace'.split('|'),0,{}))
</code></pre>
<p>这是一个被压缩过的 JavaScript 代码,拿去解码网页解码得到源码。<br>
<img src="https://img2020.cnblogs.com/blog/1774310/202008/1774310-20200821211825723-213492954.png" alt="" loading="lazy"></p>
<h1 id="代码审计">代码审计</h1>
<p>得到源码后首先查看 getCookie() 函数,这个函数会在游戏开始之后获取 cookie 中指定变量的值并返回。</p>
<pre><code>function getCookie(cname) {
    var name = cname + "=";
    var ca = document.cookie.split(';');
    for (var i = 0; i &lt; ca.length; i++) {
      var c = ca.trim();
      if (c.indexOf(name) == 0)
            return c.substring(name.length, c.length)
    }
    return ""
}
</code></pre>
<p>ertqwe() 函数是个关键的函数,首先得到的值会经过 decodeURIComponent() 函数和 decode_create() 函数进行解码,接下来就得到了游戏中的面板数据。后续的代码就是游戏的一些操作了,例如判断能不能打魔头之类的。</p>
<pre><code>function ertqwe() {
    var temp_name = "user";
    var temp = getCookie(temp_name);
    temp = decodeURIComponent(temp);
    var mingwen = decode_create(temp);
    var ca = mingwen.split(';');
    var key = "";
    for (i = 0; i &lt; ca.length; i++) {
      if (-1 &lt; ca.indexOf("flag")) {
            key = ca.split(":")
      }
    }
    key = key.replace('"', "").replace('"', "");
    document.write('&lt;img id="attack-1" src="image/1-1.jpg"&gt;');
    setTimeout(function() {
      document.getElementById("attack-1").src = "image/1-2.jpg"
    }, 1000);
    setTimeout(function() {
      document.getElementById("attack-1").src = "image/1-3.jpg"
    }, 2000);
    setTimeout(function() {
      document.getElementById("attack-1").src = "image/1-4.jpg"
    }, 3000);
    setTimeout(function() {
      document.getElementById("attack-1").src = "image/6.png"
    }, 4000);
    setTimeout(function() {
      alert("你使用如来神掌打败了蒙老魔,但不知道是真身还是假身,提交试一下吧!flag{" + md5(key) + "}")
    }, 5000)
}
</code></pre>
<p>我们可以在 F12 的控制器中调试代码,查看现在的 cookic 长什么样。</p>
<pre><code>var test = getCookie("user")
var test = decodeURIComponent(test)
var test = decode_create(test);
</code></pre>
<p>得到的字符串如下,看样子这个字符串是 PHP 中的 human 对象序列化字符串。里面存储了玩家的各个属性,其中里面的钱数量为 0。同时我们也看到了 flag 的值也为 0,也就是说想直接用解码的方式得到 flag 是没有用的。</p>
<pre><code>O:5:"human":10:{s:8:"xueliang";i:857;s:5:"neili";i:950;s:5:"lidao";i:68;s:6:"dingli";i:56;s:7:"waigong";i:0;s:7:"neigong";i:0;s:7:"jingyan";i:0;s:6:"yelian";i:0;s:5:"money";i:0;s:4:"flag";s:1:"0";}
</code></pre>
<h1 id="构造变量并逆向封装">构造变量并逆向封装</h1>
<p>因为各项属性和如来神掌都可以氪金习得,而且我们也不知道属性的上限是多少,这时可以只把 money 字段改得很高。</p>
<pre><code>O:5:"human":10:{s:8:"xueliang";i:857;s:5:"neili";i:950;s:5:"lidao";i:68;s:6:"dingli";i:56;s:7:"waigong";i:0;s:7:"neigong";i:0;s:7:"jingyan";i:0;s:6:"yelian";i:0;s:5:"money";i:100000000;s:4:"flag";s:1:"0";}
</code></pre>
<p>这个时候如果我们用这个字符串替代掉原来的变量,就可以直接购买商店的所有东西。但是这段字符串穿过去之后又会经历一系列解码,如果直接把这段字符串传上去会导致发生混乱。所以我们要把这段字符串按照前面解码的代码方向封装回去,首先要进行 decode_create() 的逆过程。</p>
<pre><code>function decode_create(temp) {
    var base = new Base64();
    var result = base.decode(temp);
    var result3 = "";
    for (i = 0; i &lt; result.length; i++) {
      var num = result.charCodeAt();
      num = num ^ i;
      num = num - ((i % 10) + 2);
      result3 += String.fromCharCode(num)
    }
    return result3
}
</code></pre>
<p>审计这段代码,传入的字符串中的每个字符先进行异或运算,然后计算表达式 “num = num - ((i % 10) + 2)”。我们在还原的时候要把顺序逆过来,先实现表达式的逆运算,然后再做异或运算。</p>
<pre><code>var result = "";
for (i = 0; i &lt; test.length; i++) {
      var num = test.charCodeAt();
      num = num + ((i % 10) + 2);
      num = num ^ i;
      result += String.fromCharCode(num)
}
</code></pre>
<p>现在 result 变量就是 test 序列化字符串的编码了,接下来要进行 base.decode() 的逆过程。这个函是也是进行解码,它的编码函数被放在 base64.js 文件中。</p>
<pre><code>this.encode = function (input) {
    var output = "";
    var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
    var i = 0;
    input = _utf8_encode(input);
    while (i &lt; input.length) {
          chr1 = input.charCodeAt(i++);
          chr2 = input.charCodeAt(i++);
          chr3 = input.charCodeAt(i++);
          enc1 = chr1 &gt;&gt; 2;
          enc2 = ((chr1 &amp; 3) &lt;&lt; 4) | (chr2 &gt;&gt; 4);
          enc3 = ((chr2 &amp; 15) &lt;&lt; 2) | (chr3 &gt;&gt; 6);
          enc4 = chr3 &amp; 63;
          if (isNaN(chr2)) {
                enc3 = enc4 = 64;
          }
          else if (isNaN(chr3)) {
                enc4 = 64;
          }
          output = output + _keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4);
      }
      return output;
}
</code></pre>
<p>但是如果我们直接调用这个函数会出问题,原因我们得审计一下解码的代码。</p>
<pre><code>this.decode = function (input) {
    var output = "";
    var chr1, chr2, chr3;
    var enc1, enc2, enc3, enc4;
    var i = 0;
    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
    while (i &lt; input.length) {
          enc1 = _keyStr.indexOf(input.charAt(i++));
          enc2 = _keyStr.indexOf(input.charAt(i++));
          enc3 = _keyStr.indexOf(input.charAt(i++));
          enc4 = _keyStr.indexOf(input.charAt(i++));
          chr1 = (enc1 &lt;&lt; 2) | (enc2 &gt;&gt; 4);
          chr2 = ((enc2 &amp; 15) &lt;&lt; 4) | (enc3 &gt;&gt; 2);
          chr3 = ((enc3 &amp; 3) &lt;&lt; 6) | enc4;
          output = output + String.fromCharCode(chr1);
          if (enc3 != 64) {
                output = output + String.fromCharCode(chr2);
          }
          if (enc4 != 64) {
                output = output + String.fromCharCode(chr3);
          }
      }
      //output = _utf8_decode(output);
      return output;
}
</code></pre>
<p>观察到该函数的 “output = _utf8_decode(output);” 这句代码被注释掉了,但是网页提供的编码函数却有 “input = _utf8_encode(input);”,因此我们执行代码的时候不能用这句。<br>
<img src="https://img2020.cnblogs.com/blog/1774310/202008/1774310-20200821215439190-1435601172.png" alt="" loading="lazy"><br>
最后进行 decodeURIComponent() 函数的逆过程,这个函数是 JavaScript 的内置函数,用于对 encodeURIComponent() 函数编码的 URI 进行解码。因此我们这里只需要直接调用 encodeURIComponent() 函数,对字符串进行最后一次编码即可。</p>
<pre><code>output = encodeURIComponent(output)
</code></pre>
<p>到此为止,我们终于获得了用于替代原来变量的字符串了。</p>
<h1 id="提交-cookie">提交 cookie</h1>
<p>根据对代码的审计,我们应该那这个字符串和 cookie 中的 user 参数替换,可以用 HackBar 也可以抓包。<br>
<img src="https://img2020.cnblogs.com/blog/1774310/202008/1774310-20200821215842139-1058632520.png" alt="" loading="lazy"><br>
提交成功之后,会发现我们的钱瞬间花不完了,赶紧把神功全部学了然后讨伐魔头获得 flag。<br>
<img src="https://img2020.cnblogs.com/blog/1774310/202008/1774310-20200821220026320-1433167322.png" alt="" loading="lazy"></p><br><br>
来源:https://www.cnblogs.com/linfangnan/p/13543498.html
頁: [1]
查看完整版本: CTF-WEB:Bugku-江湖魔头(Javascript 审计 + 逆向解密)