Have you ever considered the potential gaps that may arise when relying solely on security tests integrated into your testing tools? If so, you’re in the right place.
In this blog post, we explore the paramount importance of custom security tests. Tailored to your application’s unique features and risk scenarios, these tests provide a strategic advantage in identifying vulnerabilities. They not only reduce organizational risk but also minimize personal risk for security engineers.
Let’s begin by understanding why you need to write custom security test, specifically in the context of implementing it within the DAST (Dynamic Application Security Testing) tools.
DAST is the type of test you want to run against your fully built and running application. It’s often referred to as black-box testing since it’s not looking into the source code of the application but a fully deployed application. This is often the same way that external attackers will see your application.
Therefore, it’s extremely crucial to understand how to write security tests. By writing those, we want to create a simulated set of actions that attackers are most likely to take.
Think of it this way: if an attacker is going to approach your web application by first scanning all the open ports on that given URL and/or enumerate all the subdomains, then perhaps that’s one of the things you should account for in your security tests. This will help you to detect if any internal subdomains and/or ports accidentally got opened to the public.
Let’s look at the following scenario. We have a web application that we are scaling up. The expected number of users is growing each day, and so are our development processes. We want to ensure that we don’t expose SSH port to the public. We can leverage a tool like Nuclei to write a template that will check if port 22 is opened, and alert us on Slack if there is a find:
id: ssh-port-alert
info:
name: SSH Port Alert
author: Your Name
severity: high
description: |
This template is designed to detect the presence of an open SSH port (22) and send an alert to Slack.
requests:
- method: GET
path:
- /
matchers:
- type: port
ports:
- 22
matchers-condition: or
matchers:
- type: status
status:
- 200
notify:
- slack:
url: "https://hooks.slack.com/services/your/slack/webhook/url"
channel: "#your-channel"
username: "NucleiBot"
icon_url: "https://your-icon-url.com/icon.png"
message: "Alert: Open SSH Port (22) detected on {{Hostname}}"
We have a team of engineers that is working on the subdomain developers.example.com
. We do not want it to be publicly accessible as we haven’t launched the feature just yet. We can create a similar template that checks for a new subdomain and emails us if it’s exposed:
id: public-domain-alert
info:
name: Public Domain Alert
author: Your Name
severity: high
description: |
This template is designed to check if developers.example.com is publicly accessible and send an email notification if detected.
requests:
- method: GET
path:
- /
matchers:
- type: status
status:
- 200
notify:
- email:
to: "[email protected]"
subject: "Public Domain Alert - developers.example.com"
body: |
Hello,
This is an alert to inform you that developers.example.com is publicly accessible.
Best regards,
Your Name
We are moving from REST API to GraphQL development. We have cases when we have to use introspection for development. However, we want to ensure that introspection is disabled for a graph in production.
We can write a simple Nuclei template that sends a GraphQL introspection query to the target and looks for specific keywords ("Query," "Mutation," "Subscription") in the response. If any of these keywords are found, it indicates that introspection is enabled :
id: graphql-introspection-check
info:
name: GraphQL Introspection Check
author: Your Name
severity: medium
description: |
This template is designed to check if GraphQL introspection is enabled.
requests:
- method: POST
path:
- /
headers:
Content-Type: "application/json"
body: |
{
"query": "{__schema{types{name}}}"
}
matchers:
- type: word
words:
- "Query"
- "Mutation"
- "Subscription"
notify:
- webhook:
url: "https://your-webhook-url.com"
method: "POST"
headers:
Content-Type: "application/json"
body: '{"message": "GraphQL Introspection Enabled on {{Hostname}}"}'
One of the core pain points in custom test implementation with Nuclei is the maintenance of each test, to follow along the attack surface changes, whether they are pure API specification changes, or new APIs that extend your organization’s attack surface.
Thankfully, keeping track of everything exposed is one of the core features of Escape’s API inventory, so you can be notified of any newly exposed APIs and quickly setup a scan (with even more powerful authentication recently released)
Escape’s custom test framework can help you harness the feedback-driven crawling & testing of any API specification.
In this section, we’ll cover the various blocks that make up a custom test in Escape:
For example, you can seed your test with pre-made requests, like Nuclei.
seed:
- protocol: http
raw: |
@Host: https://example.com
GET /debug HTTP/1.1
Host: example.com
Content-Type: application/json
You can simply define what the alert would look like in your Escape dashboard thanks to an Alerting block:
alert:
name: Deletion successful forced
context: >
For compliance reasons, the non admin user must not be able to delete some
data via the API.
severity: HIGH
Of course, you can specify detection criterias to trigger the alert with an AND logical operator, and use advanced selectors such as:
And many others.
Simple examples might just inspect the response, or more advanced use-cases may combine detections on both the request and the response.
As it was mentioned previously, the language’s abilities are not limited, you can dive into deeper characteristics such as the type of objects contained in each (aka scalars), this is thanks to the engine’s inference system.
You can define your own scalars to detect internal tokens, and then write a detection rule to detect leaks at an organization-scale, it is that simple.
detect:
- if: response.object
type:
in:
- internal_api_token
But what would be the fun in a scanner if we can modify the expected structure of requests ? This is where transformations come into play.
The transform block is compose of “trigger” & “mutate” actions, here’s a simple example:
transform:
trigger:
- if: schema.url
is: "/api/v1/tested/route"
mutate:
- key: request.headers
name: X-API-version
value: "APIV2"
Want to validate that all API routes versioned to V1 are not open to V2 clients ?
transform:
trigger:
- if: schema.url
contains: "v1"
mutate:
- key: request.headers
name: X-API-version
value: "APIV2"
Or maybe you’d like to validate that any email object is only accepting a specific domain, checking all your domain validation logic across the organization’s APIs ?
This transformation will trigger only on specific routes, and will look for email scalars in requests and replace their domain.
transform:
trigger:
- if: schema.url
is: "/api/v1/tested/route"
mutate:
- key: request.object
select:
type:
is: email
name:
is: "admin_email"
value:
regex: .*@escape.tech
mutate:
regex_replace:
pattern: (.*)@escape.tech
replacement: \[email protected]
Support for JQ-style notation is also available to let you manipulate objects in complex ways.
For example, here we could test for Mass-Assignment styles of vulnerabilities by adding new properties to a request object.
transform:
trigger:
- if: request.body.json
is: { "user": "admin" }
mutate:
- key: request.body.json
jq: '. | {"user": .user + " {{modify_value}}"}'
You can read more here about Escape's Custom Test language reference.
As we've showed you with custom security tests, it’s clear that the future of application security is not just in the tools we use, but in how we tailor them to our unique needs.
What does this all mean for you? Well, by creating tests that are tailor-made for your application, you're not just fixing problems as they pop up – you're working to stop them proactively. This way, you're not just reacting to threats, you're outsmarting them.
💡 Want to learn more about testing? Check out the following articles:
*** This is a Security Bloggers Network syndicated blog from Escape - The API Security Blog authored by Guest Expert - Aleksandr Krasnov. Read the original post at: https://escape.tech/blog/writing-custom-security-tests/