Today I got alerts about a spamming web server and I quickly identified the document root to be a Magento web shop installation.
mail() on [/srv/websites/shop.example.com/magento/ljamailer.php(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code:220]: To: email.result@yahoo.com -- Headers: From : Miler Info
So the bad file here is of course ljamailer.php. The question is: How did it come on the server? An overview of the document root gives several conclusions already:
webserver:/srv/websites/shop.example.com/magento # ls -ltr
total 868
-rw-r--r-- 1 root root 2112 Nov 13 2015 test.php
drwxrwxrwx 3 inmed web 4096 Sep 28 10:58 pkginfo
drwxrwxrwx 5 inmed web 4096 Sep 28 10:58 skin
-rw-rw-rw- 1 wwwrun www 2240 Sep 28 10:58 mage
-rw-rw-rw- 1 wwwrun www 6451 Sep 28 10:58 .htaccess
drwxrwxrwx 2 wwwrun www 4096 Sep 28 10:59 includes
drwxrwxrwx 2 wwwrun www 4096 Sep 28 10:59 shell
-rw-rw-rw- 1 wwwrun www 886 Sep 28 10:59 php.ini.sample
drwxrwxrwx 14 inmed web 4096 Sep 28 10:59 media
-rw-rw-rw- 1 wwwrun www 6460 Sep 28 10:59 install.php
-rw-rw-rw- 1 wwwrun www 2323 Sep 28 10:59 index.php.sample
-rw-rw-rw- 1 wwwrun www 5970 Sep 28 10:59 get.php
-rw-rw-rw- 1 wwwrun www 1150 Sep 28 10:59 favicon.ico
drwxrwxrwx 3 wwwrun www 4096 Sep 28 10:59 errors
-rw-rw-rw- 1 wwwrun www 1639 Sep 28 10:59 cron.sh
-rw-rw-rw- 1 wwwrun www 2915 Sep 28 10:59 cron.php
-rw-rw-rw- 1 wwwrun www 3141 Sep 28 10:59 api.php
-rw-rw-rw- 1 wwwrun www 590092 Sep 28 10:59 RELEASE_NOTES.txt
-rw-rw-rw- 1 wwwrun www 10421 Sep 28 10:59 LICENSE_AFL.txt
-rw-rw-rw- 1 wwwrun www 10410 Sep 28 10:59 LICENSE.txt
-rw-rw-rw- 1 wwwrun www 10679 Sep 28 10:59 LICENSE.html
-rw-rw-rw- 1 wwwrun www 5351 Sep 28 10:59 .htaccess.sample
drwxrwxrwx 16 inmed web 4096 Sep 28 10:59 lib
drwxrwxrwx 10 inmed web 4096 Sep 28 11:01 downloader
drwxrwxrwx 10 inmed web 4096 Dec 4 22:53 var
drwxrwxrwx 16 inmed web 4096 Dec 4 22:53 js
-rw-rw-rw- 1 wwwrun www 25862 Dec 4 23:05 configurations.php
drwxr-xr-x 2 wwwrun www 4096 Dec 4 23:22 tmp
-rw-r--r-- 1 wwwrun www 13639 Dec 4 23:27 Sym.php
drwxr-xr-x 2 wwwrun www 4096 Dec 4 23:28 sym
-rw-r--r-- 1 wwwrun www 25862 Dec 4 23:40 bootstrap.php
drwxrwxrwx 6 inmed web 4096 Dec 4 23:40 app
-rw-r--r-- 1 wwwrun www 49449 Dec 5 03:43 ljamailer.php
-rw-r--r-- 1 wwwrun www 2391 Dec 5 03:43 index.php
1) Several recent modifications happened on December 4th and 5th.
2) The current permissions are catastrophic. wwwrun (the Apache user) is able to modify everything. I'm not a Magento specialist, but I doubt that the shop needs to have write permissions on every file and folder.
Taking a look at ljamailer.php and no big surprise. An obfuscated PHP code appeared:
# more ljamailer.php
rjaya";$oiIohAhAaASSA = $oiIhAiaoaoASA{5}.$oiIhAiaoaoASA{8}.$oiIhAiaoaoASA{20}.$oiIhAiaoaoASA{24}.$oiIhAiaoaoASA{34}.$oiIhAiaoaoASA{27}.$s0oooo0000h41{19}.$s0
oooo0000h41{4}.$s0oooo0000h41{5}.$s0oooo0000h41{31}.$s0oooo0000h41{12}.$s0oooo0000h41{37}.$s0oooo0000h41{38};$oiIoASAhAiaoahoSAShh=file(__FILE__);$s0oo0000ooh
41=$s0oooo0000h41{30}.$s0oooo0000h41{33};$s0oo0000
I didn't bother to decode that as at the end it probably turns out to be a mailing/spamming form. More interestingly is the timestamp, when the file was uploaded:
120.188.94.254 - - [05/Dec/2016:03:43:05 +0100] "POST /configurations.php HTTP/1.1" 200 5439 "http://shop.example.com/configurations.php" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [05/Dec/2016:03:43:12 +0100] "GET /ljamailer.php HTTP/1.1" 200 2524 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [05/Dec/2016:03:43:37 +0100] "POST /ljamailer.php HTTP/1.1" 200 3921 "http://shop.example.com/ljamailer.php" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [05/Dec/2016:03:44:04 +0100] "POST /ljamailer.php HTTP/1.1" 200 3995 "http://shop.example.com/ljamailer.php" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [05/Dec/2016:03:44:18 +0100] "POST /ljamailer.php HTTP/1.1" 200 6272 "http://shop.example.com/ljamailer.php" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [05/Dec/2016:03:50:47 +0100] "POST /ljamailer.php HTTP/1.1" 200 4122 "http://shop.example.com/ljamailer.php" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
That's right. A POST on configurations.php, which, itself, was uploaded on December 4th. Let's take a look at configurations.php:
# more configurations.php
$idc = "=Ew/P7//fvf/e20P17/LpI7L34PwCabTrwvXJbPWN3TV+/T/mE3R5//n3zfJlHOEt/33HXCPomvNr8X9Of74N/C8u0KblTAt8+AAh3gjnKzeZWDyELiXuc/7SOiIls0QNVmiN1MH5kCOokgIho0VyV
lIZHndcCwaMgR5nwCh/GxaYNISzsK91a53vpwsnQTRCFXxDaksGVhhIOe2jUvc5hdLsdwYpT5Zv6jPE5ROlFxfzMqQfVLiAxq6/bkdRSp5I2a2ZjNfdr5moDVYbFCA6wOJdun6y30g4lv+4OL+wUVJrAZWuv6o
Xc7lHJ+jJTCGqBFKG3C1zkwLoPLQhYTQ/d5Y3K4zlhpznx7LFjlMHLLdTBunJneZugalBjR/Gpv5G/5Is6+afGoQCSDc6thAuYyC6jIsCuWATFRG6/zdlbPTJfVyM1a3wZBXHbITk2dnXV1+DDqpD3ybWE4f90
cYEglGMxuKik8QJoSP/mg16EWDi6K7EzQ/N6
No big surprise there either. Interestingly this file was uploaded twice. Once with filename configurations.php, once as bootstrap.php. Both files are identical in size and content.
# more bootstrap.php
$idc = "=Ew/P7//fvf/e20P17/LpI7L34PwCabTrwvXJbPWN3TV+/T/mE3R5//n3zfJlHOEt/33HXCPomvNr8X9Of74N/C8u0KblTAt8+AAh3gjnKzeZWDyELiXuc/7SOiIls0QNVmiN1MH5kCOokgIho0VyV
lIZHndcCwaMgR5nwCh/GxaYNISzsK91a53vpwsnQTRCFXxDaksGVhhIOe2jUvc5hdLsdwYpT5Zv6jPE5ROlFxfzMqQfVLiAxq6/bkdRSp5I2a2ZjNfdr5moDVYbFCA6wOJdun6y30g4lv+4OL+wUVJrAZWuv6o
Xc7lHJ+jJTCGqBFKG3C1zkwLoPLQhYTQ/d5Y3K4zlhpznx7LFjlMHLLdTBunJneZugalBjR/Gpv5G/5Is6+afGoQCSDc6thAuYyC6jIsCuWATFRG6/zdlbPTJfVyM1a3wZBXHbITk2dnXV1+DDqpD3ybWE4f90
cYEglGMxuKik8QJoSP/mg16EW
When configurations.php or bootstrap.php was launched in a browser, a WebShell appeared:
Another interesting discovery was the file Sym.php. In the browser something the script looked like this:
In the access logs I saw the following requests:
120.188.94.254 - - [04/Dec/2016:23:27:50 +0100] "GET /Sym.php HTTP/1.1" 200 1019 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:27:59 +0100] "GET /Sym.php? HTTP/1.1" 200 1019 "http://shop.example.com/Sym.php" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:28:00 +0100] "GET /Sym.php?sws=sym HTTP/1.1" 200 820 "http://shop.example.com/Sym.php?" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:28:30 +0100] "GET /Sym.php?sws=sym HTTP/1.1" 200 820 "http://shop.example.com/Sym.php?sws=sym" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:28:31 +0100] "GET /Sym.php?sws=sec HTTP/1.1" 200 820 "http://shop.example.com/Sym.php?sws=sym" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:28:33 +0100] "GET /Sym.php?sws=file HTTP/1.1" 200 994 "http://shop.example.com/Sym.php?sws=sec" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:28:37 +0100] "POST /Sym.php?sws=file HTTP/1.1" 200 1023 "http://shop.example.com/Sym.php?sws=file" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
It took me a bit to figure out the real function of this script (besides another upload function): It creates a symlink to a certain target. I found the following subfolder and its content:
webserver:/srv/websites/shop.example.com/magento/sym # ll
total 4
-rw-r--r-- 1 wwwrun www 175 Dec 5 10:03 .htaccess
lrwxrwxrwx 1 wwwrun www 32 Dec 4 23:28 file.name_sym ( Ex. :: 1.txt ) -> /home/user/public_html/file.name
lrwxrwxrwx 1 wwwrun www 1 Dec 4 23:27 root -> /
Uh oh... A symlink called root was created which points to /. Did it work? Unfortunately yes, it seems that the Apache setting "FollowSymlinks" is enabled:
And yes, this was accessed by the hacker, too:
120.188.94.254 - - [04/Dec/2016:23:41:59 +0100] "GET /sym/ HTTP/1.1" 200 408 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:42:03 +0100] "GET /sym/root/ HTTP/1.1" 200 780 "http://shop.example.com/sym/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
Information disclosure at it's finest! :-(
Uploading files through a web shell already in place is easy. Especially if the file permissions are set so mindlessly (Magento, is this really necessary?).
But how did all these php files get on the server in the first place? This is the interesting part. According to the timestamp, the older file (configurations.php) was uploaded on Dec 4 at 23:05. The log file doesn't give a helpful trace at 23:05:
120.188.94.254 - - [04/Dec/2016:23:05:03 +0100] "GET / HTTP/1.1" 200 5215 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:05:27 +0100] "POST / HTTP/1.1" 200 23614 "http://shop.example.com/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:05:30 +0100] "GET / HTTP/1.1" 200 5013 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:05:41 +0100] "POST / HTTP/1.1" 200 3046 "http://shop.example.com/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:05:52 +0100] "POST / HTTP/1.1" 200 2597 "http://shop.example.com/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:05:55 +0100] "POST / HTTP/1.1" 404 716 "http://shop.example.com/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:05:59 +0100] "GET / HTTP/1.1" 403 686 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
... but just a few seconds before that, some very interesting requests happened:
120.188.94.254 - - [04/Dec/2016:23:03:49 +0100] "POST /index.php/filesystem/adminhtml_filesystem/tree/isAjax/1/form_key/JzG8egEj5wWSin3q/key/b853d2a14508a9fa48d60ff5c48119c1/ HTTP/1.1" 200 222 "http://shop.example.com/index.php/filesystem/adminhtml_filesystem/index/key/a6af76c351b7130cd2be64c31656c082/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:03:52 +0100] "GET /index.php/filesystem/adminhtml_filesystem/load/fn/L3Nydi93ZWJzaXRlcy9zaG9wLnRhZ2JsYXR0LmNoL21hZ2VudG8vc2hlbGwvaW5kZXhlci5waHA=/key/9fa08aac281c5d2fbdb3c6d660489940/?isAjax=true&&form_key=JzG8egEj5wWSin3q HTTP/1.1" 200 2697 "http://shop.example.com/index.php/filesystem/adminhtml_filesystem/index/key/a6af76c351b7130cd2be64c31656c082/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:03:57 +0100] "GET /index.php/filesystem/adminhtml_filesystem/load/fn/L3Nydi93ZWJzaXRlcy9zaG9wLnRhZ2JsYXR0LmNoL21hZ2VudG8vaW5kZXgucGhw/key/9fa08aac281c5d2fbdb3c6d660489940/?isAjax=true&&form_key=JzG8egEj5wWSin3q HTTP/1.1" 200 1553 "http://shop.example.com/index.php/filesystem/adminhtml_filesystem/index/key/a6af76c351b7130cd2be64c31656c082/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:04:08 +0100] "GET /index.php/filesystem/adminhtml_filesystem/close/file/996204877/key/372934d1769190b1cbb62eb3d7803622/?isAjax=true&&form_key=JzG8egEj5wWSin3q HTTP/1.1" 200 16 "http://shop.example.com/index.php/filesystem/adminhtml_filesystem/index/key/a6af76c351b7130cd2be64c31656c082/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:04:09 +0100] "GET /index.php/filesystem/adminhtml_filesystem/close/file/1073874282/key/372934d1769190b1cbb62eb3d7803622/?isAjax=true&&form_key=JzG8egEj5wWSin3q HTTP/1.1" 200 16 "http://shop.example.com/index.php/filesystem/adminhtml_filesystem/index/key/a6af76c351b7130cd2be64c31656c082/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:04:39 +0100] "GET /index.php/filesystem/adminhtml_filesystem/load/fn/L3Nydi93ZWJzaXRlcy9zaG9wLnRhZ2JsYXR0LmNoL21hZ2VudG8vaW5kZXgucGhw/key/9fa08aac281c5d2fbdb3c6d660489940/?isAjax=true&&form_key=JzG8egEj5wWSin3q HTTP/1.1" 200 80 "http://shop.example.com/index.php/filesystem/adminhtml_filesystem/index/key/a6af76c351b7130cd2be64c31656c082/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:04:42 +0100] "GET /index.php/filesystem/adminhtml_filesystem/load/fn/L3Nydi93ZWJzaXRlcy9zaG9wLnRhZ2JsYXR0LmNoL21hZ2VudG8vY3Jvbi5zaA==/key/9fa08aac281c5d2fbdb3c6d660489940/?isAjax=true&&form_key=JzG8egEj5wWSin3q HTTP/1.1" 200 1007 "http://shop.example.com/index.php/filesystem/adminhtml_filesystem/index/key/a6af76c351b7130cd2be64c31656c082/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:04:44 +0100] "GET /index.php/filesystem/adminhtml_filesystem/load/fn/L3Nydi93ZWJzaXRlcy9zaG9wLnRhZ2JsYXR0LmNoL21hZ2VudG8vaW5kZXgucGhw/key/9fa08aac281c5d2fbdb3c6d660489940/?isAjax=true&&form_key=JzG8egEj5wWSin3q HTTP/1.1" 200 80 "http://shop.example.com/index.php/filesystem/adminhtml_filesystem/index/key/a6af76c351b7130cd2be64c31656c082/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:04:51 +0100] "POST /index.php/filesystem/adminhtml_filesystem/save/file/2083702503/key/29bbfb400565c179873af433f751fe64/?isAjax=true HTTP/1.1" 200 16 "http://shop.example.com/index.php/filesystem/adminhtml_filesystem/index/key/a6af76c351b7130cd2be64c31656c082/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:04:53 +0100] "GET /index.php/filesystem/adminhtml_filesystem/close/file/2083702503/key/372934d1769190b1cbb62eb3d7803622/?isAjax=true&&form_key=JzG8egEj5wWSin3q HTTP/1.1" 200 5198 "http://shop.example.com/index.php/filesystem/adminhtml_filesystem/index/key/a6af76c351b7130cd2be64c31656c082/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:04:55 +0100] "GET /index.php/filesystem/adminhtml_filesystem/close/file/1089227211/key/372934d1769190b1cbb62eb3d7803622/?isAjax=true&&form_key=JzG8egEj5wWSin3q HTTP/1.1" 200 5273 "http://shop.example.com/index.php/filesystem/adminhtml_filesystem/index/key/a6af76c351b7130cd2be64c31656c082/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
120.188.94.254 - - [04/Dec/2016:23:04:57 +0100] "GET /index.php/filesystem/adminhtml_filesystem/load/fn/L3Nydi93ZWJzaXRlcy9zaG9wLnRhZ2JsYXR0LmNoL21hZ2VudG8vaW5kZXgucGhw/key/9fa08aac281c5d2fbdb3c6d660489940/?isAjax=true&&form_key=JzG8egEj5wWSin3q HTTP/1.1" 200 5361 "http://shop.example.com/index.php/filesystem/adminhtml_filesystem/index/key/a6af76c351b7130cd2be64c31656c082/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
It seems that some filesystem plugin/extension was installed. My research pointed me to a Reddit thread where the extension "Magpleasure Filesystem" was installed after a hack. And as in the reddit post, the extension seems to have been installed through the "/downloader" subfolder (hat's the fixed URL for the Magento Connect Manager):
125.161.32.96 - - [04/Dec/2016:22:52:22 +0100] "POST //downloader/ HTTP/1.1" 200 6990 "http://shop.example.com//downloader/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
125.161.32.96 - - [04/Dec/2016:22:52:33 +0100] "GET /downloader/index.php?A=empty HTTP/1.1" 200 1167 "http://shop.example.com//downloader/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
125.161.32.96 - - [04/Dec/2016:22:53:26 +0100] "POST /downloader/index.php?A=connectInstallPackageUpload&maintenance=1&archive_type=0&backup_name= HTTP/1.1" 200 1192 "http://shop.example.com//downloader/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
125.161.32.96 - - [04/Dec/2016:22:53:29 +0100] "POST /downloader/index.php?A=cleanCache HTTP/1.1" 200 95 "http://shop.example.com//downloader/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
And right after that, a successful login in the admin interface seems to have happened (notice the successful css loaded):
91.211.2.12 - - [04/Dec/2016:22:53:41 +0100] "GET /index.php/admin/ HTTP/1.1" 200 1255 "http://shop.example.com/index.php/admin/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36"
91.211.2.12 - - [04/Dec/2016:22:53:41 +0100] "POST /index.php/admin/ HTTP/1.1" 200 1314 "http://shop.example.com/index.php/admin/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36"
125.161.32.96 - - [04/Dec/2016:22:55:12 +0100] "GET /skin/adminhtml/default/default/reset.css HTTP/1.1" 200 2925 "http://shop.example.com/index.php/admin/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
In general the Magento Connect Manager, which is always available under MAGENTOURL/downloader, should be disabled. This page has some additional information.
Through the upload in Connect Manager (non-authenticated? a vulnerability in Connect Manager? I'm not sure) the hacker was able to gain access to the admin dashboard and a few minutes later created his own admin user (note: I am only showing the newly created account, all other accounts are removed from the output for privacy reasons):
mysql> select user_id, email, username, password, created, modified from admin_user;
+---------+--------------------------------+----------------+------------------------------------------+---------------------+---------------------+
| user_id | email | username | password | created | modified |
+---------+--------------------------------+----------------+------------------------------------------+---------------------+---------------------+
| 20 | admin@demo.com | admins | 864eb4c20f8ab86d595c28434fff16a7:xX | 2016-12-04 22:56:15 | NULL |
+---------+--------------------------------+----------------+------------------------------------------+---------------------+---------------------+
5 rows in set (0.00 sec)
The hacker then successfully created his own admin account (Username admins) at 22:56:15, which was this POST:
125.161.32.96 - - [04/Dec/2016:22:56:15 +0100] "POST /index.php/admin/ HTTP/1.1" 302 0 "http://shop.example.com/index.php/admin/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
So what is the source for the hack? From what I can see in the logs there are two possibilities:
1) There's a vulnerability in Magento Connect Manager which allows to upload and install extensions without authentication
2) The hacker used an already existing admin user. Either through brute-force login or (worse) through a previous hack. Potentially the "shoplift" vulnerability (see here for more information) could have been used in the past to create a new admin account before the shop was patched.
Either way, I contacted the shop administrator and cleaned up the files modified since Dec 4th. Here's a complete list:
webserver:/srv/websites/shop.example.com/magento # find . -user wwwrun -mtime -2
./app/design/adminhtml/default/default/template/filesystem
./app/design/adminhtml/default/default/template/filesystem/ide
./app/design/adminhtml/default/default/template/filesystem/ide/editor.phtml
./app/design/adminhtml/default/default/template/filesystem/ide/tree.phtml
./app/design/adminhtml/default/default/template/filesystem/ide.phtml
./app/design/adminhtml/default/default/template/filesystem/wrapper.phtml
./app/design/adminhtml/default/default/layout/filesystem.xml
./app/etc/modules/Magpleasure_Filesystem.xml
./app/code/community/Magpleasure
./app/code/community/Magpleasure/Filesystem
./app/code/community/Magpleasure/Filesystem/Helper
./app/code/community/Magpleasure/Filesystem/Helper/Data.php
./app/code/community/Magpleasure/Filesystem/sql
./app/code/community/Magpleasure/Filesystem/sql/filesystem_setup
./app/code/community/Magpleasure/Filesystem/sql/filesystem_setup/mysql4-install-1.0.php
./app/code/community/Magpleasure/Filesystem/Model
./app/code/community/Magpleasure/Filesystem/Model/Tree.php
./app/code/community/Magpleasure/Filesystem/controllers
./app/code/community/Magpleasure/Filesystem/controllers/Adminhtml
./app/code/community/Magpleasure/Filesystem/controllers/Adminhtml/FilesystemController.php
./app/code/community/Magpleasure/Filesystem/etc
./app/code/community/Magpleasure/Filesystem/etc/adminhtml.xml
./app/code/community/Magpleasure/Filesystem/etc/system.xml
./app/code/community/Magpleasure/Filesystem/etc/config.xml
./app/code/community/Magpleasure/Filesystem/Block
./app/code/community/Magpleasure/Filesystem/Block/Adminhtml
./app/code/community/Magpleasure/Filesystem/Block/Adminhtml/Ide
./app/code/community/Magpleasure/Filesystem/Block/Adminhtml/Ide/Editor.php
./app/code/community/Magpleasure/Filesystem/Block/Adminhtml/Ide/Tree.php
./app/code/community/Magpleasure/Filesystem/Block/Adminhtml/Ide.php
./js/filesystem
./js/filesystem/script.js
./js/filesystem/base64.js
./js/filesystem/jquery-1.4.2.min.js
./js/filesystem/script.coffee
./js/filesystem/jqueryfiletree.js
./js/editarea
./js/editarea/license_apache.txt
./js/editarea/edit_area_full.js
./js/editarea/highlight.js
./js/editarea/reg_syntax
./js/editarea/reg_syntax/css.js
./js/editarea/reg_syntax/python.js
./js/editarea/reg_syntax/pas.js
./js/editarea/reg_syntax/robotstxt.js
./js/editarea/reg_syntax/coldfusion.js
./js/editarea/reg_syntax/html.js
./js/editarea/reg_syntax/java.js
./js/editarea/reg_syntax/perl.js
./js/editarea/reg_syntax/cpp.js
./js/editarea/reg_syntax/phtml.js
./js/editarea/reg_syntax/php.js
./js/editarea/reg_syntax/brainfuck.js
./js/editarea/reg_syntax/ruby.js
./js/editarea/reg_syntax/basic.js
./js/editarea/reg_syntax/c.js
./js/editarea/reg_syntax/vb.js
./js/editarea/reg_syntax/js.js
./js/editarea/reg_syntax/sql.js
./js/editarea/reg_syntax/tsql.js
./js/editarea/reg_syntax/xml.js
./js/editarea/license_lgpl.txt
./js/editarea/search_replace.js
./js/editarea/langs
./js/editarea/langs/ja.js
./js/editarea/langs/bg.js
./js/editarea/langs/hr.js
./js/editarea/langs/zh.js
./js/editarea/langs/cs.js
./js/editarea/langs/de.js
./js/editarea/langs/es.js
./js/editarea/langs/fi.js
./js/editarea/langs/pl.js
./js/editarea/langs/pt.js
./js/editarea/langs/en.js
./js/editarea/langs/nl.js
./js/editarea/langs/fr.js
./js/editarea/langs/eo.js
./js/editarea/langs/dk.js
./js/editarea/langs/ru.js
./js/editarea/langs/mk.js
./js/editarea/langs/sk.js
./js/editarea/langs/it.js
./js/editarea/images
./js/editarea/images/search.gif
./js/editarea/images/processing.gif
./js/editarea/images/close.gif
./js/editarea/images/newdocument.gif
./js/editarea/images/redo.gif
./js/editarea/images/load.gif
./js/editarea/images/help.gif
./js/editarea/images/spacer.gif
./js/editarea/images/fullscreen.gif
./js/editarea/images/opacity.png
./js/editarea/images/word_wrap.gif
./js/editarea/images/reset_highlight.gif
./js/editarea/images/smooth_selection.gif
./js/editarea/images/move.gif
./js/editarea/images/autocompletion.gif
./js/editarea/images/undo.gif
./js/editarea/images/save.gif
./js/editarea/images/highlight.gif
./js/editarea/images/statusbar_resize.gif
./js/editarea/images/go_to_line.gif
./js/editarea/edit_area_compressor.php
./js/editarea/edit_area.css
./js/editarea/plugins
./js/editarea/plugins/charmap
./js/editarea/plugins/charmap/charmap.js
./js/editarea/plugins/charmap/langs
./js/editarea/plugins/charmap/langs/ja.js
./js/editarea/plugins/charmap/langs/bg.js
./js/editarea/plugins/charmap/langs/hr.js
./js/editarea/plugins/charmap/langs/zh.js
./js/editarea/plugins/charmap/langs/cs.js
./js/editarea/plugins/charmap/langs/de.js
./js/editarea/plugins/charmap/langs/es.js
./js/editarea/plugins/charmap/langs/pl.js
./js/editarea/plugins/charmap/langs/pt.js
./js/editarea/plugins/charmap/langs/en.js
./js/editarea/plugins/charmap/langs/nl.js
./js/editarea/plugins/charmap/langs/fr.js
./js/editarea/plugins/charmap/langs/eo.js
./js/editarea/plugins/charmap/langs/dk.js
./js/editarea/plugins/charmap/langs/ru.js
./js/editarea/plugins/charmap/langs/mk.js
./js/editarea/plugins/charmap/langs/sk.js
./js/editarea/plugins/charmap/langs/it.js
./js/editarea/plugins/charmap/images
./js/editarea/plugins/charmap/images/charmap.gif
./js/editarea/plugins/charmap/popup.html
./js/editarea/plugins/charmap/css
./js/editarea/plugins/charmap/css/charmap.css
./js/editarea/plugins/charmap/jscripts
./js/editarea/plugins/charmap/jscripts/map.js
./js/editarea/plugins/test
./js/editarea/plugins/test/test2.js
./js/editarea/plugins/test/langs
./js/editarea/plugins/test/langs/ja.js
./js/editarea/plugins/test/langs/bg.js
./js/editarea/plugins/test/langs/hr.js
./js/editarea/plugins/test/langs/zh.js
./js/editarea/plugins/test/langs/cs.js
./js/editarea/plugins/test/langs/de.js
./js/editarea/plugins/test/langs/es.js
./js/editarea/plugins/test/langs/pl.js
./js/editarea/plugins/test/langs/pt.js
./js/editarea/plugins/test/langs/en.js
./js/editarea/plugins/test/langs/nl.js
./js/editarea/plugins/test/langs/fr.js
./js/editarea/plugins/test/langs/eo.js
./js/editarea/plugins/test/langs/dk.js
./js/editarea/plugins/test/langs/ru.js
./js/editarea/plugins/test/langs/mk.js
./js/editarea/plugins/test/langs/sk.js
./js/editarea/plugins/test/langs/it.js
./js/editarea/plugins/test/images
./js/editarea/plugins/test/images/Thumbs.db
./js/editarea/plugins/test/images/test.gif
./js/editarea/plugins/test/test.js
./js/editarea/plugins/test/css
./js/editarea/plugins/test/css/test.css
./js/editarea/edit_area.js
./js/editarea/edit_area_functions.js
./js/editarea/regexp.js
./js/editarea/license_bsd.txt
./js/editarea/edit_area_loader.js
./js/editarea/resize_area.js
./js/editarea/keyboard.js
./js/editarea/template.html
./js/editarea/edit_area_full.gz
./js/editarea/autocompletion.js
./js/editarea/elements_functions.js
./js/editarea/manage_area.js
./js/editarea/reg_syntax.js
./bootstrap.php
./Sym.php
./index.php
./tmp
./tmp/mobile
./tmp/.htaccess
./configurations.php
./skin/adminhtml/default/default/filesystem
./skin/adminhtml/default/default/filesystem/images
./skin/adminhtml/default/default/filesystem/images/script.png
./skin/adminhtml/default/default/filesystem/images/ppt.png
./skin/adminhtml/default/default/filesystem/images/music.png
./skin/adminhtml/default/default/filesystem/images/html.png
./skin/adminhtml/default/default/filesystem/images/pdf.png
./skin/adminhtml/default/default/filesystem/images/application.png
./skin/adminhtml/default/default/filesystem/images/phtml.png
./skin/adminhtml/default/default/filesystem/images/flash.png
./skin/adminhtml/default/default/filesystem/images/linux.png
./skin/adminhtml/default/default/filesystem/images/zip.png
./skin/adminhtml/default/default/filesystem/images/db.png
./skin/adminhtml/default/default/filesystem/images/doc.png
./skin/adminhtml/default/default/filesystem/images/psd.png
./skin/adminhtml/default/default/filesystem/images/film.png
./skin/adminhtml/default/default/filesystem/images/spinner.gif
./skin/adminhtml/default/default/filesystem/images/file.png
./skin/adminhtml/default/default/filesystem/images/ruby.png
./skin/adminhtml/default/default/filesystem/images/picture.png
./skin/adminhtml/default/default/filesystem/images/directory.png
./skin/adminhtml/default/default/filesystem/images/folder_open.png
./skin/adminhtml/default/default/filesystem/images/java.png
./skin/adminhtml/default/default/filesystem/images/code.png
./skin/adminhtml/default/default/filesystem/images/txt.png
./skin/adminhtml/default/default/filesystem/images/xls.png
./skin/adminhtml/default/default/filesystem/images/css.png
./skin/adminhtml/default/default/filesystem/images/php.png
./skin/adminhtml/default/default/filesystem/css
./skin/adminhtml/default/default/filesystem/css/styles.css
./skin/adminhtml/default/default/filesystem/css/jqueryfiletree.css
./ljamailer.php
./downloader/Maged/index.php
./sym
./sym/file.name_sym ( Ex. :: 1.txt )
./sym/root
./sym/.htaccess
./[Cache files]
./var/resource_config.json
./var/package/File_System-1.0.0.xml
./var/package/tmp
./var/package/tmp/package.xml
At the end of the analysis I realized, the hacker was able to do:
- Information disclosure (by using a symlink to the web servers root directory)
- File uploads
- Spamming (through uploaded php script)
- Successful login as admin
- Create a new admin account
- Install an extension (Magpleasure Filesystem) through Connect Manager (authenticated or not, that's still the big question to me)
A successful day for a hacker - not so much for me as I have to clean up, restore backups and shout at the responsible webmaster.
No comments yet.
AWS Android Ansible Apache Apple Atlassian BSD Backup Bash Bluecoat CMS Chef Cloud Coding Consul Containers CouchDB DB DNS Database Databases Docker ELK Elasticsearch Filebeat FreeBSD Galera Git GlusterFS Grafana Graphics HAProxy HTML Hacks Hardware Icinga Influx Internet Java KVM Kibana Kodi Kubernetes LVM LXC Linux Logstash Mac Macintosh Mail MariaDB Minio MongoDB Monitoring Multimedia MySQL NFS Nagios Network Nginx OSSEC OTRS Observability Office OpenSearch PGSQL PHP Perl Personal PostgreSQL Postgres PowerDNS Proxmox Proxy Python Rancher Rant Redis Roundcube SSL Samba Seafile Security Shell SmartOS Solaris Surveillance Systemd TLS Tomcat Ubuntu Unix VMWare VMware Varnish Virtualization Windows Wireless Wordpress Wyse ZFS Zoneminder