Daniel Neagaru

Attacking own APIs to find security bugs

Ruby Unconf 2019

00:00:02.600 The next talk is by Daniel Neagaru, so please give him a warm welcome. I just found out that his hobbies are drawing and lock-picking. That is still legal; I just checked.
00:00:21.359 Hello, everyone! I'm a penetration tester for my tech company, and today I'm going to show you the techniques I use to find security bugs in our APIs. As I mentioned, I've been working here for a bit over a year and have three years of experience in IT security. I’m also a command-line freak. If I can do a task in the command line, I probably will, even editing in Emacs. If you want to contact me, this is my email address.
00:01:04.879 Here’s the agenda for today. At my tech company, we have a huge codebase, which makes it kind of difficult to find a starting point for attacks. The best way I've found to deal with this issue is to act like an external attacker who doesn’t know how our systems work. This allows me to observe just the traffic between the mobile device and our servers. The easiest way to do this is by performing a man-in-the-middle attack on the device, installing a new certificate to sniff the traffic. With a basic understanding of how the functionality works, I can write a script that automates basic tasks. After that, I can fine-tune the parameters to check for different unusual responses from the server. Using my findings, I can then create additional tests by extending my script and present them to the developers responsible for those areas.
00:01:46.000 The first step is to root or jailbreak the Android or iOS device because we are performing penetration tests from mobile devices. Both operating systems are quite restrictive, so I need to gain access to the data that is usually inaccessible to normal users. Sometimes, this step is necessary to install a new certificate authority on the device. I won’t go into too much detail about how to do this, as you can find most of the information online. The next step is to intercept the connection between the application and our servers. To do this, the application must trust the certificate I’m injecting, which is why I need to install it first. I will then redirect the traffic from the user to my own device, and from there, I create a connection to our back-end. This way, I can monitor all connections that are made. For this purpose, I use a web proxy. There are many available, but I prefer Burp Suite and mitmproxy. I favor the latter because it can be easily automated with Python, which is my favorite language. I don't enjoy using a mouse for these tasks.
00:03:00.000 After I sniff the connection, I filter out unnecessary requests that don’t play any role in my attacks. I then try to replicate all the relevant requests with my script. The key is to ensure that the code is flexible, allowing me to test it in different development environments or user accounts to see if the bug is reproducible elsewhere. My goal is to automate as much as possible with my scripts to make it easier to share my findings with others. Once this is done, I start fuzzing the requests. I run the script, send the requests through the web proxy, and analyze interesting parameters that can be exploited. Subsequently, I find security payloads, which I’ll demonstrate later, and check for unusual responses that can trigger the application to behave unexpectedly. Here is a list of tools I find useful. The first one is called Big List of Naughty Strings, which consists of around 600 to 700 different inputs that can cause applications to behave unexpectedly. Another useful resource is PayloadsAllTheThings, which contains various attack vectors like XSS and SQL injection payloads. Lastly, I recommend checking out the SQLi Injection list, which combines attack vectors and includes username/password combinations, leaked databases, and so forth.
00:04:52.000 The next step is to create attack scenarios. If I find any bugs, I check if they are reproducible in other environments, in real life, and so on. I extend my scripts to create attack flows that will make it easier to show them to developers. After reporting my findings, I wait for a fix and use the same script to verify if it's resolved or not. For the demonstration, instead of using our internal tools, I will present the OWASP Juice Shop project. It’s a deliberately vulnerable web application designed to allow users to practice finding security bugs. If you have any questions during the demo, feel free to stop me.
00:07:00.000 Here’s how the Juice Shop looks: it has a scoreboard with challenges categorized by difficulty levels. If you want to learn about XSS, for example, you can easily do so here. I highly recommend checking it out. For this demo, I’ll show how to exploit the login function. I attempt to log in with an admin user, but without the password, I receive an invalid email or password error.
00:08:07.000 All this traffic is sent to the web proxy I’m using, called mitmproxy. You can see the requests, along with a bunch of JavaScript files and images that are not relevant to my attack, so I filter them out to have a cleaner view of the traffic. After filtering, the view looks much nicer, and at the end of a particular POST request, I see a 401 response indicating that the user was not authenticated when I tried to log in.
00:09:07.000 You can see how the request is structured; it simply contains the JSON with email and password. The response is a 401 status code. For this demo, I wrote Python code that executes three different functions. The first function retrieves the security questions necessary for creating a user account, and it sends requests through my web proxy. The second function creates a new user, where parameters are optional, so you don’t have to specify an email or password; it will automatically generate a random one to facilitate testing without manually changing the username each time. The third function logs in the user and runs as two parts: the first creates a random user and assigns it to variables, followed by an attempt to log in.
00:10:08.480 If I run the script now, I will observe six new requests. The first request retrieves the security questions, which return JSON data. The second attempts to register a new random user with random passwords while making use of the security questions obtained earlier. The server responds with confirmation that the user was created successfully. When logging in, the request consists of JSON with email and password as before, leading to an access token being returned, indicating successful authentication!
00:11:36.499 Now the next step is to fuzz the parameters. Since mitmproxy does not inherently include a fuzzing functionality, I wrote my own Python script. This script parses the requests and extracts all parameters to allow flexibility in testing. I know that vulnerabilities can exist within the email field. Generally, it involves a lot of trial and error to understand how everything works. This bottom side shows my script, targeting the email field with payloads from the Big List of Naughty Strings. My script also accepts two different parameters: one for encoding type (base64 URL encoding or none) and another for specifying where to place the payload in relation to the value. In this case, it will replace the existing string with the benign
00:12:57.000 After running the fuzzing process with my list of generic SQL injections, I received a variety of error responses and a range of 401 responses indicating that I was not authenticated. Eventually, I encountered some 500 responses, which are intriguing because they often reveal underlying issues within the application. Opening those responses indicated SQLite errors, effectively suggesting vulnerabilities due to improper handling of parameters. Therefore, I plan to substitute valid test cases with single quotes in the email field next time to further investigate how the application handles these erroneous inputs.
00:16:10.290 Reviewing the responses, I start filtering for various important indicators and just as importantly documenting failures and potential blind paths for each of those checks. It's crucial to continue replaying tests while changing payloads and keeping track of successful responses, especially anytime access tokens return valid. It's invaluable information that can often lead to the injection of SQL or similar successfully bypassing authentication.
00:17:31.000 The demonstration is now complete; I’m happy to answer any questions you may have!
00:18:12.000 If there are any questions regarding determining potential security vulnerabilities, I’d say that business logic errors keep surfacing as a common challenge. It's difficult to defend against them, and effectively it’s necessary to hire penetration testers for assistance. Regarding techniques like request signing and certificate pinning, these additional controls will elevate the difficulty for malicious users. However, for other applications not employing these measures, it may indeed become easier to execute a successful attack. Overall, while frameworks that escape parameters reduce the likelihood of successful SQL injections, the business logic issues remain problematic.