-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add audit.open_api_auth plugin #17363
base: develop
Are you sure you want to change the base?
Add audit.open_api_auth plugin #17363
Conversation
…ons for all auth schemes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome job!
def __init__(self): | ||
AuditPlugin.__init__(self) | ||
self._spec = None | ||
self._expected_codes = [401] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add 403?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also thought about it. In a perfect world, an application should return 401 if no auth info is present at all. 403 should be usually returned if a request contains auth info, but authorization failed. So now the plugin assumes that it lives in a perfect world :) I think it is not a big deal If an application returns 403 instead of 401 but strictly speaking it doesn't sound correct to me. Anyway, I don't mind adding 403 here. I would even let a user to configure it - it may be next improvement.
w3af/plugins/audit/open_api_auth.py
Outdated
# The check works only with API endpoints. | ||
# crawl.open_api plugin has to be called before. | ||
if not self._is_api_spec_available(): | ||
om.out.information("Could not find an API specification, skip") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
During a regular w3af run, this line will be printed to the output MANY times. I recommend using om.out.debug
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also the message could be improved: "Could not find an API specification in the KB, skipping open_api_auth"
w3af/plugins/audit/open_api_auth.py
Outdated
|
||
# The API spec must define authentication mechanisms. | ||
if not self._has_security_definitions_in_spec(): | ||
om.out.information("The API spec has no security definitions, skip") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change message to The Open API spec has no security definitions, not running open_api_auth checks.
Also, this should be printed only once per each open-api specification document w3af finds.
w3af/plugins/audit/open_api_auth.py
Outdated
# The check only runs if the API specification | ||
# requires authentication for a endpoint. | ||
if not self._has_auth(freq): | ||
om.out.information("Request doesn't have auth info, skip") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve message and change to debug.
w3af/plugins/audit/open_api_auth.py
Outdated
:param response: The HTTP response associated with the fuzzable request. | ||
""" | ||
if response.get_code() not in self._expected_codes: | ||
desc = 'A %s request without auth info was send to %s. ' \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... request without authentication information ...
was sent to
replied with unexpected HTTP response code %d (expected one of 401, 403, etc.)
And add: This is a strong indicator of a REST API authentication bypass
.
response.get_code(), self._expected_codes) | ||
|
||
# Should severity depend on response codes? | ||
# For example, 2xx codes results to HIGH, but 4xx may be MEDIUM/LOW |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is a good idea to remove false positives.
The way I recommend doing it is to add a new check below if not self._has_auth(freq)
, like this:
if not self._response_is_successful(freq):
om.out.debug("OpenAPI request {some request info here} did not return a successful response. Skipping open_api_auth check for this endpoint.")
Where _response_is_successful
would send the request (with credentials) and check that the response code is 200, 201, or some other codes which are commonly used to indicate success.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean adding _response_is_successful
check between _remove_auth
and _remove_auth
and _uri_opener.send_mutant
calls in audit()
method? But wasn't this request already sent? Is the result already available in orig_response
?
What would be a definition of a successful response? 200 may be not enough since a endpoint may return 201, 202, etc. In fact, response codes should be defined in the spec. In general, it would be a nice improvement. Do you think such a check is strictly necessary?
In fact, I meant another thing here. I am talking about assigning different severity levels what we report a vulnerability to the KB. It should be done if/after _response_is_successful
check passed.
w3af/plugins/audit/open_api_auth.py
Outdated
if self._spec: | ||
return True | ||
|
||
specification_handler = kb.kb.raw_read('open_api', 'specification_handler') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if there is more than one openapi document found during a scan?
Maybe you should store a list and call it kb.kb.raw_read('open_api', 'specification_handlers')
(added an s
at the end)
This also means that you'll have to touch other areas of this plugin to process all specification handlers, not just one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good point although it doesn't look like a typical situation. I'll try to address it. Hope it won't make the plugin too complicated.
w3af/plugins/audit/open_api_auth.py
Outdated
* Check if the response has 401 error code (access denied). | ||
|
||
A couple of important notes: | ||
* The plugin requires the 'crawl.open_api' plugin to be enabled. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can force that. Plugins can have other plugins as dependencies.
https://github.com/andresriancho/w3af/blob/master/w3af/core/controllers/plugins/plugin.py#L123
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When plugin A depends on B, and plugin A is enabled, plugin B will be automatically enabled too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's nice, I didn't know about it. Let me add such a dependency.
Here is a couple of updates:
I also have a couple of comments/questions. Please let me know what you think. |
@andresriancho Still waiting for feedback (and for other pull requests too). Please take a look when you have time. |
This pull request introduces a new plugin
audit.open_api_auth
which is described in #17343Implementation notes:
crawl.open_api
plugin.crawl.open_api
plugin shares API specification via the knowledge base.Please consider it as a first version of the plugin. I was going to make the plugin smaller but it turned out that it needs quite a lot of checks, so now the plugin is not that small :)
I believe the plugin may be improved. Please take a look at several enhancements in the plugin description, and let me know what you think. I am planning to work on them but would prefer to implement them separately after the first version of the plugin is integrated.