APIs are a common target for attackers because they expose underlying business logic and data.
Some endpoints may refer to API documentation, which can reveal available endpoints and request structures.
/api
/swagger/index.html
/openapi.jsonIf you identify an endpoint for a resource, make sure to
investigate the base path. E.g.,
/api/swagger/v1/users/123
/api/swagger/v1
/api/swagger
/apiEven if documentation is available, manually exploring the application can uncover undocumented endpoints.
/api/user/update and test
variations such as /delete, /add,
etc.Tip: Use the JS Link Finder (Burp extension) to extract API endpoints from JavaScript files.
Test all possible HTTP methods (GET,
POST, PUT, DELETE, etc.) to
check for unintended access control weaknesses.
Tip: Use Burp Intruder with a list of HTTP verbs to automate testing.
APIs often include hidden parameters that could be exploited.
Changing the Content-Type header can lead to:
Modify the Content-Type header and reformat request
data to test for such issues.
Tip: The Content-Type Converter BApp in Burp Suite can automatically switch data formats between JSON and XML.
Many modern APIs use frameworks that allow automatic assignment of incoming request data to an object. If the application does not properly filter which fields can be updated, an attacker can send unexpected data to modify sensitive fields that they shouldn’t be able to change.
Example
Consider an API that allows users to update their profile with a
PUT /users/{id} request. A User model might look like
this:
class User {
constructor(id, name, email, role, isAdmin) {
this.id = id;
this.name = name;
this.email = email;
this.role = role;
this.isAdmin = isAdmin;
}
}If the API assigns the request body to the user object like this:
app.put('/users/:id', (req, res) => {
let user = getUserFromDatabase(req.params.id);
Object.assign(user, req.body); // Vulnerability: blindly assigns all fields!
saveUserToDatabase(user);
res.json(user);
});An attacker could send a request like this:
{
"name": "attacker",
"isAdmin": true
}Testing for Mass Assignment
Send two request with: - Valid expected parameter:
{
"name": "attacker",
"isAdmin": "foo"
}Invalid expected parameter:
{
"name": "attacker",
"isAdmin": true
}If the app behaves differently, the invalid value may affect the query, while the valid one doesn’t suggesting the user can update the parameter.
APIs that pass query parameters between internal services may be vulnerable to manipulation.
A browser request:
GET /userSearch?name=test&back=/home
Might result in an internal query:
GET /users/search?name=test&publicProfile=true
By injecting a URL-encoded #, you may truncate
parameters:
GET /userSearch?name=test%23foo&back=/home
Which could modify the internal query:
GET /users/search?name=test#foo&publicProfile=true
Use a URL-encoded & to attempt parameter
injection and observe responses:
GET /userSearch?name=test%26foo=xyz&back=/home
Resulting in:
GET /users/search?name=test&foo=xyz&publicProfile=true
if the response is unchanged it may indicate that the parameter was successfully injected but ignored by the application.
If you’ve identified a parameter, add it and see if the server processes it.
GET /userSearch?name=test%26email=foo&back=/home
Resulting in:
GET /userSearch?name=test%26email=foo&publicProfile=true
The impact of this depends on how the application processes the second parameter.
GET /userSearch?name=test%26name=test2&back=/home
Resulting in:
GET /users/search?name=test&26name=test2&publicProfile=true