RSS Feed
更好更安全的互联网
  • 抓住“新代码”的影子 —— 基于GoAhead系列网络摄像头多个漏洞分析

    2017-03-21

    Author :知道创宇404安全实验室

    Date:2017年03月19日 (注:本文首发自 paper.seebug.org) 

    PDF 版本下载:抓住“新代码”的影子 —— 基于GoAhead系列网络摄像头多个漏洞分析

     

    一、漏洞背景

    GoAhead作为世界上最受欢迎的嵌入式Web服务器被部署在数亿台设备中,是各种嵌入式设备与应用的理想选择。当然,各厂商也会根据不同产品需求对其进行一定程度的二次开发。

    2017年3月7日,Seebug漏洞平台收录了一篇基于GoAhead系列摄像头的多个漏洞。事件源于Pierre Kim在博客上发表的一篇文章,披露了存在于1250多个摄像头型号的多个通用型漏洞。作者在文章中将其中一个验证绕过漏洞归类为GoAhead服务器漏洞,但事后证明,该漏洞却是由厂商二次开发GoAhead服务器产生。于此同时,Pierre Kim将其中两个漏洞组合使用,成功获取了摄像头的最高权限。

    二、漏洞分析

    当我们开始着手分析这些漏洞时发现GoAhead官方源码不存在该漏洞,解开的更新固件无法找到对应程序,一系列困难接踵而至。好在根据该漏洞特殊变量名称loginuse和loginpas,我们在github上找到一个上个月还在修改的门铃项目。抓着这个“新代码”的影子,我们不仅分析出了漏洞原理,还通过分析结果找到了关于此漏洞的新的利用方式。

    由于该项目依赖的一些外部环境导致无法正常编译,我们仅仅通过静态代码分析得出结论,因此难免有所疏漏。如有错误,欢迎指正。:)

    1. 验证绕过导致的信息(登录凭据)泄漏漏洞

    根据作者给出的POC,我们进行了如下测试:

    可以看出,只要url中含有loginuseloginpas这两个值即无需验证。甚至当这两个值对应的账号密码为空或者为错误的zzzzzzzzzzzzzz时均可通过验证。

    看到这里,我们大致可以判断出验证loginuseloginpas的逻辑问题导致该漏洞的出现。于是,在此门铃项目中直接搜索loginuse定位到关键函数。

    /func/ieparam.c6407-6485AdjustUserPri函数如下:

    我们对其中步骤做了注释,根据这段逻辑,我们先通过GetStrParamValue()获取loginuseloginpas对应值,然后将获取值通过GetUserPri()函数进行验证。跟进GetStrParamValue()这个函数,我们发现了更奇怪的事情。command/cmd_thread.c中第13-51GetStrParamValue()函数如下:

    根据作者给出的PoC,在memcpy()函数处会导致崩溃,但事实上,我们的web服务器正常运行并返回system.ini具体内容。这一点令我们百思不得其解。当我们对AdjustUserPri()函数向上溯源时终于弄清楚是上层代码问题导致代码根本无法运行到这里,所以也不会导致崩溃。 func/ieparam.c文件第7514-7543行调用了AdjustUserPri()函数:

    在之前跟GetUserPri()函数时有一行注释://result:0->error user or passwd error 1->vistor 2->opration 255->admin。当我们回头再看这段函数时,可以发现开发者直接将验证部分注释掉,byPri被直接赋值为255,这就意味着只要进入这段逻辑,用户权限就直接是管理员了。这里已经可以解释本小节开篇进行的测试,也就是为什么我们输入空的用户名和密码或者错误的用户名和密码也可以通过验证。

    很遗憾,我们没有继续向上溯源找到这里的auth这个值到底是如何而来。不过根据这里的代码逻辑,我们可以猜测,当auth0时,通过GET请求中的参数验证用户名密码。当auth不为0时,通过HTTP摘要验证方式来验证用户名密码。

    再看一遍上方代码,GET请求中含有参数loginuseloginpas就直接可以通过验证。那么AdjustUserPri()函数中另外两个具有相同逻辑的参数userpwd呢?

    成功抓住"新代码"的影子。

    2. 远程命令执行漏洞一(需登录)

    作者给出的exp如下:

    可以看到,该exp分为两步,第一步先设置ftp各种参数,第二步按照第一步设置的各参数测试ftp链接,同时导致我们在第一步设置的命令被执行。

    我们在func/ieparam.c文件中找到了set_ftp.cgiftptest.cgi的调用过程:

    首先跟踪cgisetftp( pbuf, pparam, byPri );这个函数,我们发现,该函数仅仅是获取到我们请求的参数并将参数赋值给结构体中的各个变量。关键代码如下:

    综上所述,set_ftp.cgi仅仅是将我们请求的各参数写入全局变量中。 接下来是ftptest.cgi部分,也就是调用了iRet = cgisetftptest( pbuf, pparam, byPri );这个函数。在该函数中,最为关键的函数为DoFtpTest();。直接跳到func/ftp.c文件中找到函数DoFtpTest()

    可以看到,执行 FtpConfig()函数后运行了/tmp/ftpupdate1.sh。我们先看 FtpConfig()函数如何处理该问题:

    至此,逻辑很清晰了。在FtpConfig()函数中,将我们之前在设置的时候输入的各个值写入了/tmp/ftpupdate1.sh中,然后在DoFtpTest()中运行该脚本,导致最后的命令执行。这一点,同样可以在漏洞作者原文中得到证明:

    实际测试中,我们发现:如果直接用作者给出的exp去尝试RCE往往无法成功运行。从http://ip:port/get_params.cgi?user=username&pwd=password可以发现,我们注入的命令在空格处被截断。

    于是我们用${IFS}替换空格(还可以采用+代替空格):

    但由于有长度限制再次被截断,调整长度后最终成功执行命令:

    成功抓住新代码的影子。

    3. GoAhead绕过验证文件下载漏洞

    2017年3月9日,Pierre Kim在文章中增加了两个链接,描述了一个GoAhead 2.1.8版本之前的任意文件下载漏洞。攻击者通过使用该漏洞,再结合一个新的远程命令执行漏洞可以再次获取摄像头的最高权限。有意思的是,这个漏洞早在2004年就已被提出并成功修复http://aluigi.altervista.org/adv/goahead-adv2.txt)。但是由于众多摄像头仍然使用存在该漏洞的老代码,该漏洞仍然可以在众多摄像头设备中复现。

    我们也查找了此门铃项目中的GoAhead服务器版本。web/release.txt前三行内容如下:

    再仔细查看websUrlHandlerRequest()内容,发现并未对该漏洞进行修复,说明该漏洞也影响这个门铃项目。以此类推,本次受影响的摄像头应该也存在这个漏洞,果不其然:

    那么,具体的漏洞成因又是如何呢?让我们来跟进./web/LINUX/main.c了解该漏洞的成因。 initWebs()函数中,关键代码如下:

    其中,150-160um开头的函数为用户权限控制的相关函数。主要做了以下四件事情:

    1. umOpen() 打开用户权限控制;
    2. umAddGroup() 增加用户组adm,并设置该用户组用户使用HTTP摘要认证方式登录;
    3. umAddUser() 增加用户admin,admin0,admin1,并且这三个用户均属于adm用户组;
    4. umAddAccessLimit() 增加限制路径/,凡是以/开头的路径都要通过HTTP摘要认证的方式登录属于adm组的用户。

    紧接着,在220多行通过websUrlHandlerDefine()函数运行了两个HandlerwebsSecurityHandlerwebsDefaultHandler。在websSecurityHandler中,对HTTP摘要认证方式进行处理。关键代码如下:

    第86行,umGetAccessLimit()函数用于将我们请求的路径规范化,主要逻辑就是去除路径最后的/或者\\,确保我们请求的是一个文件。umGetAccessMethodForURL()函数用于获取我们请求的路径对应的权限。这里,我们请求的路径是system.ini。根据上文,设置需要对/路径需要进行HTTP摘要认证,由于程序判断system.ini不属于/路径,所以这里am为默认的AM_INVALID,即无需验证。

    紧接着向下,nRet初始化赋值为0.在118-242行中,如果出现了账号密码错误等情况,则会将nRet赋值为1,表示验证不通过。但是由于我们请求的路径无需验证,所以判断结束时nRet仍为0。因此,顺利通过验证,获取到对应的文件内容。

    就这样,我们再次抓住了这个“新代码”的影子。这个2004年的漏洞让我们不得不为新代码这三个字加上了双引号。

    4. 远程命令执行漏洞二(需登录)

    在Pierre Kim新增的两个链接中,还介绍了一种新的远程命令执行的方式,即通过set_mail.cgimailtest.cgi来执行命令。 与上一个远程命令执行漏洞一样,我们先在func/ieparam.c文件中找到set_mail.cgimailtest.cgi的调用过程:

    与上一个远程命令执行漏洞类似,cgisetmail()函数用于将各参数储存到结构体,例如sender参数赋值给bparam.stMailParam.szSenderreceiver1参数赋值给bparam.stMailParam.szReceiver1。 接着,来到了cgisetmailtest()函数:

    该函数第十行已被注释掉。这是使用此函数发送邮件证据的唯一可寻之处。虽然被注释掉了,我们也要继续跟踪DoMailTest()这个函数:

     

    可以看到sprintf( cmd, "echo \"mail test ok\" | /system/system/bin/mailx -r %s -s \"mail test\" %s",bparam.stMailParam.szSender, bparam.stMailParam.szReceiver1 ),发件人和收件人都直接被拼接成命令导致最后的命令执行。

    三、漏洞影响范围

    ZoomEye网络空间探测引擎探测结果显示,全球范围内共查询到78万条历史记录。我们根据这78万条结果再次进行探测,发现这些设备一共存在三种情况:

    • 第一种是设备不存在漏洞。
    • 第二种是设备存在验证绕过漏洞,由于web目录下无 system.ini,导致最终无法被利用。可以看到,当我们直接请求system.ini时显示需要认证,但当我们绕过验证之后却显示404 not found
    • 最后一种是设备既存在验证绕过漏洞,又存在system.ini文件。这些设备就存在被入侵的风险。

    我们统计了最后一种设备的数量。数据显示有近7万的设备存在被入侵的风险。这7万设备的国家分布图如下:

    可以看出,美国、中国、韩国、法国、日本均属于重灾区。我国一共有 7000 多台设备可能被入侵,其中近 6000 台位于香港。我们根据具体数据做成两张柱状图以便查看:

    (注:None为属于中国,但未解析出具体地址的IP)

    我们通过查询ZoomEye网络空间探测引擎历史记录导出2016年1月1日、2017年1月1日以及本报告编写日期2017年3月14日三个时间点的数据进行分析。

    在这三个时间点,我们分别收录了banner中含有GoAhead 5ccc069c403ebaf9f0171e9517f40e41的设备26万台、65万台和78万台。

    但对于这些IP而言,存在漏洞的设备增长趋势却完全不同。

    可以看到,2016年1月1日已探明的设备中目前仅有2000多台存在漏洞,2017年1月1日之前探明的设备中有近3万台存在漏洞,截至仅仅两个多月后的今天,已有近7万台设备存在漏洞。

    根据以上数据,我们可以做出如下判断:该漏洞出现时间大约是去年,直到今年被曝光之后才被大家所关注。在此期间,旧摄像头通过更新有漏洞固件的方式导致该漏洞的出现,而那些新生产的摄像头则被销往世界各地。根据今年新增IP地理位置,我们可以大致判断出这些存在漏洞的摄像头今年被销往何地。

    根据数据,我们可以看到,主要销往美国、中国、韩国、日本。中国新增了5316台存在漏洞的摄像头,其中4000多台位于香港。

    四、修复方案

    1.将存在漏洞的摄像头设备置于内网。
    2.及时升级到最新固件。
    3.对于可能被感染的设备,可以采取重启的方式来杀死驻留在内存中的恶意进程。

    五、参考链接

    1. https://www.seebug.org/vuldb/ssvid-92789
    2. https://www.seebug.org/vuldb/ssvid-92748
    3. https://pierrekim.github.io/blog/2017-03-08-camera-goahead-0day.html
    4. https://github.com/kuangxingyiqing/bell-jpg
    5. http://aluigi.altervista.org/adv/goahead-adv2.txt

    附录:Pierre Kim给出的受影响设备列表

    3G+IPCam Other
    3SVISION Other
    3com CASA
    3com Other
    3xLogic Other
    3xLogic Radio
    4UCAM Other
    4XEM Other
    555 Other
    7Links 3677
    7Links 3677-675
    7Links 3720-675
    7Links 3720-919
    7Links IP-Cam-in
    7Links IP-Wi-Fi
    7Links IPC-760HD
    7Links IPC-770HD
    7Links Incam
    7Links Other
    7Links PX-3615-675
    7Links PX-3671-675
    7Links PX-3720-675
    7Links PX3309
    7Links PX3615
    7Links ipc-720
    7Links px-3675
    7Links px-3719-675
    7Links px-3720-675
    A4Tech Other
    ABS Other
    ADT RC8021W
    AGUILERA AQUILERA
    AJT AJT-019129-BBCEF
    ALinking ALC
    ALinking Other
    ALinking dax
    AMC Other
    ANRAN ip180
    APKLINK Other
    AQUILA AV-IPE03
    AQUILA AV-IPE04
    AVACOM 5060
    AVACOM 5980
    AVACOM H5060W
    AVACOM NEW
    AVACOM Other
    AVACOM h5060w
    AVACOM h5080w
    Acromedia IN-010
    Acromedia Other
    Advance Other
    Advanced+home lc-1140
    Aeoss J6358
    Aetos 400w
    Agasio A500W
    Agasio A502W
    Agasio A512
    Agasio A533W
    Agasio A602W
    Agasio A603W
    Agasio Other
    AirLink Other
    Airmobi HSC321
    Airsight Other
    Airsight X10
    Airsight X34A
    Airsight X36A
    Airsight XC39A
    Airsight XX34A
    Airsight XX36A
    Airsight XX40A
    Airsight XX60A
    Airsight x10
    Airsight x10Airsight
    Airsight xc36a
    Airsight xc49a
    Airsight xx39A
    Airsight xx40a
    Airsight xx49a
    Airsight xx51A
    Airsight xx51a
    Airsight xx52a
    Airsight xx59a
    Airsight xx60a
    Akai AK7400
    Akai SP-T03WP
    Alecto 150
    Alecto Atheros
    Alecto DVC-125IP
    Alecto DVC-150-IP
    Alecto DVC-1601
    Alecto DVC-215IP
    Alecto DVC-255-IP
    Alecto dv150
    Alecto dvc-150ip
    Alfa 0002HD
    Alfa Other
    Allnet 2213
    Allnet ALL2212
    Allnet ALL2213
    Amovision Other
    Android+IP+cam IPwebcam
    Anjiel ip-sd-sh13d
    Apexis AH9063CW
    Apexis APM-H803-WS
    Apexis APM-H804-WS
    Apexis APM-J011
    Apexis APM-J011-Richard
    Apexis APM-J011-WS
    Apexis APM-J012
    Apexis APM-J012-WS
    Apexis APM-J0233
    Apexis APM-J8015-WS
    Apexis GENERIC
    Apexis H
    Apexis HD
    Apexis J
    Apexis Other
    Apexis PIPCAM8
    Apexis Pyle
    Apexis XF-IP49
    Apexis apexis
    Apexis apm-
    Apexis dealextreme
    Aquila+Vizion Other
    Area51 Other
    ArmorView Other
    Asagio A622W
    Asagio Other
    Asgari 720U
    Asgari Other
    Asgari PTG2
    Asgari UIR-G2
    Atheros ar9285
    AvantGarde SUMPPLE
    Axis 1054
    Axis 241S
    B-Qtech Other
    B-Series B-1
    BRAUN HD-560
    BRAUN HD505
    Beaulieu Other
    Bionics Other
    Bionics ROBOCAM
    Bionics Robocam
    Bionics T6892WP
    Bionics t6892wp
    Black+Label B2601
    Bravolink Other
    Breno Other
    CDR+king APM-J011-WS
    CDR+king Other
    CDR+king SEC-015-C
    CDR+king SEC-016-NE
    CDR+king SEC-028-NE
    CDR+king SEC-029-NE
    CDR+king SEC-039-NE
    CDR+king sec-016-ne
    CDXX Other
    CDXXcamera Any
    CP+PLUS CP-EPK-HC10L1
    CPTCAM Other
    Camscam JWEV-372869-BCBAB
    Casa Other
    Cengiz Other
    Chinavasion Gunnie
    Chinavasion H30
    Chinavasion IP611W
    Chinavasion Other
    Chinavasion ip609aw
    Chinavasion ip611w
    Cloud MV1
    Cloud Other
    CnM IP103
    CnM Other
    CnM sec-ip-cam
    Compro NC150/420/500
    Comtac CS2
    Comtac CS9267
    Conceptronic CIPCAM720PTIWL
    Conceptronic cipcamptiwl
    Cybernova Other
    Cybernova WIP604
    Cybernova WIP604MW
    D-Link DCS-910
    D-Link DCS-930L
    D-Link L-series
    D-Link Other
    DB+Power 003arfu
    DB+Power DBPOWER
    DB+Power ERIK
    DB+Power HC-WV06
    DB+Power HD011P
    DB+Power HD012P
    DB+Power HD015P
    DB+Power L-615W
    DB+Power LA040
    DB+Power Other
    DB+Power Other2
    DB+Power VA-033K
    DB+Power VA0038K
    DB+Power VA003K+
    DB+Power VA0044_M
    DB+Power VA033K
    DB+Power VA033K+
    DB+Power VA035K
    DB+Power VA036K
    DB+Power VA038
    DB+Power VA038k
    DB+Power VA039K
    DB+Power VA039K-Test
    DB+Power VA040
    DB+Power VA390k
    DB+Power b
    DB+Power b-series
    DB+Power extcams
    DB+Power eye
    DB+Power kiskFirstCam
    DB+Power va033k
    DB+Power va039k
    DB+Power wifi
    DBB IP607W
    DEVICECLIENTQ CNB
    DKSEG Other
    DNT CamDoo
    DVR DVR
    DVS-IP-CAM Other
    DVS-IP-CAM Outdoor/IR
    Dagro DAGRO-003368-JLWYX
    Dagro Other
    Dericam H216W
    Dericam H502W
    Dericam M01W
    Dericam M2/6/8
    Dericam M502W
    Dericam M601W
    Dericam M801W
    Dericam Other
    Digix Other
    Digoo BB-M2
    Digoo MM==BB-M2
    Digoo bb-m2
    Dinon 8673
    Dinon 8675
    Dinon SEGEV-105
    Dinon segev-103
    Dome Other
    Drilling+machines Other
    E-Lock 1000
    ENSIDIO IP102W
    EOpen Open730

     

     

     

     

    作者:kk | Categories:安全研究技术分享 | Tags:
  • WordPress REST API 内容注入漏洞事件分析报告

    2017-03-01

    作者:知道创宇404安全实验室

    报告发布日期:2017年02月28日 (注:本文首发自 paper.seebug.org) 

     

    PDF 版报告下载:WordPress REST API 内容注入漏洞事件分析报告

    English Version:WordPress REST API Content Injection Vulnerability Incident Analysis Report

     

    一、事件概述

     

    1 漏洞简介:

    WordPress是一个以PHP和MySQL为平台的自由开源的博客软件和内容管理系统。在4.7.0版本后,REST API插件的功能被集成到 WordPress 中,由此也引发了一些安全性问题。近日,一个由 REST API 引起的影响 WordPress 4.7.0和4.7.1版本的漏洞被披露,该漏洞可以导致 WordPress 所有文章内容可以未经验证被查看、修改、删除,甚至创建新的文章,危害巨大。

    2017年2月11日,知道创宇404安全实验室使用 ZoomEye 网络空间探测引擎进行扫描探测后发现,受该漏洞影响的网站仍然有15361个,其中有9338个网站已经被黑客入侵并留下了组织代号。我们针对组织代号进行统计,共发现八十余种代号。

    我们使用 ZoomEye 网络空间搜索引擎搜索 "app:WordPress ver:4.7.1" ,获得36603条结果。以下是 https://www.zoomeye.org/search?t=web&q=app%3AWordPress+ver%3A4.7.1的搜索结果:

    2 漏洞影响:

    导致 WordPress 所有文章内容可以未经验证被查看、修改、删除,甚至创建新的文章,危害巨大。

    3 影响版本:

    • WordPress 4.7.0
    • WordPress 4.7.1

     

    二、时间线

     

    三、漏洞验证与分析

    1 漏洞验证:

    PoC:

    Seebug 已给出详细的复现过程,在此过程中可以使用 Seebug 收录的 PoC 来进行测试。 https://www.seebug.org/vuldb/ssvid-92637

    漏洞验证扫描插件: Seebug 已更新 WordPress REST API 内容注入漏洞扫描插件。
    https://www.seebug.org/monster/ )

     

    简单复现过程:

    安装 WordPress存在漏洞版本并配置 REST API 以及 Apache+PHP+Mysql 的运行环境。加载 Apache 的 rewrite 模块以及主配置文件配置如下图:

    设置 WordPress 站点为固定链接:

    构造数据包:可以看到不存在任何验证信息,提示不允许编辑文章:

    构造可利用数据包:当 url 为 /wp-json/wp/v2/posts/1?id=1a 时,可以看到成功跳过验证获取文章内容:

    木马后门插入:

    需要安装如 insert_php , exec_php 等允许页面执行PHP 代码的插件。 可以构造数据包如下:

    上传后木马后门被插件当做 PHP 代码执行,网站被植入后门。

    2 漏洞分析:

    Seebug Paper (http://paper.seebug.org/208/ )已经发表了关于此漏洞的详细分析,以此作为参考。

    首先,在 ./wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php中,

    这里对路由进行了正则限制,防止攻击者恶意构造 id 值,但是我们可以发现 $get 和 $post值优先于路由正则表达式生成的值。

    接下来在 update_item 方法及其权限检查函数update_item_permissions_check 中:

    可以看出,当我们发送一个没有响应文章的 id 时,可以通过权限检查并允许继续执行对 update_item 方法的请求。具体到代码就是让 $post 为空来绕过权限检查。

    至于如何使 $post 为空,可跟进至 get_post 方法,发现其使用 wp_posts 中的 get_instance 静态方法获取文章:

    当我们传入的id 不是全由数字字符组成时返回 false,从而 get_post 方法返回null,接着绕过权限检查。 回头再看可执行方法 upload_item

    这里 $id 这个参数做了类型转化传递给 get_post ,而PHP类型转换时会出现这种情况:

    也就是说攻击者发起 /wp-json/wp/v2/posts/1?id=1hhh 的请求就是发起了对id 为1的文章的请求。

    3 漏洞修复:

    在 /wp-includes/class-wp-post.php 中:

    更改了对于 $post_id 的参数的传入顺序和判断条件,防止我们传入“数字+字母”这样的格式进行绕过。

     

    四、漏洞影响分布

     

    第一次扫描探测结果:

    我们于 2017/02/11 对全球的 WordPress 网站进行了扫描探测,发现当时仍旧受影响的 WordPress 网站共有 15361个。

    这些网站分别归属于82个国家与地区,其中 Top 20 国家与地区分布如下图:

    第二次扫描探测结果:

    我们于 2017/02/14 对全球的 WordPress 网站再次进行了扫描探测,获取最新数据如下:

    现存漏洞站数量:13390 个,与 2017/02/11 数据对比减少了1971 个。 其中数据重合量为12584 个,网站新增量为 806 个,存在代码执行插件的网站数量为 905 个。

    第三次扫描探测结果:

    我们于 2017/02/20 对全球 WordPress 网站进行了第三次扫描探测。

    根据第三次得到的数据,我们发现全球依旧存在漏洞的 WordPress 网站数量为11573个,其中与第二次数据重合量为11182个,新增数量为391个,消失数量为2208个,存在代码执行插件的网站数量为69个。

    三次扫描探测数据对比:

    分析上图,我们发现:

    1. 存在漏洞且一直未修复的网站基数还是很大。
    2. 存在允许代码执行插件的漏洞网站数量不多,对现存漏洞网站影响不大。

    Top 10国家存在漏洞网站总量与消失量对比:

    根据上图我们能很清晰的看出, 02/11 后消失的漏洞网站数量约占原有漏洞网站总量的三分之一 。

    网页污染行为分析:

    我们于 2017/02/13 探测这些网站的运行情况,发现共有 9338 个网站已经留下了黑客的痕迹(痕迹如 hacked by xxx)。

    Ps:我们探测的是依旧存在漏洞的网站并获取网站最新文章信息,而在经过修复的网站上还是有可能存在黑客入侵的痕迹。

    我们统计了黑客组织留下的黑客代号,发现不同的黑客代号共出现了85种。其中 Top 20黑客组织代号如下表:

    上表说明的是此时依旧活跃在互联网上的针对该漏洞的黑客组织的排名。 我们分析了黑客留下的痕迹,初步总结了以下几点信息:

    1. 代号为w4l3XzY3 的黑客是事件早期被报道出来的黑客之一,此人曾经于2014年针对 Drupal 网站进行过相同性质的入侵行为。分析其过往行为发现该黑客一直在入侵网站挂黑页,Google搜索该代号已有295000条记录,已经是个惯犯了。
    https://www.drupal.org/node/2394801

    此人推特链接如下: https://twitter.com/w4l3xzy3

    在 nairaland 论坛上有他留下的一些个人信息以及售卖php shell等工具的主题:http://www.nairaland.com/w4l3xzy3

    2. 代号为 SA3D HaCk3D 与 MuhmadEmad 的黑客入侵后留下的页面是相似的,宣传反 ISIS 的信息。前者提到了 peshmarga ,应该是一个中东国家,具有反美倾向。后者提到了 kurdistan ,是黑客组织 “ KurdLinux_Team ” 的成员。该人疑似曾在推特上炫耀自己的黑客行为。
    https://twitter.com/muhmademad

    3. 代号为 GeNErAL HaCkEr ,GeNErAL 与 RxR HaCkEr 的黑客同样疑似出自同一组织。他们还留下了一个 qq 号码:21*****233 。
    搜索该账号获得信息如下图:

    可以看到组织名为 “ Team Emirates” 搜索相关信息发现一个疑似的相关推特https://twitter.com/rxrhackerr

    4. 代号为 GHoST61 的黑客留下的信息为土耳其语,翻译出来大意是“土耳其无处不在”,疑似为出自土耳其的黑客组织。

     

    五、后续影响分析

     

    暗链与插件导致的PHP代码注入与 RCE :

    我们发现当未修复漏洞的网站启用了如 insert_php 或 exec_php 等允许网页执行 PHP 代码的插件时,黑客利用此漏洞除了能够在网页中插入暗链还能在网站中注入后门并以此牟利。

    我们在15361个未修复漏洞的目标站点中,探测到的使用了这两种插件的网站有905个,已经被注入木马后门的网站一共有158个。其中插入的一句话木马口令共有98种。

    暗链发现情况:

    在本次探测到的数据中发现暗链出现频率第一的网址 http://biturlz.com ,重定向到 https://bitly.com 这个网址,出现次数355次。

    出现频率第二的是 www.yellowfx.com 这个网址,53次。

    余下的网址出现频率则比较接近,分布范围较广。

    本次探测到的黑客shell地址如下:

    http://pastebin.com/raw/ku5zcKfu
    https://paste.ee/r/3TwsC/0
    http://pastebin.com/k20u5zcKfu
    http://pastebin.com/raw/F9ffPKBM
    http://pastebin.com/raw/gYyy6Jd7
    http://pastebin.com/raw/fXB166iS
    http://pastebin.com/raw/gLc9bi4z
    http://acommeamour.fr/tmp/3jqy4.php

    PHP shell 种类:

    从探测到的数据分析,此次事件中出现的shell种类如下:

    总结:

    黑客利用 pastebin.com 等网站存放 shell,目前为止这些网站已经开始陆续关闭。攻击峰潮已过,我们需要抓紧进行事后补救工作。

    值得注意的是虽然本次探测到的被植入后门的网站数量并不是很多,但是修复漏洞并不代表清理了后门,所以实际被挂马的网站数量将会更多。

    建议启用类似 insert-php 插件的用户在升级 WordPress之后检查网站目录,查杀木马。尤其是 wp-content/uploads/ 目录,检查网站目录下是否出现文件改动如 wp.php、 info.php、db.php 等文件并核查文件内容。

    从获取到的黑客shell 内容分析,index.php 、 apis.php、wp.php、info.php、db.php、css.php、insert_php.php 这些文件需要重点检查。

    对于此次事件,我们还将予以持续跟进。

     

    六、漏洞修复方案

     

    升级 WordPress到最新版 4.7.2 ,可以选择下载 WordPress 4.7.2 或者前往后台更新面板手动点击升级。支持后台自动升级的网站已经自动完成升级过程。

     

    七、相关链接

     

    关于

     

    404 Team,国内黑客文化浓厚的知名安全公司知道创宇神秘而核心的部门,最为大家熟知的分享包括:KCon 黑客大会、Seebug 漏洞平台、ZoomEye 钟馗之眼网络空间搜索引擎。

    404 Team 依托 Seebug 与 ZoomEye 两大平台能力及内部的漏洞相关工业化平台能力(WSL),总能在漏洞爆发的最小黄金周期内完成全球性响应。

    除了依托这些开放平台打造了全球黑客生态圈之外,404 Team 还在持续创新创造,为整个知道创宇业务需求输出精心打磨的漏洞弹药及相关安全产品。

    404 Team,守正出奇,知行合一。

     

     

    作者:kk | Categories:安全研究技术分享 | Tags:
  • Joomla 权限提升漏洞(CVE-2016-9838)分析

    2016-12-23

    Author: p0wd3r (知道创宇404安全实验室)

    Date: 2016-12-21

    0x00 漏洞概述


    1.漏洞简介

    Joomla 于12月13日发布了3.6.5的升级公告,此次升级修复了三个安全漏洞,其中 CVE-2016-9838 被官方定为高危。根据官方的描述,这是一个权限提升漏洞,利用该漏洞攻击者可以更改已存在用户的用户信息,包括用户名、密码、邮箱和权限组 。经过分析测试,成功实现了水平用户权限突破,但没有实现垂直权限提升为管理员。

    2.漏洞影响

    触发漏洞前提条件:

    1. 网站开启注册功能
    2. 攻击者知道想要攻击的用户的 id (不是用户名)

    成功攻击后攻击者可以更改已存在用户的用户信息,包括用户名、密码、邮箱和权限组 。

    3.影响版本

    1.6.0 - 3.6.4

    0x01 漏洞复现


    1. 环境搭建

    docker-compose.yml:

    然后在 docker-compose.yml 所在目录执行docker-compose up,访问后台开启注册再配置SMTP即可。

    2.漏洞分析

    官方没有给出具体的分析,只给了描述:description

    翻译过来就是:

    对表单验证失败时存储到 session 中的未过滤数据的不正确使用会导致对现有用户帐户的修改,包括重置其用户名,密码和用户组分配。

    因为没有具体细节,所以我们先从补丁下手,其中这个文件的更改引起了我的注意:

    https://github.com/joomla/joomla-cms/commit/435a2226118a4e83ecaf33431ec05f39c640c744

    patch-1

    可以看到这里的$temp是 session 数据,而该文件又与用户相关,所以很有可能就是漏洞点。

    我们下面通过这样两个步骤来分析:

    1. 寻找输入点
    2. 梳理处理逻辑

    1.寻找输入点

    我们找一下这个 session 是从哪里来的:

    find-session

    components/com_users/controllers/registration.php中设置,在components/com_users/models/registration.php中获取。我们看components/com_users/controllers/registration.php中第108-204行的register函数:

    这两处设置 session 均在产生错误后进行,和漏洞描述相符,并且$requestData是我们原始的请求数据,并没有被过滤,所以基本可以把这里当作我们的输入点。

    我们来验证一下,首先随便注册一个用户,然后再注册同样的用户并开启动态调试:save-session

    由于这个用户之前注册过,所以验证出错,从而将请求数据写入了 session 中。

    取 session 的地方在components/com_users/models/registration.phpgetData函数,该函数在访问注册页面时就会被调用一次,我们在这时就可以看到 session 的值:session-value

    由于存储的是请求数据,所以我们还可以通过构造请求来向 session 中写入一些额外的变量。

    2.梳理处理逻辑

    输入点找到了,下面来看我们输入的数据在哪里被用到。我们看components/com_users/models/registration.phpregister函数:

    在这里调用了之前的getData函数,然后使用请求数据对$data赋值,再用$data对用户数据做更改。

    首先跟进$user->bind($data),在libraries/joomla/user/user.php中第595-693行:

    这里根据我们传入的数据对对象的属性进行赋值,setProperties并没有对赋值进行限制。

    接下来我们看$user->save($data),在libraries/joomla/user/user.php中第706-818行:

    具体内容就是将$user的属性绑定到$table中,然后对$table进行检查,这里仅仅是过滤特殊符号和重复的用户名和邮箱,如果检查通过,将数据存入到数据库中,存储数据的函数在libraries/joomla/table/user.php中:

    如果主键存在则更新,主键不存在则插入。

    整个的流程看下来我发现这样一个问题:

    如果$data中有id这个属性并且其值是一个已存在的用户的 id ,由于在bindsave中并没有对这个属性进行过滤,那么最终保存的数据就会带有 id 这个主键,从而变成了更新操作,也就是用我们请求的数据更新了一个已存在的用户。

    实际操作一下,我们之前注册了一个名字为 victim 的用户,数据库中的 id 是57:

    initial-id

    然后我们以相同的用户名再发起一次请求,然后截包,添加一个值为57名为jform[id]的属性:add-id

    放行后由于重复注册从而发生错误,程序随后将请求数据记录到了 session 中:

    session-id

    接下来我们发送一个新的注册请求,用户名邮箱均为之前未注册过的,在save函数处下断点:user-id

    id 被写进了$user中。然后放行请求,即可在数据库中看到结果:change-user

    之前的 victim 已被新用户 attacker 取代。

    整个攻击流程总结如下:

    1. 注册用户A
    2. 重复注册用户A,请求包中加上想要攻击的用户C的 id
    3. 注册用户B
    4. 用户B替代了用户C

    (上面的演示中A和C是同一个用户)

    需要注意的是我们不能直接发送一个带有 id 的请求来更新用户,这样的请求会在validate函数中被过滤掉,在components/com_users/controllers/registration.phpregister函数中:

    所以我们采用的是先通过validate触发错误来将 id 写到 session 中,然后发送正常请求,在register中读取 session 来引入 id,这样就可以绕过validate了。

    另外一点,实施攻击后被攻击用户的权限会被改为新注册用户的权限(一般是 Registered),这个权限目前我们无法更改,因为在getData函数中对groups做了强制赋值:

    所以目前只是实现了水平权限的提升,至于是否可以垂直权限提升以及怎么提升还要等官方的说明或者是大家的分析。

    由于没有技术细节,一切都是根据自己的推断而来,如有错误,还望指正 🙂

    3.补丁分析

    patch-2

    使用 session 时仅允许使用指定的属性。

    0x02 修复方案


    升级至3.6.5

    https://www.joomla.org/announcements/release-news/5693-joomla-3-6-5-released.html

    0x03 参考


     

     

    作者:kk | Categories:技术分享 | Tags:
  • NTPD拒绝服务漏洞(CVE-2016-7434) 分析

    2016-12-23

    Author: LG, dawu (知道创宇404实验室)

    前言


    NTP服务对于互联网来说是不可或缺的,很多东西都能和它联系到一起。就在不久前,轰动一时的德国断网事件中也出现了它的影子。保证NTP服务器的安全是很重要的!

    0x00 漏洞概述


    1.漏洞简介

    NTPD是一个linux系统下同步不同机器时间的服务程序。
    近日NTP.org公布了一个拒绝服务漏洞,该漏洞能够导致NTPD服务遭受远程DoS攻击。

    2.漏洞影响

    受影响版本面临DoS攻击风险

    3.影响版本

    • 4.3.90
    • 4.3.25
    • 4.3
    • 4.3.93
    • 4.3.92
    • 4.3.77
    • 4.3.70
    • 4.2.8p8
    • 4.2.8p7
    • 4.2.8p6
    • 4.2.8p5
    • 4.2.8p4
    • 4.2.8p3
    • 4.2.8p2
    • 4.2.8p1
    • 4.2.7p22

    0x01 漏洞详情


    1.漏洞细节

    NTPD服务端配置安全性低,能接收任意端mrulist数据包。此时攻击者能够远程发送经过构造的mrulist数据包对其进行Dos攻击。

    2.漏洞检测方法

    使用以下命令检测NTP版本: # ntpq -c version 受影响版本列表中的版本未作相关安全配置将受漏洞影响。 github上已有公布的漏洞利用poc,但是该poc会使NTPD服务崩溃,利用后需要重启服务。
    漏洞利用poc

    3.漏洞复现

    docker搭建环境:

    ntpd1

    之后命令行输入:

    最后NTPD服务崩溃

    --1

    --2

    4.漏洞分析

    4.1 payload分析

    漏洞发现者构造了这样一段mrulist数据包

    base64解码后

    base64

    base64解码(以16进制显示):

    此处参考NTP协议格式:

    NTP packet = NTP header + Four TimeStamps = 48byte

    NTP header : 16byte

    LI(LeapYearIndicator) VN(VersionNumber) Mode Stratum Poll(PollInterval) Precision
    2bit 3bit 3bit 8bit 8bit 8bit

    详情请看 NTP报文格式

    主要字段的解释如下: ·LI(Leap Indicator,闰秒提示):长度为2比特,值为“11”时表示告警状态,时钟未被同步。为其他值时NTP本身不做处理。 ·VN(Version Number,版本号):长度为3比特,表示NTP的版本号,目前的最新版本为4。 ·Mode:长度为3比特,表示NTP的工作模式。不同的值所表示的含义分别是:0未定义、1表示主动对等体模式、2表示被动对等体模式、3表示客户模式、4表示服务器模式、5表示广播模式或组播模式、6表示此报文为NTP控制报文、7预留给内部使用。 ·Stratum:系统时钟的层数,取值范围为1~16,它定义了时钟的准确度。层数为1的时钟准确度最高,准确度从1到16依次递减,层数为16的时钟处于未同步状态。 ·Poll:轮询时间,即两个连续NTP报文之间的时间间隔。 ·Precision:系统时钟的精度。

    了解了NTP的报文格式后,上文数据包中的NTP header:

    payload分析到这里暂时无下文,于是我们转去研究了漏洞触发点部分

    4.2漏洞触发点分析

    如下图,我们根据valgrind给出的调试信息寻找漏洞触发点

    ----

    判断漏洞触发点位于ntpd/ntpcontrol.c:4041,readmru_list()函数体内

    ----1

    漏洞触发原因是estrdup函数空指针的引用。

    estrdup函数的参数不能为NULL,否则会使程序崩溃。因为estrdup函数包含了strdup函数,而strdup函数又包含了strlen函数,该函数参数不能是NULL

    那么这说明val是有可能引入空指针的了? val是由ctl_getitem()函数引入的,稍后我们上溯去看。

    我们先来看readmrulist函数中var list

    它把mrulist数据包中各字段输入in_parms中,并且用到了sizeof()

    看到这里,我们明白了nonce_text字段长度是6字节。

    接着我们上溯ctl_getitem函数。它的功能是处理解码后数据包中的数据。首先处理的就是nonce_text字段。ctl1

    这里val就是*data,*var_list就是in_parms,v就是每个字段的数据

    ctl3

    注意line(3103-3107)以及line(3111-3116)处的代码: while循环的条件是从字符串首部开始扫描,出现空或者,就把reqpt右移一个字节。
    for循环的判断语句中以,为终止标志,以=为赋值标志。如果没有出现=来进行判断后赋值,字段就会一直为空。
    关键点出现了:在之前构造payload时,目标就是这里(阻止=的出现)。

    4.3payload构造分析

    我们延续对payload的分析,结合上文payload分析base64解码部分信息,其实已经显示出来了。6nonce,大家注意到了吗? nonce前面的一个字节正常格式原本应该是=的,漏洞发现者把它置为6了(16进制就是\x36)。其实只要构造"nonce"前一个字节不是=(16进制是\x3d)而且前3个字节都为NULL,漏洞触发条件就满足了。

    我们来验证一下:

    --1-1

    --2-1

    到此总结一下: payload构造关键点在于上文中NTP header格式如下
    NULL NULL NULL 非= nonce,

    ps:非=需要的是能正常解码出来的字符串,乱码是不行的。

    5.补丁分析

    ntp4.2.8p9版本修补了这个漏洞。官方修补方案是在read_mru_list()中严格地进行val参数的检测并做了一些限制措施。此外官方在最新版ntp_control.c的release中又对ctl_getitem函数进行了安全修改。

    GitLab代码补丁对比链接

    1

    2

    我们分析后认为补丁关键点如下:

    其中char * val 变为const char * val

    修改后逻辑运行为必须通过void * 从指针中去掉const属性。 接着严格判断val是不是NULL,若val指针为NULL则中断。 在此情况下原漏洞触发点处变为先判断*val,判断式只会为真,避免了空指针的引用,从而修复了此处漏洞。

    6.漏洞利用分析

    在实际场景中,存在漏洞的NTPD服务器如果未作任何防护措施,攻击者极易对其进行远程DoS攻击。但是攻击结果仅是使服务崩溃,重启服务就能正常运行,对NTP服务器本身无其他深层影响。 但是,如实验室,飞机场,银行等机构的业务结算对于时间的校验应该非常严格。一旦针对性地攻击与它们相关联的NTP服务器导致系统时间无法正常同步,对于业务结算等是能够造成一定冲击的。

    0x02 漏洞防护措施


    1. 只允许接收来自信任主机的mrulist查询包
    2. 升级到ntpd4.2.8p9
    3. 执行BCP-38标准

    0x03 参考


     

    作者:kk | Categories:技术分享 | Tags:
  • Sparkjava Framework 文件遍历漏洞(CVE-2016-9177)分析与探究

    2016-11-17

    Author:dawu(知道创宇404实验室) data:2016-11-16

    0x00 漏洞概述


    1.漏洞简介

    Sparkjava是一款小型的web框架,它能够让你以很少的代码构建出一个java web应用。近日,某国外安全研究人员发现其存在文件遍历漏洞,可以通过该漏洞读取任意文件内容。在对这个漏洞进行复现与分析的时候,我们又发现了一些可能可以利用的地方,但是利用条件更加苛刻。

    2.漏洞影响

    Sparkjava版本 < 2.5.2

    0x01 漏洞复现

    1.验证环境

    Jdk-1.8.111 Apache maven 3.3.9 在写好Sparkjava代码后,在文件所在目录打开命令行,运行mvn package进行编译打包。

    2.漏洞复现

    根据官网给出的示例,我们写了一个简单的函数去复现这个漏洞:

    pom.xml的配置为xml <dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-core</artifactId> <version>2.5</version> </dependency>这里提供已经打包好的jar文件供大家下载。可以用如下命令运行:bash java -jar sparkexample-jar-with-dependencies.jar 我们可以通过(..\)来改变路径从而读取任意文件。如图,我们读取到/etc/passwd:

    复现

    在漏洞发现者的描述中,Spark.staticFileLocation()和Spark.externalStaticFileLocation()这两个函数都存在这个问题。经过开发者测试,在IDE中运行时,两个函数都可以复现这个漏洞;运行打包好的jar包时,只有Spark.externalStaticFileLocation()这个函数可以触发漏洞。

    0x02 补丁分析与深入研究


    1.补丁分析

    很明显,在漏洞被发现时,官方没有对url中的路径做任何处理。在漏洞被修补之后,官方推出了新的版本2.5.2。这里我们对比之前的版本,并且通过调试,尝试分析官方的修补方案。 官方修补链接(https://github.com/perwendel/spark/commit/efcb46c710e3f56805b9257a63d1306882f4faf9) 当我们正常请求时:bash curl "127.0.0.1:4567/l.txt" 跟到关键代码处,我们可以看到在判断文件是否存在之后,官方添加了DirectoryTraversal.protectAgainstInClassPath(resource.getPath());进行判断。

    动态调试1

    这里,path就是我们HTTP请求的地址,addedPath就是我们通过staticFiles.externalLocation()函数设置的路径与path拼接之后的值,resource中的file的值就是addedPath值经过路径的处理的值(例如:/tmp/test/..\l.txt先将所有的\换成/,再对路径进行处理,最后结果为/tmp/l.txt),resource.getPath()就是addedPath的值。

    动态调试2

    protectAgainstInClassPath()函数中,需要判断removeLeadingAndTrailingSlashesFrom(path).startsWith(StaticFilesFolder.external())是否为false,为false就抛出。

    removeLeadingAndTrailingSlashesFrom(path)为新添加的函数,作用是将path首尾的/去掉和将尾部的\去掉。在这里经过处理之后,path的值为tmp/l.txt

    StaticFilesFolder.external()则是返回external的值,在这里就是tmp。如果removeLeadingAndTrailingSlashesFrom(path)前面的字母是tmp,则进入下一步。

    综上所述,官方通过比较经过处理后的路径的开头和我们设置的externalLocation()的路径是否相同来防止我们利用..\读取任意文件。

    2.深入探究

    我们修改了pom.xml,使用新的Sparkjava版本进行编译尝试,做了如下探究。xml <dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-core</artifactId> <version>2.5.2</version> </dependency>

    ①软链接的利用

    与Sparkjava(CVE-2016-9177)同时爆出来的一个漏洞GitLab的任意文件读取(CVE-2016-9086)是利用软链接的特性,我们就顺手测试了软链接在Sparkjava下的利用。 直接读取文件:file

    path

    怎么才能利用软链接呢?这里的利用条件比较苛刻。笔者想到了两种途径: 1.网站允许上传压缩包,上传后解压并且还能访问到解压后的文件才能利用 2.网站通过wget(wget配置文件中需要retr-symlinks=on)从ftp上下载文件并且能够访问到下载的文件。

    ②再次读取文件

    我们在根目录下新建两个文件tmp.txt,tmp2.txttmp

    再访问

    tmp2

    读取到了tmp.txt和tmp2.txt的内容。 我们分析一下能够再次读取的原因,当我们请求为: bash curl “127.0.0.1:4567/tmp\..\..\tmp.txt” 分析过滤代码处: read1

    addedPath的值为/tmp/tmp/..\..\tmp.txt,经过处理后resource中的file值为/tmp.txt,对于下面的函数removeLeadingAndTrailingSlashesFrom(path).startsWith(StaticFilesFolder.external()),由于tmp.txt也是由tmp开头,所以判断可以通过,进而读取到tmp.txt

    同样的道理,我们也可以读取到/tmp2/test.txt的内容。

    tmp3

    通过以上分析,笔者认为这个读取很鸡肋,首先staticFiles.externalLocation()中定义的路径只能是一级路径,其次我们要读取的文件的完整路径开头必须和staticFiles.externalLocation()中定义的路径相同。这就限制了这个新的读取,也许只有在某些特定的场合才能有奇效。

    如有错误,欢迎指正:)

    0x03 参考链接


    1.https://www.seebug.org/vuldb/ssvid-92517 2.http://seclists.org/fulldisclosure/2016/Nov/133.https://github.com/perwendel/spark/commit/efcb46c710e3f56805b9257a63d1306882f4faf94.https://github.com/perwendel/spark/issues/700 5.http://sparkjava.com/documentation.html

     

    作者:kk | Categories:安全研究技术分享 | Tags:
  • GitLab 任意文件读取漏洞 (CVE-2016-9086) 和任意用户 token 泄露漏洞 分析

    2016-11-10

    Author:dawu,LG(知道创宇404安全实验室) Data:2016-10-09

    0x00 漏洞概述


    1.漏洞简介

    GitLab 是一个利用Ruby on Rails开发的开源应用程序,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目。近日研究者发现在其多个版本中存在文件读取漏洞(CVE-2016-9086)任意用户authentication_token泄漏漏洞,攻击者可以通过这两个漏洞来获取管理员的权限,进而控制所有gitlab项目。

    2.漏洞影响

    • 任意文件读取漏洞(CVE-2016-9086):
      GitLab CE/EEversions 8.9, 8.10, 8.11, 8.12, and 8.13
    • 任意用户authentication_token泄露漏洞:
      Gitlab CE/EE versions 8.10.3-8.10.5

    0x01 漏洞复现


    1.环境搭建

    这里使用8.10.3版本是为了任意用户authentication_token泄露漏洞的复现。

    安装完成后,访问服务器80端口即可看到GitLab登录页面。

    注:8.9.0-8.13.0版本的GitLab的项目导入功能需要管理员开启,8.13.0版本之后所有用户都可以使用导入功能。管理员可以访问http://domain/admin/application_settings 开启,开启之后用任意用户新建项目的时候,可以在import project from一项中看到gitlab export。

    2.漏洞分析

    任意文件读取漏洞(CVE-2016-9086)

    8.9.0版本开始,GitLab新增了导入导出项目的功能。
    一个空的gitlab项目导出后结构如下:

    export

    其中VERSION文件内容为GitLab的导出模块的版本,project.json则包含了项目的配置文件。

    当我们导入GitLab的导出文件的时候,GitLab会按照如下步骤处理: 1.服务器根据VERSION文件内容检测导出文件版本,如果版本符合,则导入。
    2.服务器根据Project.json文件创建一个新的项目,并将对应的项目文件拷贝到服务器上对应的位置。

    检测VERSION文件的代码位于:/lib/gitlab/import_export/version_checker.rb中:

    我们可以看到这里的逻辑是读取VERSION文件的第一行赋值给变量version,然后检测verison与当前版本是否相同,相同返回true,不相同则返回错误信息(错误信息中包括变量version的值). 于是漏洞发现者Jobert Abma巧妙的使用了软链接来达到读取任意文件的目的。首先,我们给VERSION文件加上软链接并重新打包。

    version_link

    这样,读取VERSION文件的时候服务器就会根据软链接读取到/etc/passwd的第一行内容并赋值给version。但是由于version与当前版本不相同,所以会输出version的值,也就是/etc/passwd第一行的内容。

    访问之前搭建好的GitLab服务器,创建一个新的项目,填写完项目名称后在Import project from一栏中选择GitLab export,上传我们修改后的导入包,然后就可以看到/etc/passwd文件第一行

    VERSION

    但是,如果只读取任意文件的第一行,能做的事情还是太少了。漏洞发现者显然不满足这一结果,他继续找了下去.
    读取Project.json这一配置文件的代码位于:/lib/gitlab/import_export/project_tree_restorer.rb中:

    在这里,我们可以再次使用软链接使变量json获取到任意文件的内容,但是由于获取的文件不是json格式,无法decode,导致异常抛出,最终在前端显示出任意文件的内容。 添加软链接并打包:

    json_link

    上传导出包,页面上显示的结果:

    json

    任意用户authentication_token泄露漏洞

    复现步骤为:

    1.注册一个普通用户,创建一个新的项目
    2.在项目的member选项中,添加管理员到项目中。

    add_admin

    3.点击edit project,找到Export project部分,点击Export project,等待几分钟去查看注册邮箱收到的下载地址或者刷新页面,点击Download export下载导出包。

    download

    4.导出包的project.json中已经含有了管理员的authentication_token

    admin_token

    得到authentication_token之后我们就可以通过api做管理员可以做的事情了,比如查看管理员所在的项目:

    get_all_project

    分析原因:

    我们在\app\controllers\projects_controller.rb中找到了export函数,这个函数被用来导出项目文件。

    往下跟add_export_job(),在\app\models\project.rb中:

    继续到\app\workers\project_export_worker.rb文件的ProjectExportWorker.perform_async():

    这里我们可以看到current获取的是User.find(current_user_id)的内容,然后调用::Projects::ImportExport::ExportService.new(project, current_user).execute 由于笔者之前没有接触过ruby,这里只好采用gitlab-rails console来找到User.find()的值。可以看到,在User.find()中,存在authentication_token的值。

    User.find

    跟到\app\services\project\import_export\export_service.rb,这里执行version_saver, avatar_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver这五个函数来写各种导出文件,其中project_tree_saver()负责导出project.json

    跳过之后的几个繁琐的调用之后,执行了lib/gitlab/import_export/json_hash_builder.rb中的create_model_value函数。

    这里出现了逻辑问题,由于parsed_hash这个变量不是全局变量,所以create_model_value()中执行parse_hash()时,parse_hash()中的parsed_hash被改变,但是create_model_value()函数中的parsed_hash不会变,这就造成了parse_hash()这个函数执行后create_model_value()parsed_hash这个值并没有改变。因此最后导出的文件包含了authentication_token

    我们在gitlab-rails console里展示了这两者的区别。当value=user的时候,parsed_hash={:include=>:user},输出的结果如同图中的user.as_json(),会将所有内容输出,包括authentication_token。当parsed_hash为经过parse_hash()处理后的{:include=>{:user=>{:only=>[:id, :email, :username]}}}时,输出结果与user.as_json(only: [:id, :email, :username])相同。

    include_only

    后续RCE方式的探讨

    hackone的两个报告中,漏洞发现者都提到了leads to RCE,笔者尝试去实现这一点。由于GitLab源码在gitlab.com上,所以当获取了GitLab的管理员权限后,我们可以通过authentication_token修改GitLab项目的源码,留下自己的后门。 为了重现这种情况,我们在本地新建一个新的项目去通过authentication_tokenGitLab api来修改项目文件。

    root账户创建一个项目:test_rce,其中README.md的内容为created by root

    admin_test

    接下来,我们要用gitlabapi来修改它。首先,根据projects的api找到test_rce项目对应的id,这里是18

    find_project_id

    我们再根据api读取一下文件

    read_file

    这里,contentY3JlYXRlZCBieSByb290,这是文件内容被base64加密后的结果,解密一下就可以看到created by root

    base64decode1

    根据api的要求,我们通过PUT数据来修改文件,将README.md修改为change by notroot。 当我们再读一次,content内容为:Y2hhbmdlIGJ5IG5vdHJvb3Q=,解码之后就是change by notroot

    changefile

    base64decode2

    不得不说,笔者所实现的这种方式攻击时间跨度很长,能否执行命令取决于开发者下一次更新的时间,这也是这种方法的缺点之一。

    0x02 官方修复分析


    任意文件读取漏洞(CVE-2016-9086)修复分析

    symbolic_link_repair

    我们可以看到,官方先移除了导入包里的软连接,其次,读取VERSION的内容和project.json的内容出错后将内容输出到日志里而非返回到前端。

    任意用户authentication_token泄露漏洞修复分析

    token_repair

    官方让json_config_hash[current_key]获取到parse_hash()处理后的值。

    0x03 参考

     


    作者:kk | Categories:安全研究技术分享 | Tags:
  • GNU tar 解压路径绕过漏洞(CVE-2016-6321) 分析

    2016-11-10

    Author: LG(知道创宇404安全实验室) Date: 2016-11-09

    0x00 漏洞概述


    1.漏洞简介

    GNU tar文档管理命令是Linux系统下常用的一个打包、压缩的命令。经 CSS(FSC1V Cyber Security Services)团队的研究员 Harry Sintonen 研究发现,tar 命令在提取路径时能够被绕过,在某些情况下导致文件被覆盖。在一些特定的场景下,利用此漏洞可导致远程代码执行。

    2.漏洞影响

    受害者使用tar命令解压由攻击者构造的特殊 tar 包时,tar 包不会解压到受害者制定的目标路径,而是被解压到攻击者指定的目录位置。

    3.影响范围

    从GNU tar 1.14 to 1.29 (包含1.29) 影响包括 Red Hat,Alphine Linux,Red Star OS以及其他所有使用 GNU tar 的 Linux 系统。

    0x01 漏洞详情


    1. 漏洞检测

    方法一:

    漏洞发现者给出了示例 PoC,用户可用其自检。 (该方法会覆盖用户帐号密码,导致 root 用户密码为空,建议使用实验环境测试或者采用方法二)

    示例poc:

    example_poc

    示例poc中含有一个文件shadow,路径为etc/motd/../etc/shadow。在根目录下解压该包,由于漏洞的影响,../前面的内容给去掉了,路径文件名只剩下etc/shadow,原有etc/shadow文件就被其覆盖了。

     

    方法二:

    访问https://sintonen.fi/advisories/tar-poc.tar下载测试tar包后在提取前重命名 tar 包内的 shadow 文件名,如重命名为 test。然后运行如下命令:

    查看 etc 目录下,若生成了 test 文件,证明该漏洞存在。

    2.具体攻击场景

    以下为漏洞发现者提供的实际攻击场景

    1.攻击者可以用这种手段诱使用户替换一些重要的文件,例如 .ssh/authorized_keys.bashrc , .bash_logout , .profile, .subversion.anyconnect

    2.有一些从 web 应用或者其它类似来源自动解压文件的脚本,这些脚本一般会以 setuid root 权限执行,通常这类脚本的解压命令如下:

    在这种情况下,攻击者可以重写/var/spoon/cron/crontabs/root以获取 root 身份的代码执行能力; 也可以将可能被 root 身份执行的二进制文件替换成一个有后门的版本; 或者投放一个 setuid root 的二进制文件,等待被管理员执行,使攻击者有机会获取 root 权限。

    3.以 root 身份执行解压命令也可能被攻击 。例如上文中提到覆写/etc/shadow的例子

    tar1

    如果--exclude 规则与--anchored 选项同时使用,那么即使手动加了--exclude 规则也没有用,例如:

    tar2

    在两种情况下,攻击者都成功地把/etc/test替换成了任意内容。

    不过,在实际利用这个漏洞时,攻击者需要首先知道一些特定的前导信息,例如解压命令执行时实际在命令行下指定的路径名,毕竟在构造攻击 tar 包时 “../” 序列之前的路径前缀需要符合 tar 命令中所输入的路径,攻击才能奏效。

    3.漏洞分析

    根据漏洞发现者的分析,在lib/paxnames.c文件中,有一个safernamesuffix()函数,这个函数取代了1.13版本的检查机制。

    从代码注释可以看出,如果absolute_names变量为1,将 filename 赋值给 p 继续.反之若为 0 则将文件名中文件系统的前缀给去掉,并且也会对 filename 进行一些安全检查 。 因此,当 tar 解包时若文件名中包含“../”, safernamesuffix 函数会删除"../"及其之前的部分,将其与解压目录路径变为相对关系。这么做的目的是在兼顾文件名的安全性时保证文件的提取,而不是之前版本中改动的跳过含有恶意文件名的文件。在经过长达13年的应用后,这个漏洞终于被 Harry Sintonen 发现并公布出来。

    于是,笔者研究了这个漏洞相关的发展历史。 tar所有版本下载链接 发现:

    • tar通过 src/extarct.c 提取文件
    • extract.c Revision 1.35 前未加入安全检测,可以通过“../”字符串直接绕过解压路径问题,并将文件写到任意位置
    • extract.c Revision 1.35 加入安全检测,会警告压缩文件文件名中存在“..”字符串,并且会跳过不去处理这些文件
    • extract.c Revision 1.47引入 safernamesuffix 函数 - tar 1.16版本后,extract.c文件代码重构,在lib/paxnames.c 文件中定义 safernamesuffix 函数

    然后笔者继续深入,通过tar官网extract.c文件更新列表对比,从源代码分析 tar 的安全检测行为。

    1999/12/13 commit 前后对比

    Revision 1.35官方tag中有一条: ++(extractarchive): By default, warn about ".." in member names, and skip them.++ 即Revision 1.35加入了(extractarchive):默认情况下,在成员名称中警告“..”,并跳过它们 初版修复

    上图中,绿色代码区的功能就填补了之前安全检测的空白。它首先遍历 CURRENTFILENAME,如果存在".."就会警告"Member name contains'..'",然后跳过这些文件,不去处理它们。而左边的灰色空白区域表明之前的版本缺少安全检测,"../"字符串就能绕过解压路径将文件写到任意位置。

    2003/07/05 commit 前后对比

    在Revision 1.47官方 tag 中: ++(extractarchive): Use safername_suffix rather than rolling our own.++ 这就是漏洞初始出现的位置了。

    漏洞之处

    通过代码对比我们可以看到,更新的版本使用 safernamesuffix 函数来替代了开发者自己写的规则。

    4.补丁分析

    官方补丁地址 GNU tar修复了该漏洞,将安全检测机制重新替换回了 extract.c Revision 1.35的规则。

    补丁

    0x02 修复方案


    更新补丁

    http://git.savannah.gnu.org/cgit/tar.git/commit/?id=7340f67b9860ea0531c1450e5aa261c50f67165d

    0x03 参考


    https://www.seebug.org/vuldb/ssvid-92524

    https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=842339

    https://sintonen.fi/advisories/tar-extract-pathname-bypass.proper.txt

    https://sintonen.fi/advisories/tar-extract-pathname-bypass.patch

    https://www.gnu.org/software/tar/

    http://cvs.savannah.gnu.org/viewvc/tar/tar/src/extract.c?view=log&pathrev=release115_1#rev1.47

    作者:kk | Categories:安全研究技术分享 | Tags:
  • Joomla未授权创建特权用户漏洞(CVE-2016-8869)分析

    2016-10-28

    Author: p0wd3r (知道创宇404安全实验室) Date: 2016-10-26

    0x00 漏洞概述


    1.漏洞简介

    Joomla是一个自由开源的内容管理系统,近日研究者发现在其3.4.4到3.6.3的版本中存在两个漏洞:CVE-2016-8869CVE-2016-8870。我们在这里仅分析 CVE-2016-8869,利用该漏洞,攻击者可以在网站关闭注册的情况下注册特权用户。Joomla 官方已对此漏洞发布升级公告

    2.漏洞影响

    网站关闭注册的情况下仍可创建特权用户,默认状态下用户需要用邮件激活,但需要开启注册功能才能激活。

    3.影响版本

    3.4.4 to 3.6.3

    0x01 漏洞复现


    1. 环境搭建

    解压后放到服务器目录下,例如/var/www/html

    创建个数据库:

    2.漏洞分析

    注册

    注册部分可参考:《Joomla未授权创建用户漏洞(CVE-2016-8870)分析》

    提权

    下面我们来试着创建一个特权用户。

    在用于注册的register函数中,我们先看一下$model->register($data)这个存储注册信息的方法,在components/com_users/models/registration.php中:

    可以看到这里使用我们可控的$temp$data赋值,进而存储注册信息。正常情况下,$data在赋值之前是这样的:

    data-common

    而正常情况下我们可控的$temp中是没有groups这个数组的,所以正常注册用户的权限就是我们配置中设置的权限,对应的就是groups的值。

    那么提升权限的关键就在于更改groups中的值,因为$data由我们可控的$temp赋值,$temp的值来自于请求包,所以我们可以构造如下请求包:

    这里我们添加一组值:name="user[groups][]" value=7,让user被当作二维数组,从而groups被识别为数组,并设置数组第一个值为7,对应着Administrator的权限。

    然后发包,通过调试可以看到$temp中已经有了groups数组:

    temp

    最后创建了一个权限为Administrator的用户attacker2:

    exp

    通过存在漏洞的注册函数我们可以提权,那么在允许注册的情况下我们可不可以通过正常的注册函数来提权呢?

    通过对比这两个函数,可以发现这样一点:

    UsersControllerRegistration::register()

    UsersControllerUser::register()

    可以看到UsersControllerRegistration::register()中存储了对$requestData验证后的$data,而UsersControllerUser::register()虽然同样进行了验证,但是存储的仍是之前的$data。所以重点是validate函数是否对groups进行了过滤,我们跟进一下,在libraries/legacy/model/form.php中:

    再跟进filter函数,在libraries/joomla/form/form.php中:

    可以看到这里仅允许$fields中的值出现在$data中,而$fields中是不存在groups的,所以groups在这里被过滤掉,也就没有办法进行权限提升了。

    2016-10-27 更新

    默认情况下,新注册的用户需要通过注册邮箱激活后才能使用。并且:no-activation

    由于$data['activation']的值会被覆盖,所以我们也没有办法直接通过请求更改用户的激活状态。

    2016-11-01 更新

    感谢三好学生D的提示,可以使用邮箱激活的前提是网站开启了注册功能,否则不会成功激活。

    我们看激活时的代码,在components/com_users/controllers/registration.php中第28-99行的activate函数:

    这里可以看到仅当开启注册功能时才允许激活,否则返回403。

    3.补丁分析

    patch

    官方删除了UsersControllerUser::register()方法。

    0x02 修复方案


    升级到3.6.4

    0x03 参考


    https://www.seebug.org/vuldb/ssvid-92495

    https://developer.joomla.org/security-centre/659-20161001-core-account-creation.html

    http://www.fox.ra.it/technical-articles/how-i-found-a-joomla-vulnerability.html

    https://www.youtube.com/watch?v=Q_2M2oJp5l4

     

    作者:kk | Categories:技术分享 | Tags:
  • Joomla未授权创建用户漏洞(CVE-2016-8870) 分析

    2016-10-26

    Author: p0wd3r (知道创宇404安全实验室) Date: 2016-10-26

    0x00 漏洞概述


    1.漏洞简介

    Joomla是一个自由开源的内容管理系统,近日研究者发现在其3.4.4到3.6.3的版本中存在两个漏洞:CVE-2016-8869CVE-2016-8870。我们在这里仅分析CVE-2016-8870,利用该漏洞,攻击者可以在网站关闭注册的情况下注册用户。Joomla官方已对此漏洞发布升级公告

    2.漏洞影响

    网站关闭注册的情况下仍可创建用户,默认状态下用户需要用邮件激活,但需要开启注册功能才能激活。

    3.影响版本

    3.4.4 to 3.6.3

    0x01 漏洞复现


    1. 环境搭建

    解压后放到服务器目录下,例如/var/www/html

    创建个数据库:

    最后访问服务器路径进行安装即可。

    2.漏洞分析

    在存在漏洞的版本中我们可以看到一个有趣的现象,即存在两个用于用户注册的方法:

    • 位于components/com_users/controllers/registration.php中的UsersControllerRegistration::register()
    • 位于components/com_users/controllers/user.php中的UsersControllerUser::register()

    我们对比一下代码:

    UsersControllerRegistration::register():

    UsersControllerUser::register():

    可以看到相对于UsersControllerRegistration::register()UsersControllerUser::register()的实现中并没有这几行代码:

    这几行代码是检查是否允许注册,也就是说如果我们可以用UsersControllerUser::register()这个方法来进行注册就可以绕过这个检测。

    通过测试可知正常的注册使用的是UsersControllerRegistration::register(),请求包如下:

    虽然正常注册并没有使用UsersControllerUser::register(),但是并不代表我们不能使用。阅读代码可知,只要将请求包进行如下修改即可使用存在漏洞的函数进行注册:

    • registration.register -> user.register
    • jform[*] -> user[*]

    所以完整的复现流程如下:

    1. 首先在后台关闭注册功能,关闭后首页没有注册选项:
      no-register
    2. 然后通过访问index.php抓包获取cookie,通过看index.php源码获取token:
      get-cookie
      get-token
    3. 构造注册请求:
    4. 发包,成功注册:
      attack

    2016-10-27 更新

    默认情况下,新注册的用户需要通过注册邮箱激活后才能使用。并且:no-activation

    由于$data['activation']的值会被覆盖,所以我们也没有办法直接通过请求更改用户的激活状态。

    2016-11-01 更新

    感谢三好学生D的提示,可以使用邮箱激活的前提是网站开启了注册功能,否则不会成功激活。

    我们看激活时的代码,在components/com_users/controllers/registration.php中第28-99行的activate函数:

    这里可以看到仅当开启注册功能时才允许激活,否则返回403。

    3.补丁分析

    patch-1

    官方删除了UsersControllerUser::register()方法。

    0x02 修复方案


    升级到3.6.4

    0x03 参考


    https://www.seebug.org/vuldb/ssvid-92496

    https://developer.joomla.org/security-centre/659-20161001-core-account-creation.html

    http://www.fox.ra.it/technical-articles/how-i-found-a-joomla-vulnerability.html

    https://www.youtube.com/watch?v=Q_2M2oJp5l4

    作者:kk | Categories:安全研究技术分享 | Tags:
  • Spring Security OAuth RCE (CVE-2016-4977) 漏洞分析

    2016-10-18

    Author: p0wd3r (知道创宇404安全实验室) Date: 2016-10-17

    0x00 漏洞概述


    1.漏洞简介

    Spring Security OAuth 是为 Spring 框架提供安全认证支持的一个模块,在7月5日其维护者发布了这样一个升级公告,主要说明在用户使用Whitelabel views来处理错误时,攻击者在被授权的情况下可以通过构造恶意参数来远程执行命令。漏洞的发现者在10月13日公开了该漏洞的挖掘记录

    2.漏洞影响

    授权状态下远程命令执行

    3.影响版本

    2.0.0 to 2.0.9

    1.0.0 to 1.0.5

    0x01 漏洞复现


    1. 环境搭建


    2.漏洞分析

    首先我们查看src/resources/application.properties的内容来获取clientid和用户的密码:

    properties

    接着我们访问这个url:

    http://localhost:8080/oauth/authorize?responsetype=token&clientid=acme&redirect_uri=hellotom

    其中client_id就是我们前面获取到的,然后输入任意用户名,密码填上面的password

    点击登录后程序会返回这样一个页面:

    bug-raw

    可以看到由于hellotom对于redirect_uri来说是不合法的值,所以程序会将错误信息返回并且其中带着hellotom,那么这个不合法的值可不可以是一个表达式呢?我们再访问这个url:

    http://localhost:8080/oauth/authorize?responsetype=token&clientid=acme&redirect_uri=${2334-1}

    结果如下:

    bug-num

    可以看到表达式被执行,触发了漏洞。

    下面看代码,由于程序使用Whitelabel作为视图来返回错误页面,所以先看/spring-security-oauth/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/WhitelabelErrorEndpoint.java中第18-40行:


    这里定义了Whitelabel对错误的处理方法,可以看到程序通过oauthError.getSummary()来获取错误信息,我们再次访问这个 url 并开启动态调试:

    http://localhost:8080/oauth/authorize?response_type=token&client_id=acme&redirect_uri=${2334-1}

    error

    请求中的${2334-1}已经被带入了errorSummary中,然后errorSummary被装入model中,再用SpelView进行渲染。

    我们跟进SpelViewspring-security-oauth/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/SpelView.java中第21-54行:

    可以看到在render时通过helper${}中的值作为表达式,再用parser.parseExpression来执行,跟进一下replacePlaceholders这个函数,在/org/springframework/util/PropertyPlaceholderHelper.class第47-56行:

    这个函数是个递归,也就是说如果表达式的值中有${xxx}这样形式的字符串存在,就会再取xxx作为表达式来执行。

    我们看动态调试的结果:

    resolve-errosummary

    首先因为传入了${errorSummary},取errorSummary作为表达式来执行,继续执行程序:

    resolve-poc

    由于errorSummary中存在${2334-1},所以又取出了2334-1作为表达式来执行,从而触发了漏洞。所以从这里可以看出,漏洞的关键点在于这个对表达式的递归处理使我们可控的部分也会被当作表达式执行。

    3.补丁分析

    patch

    可以看到在第一次执行表达式之前程序将$替换成了由RandomValueStringGenerator().generate()生成的随机字符串,也就是${errorSummary} -> random{errorSummary},但是这个替换不是递归的,所以${2334-1}并没有变。

    然后创建了一个helper使程序取random{}中的内容作为表达式,这样就使得errorSummary被作为表达式执行了,而${2334-1}因为不符合random{}这个形式所以没有被当作表达式,从而也就没有办法被执行了。

    不过这个Patch有一个缺点:RandomValueStringGenerator生成的字符串虽然内容随机,但长度固定为6,所以存在暴力破解的可能性。

    0x02 修复方案

    作者:kk | Categories:技术分享 | Tags: