通过入侵官方 Cask 存储库在 Homebrew 中实现远程代码执行
在Homebrew/homebrew-cask存储库中,可以通过混淆 Homebrew 项目开发的自动拉取请求审查脚本中使用的库来合并恶意拉取请求。
通过滥用它,攻击者可以在使用 的用户机器上执行任意 Ruby 代码brew。
一天下午,在下一个约会1之前我还有一点时间,所以我决定在 HackerOne 上寻找一个有趣的程序。
因为我想在我使用的软件/服务中找到一个漏洞,所以我在电脑上四处张望,这个brew命令引起了我的注意。
然后,我记得我在 HackerOne 上看到了一个名为 Homebrew 的程序,所以我决定在其中找到漏洞。
为了选择目标,我查看了漏洞披露计划的政策页面。我注意到Homebrew/homebrew-*存储库在范围内。
由于我不擅长阅读复杂的 Ruby 代码,我决定在 中查找漏洞Homebrew/homebrew-
在Homebrew/homebrew-cask存储库中,可以通过混淆 Homebrew 项目开发的自动拉取请求审查脚本中使用的库来合并恶意拉取请求。
通过滥用它,攻击者可以在使用 的用户机器上执行任意 Ruby 代码brew。
一天下午,在下一个约会1之前我还有一点时间,所以我决定在 HackerOne 上寻找一个有趣的程序。
因为我想在我使用的软件/服务中找到一个漏洞,所以我在电脑上四处张望,这个brew命令引起了我的注意。
然后,我记得我在 HackerOne 上看到了一个名为 Homebrew 的程序,所以我决定在其中找到漏洞。
为了选择目标,我查看了漏洞披露计划的政策页面。我注意到Homebrew/homebrew-*存储库在范围内。
由于我不擅长阅读复杂的 Ruby 代码,我决定在 中查找漏洞Homebrew/homebrew-
Skywalking远程代码执行漏洞预警
Skywalking历史上存在两次sql注入漏洞,CVE-2020-9483、CVE-2020-13921。经过源码分析,发现两次sql注入漏洞修复并不完善,仍存在一处sql注入漏洞。(该请求无页面入口,需要根据graphql配置文件手动进行请求构造,或许这就是官方遗漏该注入点的原因)
Skywalking默认配置下使用的数据库为h2,且使用sa权限启动。
另一个LINK_SCHEMA函数可以指定并发起一次jdbc或者jndi请求,但由于目标环境中无tomcat和springboot依赖,所以在高版本jdk中也无法进行通用的jndi利用,需要在本地依赖中寻找reference链。
于是我们可以先利用任意文件写入函数在classpath中写一个恶意类,然后再使用LINK_SCHEMA函数来加载这个恶意类,实现远程代码执行。
Skywalking历史上存在两次sql注入漏洞,CVE-2020-9483、CVE-2020-13921。经过源码分析,发现两次sql注入漏洞修复并不完善,仍存在一处sql注入漏洞。(该请求无页面入口,需要根据graphql配置文件手动进行请求构造,或许这就是官方遗漏该注入点的原因)
Skywalking默认配置下使用的数据库为h2,且使用sa权限启动。
另一个LINK_SCHEMA函数可以指定并发起一次jdbc或者jndi请求,但由于目标环境中无tomcat和springboot依赖,所以在高版本jdk中也无法进行通用的jndi利用,需要在本地依赖中寻找reference链。
于是我们可以先利用任意文件写入函数在classpath中写一个恶意类,然后再使用LINK_SCHEMA函数来加载这个恶意类,实现远程代码执行。
UNION 注入
和其他类型的数据库一样,基本没什么差别
先通过 order by 查当前表的 column 数量,13 返回正常,14报错,说明 column 有 13 个。注入出数据库账号密码
和其他类型的数据库一样,基本没什么差别
先通过 order by 查当前表的 column 数量,13 返回正常,14报错,说明 column 有 13 个。注入出数据库账号密码
报错注入
主流的报错注入方式主要分为两类。
一类是 mysql、Oracle 这种使用自带函数使错误抛出的信息中携带参数值。另一类是 sqlserver 这种转型报错。
因为转型在这里并不适用,所以开始看一些函数的源码。
其中我关注到了 LOAD_FILE函数,发现它会把参数当做文件去读取,一旦遇到文件无法读取的情况就会把文件路径带入到报错信息中输出。
从源码中来看就是文件没读到,抛出了一个异常,在message中把这个路径给带了出来。
主流的报错注入方式主要分为两类。
一类是 mysql、Oracle 这种使用自带函数使错误抛出的信息中携带参数值。另一类是 sqlserver 这种转型报错。
因为转型在这里并不适用,所以开始看一些函数的源码。
其中我关注到了 LOAD_FILE函数,发现它会把参数当做文件去读取,一旦遇到文件无法读取的情况就会把文件路径带入到报错信息中输出。
从源码中来看就是文件没读到,抛出了一个异常,在message中把这个路径给带了出来。
CVE-2021-3156:Sudo 中的基于堆的缓冲区溢出(Baron Samedit)
技术细节
如果执行Sudo以“shell”模式运行命令(shell -c命令):
通过 -s 选项设置 Sudo 的 MODE_SHELL 标志;或者
通过 -i 选项设置 Sudo 的 MODE_SHELL 和 MODE_LOGIN_SHELL 标志;然后,在 Sudo 的 main() 的开头,parse_args() 重写 argv(第 609-617 行),通过连接所有命令行参数(第 587-595 行)并使用反斜杠转义所有元字符(第 590-591 行):之后,在 sudoers_policy_main() 中,set_cmnd() 将命令行参数连接到基于堆的缓冲区“user_args”(第 864-871 行)并取消转义元字符(第 866-867v 行),“以便 sudoers 匹配和记录目的”
技术细节
如果执行Sudo以“shell”模式运行命令(shell -c命令):
通过 -s 选项设置 Sudo 的 MODE_SHELL 标志;或者
通过 -i 选项设置 Sudo 的 MODE_SHELL 和 MODE_LOGIN_SHELL 标志;然后,在 Sudo 的 main() 的开头,parse_args() 重写 argv(第 609-617 行),通过连接所有命令行参数(第 587-595 行)并使用反斜杠转义所有元字符(第 590-591 行):之后,在 sudoers_policy_main() 中,set_cmnd() 将命令行参数连接到基于堆的缓冲区“user_args”(第 864-871 行)并取消转义元字符(第 866-867v 行),“以便 sudoers 匹配和记录目的”
Apache Shiro权限绕过漏洞分析(CVE-2020-17523)
Apache Shiro是一个Java安全框架,它可以用来执行身份验证、授权、密码和会话管理。
在Shiro与Spring进行组合应用时,两者在对URI的处理中存在差异,常常导致Shiro认证绕过问题。
CVE-2020-17523复现
非漏洞作者,所以只能半猜测地复现该问题。先上payload:
> curl -v "http://host/admin/%20"
> curl -v "http://host/admin/%20/"
复现配置
SpringBoot + Shiro认证
(部分内容取自https://github.com/xhycccc/Shiro-Vuln-Demo/blob/main/shiro_cve-2020-13933)
向Spring中注入Bean
@Bean
ShiroFilterFactoryBean shiroFilterFactoryBean(){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager());
Map<String, String> map = new LinkedHashMap<>();
map.put("/admin/*", "authc");
bean.setFilterChainDefinitionMap(map);
return bean;
}
构造接口
@GetMapping("/admin/{name}")
public String admin(@PathVariable String name) {
return "admin page";
}
可见,在未经过认证的情况下,可以访问到admin page.
Apache Shiro是一个Java安全框架,它可以用来执行身份验证、授权、密码和会话管理。
在Shiro与Spring进行组合应用时,两者在对URI的处理中存在差异,常常导致Shiro认证绕过问题。
CVE-2020-17523复现
非漏洞作者,所以只能半猜测地复现该问题。先上payload:
> curl -v "http://host/admin/%20"
> curl -v "http://host/admin/%20/"
复现配置
SpringBoot + Shiro认证
(部分内容取自https://github.com/xhycccc/Shiro-Vuln-Demo/blob/main/shiro_cve-2020-13933)
向Spring中注入Bean
@Bean
ShiroFilterFactoryBean shiroFilterFactoryBean(){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager());
Map<String, String> map = new LinkedHashMap<>();
map.put("/admin/*", "authc");
bean.setFilterChainDefinitionMap(map);
return bean;
}
构造接口
@GetMapping("/admin/{name}")
public String admin(@PathVariable String name) {
return "admin page";
}
可见,在未经过认证的情况下,可以访问到admin page.
一种无法被Dump的jar包加密保护解决方案
市面上常见的加密保护方案
由于Java字节码的抽象级别较高,使得它较容易被反编译,因此Java代码反编译要比其他开发语言更容易实现,并且反编译的代码经过优化后几乎可与源代码相媲美。为了避免出这种情况,保护软件知识产权的目的,就出现了各种各样的加密保护方式。
1.远程调用Java程序
最简单的方法就是让用户不能够拿到Jar程序,这种方法是最根本的方法,具体实现有多种方式。例如,开发人员可以将关键的jar放在服务器端,客户端通过访问服务器的相关接口来获得服务,而不是直接本地调用jar文件。这样黑客就没有办法反编译Class文件。目前,通过接口提供服务的标准和协议也越来越多,例如 HTTP、Web Service、RPC等。但是有很多应用都不适合这种保护方式,例如单机运行的程序或者须要很大网络流量的程序就无法远程调用Java程序。这种保护方式如图1所示。
自定义ClassLoader
为了防止Class文件被直接反编译,许多开发人员将一些关键算法的Class文件进行加密,在使用这些class被加载之前,程序首先需要对这些类进行解密,而后再将这些类装载到JVM当中。大致流程图2
这种方法首先需要对编写代码对类文件进行加密,然后自己编写类加载器解密加载,在往JAVA虚拟机导入class文件的同时进行class文件的解密,这种方式存在被内存dump的风险。
3.代码混淆
代码混淆是对Class文件进行重新组织和处理,使得处理后的代码与处理前代码完成相同的功能(语义)。但是反编译后得出的代码是非常难懂、晦涩的,因此反编译人员很难得出程序的真正语义。但是也只是增加了分析时间,被混淆的代码仍然可能被破解的风险。
4.转换成本地代码
将程序转换成本地代码也是一种防止反编译的有效方法。因为本地代码往往难以被反编译。开发人员可以选择将整个应用程序转换成本地代码,也可以选择关键模块转换。如果仅仅转换关键部分模块,Java程序在使用这些模块时,需要使用JNI技术进行调用。
当然,在使用这种技术保护Java程序的同时,也牺牲了Java的跨平台特性。对于不同的平台,我们需要维护不同版本的本地代码,这将加重软件支持和维护的工作。不过对于一些关键的模块,有时这种方案往往是必要的。
为了保证这些本地代码分析难度,我们可以通过对这些代码进行二进制混淆或部分VM,加大分析难度。我们本次也是使用这种方式。
市面上常见的加密保护方案
由于Java字节码的抽象级别较高,使得它较容易被反编译,因此Java代码反编译要比其他开发语言更容易实现,并且反编译的代码经过优化后几乎可与源代码相媲美。为了避免出这种情况,保护软件知识产权的目的,就出现了各种各样的加密保护方式。
1.远程调用Java程序
最简单的方法就是让用户不能够拿到Jar程序,这种方法是最根本的方法,具体实现有多种方式。例如,开发人员可以将关键的jar放在服务器端,客户端通过访问服务器的相关接口来获得服务,而不是直接本地调用jar文件。这样黑客就没有办法反编译Class文件。目前,通过接口提供服务的标准和协议也越来越多,例如 HTTP、Web Service、RPC等。但是有很多应用都不适合这种保护方式,例如单机运行的程序或者须要很大网络流量的程序就无法远程调用Java程序。这种保护方式如图1所示。
自定义ClassLoader
为了防止Class文件被直接反编译,许多开发人员将一些关键算法的Class文件进行加密,在使用这些class被加载之前,程序首先需要对这些类进行解密,而后再将这些类装载到JVM当中。大致流程图2
这种方法首先需要对编写代码对类文件进行加密,然后自己编写类加载器解密加载,在往JAVA虚拟机导入class文件的同时进行class文件的解密,这种方式存在被内存dump的风险。
3.代码混淆
代码混淆是对Class文件进行重新组织和处理,使得处理后的代码与处理前代码完成相同的功能(语义)。但是反编译后得出的代码是非常难懂、晦涩的,因此反编译人员很难得出程序的真正语义。但是也只是增加了分析时间,被混淆的代码仍然可能被破解的风险。
4.转换成本地代码
将程序转换成本地代码也是一种防止反编译的有效方法。因为本地代码往往难以被反编译。开发人员可以选择将整个应用程序转换成本地代码,也可以选择关键模块转换。如果仅仅转换关键部分模块,Java程序在使用这些模块时,需要使用JNI技术进行调用。
当然,在使用这种技术保护Java程序的同时,也牺牲了Java的跨平台特性。对于不同的平台,我们需要维护不同版本的本地代码,这将加重软件支持和维护的工作。不过对于一些关键的模块,有时这种方案往往是必要的。
为了保证这些本地代码分析难度,我们可以通过对这些代码进行二进制混淆或部分VM,加大分析难度。我们本次也是使用这种方式。
CVE-2021-21287: 容器与云的碰撞——一次对MinIO的测试
随着工作和生活中的一些环境逐渐往云端迁移,对象存储的需求也逐渐多了起来,MinIO就是一款支持部署在私有云的开源对象存储系统。MinIO完全兼容AWS S3的协议,也支持作为S3的网关,所以在全球被广泛使用,在Github上已有25k星星。
我平时会将一些数据部署在MinIO中,在CI、Dockerfile等地方进行使用。本周就遇到了一个环境,其中发现一个MinIO,其大概情况如下:
MinIO运行在一个小型Docker集群(swarm)中
MinIO开放默认的9000端口,外部可以访问,地址为http://192.168.227.131:9000,但是不知道账号密码
192.168.227.131这台主机是CentOS系统,默认防火墙开启,外部只能访问9000端口,dockerd监听在内网的2375端口(其实这也是一个swarm管理节点,swarm监听在2377端口)
本次测试目标就是窃取MinIO中的数据,或者直接拿下。
既然我们选择了从MinIO入手,那么先了解一下MinIO。其实我前面也说了,因为平时用到MinIO的时候很多,所以这一步可以省略了。其使用Go开发,提供HTTP接口,而且还提供了一个前端页面,名为“MinIO Browser”。当然,前端页面就是一个登陆接口,不知道口令无法登录。
那么从入口点(前端接口)开始对其进行代码审计吧。
在User-Agent满足正则.*Mozilla.*的情况下,我们即可访问MinIO的前端接口,前端接口是一个自己实现的JsonRPC
jwt常见的攻击方法主要有下面这几种:
将alg设置为None,告诉服务器不进行签名校验
如果alg为RSA,可以尝试修改为HS256,即告诉服务器使用公钥进行签名的校验
爆破签名密钥
随着工作和生活中的一些环境逐渐往云端迁移,对象存储的需求也逐渐多了起来,MinIO就是一款支持部署在私有云的开源对象存储系统。MinIO完全兼容AWS S3的协议,也支持作为S3的网关,所以在全球被广泛使用,在Github上已有25k星星。
我平时会将一些数据部署在MinIO中,在CI、Dockerfile等地方进行使用。本周就遇到了一个环境,其中发现一个MinIO,其大概情况如下:
MinIO运行在一个小型Docker集群(swarm)中
MinIO开放默认的9000端口,外部可以访问,地址为http://192.168.227.131:9000,但是不知道账号密码
192.168.227.131这台主机是CentOS系统,默认防火墙开启,外部只能访问9000端口,dockerd监听在内网的2375端口(其实这也是一个swarm管理节点,swarm监听在2377端口)
本次测试目标就是窃取MinIO中的数据,或者直接拿下。
既然我们选择了从MinIO入手,那么先了解一下MinIO。其实我前面也说了,因为平时用到MinIO的时候很多,所以这一步可以省略了。其使用Go开发,提供HTTP接口,而且还提供了一个前端页面,名为“MinIO Browser”。当然,前端页面就是一个登陆接口,不知道口令无法登录。
那么从入口点(前端接口)开始对其进行代码审计吧。
在User-Agent满足正则.*Mozilla.*的情况下,我们即可访问MinIO的前端接口,前端接口是一个自己实现的JsonRPC
jwt常见的攻击方法主要有下面这几种:
将alg设置为None,告诉服务器不进行签名校验
如果alg为RSA,可以尝试修改为HS256,即告诉服务器使用公钥进行签名的校验
爆破签名密钥