segunda-feira, 5 de junho de 2023

New Malware Used By SolarWinds Attackers Went Undetected For Years

 


The threat actor behind the supply chain compromise of SolarWinds has continued to expand its malware arsenal with new tools and techniques that were deployed in attacks as early as 2019, once indicative of the elusive nature of the campaigns and the adversary's ability to maintain persistent access for years.

According to cybersecurity firm CrowdStrike, which detailed the novel tactics adopted by the Nobelium hacking group last week, two sophisticated malware families were placed on victim systems — a Linux variant of GoldMax and a new implant dubbed TrailBlazer — long before the scale of the attacks came to light.

Nobelium, the Microsoft-assigned moniker for the SolarWinds intrusion in December 2020, is also tracked by the wider cybersecurity community under the names UNC2452 (FireEye), SolarStorm (Unit 42), StellarParticle (CrowdStrike), Dark Halo (Volexity), and Iron Ritual (Secureworks).

The malicious activities have since been attributed to a Russian state-sponsored actor called APT29 (also known as The Dukes and Cozy Bear), a cyber espionage operation associated with the country's Foreign Intelligence Service that's known to be active since at least 2008.

GoldMax (aka SUNSHUTTLE), which was discovered by Microsoft and FireEye (now Mandiant) in March 2021, is a Golang-based malware that acts as a command-and-control backdoor, establishing a secure connection with a remote server to execute arbitrary commands on the compromised machine.

Mandiant also pointed out that Dark Halo actors had used the malware in attacks going back to at least August 2020, or four months before SolarWinds discovered its Orion updates had been tampered with malware designed to drop post-compromise implants against thousands of its customers.

In September 2021, Kaspersky revealed details of a second variant of the GoldMax backdoor called Tomiris that was deployed against several government organizations in an unnamed CIS member state in December 2020 and January 2021.

The latest iteration is a previously undocumented but functionally identical Linux implementation of the second-stage malware that was installed in victim environments in mid-2019, predating all other identified samples built for the Windows platform to date.


Also delivered around the same timeframe was TrailBlazer, a modular backdoor that offers attackers a path to cyber espionage, while sharing commonalities with GoldMax in the way it masquerades its command-and-control (C2) traffic as legitimate Google Notifications HTTP requests.

Other uncommon channels used by the actor to facilitate the attacks include —

  • Credential hopping for obscuring lateral movement
  • Office 365 (O365) Service Principal and Application hijacking, impersonation, and manipulation, and
  • Theft of browser cookies for bypassing multi-factor authentication

Additionally, the operators carried out multiple instances of domain credential theft months apart, each time leveraging a different technique, one among them being the use of Mimikatz password stealer in-memory, from an already compromised host to ensure access for extended periods of time.

"The StellarParticle campaign, associated with the Cozy Bear adversary group, demonstrates this threat actor's extensive knowledge of Windows and Linux operating systems, Microsoft Azure, O365, and Active Directory, and their patience and covert skill set to stay undetected for months — and in some cases, years," the researchers said.

More info

Security And Privacy Of Social Logins (I): Single Sign-On Protocols In The Wild

This post is the first out of three blog posts summarizing my (Louis Jannett) research on the design, security, and privacy of real-world Single Sign-On (SSO) implementations. It is based on my master's thesis that I wrote between April and October 2020 at the Chair for Network and Data Security.

We structured this blog post series into three parts according to the research questions of my master's thesis: Single Sign-On Protocols in the Wild, PostMessage Security in Single Sign-On, and Privacy in Single Sign-On Protocols.

Overview

Part I: Single Sign-On Protocols in the Wild

Although previous work uncovered various security flaws in SSO, it did not work out uniform protocol descriptions of real-world SSO implementations. We summarize our in-depth analyses of Apple, Google, and Facebook SSO. We also refer to the sections of the thesis that provide more detailed insights into the protocol flows and messages.
It turned out that the postMessage API is commonly used in real-world SSO implementations. We introduce the reasons for this and propose security best practices on how to implement postMessage in SSO. Further, we present vulnerabilities on top-visited websites that caused DOM-based XSS and account takeovers due to insecure use of postMessage in SSO.

Part III: Privacy in Single Sign-On Protocols (coming soon)

Identity Providers (IdPs) use "zero-click" authentication flows to automatically sign in the user on the Service Provider (SP) once it is logged in on the IdP and has consented. We show that these flows can harm user privacy and enable new targeted deanonymization attacks of the user's identity.

Single Sign-On Protocols in the Wild

We presume basic knowledge of the SSO protocols OAuth 2.0 and OpenID Connect 1.0
Also, you should be familiar with the postMessage API and the general concept of frames and popups in web browsers. Chapter 2 of the thesis introduces all basics.

To understand real-world SSO implementations, we selected three frequently used IdPs for detailed protocol analyses: Apple, Google, and Facebook. You can find an overview of all Authentication Request/Response and Token Request/Response messages in Appendix A.1 of the thesis.

Identity Provider: Apple

Sign in with Apple is intended for user authentication only, whereas the authorization part is reserved for future use. Besides native libraries for iOS, macOS, tvOS, and watchOS, REST endpoints provide SSO functionality to third-party native apps. Websites can integrate the JavaScript SDK that is based on these endpoints. Although the Authentication and Token Endpoints perform standard-compliant OpenID Connect Code and Hybrid flows (`response_type=code[&id_token]`, `response_mode=query|fragment|form_post|web_message`), there are some features in the authentication & consent part worth mentioning:
  • The native libraries are tightly integrated into the OS using the existing authentication on the device. Thus, biometric user authentication is possible.
  • Apple does not maintain an authenticated session at the IdP. Thus, each (web) SSO flow requires reauthentication.
  • The user authentication is protected with 2FA by default. If the 2FA succeeds, users can choose to trust the browser, which stores a cookie that supersedes future 2FA.
  • The scope is limited to the name, which can be modified, and email.
  • Users can choose to share their real email with the SP or request Apple to generate an anonymous random email that acts as a proxy between the SP and the user's email account.
More details are provided in Section 3.2 of the thesis.

Identity Provider: Google

The Google Identity Platform provides several identity tools, including:
  • Google OAuth 2.0 and OpenID Connect 1.0: Certified OpenID Connect endpoints enable user authentication and authorization for Google APIs (i.e., Calendar, Drive, and more).
  • Google Sign-In: Custom authentication SDK based on the OAuth 2.0 IDP-IFrame-based Implicit Flow and available for Android, iOS, and the web. The web SDK embeds a hidden proxy iframe on the SP website and uses the postMessage API to communicate between Google and the SP. Since the proxy iframe is same-origin with Google, it has access to the session, receives the Authentication Response, and forwards it to the SP utilizing the postMessage API.
  • Google One Tap Sign-In and Sign-Up: SDK for Android and the web that introduces the account creation process on websites with a single tap on a button. The web SDK presumes an active session on Google, embeds the consent page in an iframe on the SP website, and uses the Channel Messaging API for communication between the SP and Google. Therefore, the web SDK on the SP generates a new `MessageChannel` with two ports and transfers `port2` to the consent page iframe with postMessage. Henceforth, the consent page iframe sends messages (i.e., the `id_token`) to `port2` while the web SDK receives them on `port1` and vice versa.
Since the One Tap SDK is quite different from traditional SSO flows, we will briefly outline its unique use of new web APIs. The project initially launched as Google YOLO (You Only Login Once) and had a significant drawback: the consent page iframe was vulnerable to clickjacking. This issue was reported in early 2018 and fixed with restricted API access to trusted websites. Later, Google redesigned the SDK with the new Intersection Observer API v2 that it announced in February 2019:
Intersection Observer v2 introduces the concept of tracking the actual "visibility" of a target element as a human being would define it. [...] A true value for isVisible is a strong guarantee from the underlying implementation that the target element is completely unoccluded by other content and has no visual effects applied that would alter or distort its display on screen. In contrast, a false value means that the implementation cannot make that guarantee. 

This new API enables the consent page iframe to check whether it is visible on the SP website. If it is not visible, the iframe can block the consent or start alternative flows. Unlike the `X-Frame-Options` and `frame-ancestors` directives, Intersection Observer v2 does not prohibit iframe embedding. Still, it prevents clickjacking, which is helpful for the SSO consent page.

Sidenote 1: OAuth 2.0 Assisted Token describes a new flow that similarly embeds the consent page in an iframe but uses `X-Frame-Options`, `frame-ancestors`, or JavaScript frame busting as clickjacking mitigation. Since the IdP knows the SP to which it serves the consent page, it whitelists the SP origin within the framing directives, i.e., `X-Frame-Options: allow-from https://sp.com`:
Due to the use of an iframe to host the assisted token endpoint, the authorization server MUST take precautions to ensure that only trusted origins are allowed to frame it. The authorization server MUST prevent any origin from framing the assisted token endpoint except ones that an administrator has explicitly allowed. 

However, these anti-framing techniques do not prevent the trusted origins from executing a clickjacking attack to obtain consent by fraud. Thus, the IdP must take any measures deemed appropriate to ensure that the SP is trusted to not execute any clickjacking attacks. This limitation causes problems to public IdPs (i.e., Google and Facebook) as they certainly cannot ensure the trustworthiness of their self-registered SPs. If the SP cannot be trusted, the consent page must be protected against framing (i.e., using `X-Frame-Options: deny`) and alternative flows may be started.

We are confident that the Intersection Observer v2 API provides a promising concept for future "one-tap" SSO flows because it allows framing the consent page (and thus entire SSO flows in iframes) without the risk of clickjacking. Currently, only Chromium-based browsers are compatible with Intersection Observer v2, but this might change in the future.

Sidenote 2: If you analyze the security of postMessage on websites, you probably use a browser extension that logs all messages exchanged via the postMessage API. We developed a Chrome extension that logs all messages sent via the Channel Messaging API to the console. If you conduct postMessage security analyses, we highly recommend checking the Channel Messaging API as well.

More details are provided in Section 3.3 of the thesis.

Identity Provider: Facebook

Facebook Login implements the OAuth 2.0 protocol for data access authorization and user authentication. Although OpenID Connect 1.0 defines the signed `id_token`, Facebook issues an `access_token` for user authentication. The `access_token` provides authorized access to Facebook's Token Debugging Endpoint, which returns the `app_id` of the SP that this token is intended for (`aud` claim), the `user_id` of the user that owns this token (`sub` claim), the validity, the expiration, the associated scopes, and more.

Also, Facebook issues a `signed_request`, which is a base64url-encoded and symmetrically integrity protected token. It is not a JWT – instead, it prepends the HMAC to the claims as follows: `<hmac_bytes>.{"user_id": "[...]", "code": "[...]", "algorithm": "HMAC-SHA256", "issued_at": 1577836800}`. Although the `signed_request` does not include an audience (`aud`) claim, it implicitly provides audience restriction with its symmetric HMAC that is generated with the `app_secret` of the appropriate SP. If the SP successfully verifies the HMAC, it can assume that it was issued by Facebook for itself. The SP uses the `user_id` and `code` claims to authenticate the user, i.e., it retrieves the user entry matching the `user_id` from its database or redeems the `code` in exchange for an `access_token`, which is finally sent to the Token Debugging Endpoint.

Facebook does not issue `refresh_tokens` but instead distinguishes between short-lived (approx. 60 minutes) and long-lived (approx. 60 days) `access_tokens`. Short-lived tokens are converted into long-lived tokens with `grant_type=fb_exchange_token` at the Token Endpoint. If long-lived tokens expire, the SP needs to restart the login flow from scratch to receive new short-lived `access_tokens`.

More details are provided in Section 3.4 of the thesis.

Acknowledgments

My thesis was supervised by Christian Mainka, Vladislav Mladenov, and Jörg Schwenk. Huge "thank you" for your continuous support, advice, and dozens of helpful tips. 
Also, special thanks to Lauritz for his feedback on this post and valuable discussions during the research. Check out his blog post series on Real-life OIDC Security as well.

Authors of this Post

Louis Jannett
Related news

Automating REST Security Part 2: Tool-based Analysis With REST-Attacker

Our previous blog post described the challenges in analyzing REST API implementations. Despite the lack of REST standardization, we learned that similarities between implementations exist and that we can utilize them for tool-based REST security analysis.

This blog post will now look at our own implementation. REST-Attacker is a free software analysis tool specifically built to analyze REST API implementations and their access control measures. Using REST-Attacker as an example, this blog post will discuss how a REST security tool can work and where it can improve or streamline the testing process, especially in terms of automation.

Author

Christoph Heine

Overview

 Premise

REST-Attacker was developed as part of a master's thesis at the Chair for Network & Data Security at the Ruhr University Bochum. The primary motivation behind creating REST-Attacker was to evaluate how far we could push automation for REST security analysis. Hence, REST-Attacker provides several automation features such as automated test generation, test execution, and API communication. The tool essentially takes a "lazy tester" approach that tries to minimize the necessary amount of manual interaction as much as possible.

Creating a test run requires an OpenAPI file describing the REST API. Optional configuration, such as authentication credentials, can be provided to access protected API endpoints or run advanced test cases. Based on the API description and configuration, the tool can automatically generate complete test runs and execute them automatically. For this purpose, the current release version provides 32 built-in security test cases for analyzing various security issues and best practices.

How Testing Works

REST-Attacker can be used as a stand-alone CLI tool or as a Python module for integration in your own toolchain. In this blog post, we will mainly focus on running the tool via CLI. If you want to learn more about advanced usage, we recommend you read the docs.

Starting a basic test run looks like this:

python3 -m rest_attacker openapi.json --generate 

openapi.json is an OpenAPI file that describes the API we want to test. The --generate flag activates load-time test generation to automatically create a test run. In practice, this means that the tool passes the OpenAPI file to a test generation function of every available test case, which then returns a list of tests for the specific API. After creating the test run, REST-Attacker executes all tests one by one and saves the results.

There's also a second option for run-time test generation using the --propose flag:

python3 -m rest_attacker openapi.json --generate --propose 

In comparison to --generate, which creates tests from the OpenAPI description before starting the test run, --propose generates tests during a test run by considering the results of already executed tests. This option can be useful for some test cases where we want to take the responses of the API into account and run a follow-up test based on the observed behavior.

Both test generation methods can significantly speed up testing because they allow the creation of entire test runs without manual input. However, their feasibility often heavily depends on the verbosity and accuracy of the configuration data. Remember that many definitions, such as security requirements, are optional in the OpenAPI format, i.e., services can choose to omit them. API descriptions can also be outdated or contain errors, particularly if they are unofficial user-created versions. Despite all these limitations, an automated generation often works surprisingly well.

If you don't want to use the tool's generators, test runs can also be specified manually. For this purpose, you just pass a list of tests, including their serialized input parameters, via a config file:

python3 -m rest_attacker openapi.json --run example_run.json 

Advanced Automation

So far, we have only covered the automation of the test generation. However, what's even more interesting is that we can also automate much of the test execution process in REST-Attacker. The challenging part here is the streamlining of API communication. If you remember our previous blog post, you know that it basically involves these three steps:

  1. Preparing API request parameters
  2. Preparing access control data (handling authentication/authorization)
  3. Sending the request

Since most REST APIs are HTTP-based, step 3. is relatively trivial as any standard HTTP library will do the job. For example, REST-Attacker uses the popular Python requests module for its request backend. Step 1. is part of the test generation process and can be realized by using information from the machine-readable OpenAPI file, which we've already discussed. In the final step, we have to look at the access control (step 2.), which is especially relevant for security testing. Unfortunately, it is a bit more complex.

The problem is generally not that REST APIs use different access control methods. They are either standardized (HTTP Basic Auth, OAuth2) or extremely simple (API keys). Instead, complications often arise from the API-specific configuration and requirements for how these methods should be used and how credentials are integrated into the API request. For example, implementations may decide:

  • where credentials are located in the HTTP request (e.g., header, query, cookie, ...)
  • how credentials are encoded/formatted (e.g., Base64 encoding or use of keywords)
  • whether a combination of methods is required (e.g., API key + OAuth2)
  • (OAuth2) which authorization flows are supported
  • (OAuth2) which access scopes are supported
  • ...

Thereby, we cannot rely on an access control method, e.g., OAuth2, being used in the same way across different APIs. Furthermore, a lot of this information cannot be described in the OpenAPI format, so we have to find another solution. In REST-Attacker, we solve this problem with an additional custom configuration for access control. An example can be seen below (unfold it):

