最近由於工作需要在看libinjection的原始碼,查到一位師傅發了一篇繞過libinjection的文章,
https://www.o2oxy.cn/2772.html
由於URL編碼和HEX編碼的問題,感覺這位師傅寫的不是很對,就發評論跟他討論,當然他整個libinjection的流程分析還是很不錯的。
討論了半天,這位師傅又丟擲了一個payload,說libinjection也檢測不出來
1={date(if(mid((updatexml(1,concat(0x7e,(select user()),0x7e),1)),1,1)='1',2,1))}
嘗試了一下的確如此:
很久之前看到過這種注入的手法,當時沒有深究,這次打算好好研究一下。
mysql的SQL解析語法檔案:
https://github.com/mysql/mysql-server/blob/8.0/sql/sql_yacc.yy
(這個是yacc的產生式檔案,有興趣的師傅可以去找找lex&yacc的資料,順便看看編譯原理更佳~)
PS:開頭註釋表明5.7之後語法分析就沒有變過
看這個檔案懵逼了很久,最後直接搜尋’{’(三個字元)找到了關鍵點:
'{' 標誌符 表示式 '}'
是simple_expr(簡單表示式)的一種,走PTI_odbc_date這個類的解析方法
https://github.com/mysql/mysql-server/blob/8.0/sql/parse_tree_items.cc
通過註釋可以看出,該功能是為了解析ODBC的跳脫形式語法而寫的,並且最後如果標誌符
部分不是d
、t
、ts
,就會直接返回表示式
的內容。
實際上效果上來說,標誌符
部分可以任意寫,表示式
部分都可以正常執行
所以可以嘗試通過這種形式進行SQL隱碼攻擊防禦的繞過。
https://dev.mysql.com/doc/refman/8.0/en/expressions.html
https://downloads.mysql.com/docs/refman-4.1-en.a4.pdf
這塊感謝同事donky16師傅,實際上手冊中有寫這種用法,之前搜關鍵詞braces
和curly brackets
,就是沒搜curly braces
。。。。:
上面那波分析,多少有點走彎路了。
從手冊中可以看出,從3.23開始就已經存在了ODBC跳脫語法。
1={date(if(mid((updatexml(1,concat(0x7e,(select user()),0x7e),1)),1,1)='1',2,1))}
payload中,date並不代表一個函數,而是一個識別符號
, 後面整個括號包裹的if部分是表示式
,並且可以不適用括號分割而,轉而使用空格之類的進行分割:
可以看到兩個效果是一樣的,都可以正常觸發報錯,說明updatexml函數已經被執行了。
但是後一種寫法是可以被libinjection檢測出來的:
而libinjection中實際上已經考慮了ODBC跳脫的情況:
libinjection中有種token型別為bareword
bareword可能被認為:label 、 控制程式碼 、函數 、 普通字串
libinjection特定位置上只有非關鍵字列表(方法名、變數名、select union之類的關鍵字)才會被認為是bareword。
在libjection的token摺疊函數中,’{’+BAREWORD的組合會進行摺疊,而像’{date’這種形式會被解析成’{’+FUNCTION的Token串,不會被摺疊導致了繞過。
{foo expr}
而在ODBC跳脫語法的語境中,上述foo
位置都會被認為是識別符號
或者說bareword
。
所以此處解決辦法有兩個:
最後選用了第二種方法進行完善,可以正常的檢測出來該SQL隱碼攻擊:
libinjection原始碼(語意分析SQL隱碼攻擊檢測):
https://github.com/client9/libinjection
感謝這位師傅,漲知識了~:
https://www.o2oxy.cn/2772.html
MySQL原始碼:
https://github.com/mysql/mysql-server/
MySQL手冊: