sql注入绕过
今天总结一下sql注入过程中目前阶段碰到的主要过滤问题。
0x00 基本问题
sql注入过程中,为了防止注入,代码中一般会进行输入字符的过滤或拦截。其中过滤指的是输入的内容被删除or替换成了别的字符,拦截指的是检测到指定内容存在后,直接返回错误,不再进行后续的操作。
0x01 and/or
1.尝试双写or大小写绕过
2.运算符代替:&&
,||
3.^
运算符,例如:?id=1^(1=0) (1^0=1,故返回id=1的数据)
4.拼接=
运算符,例如:?id=1=(1=1) (1=1为真,故返回id=true即id=1的数据(true=1))
0x02 空格
1.注释/**/代替
2.and/or
后面可以加偶数个!或~
可以替代空格,也可以混合使用
3.%09, %0a, %0b, %0c, %0d, %a0
等部分不可见字符绕过
4.巧用括号
0x03 括号
使用order by进行盲注,例如:
1 |
|
通过修改大括号中的值,即可进行指定字符串与目标未知字符串的比较,如果大于则位置字符串排在前,如果小于则指定字符串排在前。通过limit来控制显示问题。由此可通过编写脚本进行注入。
0x04 逗号
1.盲注
2.使用join语句。例如:
1 |
|
对于某些字符串处理函数,例如substr,可以通过substr(str from pos for len)的方法以避免使用括号,limit 0,1 可以通过limit 1 offset 1代替。
0x05 引号
1.考虑宽字节注入(存在编码问题时)
2.引号逃逸(当存在两个或以上数据输入时,通过\可以转义一个单引号,随后在下一个输入框or字段中写入注入代码,最后添加注释符,这时可巧妙的避开引号闭合问题)
0x06 注释
1.巧妙闭合单引号,例如:
1 |
|
0x07 数字
代替字符 | 数 | 代替字符 | 数 | 代替字符 | 数 | ||
---|---|---|---|---|---|---|---|
false、!pi() | 0 | ceil(pi()*pi()) | 10 | A | ceil((pi()+pi())*pi()) | 20 | K |
true、!(!pi()) | 1 | ceil(pi()*pi())+true | 11 | B | ceil(ceil(pi())*version()) | 21 | L |
true+true | 2 | ceil(pi()+pi()+version()) | 12 | C | ceil(pi()*ceil(pi()+pi())) | 22 | M |
floor(pi());~~pi() | 3 | floor(pi()*pi()+pi()) | 13 | D | ceil((pi()+ceil(pi()))*pi()) | 23 | N |
ceil(pi()) | 4 | ceil(pi()*pi()+pi()) | 14 | E | ceil(pi())*ceil(version()) | 24 | O |
floor(version()) //注意版本 | 5 | ceil(pi()*pi()+version()) | 15 | F | floor(pi()*(version()+pi())) | 25 | P |
ceil(version()) | 6 | floor(pi()*version()) | 16 | G | floor(version()*version()) | 26 | Q |
ceil(pi()+pi()) | 7 | ceil(pi()*version()) | 17 | H | ceil(version()*version()) | 27 | R |
floor(version()+pi()) | 8 | ceil(pi()*version())+true | 18 | I | ceil(pi()*pi()*pi()-pi()) | 28 | S |
floor(pi()*pi()) | 9 | floor((pi()+pi())*pi()) | 19 | J | floor(pi()*pi()*floor(pi())) | 29 | T |
0x08 等于号,大于号,小于号
等于号可以通过like绕过,大于号小于号主要使用greatest函数,strcmp函数,between and操作符代替。
0x09 常见函数/符号总结
注释符
单行注释 | 单行注释 | 单行注释 | 多行(内联)注释 |
---|---|---|---|
# |
-- x //x为任意字符 |
;%00 |
/*任意内容*/ |
常用运算符
运算符 | 说明 | 运算符 | 说明 | ||
---|---|---|---|---|---|
&& | 与,同and。 | \ | \ | 或,同or。 | |
! | 非,同not。 | ~ | 一元比特反转。 | ||
^ | 异或,同xor。 | + | 加,可替代空格,如select+user() 。 |
系统信息函数
函数 | 说明 |
---|---|
USER() | 获取当前操作句柄的用户名,同SESSION_USER()、CURRENT_USER(),有时也用SYSTEM_USER()。 |
DATABASE() | 获取当前选择的数据库名,同SCHEMA()。 |
VERSION() | 获取当前版本信息。 |
进制转换
函数 | 说明 |
---|---|
ORD(str) | 返回字符串第一个字符的ASCII值。 |
OCT(N) | 以字符串形式返回 N 的八进制数,N 是一个BIGINT 型数值,作用相当于CONV(N,10,8) 。 |
HEX(N_S) | 参数为字符串时,返回 N_or_S 的16进制字符串形式,为数字时,返回其16进制数形式。 |
UNHEX(str) | HEX(str) 的逆向函数。将参数中的每一对16进制数字都转换为10进制数字,然后再转换成 ASCII 码所对应的字符。 |
BIN(N) | 返回十进制数值 N 的二进制数值的字符串表现形式。 |
ASCII(str) | 同ORD(string) 。 |
CONV(N,from_base,to_base) | 将数值型参数 N 由初始进制 from_base 转换为目标进制 to_base 的形式并返回。 |
CHAR(N,… [USING charset_name]) | 将每一个参数 N 都解释为整数,返回由这些整数在 ASCII 码中所对应字符所组成的字符串。 |
字符截取/拼接
函数 | 说明 |
---|---|
SUBSTR(str,N_start,N_length) | 对指定字符串进行截取,为SUBSTRING的简单版。 |
SUBSTRING() | 多种格式SUBSTRING(str,pos)、SUBSTRING(str FROM pos)、SUBSTRING(str,pos,len)、SUBSTRING(str FROM pos FOR len) 。 |
RIGHT(str,len) | 对指定字符串从最右边截取指定长度。 |
LEFT(str,len) | 对指定字符串从最左边截取指定长度。 |
RPAD(str,len,padstr) | 在 str 右方补齐 len 位的字符串 padstr ,返回新字符串。如果 str 长度大于 len ,则返回值的长度将缩减到 len 所指定的长度。 |
LPAD(str,len,padstr) | 与RPAD相似,在str 左边补齐。 |
MID(str,pos,len) | 同于 SUBSTRING(str,pos,len) 。 |
INSERT(str,pos,len,newstr) | 在原始字符串 str 中,将自左数第 pos 位开始,长度为 len 个字符的字符串替换为新字符串 newstr ,然后返回经过替换后的字符串。INSERT(str,len,1,0x0) 可当做截取函数。 |
CONCAT(str1,str2…) | 函数用于将多个字符串合并为一个字符串 |
GROUP_CONCAT(…) | 返回一个字符串结果,该结果由分组中的值连接组合而成。 |
MAKE_SET(bits,str1,str2,…) | 根据参数1,返回所输入其他的参数值。可用作布尔盲注,如:EXP(MAKE_SET((LENGTH(DATABASE())>8)+1,'1','710')) 。 |
常见全局变量
变量 | 说明 | 变量 | 说明 |
---|---|---|---|
@@VERSION | 返回版本信息 | @@HOSTNAME | 返回安装的计算机名称 |
@@GLOBAL.VERSION | 同@@VERSION |
@@BASEDIR | 返回MYSQL绝对路径 |
PS:查看全部全局变量SHOW GLOBAL VARIABLES;
。
其他常用函数/语句
函数/语句 | 说明 |
---|---|
LENGTH(str) | 返回字符串的长度。 |
PI() | 返回π的具体数值。 |
REGEXP “statement” | 正则匹配数据,返回值为布尔值。 |
LIKE “statement” | 匹配数据,%代表任意内容。返回值为布尔值。 |
RLIKE “statement” | 与regexp相同。 |
LOCATE(substr,str,[pos]) | 返回子字符串第一次出现的位置。 |
POSITION(substr IN str) | 等同于 LOCATE() 。 |
LOWER(str) | 将字符串的大写字母全部转成小写。同:LCASE(str) 。 |
UPPER(str) | 将字符串的小写字母全部转成大写。同:UCASE(str) 。 |
ELT(N,str1,str2,str3,…) | 与MAKE_SET(bit,str1,str2...) 类似,根据N 返回参数值。 |
NULLIF(expr1,expr2) | 若expr1与expr2相同,则返回expr1,否则返回NULL。 |
CHARSET(str) | 返回字符串使用的字符集。 |
DECODE(crypt_str,pass_str) | 使用 pass_str 作为密码,解密加密字符串 crypt_str。加密函数:ENCODE(str,pass_str) 。 |
0x0a handler语句代替select[mysql]
mysql除可使用select查询表中的数据,也可使用handler语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler语句并不具备select语句的所有功能。它是mysql专用的语句,并没有包含到SQL标准中。[故其他数据库不可用]
语法结构:
1 |
|
如:通过handler语句查询users表的内容
1 |
|
0x0b 无列名注入
在不知道列名的情况下,除了使用order by盲注外,还可以直接使用select进行盲注,即select无列名注入。例如:
1 |
|
具体可以参考V1lu0实验室招新hard_sql或CNSS 2019招新hard_sql。
0x0c 其他
UPDATE注入重复字段赋值
即:UPDATA table_name set field1=new_value,field1=new_value2 [where子句]
,最终field1字段的内容为new_value2,可用这个特性来进行UPDATA注入。如:
1 |
|
sql约束攻击
举个例子,假设我们创建了一个数据表:
1 |
|
这时候我们进行登录注册的相关代码:
1 |
|
这里没有编码问题,也进行了转移操作,由于存在长度限制,故可尝试sql约束攻击。注册名为admin 1
的用户名,密码自定,随后使用admin登录,密码就是刚刚的密码,发现登录成功。(原理,注册时select的时候,超长度的字段不会截取,故检查出未注册,而insert的时候会截取前25个字符,故再进行登录的时候’admin’=’admin ‘,从而登录成功)
0x0c 常见过滤绕过方法
1 |
|
节选自:http://byd.dropsec.xyz/2016/08/01/SQL-Injection%E7%BB%95%E8%BF%87%E6%8A%80%E5%B7%A7/