Injection Attacks
Testing for Injection Vulnerabilities
Intro
Before you can inject a payload using an API, you must uncover the best requests to attack. The best way to discover these injection points is by fuzzing and then analyzing the responses you receive. I have mentioned fuzzing before, but you should have a strong understanding of what is meant by this. Fuzzing APIs is the process of sending various types of input to an endpoint to provoke an unintended response. The payloads used for fuzzing include symbols, numbers, system commands, SQL queries, NoSQL queries, emojis, hexadecimal, boolean statements, and more. Essentially, you want any payload that the API may not be programmed to handle to cause the API to send a verbose response or to cause the application to behave adversely. If an endpoint does not sanitize or validate user input then the right payload could cause a verbose response, a delay in processing time, an internal server error, or an error with the database.
You should attempt fuzzing against all potential inputs and especially within the following:
Headers
Query string parameters
Parameters in POST/PUT requests
Your approach to fuzzing should depend on how much information you know about your target. If you’re not worried about making noise, you could send a variety of fuzzing inputs likely to cause an issue in many possible supporting technologies. The more you know about the API, the more you can focus your attacks and increase your chance of finding a vulnerability. This is where your reconnaissance efforts will really pay off. If you know what database the application uses, what operating system is running on the web server, or the programming language in which the app was written, you’ll be able to submit targeted payloads aimed at detecting vulnerabilities in those particular technologies.
After sending your fuzzing requests, search for responses that contain a verbose error message or some other failure to properly handle the request. In particular, look for any indication that your payload bypassed security controls and was interpreted as a command, either at the operating system, programming, or database level. This response could be as obvious as a message such as “SQL Syntax Error” or something more subtle like taking a little more time to process a request. You could even receive an entire verbose error dump that can provide you with plenty of details about the host. As we know from working with crAPI throughout this course is that if you find a weakness in one endpoint, chances are that weakness is present in other endpoints. So when a request from one endpoint reveals a useful detail make sure to leverage that information in requests to other endpoints.
Discovering Injection Vulnerabilities
Before you can exploit an injection vulnerability you will need to know where to fuzz and what to fuzz with. The art of fuzzing is knowing the right payload to send in the right requests with the right tools. The right payload can be guessed or narrowed down based on reconnaissance efforts. The right requests often are those that include user input, but there it can also be worth fuzzing headers and the URL path of other requests. The right tools depend on the fuzzing strategy that is deployed. Start by casting a wide net across an entire API and then narrow in the focus of your attack. In this module, we will use Postman to fuzz wide across the entire API collection, and then we will use Burp Suite along with Wfuzz to fuzz deep into individual requests. Fuzzing is all about requesting the unexpected. When reviewing API documentation, if the API is expecting a certain type of input (number, string, boolean value) send:
A very large number
A very large string
A negative number
A string (instead of a number or boolean value)
Random characters
Boolean values
Meta characters
By sending over this input we are testing the limits of the target's input validation. If a certain type of input causes a verbose error or causes a delayed response then you could be on the trail of an injection vulnerability.
SQL Injection Metacharacters
SQL Metacharacters are characters that SQL treats as functions rather than data. For example, -- is a metacharacter that tells the SQL interpreter to ignore the following input because it is a comment. If an API endpoint does not filter SQL syntax from API requests, any SQL queries passed to the database from the API will execute.
SQL injection, allows a remote attacker to interact with the application’s backend SQL database. With this access, an attacker could obtain or delete sensitive data such as credit card numbers, usernames, passwords, and other gems. In addition, an attacker could leverage SQL database functionality to bypass authentication, exfiltrate private data, and gain system access. By requesting the unexpected, you could to discover a situation the developers didn’t predict, and the database might return an error in the response. These errors are often verbose, revealing sensitive information about the database.
When looking for requests to target for database injections, seek out those that allow client input and can be expected to interact with a database. Here are some SQL metacharacters that can cause some issues:
'
''
;%00
--
-- -
""
;
' OR '1
' OR 1 -- -
" OR "" = "
" OR 1 = 1 -- -
' OR '' = '
OR 1=1
All of these symbols and queries are meant to cause problems for SQL queries. A null byte like ;%00 could cause a verbose SQL-related error to be sent as a response. The OR 1=1 is a conditional statement that literally means “or the following statement is true,” and it results in a true condition for the given SQL query. Single and double quotes are used in SQL to indicate the beginning and ending of a string, so quotes could cause an error or a unique state. Imagine that the backend is programmed to handle the API authentication process with a SQL query like the following, which is a SQL authentication query that checks for username and password:
SELECT * FROM userdb WHERE username = 'hAPI_hacker' AND password = 'Password1!'
The query retrieves the values hAPI_hacker and Password1! from the user input. If, instead of a password, we supplied the API with the value ' OR 1=1-- -, the SQL query might instead look like this:
SELECT * FROM userdb WHERE username = 'hAPI_hacker' OR 1=1-- -
This would be interpreted as selecting the user with a true statement and skipping the password requirement, as it has been commented out. The query no longer checks for a password at all, and the user is granted access. The attack can be performed to both the username and password fields. In a SQL query, the dashes (--) represent the beginning of a single-line comment. This turns everything within the following query line into a comment that will not be processed. Single and double quotes can be used to escape the current query to cause an error or to append your own SQL query.
NoSQL Injection
APIs commonly use NoSQL databases due to how well they scale with the architecture designs common among APIs. Also, NoSQL injection techniques aren’t as well-known as their structured counterparts. Due to this one small fact, you might be more likely to find NoSQL injections.
As you hunt, remember that NoSQL databases do not share as many commonalities as the different SQL databases do. NoSQL is an umbrella term that means the database does not use SQL. Therefore, these databases have unique structures, modes of querying, vulnerabilities, and exploits. Practically speaking, you’ll conduct many similar attacks and target similar requests, but your actual payloads will vary. The following are common NoSQL metacharacters you could send in an API request to manipulate the database:
$gt
{"$gt":""}
{"$gt":-1}
$ne
{"$ne":""}
{"$ne":-1}
$nin
{"$nin":1}
{"$nin":[1]}
{"$where": "sleep(1000)"}
$gt is a MongoDB NoSQL query operator that selects documents that are greater than the provided value. The $ne query operator selects documents where the value is not equal to the provided value. The $nin operator is the “not in” operator, used to select documents where the field value is not within the specified array. Many of the others in the list contain symbols that are meant to cause verbose errors or other interesting behavior, such as bypassing authentication or waiting 10 seconds.
OS Injection
Operating system command injection is similar to the other injection attacks we’ve covered in this chapter, but instead of, say, database queries, you’ll inject a command separator and operating system commands. When you’re performing operating system injection, it helps a great deal to know which operating system is running on the target server. Make sure you get the most out of your Nmap scans during reconnaissance in an attempt to glean this information.
As with all other injection attacks, you’ll begin by finding a potential injection point. Operating system command injection typically requires being able to leverage system commands that the application has access to or escaping the application altogether. Some key places to target include URL query strings, request parameters, and headers, as well as any request that has thrown unique or verbose errors (especially those containing any operating system information) during fuzzing attempts.
Characters such as the following all act as command separators, which enable a program to pair multiple commands together on a single line. If a web application is vulnerable, it would allow an attacker to add command separators to existing command and then follow it with additional operating system commands:
|
||
&
&&
'
"
;
'"
If you don’t know a target’s underlying operating system, put your API fuzzing skills to work by using two payload positions: one for the command separator followed by a second for the operating system command. The table below is a small list of potential operating system commands to use.
Common Operating System Commands to Use in Injection Attacks
Operating system | Command |
Windows | ipconfig shows the network configuration. dir prints the contents of a directory. ver prints the operating system and version. whoami prints the current user. |
*nix (Linux and Unix) | ifconfig shows the network configuration. ls prints the contents of a directory. pwd prints the current working directory. whoami prints the current user. |
Fuzzing Wide with Postman
Postman really shines when it comes to testing an entire API collection thanks to the Collection Runner. Whereas, Burp Suite CE and WFuzz are much better at digging into individual requests. Since there are so many places for an injection vulnerability to hide it helps to cast a wide net across a collection for weaknesses with Postman and then transition to other tools. We will be testing so many requests that I recommend duplicating the entire collection so that we can add variables throughout the collection. This will continue to maintain the integrity of the original collection and let us develop a baseline of expected responses.
I have renamed the duplicate collection to crAPI_Swagger Fuzz. We can create a fuzzing environment that can be reused from one collection to another.
Injection Targets
For injection targets, we will begin by casting a wide net and seeing which requests respond in interesting ways. Let's target many of the requests that include user input. With this in mind, I have selected the following requests.
PUT videos by id
GET videos by id
POST change-email
POST verify-email-token
POST login
GET location
POST check-otp
POST posts
POST validate-coupon
POST orders
Now let's use the original collection (crAPI_Swagger) and use the collection runner on our selected requests to develop our baseline. Remember that when you use the Collection runner you can select the requests that you want to test and you can save the responses. Select the above 10 requests. Note, the baseline of requests and responses should contain well-formed requests and expected responses. We should not have a collection that fails because of authorization or because the resources are not found. The collection should be in a state where things primarily function as expected. Once again, using the Status 200 test set up in previous modules, update your token and run the entire collection to see what your baseline looks like. Take note of how many requests pass and fail.
In this baseline, we can see that there were:
Three 200 Success responses
Three requests received 500 Internal Server Error
Three 404 Not Found
One 403 Forbidden
You can explore the variety of reasons that each response was sent, but if you have well-formed requests then proceed. Now that we have a baseline, let's update our environment with some fuzzing variables.
Now depending on information from reconnaissance, you may want to start with a specific fuzzing variable. However, it is easy enough to update the values of the variables, so I will stick with {{fuzz}}. Now go through the requests that you are targeting and add fuzzing variables where user input is found.
Now run the collection with the fuzz variable set throughout the targeted requests and investigate the results for anomalies.
In this test the total count was:
One 200 Success
Four 500 Internal Server Error
Three 404 Not Found
One 400 Bad Request
In this case, one request passed which should be interesting enough to explore the response. Sure enough, the community request did not have any issues, and posted the fuzzing variables in a community post. Also, make sure to explore the "Failed" results for anything anomalous or interesting. In the case of fuzzing you could find a verbose error message. Reviewing these results did not come up with anything interesting. Next, we will repeat this process with updated fuzzing variables.
Simply update the current value of fuzz with a new test, then use the collection runner, and review the results for anomalies.
Sure enough, we see very similar results. In this test the total count was:
One 200 Success
Four 500 Internal Server Error
Two 404 Not Found
Three 400 Bad Request
The community post was successful while the others failed in similar ways. There was some deviation in the number of 400 Bad Requests, but after investigating those results the responses were expected. This is exactly what you would hope to see, a new baseline developing. When we fuzz with certain types of input the application behaves in an expected way. Therefore, if we see update our fuzz variable to the right value then any changes will be much more obvious. Up to this point, we have tried a SQL injection test and an OS injection test. Let's try a NoSQL injection test.
At first glance, this test is slightly different. The community post was not successful and upon reviewing the failed results we see the count has changed:
One 500 Internal Server Error
Eight 400 Bad Request
One 422 Unprocessable Entity
The variation in the results here is worthy of investigation, especially with the new response. First, the request to the forum that was successful is now a 400 with the response body,
{"error": "invalid character '$' after object key:value pair"}
The POST validate-coupon request has the 422 Unprocessable Entity response and also contains the same error in the response body.
These two requests are worth exploring further in Burp Suite. Proxy these two requests to Burp Suite and send the captured requests to Intruder.
Using Intruder, update the attack positions for the two requests that you are targeting.
Since the NoSQL payload was the one that triggered an anomaly, update Intruder with a NoSQL payload list. Try this attack with Payload Encoding turned on/off to see if you notice a difference in the responses. Send the attack.
Now we are receiving several "200 Success" responses and we have obtained valid coupon codes for sending true statements to the database. We have successfully exploited a NoSQL vulnerability! Next, let's check out how this would be performed with WFuzz.
Fuzzing Deep with WFuzz
To perform this attack with Wfuzz, you will need to build out the request. I suggest using the Burp Suite save to file so that you can easily copy and paste to the terminal. Using Repeater, you can right-click on the request that you would like to target and select "copy to file".
Next, you can open a second window and use cat on the file that you saved from Burp Suite.
You can start building out the WFuzz attack. For additional information about WFuzz, use: $wfuzz --help
Start by specifying the payload that you will use with -z. Use -H to add necessary headers like "Content-Type:application/json" and the authorization token. Then use -d to specify the post body. When you are using quotes in a post body, you will need to use backslashes (\) for those to show up in the request. Finally, add the URL that you are targeting.
$ wfuzz -z file,usr/share/wordlists/nosqli -H "Authorization: Bearer TOKEN" -H "Content-Type: application/json" -d "{\"coupon_code\":FUZZ} http://crapi.apisec.ai/community/api/v2/coupon/validate-coupon
Once you have a successful attack, you can add filtering options to your requests. This will help make the response very clear when there is a successful attack. In the case of this request, we know that we are looking for responses that come back with a 200 status code. Use the show code option --sc 200 to filter out the results.
Success! Congratulations on performing an injection attack!
Before you go, check out the following advice if you need to troubleshoot you WFuzz attacks. Since WFuzz attacks can get large and complicated, I recommend getting comfortable with proxying traffic to Burp Suite. Use the -p localhost:8080 option with Burp Suite set to Intercept Requests.
With the request in Burp Suite, you can see exactly what is being sent by WFuzz and troubleshoot from there. For example, see what happens if you do not add backslashes to the quotes around coupon_code.
Last updated