Intro Freedom of Testing is Freedom of Thinking. . . . Freedom of Testing is Freedom of Thinking. . . . API testing is often treated as a checklist in the present fast-paced world of software development. We often merely validate their response against acceptance criteria, quickly script them for automated execution, and then hurriedly move them aside to meet tight release deadlines. But, is this enough amid the increasing attack surfaces in API endpoints ? What if an API endpoint correctly returns a list of users yet exposes sensitive data due to weak access controls or security best practices? Or what if an endpoint prevents unauthorized users from performing admin tasks but is still vulnerable to privilege escalation? What if a 2FA passes each and every test cases from UI level but has a major flaw in the business logic ? The reality is that traditional functional testing only scratches the surface, leaving critical security gaps unnoticed. Some may argue that security testing is a separate discipline. But in my opinion it is a subset of testing in general. A simple yet proactive “security testing" may add a few extra hours to the release cycle, but it can prevent costly breaches, safeguard user trust, and strengthen the overall integrity of the product at least mitigating the low hanging fruits that will at least prevent average cyber criminals to exploit the system under release. In my previous write-up, I advocated for integrating web application security testing into our testing mindset and activities. In my previous write-up In my previous write-up In this article, I’ll take that a step further with a hands-on proof of concept. We’ll explore a vulnerable API, uncover potential risks, and outline a practical roadmap for security testing that any Software Quality Assurance professional can apply regardless of their security expertise. A Note Before Reading Further In real life testing, any vulnerability or misconfiguration does not appear exactly what we find in vulnerable applications to test. This is also not expected. We do exploit vulnerable applications and boxes for learning the basic and observe the most granular basic state of a a misconfiguration or vulnerability. On the other hand, we must not publish the real life vulnerabilities we find any system we are permitted to test. So, in real life this helps us attacking a seemingly impenetrable system based on our experience from playing with these vulnerable systems, as well as, we are free to make any Proof of Concepts from these vulnerable applications. The Pre-Testing Phase 1. Thorough Vulnerability Assessment: 1. Thorough Vulnerability Assessment: We assume that the VAmPI application's API endpoints have already undergone rigorous exploratory testing- we know their business logic, we know how to interact with them, and we know how they react on success and failure, we know how they are coupled with each other. Every primary vulnerability and misconfiguration has been meticulously documented, forming a solid foundation for our testing efforts. 2. Strategic Chaining of Vulnerabilities: 2. Strategic Chaining of Vulnerabilities: In real-world environments, a complex exploit rarely arises from a single flaw or misconfiguration. Instead, it’s the result of linking together several minor vulnerabilities- each seemingly insignificant on its own — into a cohesive chain that maximizes impact on the business. For example, finding a Broken Object Level Authentication or an SQL injection point is just the starting point. These weaknesses must be witfully and strategically combined to create an exploit that truly challenges business operations. This approach is drawn directly from my professional experience as a tester. 3. Interactive System Exploration: 3. Interactive System Exploration: Although this demo simplifies the process, our primary objective is to deeply understand the system by exploring it as much as possible. By interacting with the application, closely observing its behaviour, and breaking down complex functionalities into basic components, we can uncover simple vulnerabilities. These insights then serve as building blocks for developing impactful exploits that go beyond surface-level testing. Tooling Postman, john the ripper, and Burp Suite Community Edition are suffice to handle most of our API Hacking tasks. We also could use ffuf or dirbuster, but they would be an overkill in this scenario, while hashmap will eat up the tiny resource pool of an RPi2. LAN Setup A Raspberry Pi2 “server” running the vulnerable app, VAmPI An attacker “client” PC with necessary tools A Raspberry Pi2 “server” running the vulnerable app, VAmPI VAmPI An attacker “client” PC with necessary tools The First Attack Vector: Excessive Data Exposure An unauthorized user can enumerate email and username of the system by hitting the /users/v1 endpoint. /users/v1 Although this tiny bit of information with Broken Object Level Authorization does not provide us with explicit data exposure like the _debug endpoint, but we will take the hardest path and gradually explore the more low hanging fruits like broken object level authorisation, mass assignment, and password brute forcing, rate limiting test etc. This BOLA is also not worth reporting as it has a little to no impact in system’s security. Broken Object Level Authorization _debug Weak JWT Signing/JWT Token Forgery Create an unprivileged user named mister_simple Create an unprivileged user named mister_simple Get an access token by logging in and try to do an administrative task like deleting the user name1 This will resist mister_simple saying that only admin can remove this user Get an access token by logging in and try to do an administrative task like deleting the user name1 Get an access token by logging in and try to do an administrative task like deleting the user name1 This will resist mister_simple saying that only admin can remove this user This will resist mister_simple saying that only admin can remove this user Crack the weak JWT signing key with a dictionary attack Crack the weak JWT signing key with a dictionary attack 1. Collect a valid token of mister_simple 2. Crack it with John the Ripper John the Ripper PoC of the secret key PoC of the secret key Forge the token Forge the token Pass the delete request with the token into Burp Create a new asymmetric key in Burp with the signature Pass the delete request with the token into Burp Pass the delete request with the token into Burp Create a new asymmetric key in Burp with the signature Create a new asymmetric key in Burp with the signature Change sub into admin and sign it with the secret Change sub into admin and sign it with the secret Change sub into admin and sign it with the secret Change sub into admin and sign it with the secret Now, hit the API endpoint with the forged token Now, hit the API endpoint with the forged token Now, hit the API endpoint with the forged token The backend story VAmPI is a small flask app developed by one ere0s made for running in both vulnerable and strengthen mode. If the app is run in vulnerable mode, it can be deployed with a predictable secret key for signing the JWT token it returns. For demonstration purpose I did the following: Changed the secret key in /VAmPI/config.py vuln_app.app.config['SECRET_KEY'] = 'random' to vuln_app.app.config['SECRET_KEY'] = 'qazwsx,.-+-*/789' that belongs to rockyou.txt. Then deployed the app Note: If the key did not belong to the wordlist, we could not crack it. Changed the secret key in /VAmPI/config.py vuln_app.app.config['SECRET_KEY'] = 'random' to vuln_app.app.config['SECRET_KEY'] = 'qazwsx,.-+-*/789' that belongs to rockyou.txt. Changed the secret key in /VAmPI/config.py /VAmPI/config.py vuln_app.app.config['SECRET_KEY'] = 'random' to vuln_app.app.config['SECRET_KEY'] = 'random' vuln_app.app.config['SECRET_KEY'] = 'qazwsx,.-+-*/789' that belongs to rockyou.txt. vuln_app.app.config['SECRET_KEY'] = 'qazwsx,.-+-*/789' rockyou.txt. Then deployed the app Note: If the key did not belong to the wordlist, we could not crack it. Then deployed the app Note: If the key did not belong to the wordlist, we could not crack it. Note: So, the key takeaway is that if a JWT is signed by a predictable secret key, it can easily be cracked by a dictionary attack as hackers regularly harvest and update their dictionary for both malicious and pentesting purpose. Mass Assignment: A Forged Admin User Hit the /me endpoint and observe the output Hit the /me endpoint and observe the output Hit the /me endpoint and observe the output This is a textbook example of excessive data exposure. The key takeaway that “admin” key might be used as a tool to create a fake admin user. “admin” 2. Create an admin user using “admin”:true and “username”: “ admin”, with a preceding space. The point of creating such a duplicate admin is to confuse the system with authorisation and its users by harvesting fake data for social engineering, among other malicious activities. “admin”:true “username”: “ admin” Now delete a user with the token of our fake <space>admin <space>admin The Backend Story Say we have created a user called forged_admin. Would we be able to delete a user with its token ?The answer is no. Because the app only allows a user named admin to delete it, line 212. if user.admin: if bool(User.delete_user(username)): responseObject = { 'status': 'success', 'message': 'User deleted.' } if user.admin: if bool(User.delete_user(username)): responseObject = { 'status': 'success', 'message': 'User deleted.' } But it did not do any uniqueness validation for an admin user. So, an “admin” and “ admin” users with “admin”:true becomes the same for the vulnerable application. It could have sanitized usernames by discarding pre and post spaces in user_model.py, line 85. “admin” “ admin” “admin”:true user_model.py user_model.py new_user = User(username=username, password=password, email=email, admin=admin) new_user = User(username=username, password=password, email=email, admin=admin) Lack of Resources & Rate Limiting The vulnerable app version also lacks resources and rate limiting. No Rate Limiting No Rate Limiting Catch a login session with Burp suite Proxy Send it to Intruder Select sniper attack mode Select regex for the success message. This is an excellent handy feature of Burp that one need not to write own regex also an advanced user can modify the added regex. Catch a login session with Burp suite Proxy Send it to Intruder Select sniper attack mode Select regex for the success message. This is an excellent handy feature of Burp that one need not to write own regex also an advanced user can modify the added regex. 5. Load password wordlist 6. Bruteforce login session The vulnerable app did not limit the max retry rate for failed or successful login that led to bruteforce for any user login credential. Note: We could pause the the bruteforce in Burp intruder settings after getting the success message. Note: Unauthorized Password Change: Account Takeover The password change option of the application works like this /users/v1/{username}/password As the process is not verified by the token authorization, we can takeover the admin account by changing its password. /users/v1/{username}/password Login as mister_simple Go to update password Give admin to {username} Update the password Login as mister_simple Go to update password Give admin to {username} Update the password 5. Hit login with sub admin and changed password Get the token and verify in jwt.io. Get the token and verify in jwt.io. jwt.io A sweet memory to recall: In the third month of my formal career as a software tester, this was one of my first vulnerability reports,obviously in a more complex implementation, in an application that would later evolve into Bangladesh's leading digital platform for the nationwide import and export. A sweet memory to recall: Regex DoS This is a moderately unfamiliar attack to me but I made it unknowingly a few years back while testing a web app where I put a large string of ‘a’s in a text box that resulted in a crash of a service. The fellow developer said that there was ‘sort of validation’ in the backend that could not handle the unexpected number characters. However, I had not been clear about it whether it was a Regex DoS. Please comment if you have any clue about this “bug” or behaviour. But what I found in Wiki, that makes sense to me, that when a system takes long time to validate something with regex. Wiki Wiki After doing some research and I found that /users/v1/name1/email endpoint validates the email format with regex which can be found in update_email() method in users.py. /users/v1/name1/email if vuln: # Regex DoS match = re.search( r"^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@{1}([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$", str(request_data.get('email'))) if vuln: # Regex DoS match = re.search( r"^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@{1}([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$", str(request_data.get('email'))) Before Regex DoS Before Regex DoS Before the attack the application responses to every request while the resources consumption is normal. After Regex DoS After Regex DoS Prepare a string of 2000 ‘a‘s withe the following command Prepare a string of 2000 ‘a‘s withe the following command $ python3 -c "print('aa' * 2000)" > payload.txt $ python3 -c "print('aa' * 2000)" > payload.txt Put the payload in the email key and hit the update user email endpoint Put the payload in the email key and hit the update user email endpoint Put the payload in the email key and hit the update user email endpoint Notice that there is no response from the app’s endpoint. It takes an infinite time to fetch any data from any endpoint. Moreover, the app.py is now consuming more CPU, upto 100 per cent, of the system to process the regex. Notice that there is no response from the app’s endpoint. It takes an infinite time to fetch any data from any endpoint. Moreover, the app.py is now consuming more CPU, upto 100 per cent, of the system to process the regex. Notice that there is no response from the app’s endpoint. It takes an infinite time to fetch any data from any endpoint. Moreover, the app.py is now consuming more CPU, upto 100 per cent, of the system to process the regex. Wrapping it Up Wrapping it Up So far this demonstration has served as a proof of concept illustrating how we, the testers or SQAs, can incorporate basic API security testing into our daily works. While it is clear that security testing cannot be fully integrated overnight in our work culture- because of the constraints of tight deadlines, extensive stakeholder engagements, and pressure of finishing testing in tight timelines— it is, however, a must testing scope that we will need near future. A collaborative effort between IT companies and industry leaders, along with clients, is essential. Such a concerted initiative can pave the way for a well-defined roadmap that enables testers or SQAs to gradually adopt, at least, basic security testing practices into their regular workflows. As the attack surfaces of digital products are growing rapidly, the need for embedded security measures becomes increasingly vital. This proactive approach of API security testing will mitigates risks and reinforce the security of our systems in an ever-changing threat landscape. In my upcoming writeup, I wish to come up with exploiting injection vulnerabilities. Till then, stay good, keep hacking. Resources Resources 0. The classic series on exploratory testing of API by Micheal Bolton classic series on exploratory testing of API Hacking APIs: Breaking Web Application Programming Interfaces by Corey J. Ball API Hacking Tutorials by InsiderPhD API Hacking Guide by Ben Sedighipour aka NahamSec Hacking APIs: Breaking Web Application Programming Interfaces by Corey J. Ball Hacking APIs: Breaking Web Application Programming Interfaces API Hacking Tutorials by InsiderPhD API Hacking Tutorials API Hacking Guide by Ben Sedighipour aka NahamSec API Hacking Guide