{     "schemes": {         "scheme0": {             "type": "header",             "key_id": "authorization",             "payload": "token {0}",             "params": {                 "0": {                     "id": "access_token",                     "from": [                         "token0",                     ]                 }             }         }     },     "creds": {         "client0": {             "type": "oauth2_client",             "description": "OAuth Client",             "client_id": "aabbccddeeff123456789",             "client_secret": "abcdef12345678998765431fedcba",             "redirect_uri": "https://localhost:1234/test/",             "authorization_endpoint": "https://example.com/login/oauth/authorize",             "token_endpoint": "https://example.com/login/oauth/token",             "grants": [                 "code",                 "token"             ],             "scopes": [                 "user"             ],             "flags": []         }     },     "required_always": {         "setting0": [             "scheme0"         ]     },     "required_auth": {},     "users": {         "user0": {             "account_id": "user",             "user_id": "userXYZ",             "owned_resources": {},             "allowed_resources": {},             "sessions": {                 "gbrowser": {                     "type": "browser",                     "exec_path": "/usr/bin/chromium",                     "local_port": "1234"                 }             },             "credentials": [                 "client0"             ]         }     } } 

The config file contains everything required for getting access to the API. schemes define location and encoding of credentials in the HTTP request, while credentials contain login credentials for either users or OAuth2 clients. There are also definitions for the required access control schemes for general access to the API (required_always) as well as for user-protected access (required_auth). For the purpose of authorization, we can additionally provide user definitions with session information. The latter can be used to create or access an active user session to retrieve OAuth2 tokens from the service.

Starting REST-Attacker with an access control config is similar as before. Instead of only passing the OpenAPI file, we use a folder that contains all configuration files:

python3 -m rest_attacker cfg/example --generate 

REST-Attacker completely handles all access control requirements in the background. Manual intervention is sometimes necessary, e.g., when there's a confirmation page for OAuth2 authorization. However, most of the steps, from selecting the proper access control schemes to retrieving OAuth2 tokens and creating the request payload, are all handled by REST-Attacker.

Interpreting Results

After a test run, REST-Attacker exports the test results to a report file. Every report gives a short summary of the test run and the results for each executed test case. Here you can see an example of a report file (unfold it):

{     "type": "report",     "stats": {         "start": "2022-07-16T14-27-20Z",         "end": "2022-07-16T14-27-25Z",         "planned": 1,         "finished": 1,         "skipped": 0,         "aborted": 0,         "errors": 0,         "analytical_checks": 0,         "security_checks": 1     },     "reports": [         {             "check_id": 0,             "test_type": "security",             "test_case": "https.TestHTTPAvailable",             "status": "finished",             "issue": "security_flaw",             "value": {                 "status_code": 200             },             "curl": "curl -X GET http://api.example.com/user",             "config": {                 "request_info": {                     "url": "http://api.example.com",                     "path": "/user",                     "operation": "get",                     "kwargs": {                         "allow_redirects": false                     }                 },                 "auth_info": {                     "scheme_ids": null,                     "scopes": null,                     "policy": "DEFAULT"                 }             }         }     ] } 

Individual test reports contain a basic classification of the detected behavior in the issue parameter and the detailed reasons for this interpretation in the value object. The meaning of the classification depends on the test case ID, which is stored in the test_case parameter. In the example above, the https.TestHTTPAvailable checks if an API endpoint is accessible via plain HTTP without transport security (which is generally considered unsafe). The API response is an HTTP message with status code 200, so REST-Attacker classifies the behavior as a flaw.

By default, reports also contain every test's configuration parameters and can be supplied back to the tool as a manual test run configuration. This is very useful if we want to reproduce a run to see if detected issues have been fixed.

python3 -m rest_attacker openapi.json --run report.json 

Conclusion

By now, you should know what REST API tools like REST-Attacker are capable of and how they can automate the testing process. In our next and final blog post, we will take a deeper look at practical testing with the REST-Attacker. To do this, we will present security test categories that are well-suited for tool-based analysis and investigate how we can apply them to test several real-world API implementations.

Acknowledgement

The REST-Attacker project was developed as part of a master's thesis at the Chair of Network & Data Security of the Ruhr University Bochum. I would like to thank my supervisors Louis Jannett, Christian Mainka, Vladislav Mladenov, and Jörg Schwenk for their continued support during the development and review of the project.

Read more