前言
今天看了下團隊發的XCTF高校戰疫的WP,這里先喊一句。楊大樹師傅太強了orz 幾乎沒幫啥忙,簡單的題目都被師傅們秒了,我進度太慢,跟不上,后面難的題目基本動不了。再喊一遍,星盟的師傅tql。再接再厲,希望下次能上師傅們,幫到師傅們解題。
回顧主題,這里是從看到比賽里的sqlcheckin這道題目的WP后,才覺得很奇怪,然后百度發現並學習到了mysql的隱式類型轉換的點。(比較菜沒有仔細學習過mysql)
MYSQL弱類型轉換
這里本地搭建一下環境(用的老的語句),php7比較高的環境用新的語句
<?php $conn = mysql_connect('127.0.0.1','root','root'); //這里使用@符號屏蔽出錯信息 mysql_select_db('test',$conn); $sql ="select * from people where username = '".$_POST["username"]."' and password = '".$_POST["password"]."'"; $result = mysql_query($sql); while($row = mysql_fetch_array($result)){ echo "用戶ID:" . $row['id'] . "<br/>"; echo "用戶名:" . $row['username'] . "<br/>"; echo "用戶密碼:" . $row['password'] . "<br/>"; } mysql_close($conn); echo "<hr>"; echo "您當前執行的sql語句為:" ; echo $sql; ?>
比如很簡單的一句SQL語句
select * from people where username=' 'and password=' ';
當我們在username輸入'- password傳入'
結果輸出了一個表的內容,為什么會這樣呢。是因為mysql存在的隱式類型轉換的問題。
什么是隱式類型轉換
在MySQL中,當操作符與不同類型的操作數一起使用時,會發生類型轉換以使操作數兼容。則會發生轉換隱式
也就是說,MySQL會根據需要自動將數字轉換為字符串,將字符串轉換數字。
這里因為存在-號,隱式類型轉化將' '和' and password = '字符串進行了算術運算,轉為成數字類型。
這里再舉兩個例子
案例一:字符串轉換為數字
mysql > SELECT 1+'1';
結果:
mysql > 2
案例二: 數字轉換為字符串
mysql -> SELECT CONCAT(1024,' andyqian');
結果:
'1024,' andyqian';
而通過cast()函數測試后發現字符串轉為算數值時會將字符串開頭的一串數字作為其轉換后的數值,比如
’02admin’ ==>2 ‘admin’==>0 ’33admin’==>33
這個與php的弱類型問題相似。
所以上面這樣的輸入,其實就會轉化成0-0=0
而username的弱類型轉換值如果為0的話,那么所有的用戶都將被檢索到
這檢索的時候,將字符串類型轉化為數字,admin,yunying都被強制轉化成數字類型,即為0
這里我們插入新的一行
從這里就可以看到MYSQL隱式類型轉換帶的問題。
所以sqlcheckin里,username必須為admin,但是password可以輸出a'&'1
這里'a'&'1'執行了位運算
所以這里password為0,類型轉化后,就可以檢索到username='admin',字符串開頭的password的數據行
若password是數字不為0,且是非0開頭的字符串的話,就檢索不到,這樣的轉換就是隱式類型轉換的表現
如何避免隱式類型轉換
只有當清楚的知道隱式類型轉換的規則,才能從根本上避免產生隱式類型轉換。MySQL也在官網描述了進行隱式類型轉換的一些規則如下:
1. 隱式類型轉換規則:
-
如果一個或兩個參數都是NULL,比較的結果是NULL,除了NULL安全的<=>相等比較運算符。對於NULL <=> NULL,結果為true。不需要轉換
-
如果比較操作中的兩個參數都是字符串,則將它們作為字符串進行比較。
-
如果兩個參數都是整數,則將它們作為整數進行比較。
-
如果不與數字進行比較,則將十六進制值視為二進制字符串
-
如果其中一個參數是十進制值,則比較取決於另一個參數。 如果另一個參數是十進制或整數值,則將參數與十進制值進行比較,如果另一個參數是浮點值,則將參數與浮點值進行比較
-
如果其中一個參數是TIMESTAMP或DATETIME列,另一個參數是常量,則在執行比較之前將常量轉換為時間戳。
-
在所有其他情況下,參數都是作為浮點數(實數)比較的。
2.使用CAST函數顯示轉換
select * from people where username=cast(0 as char);
3. 類型一致
這里說的類型一致,指的是在寫SQL時,參數類型一定要與數據庫中的類型一致,避免產生隱式類型轉換,就如剛才在文首時,如果多檢查,寫的SQL的參數類型與數據庫中字段類型一致,也就不會不走索引了,你說是不是?
參考文章: