RSS Feed
更好更安全的互联网
  • CVE-2019-11229详细分析 –git config可控-RCE

    2019-07-24

    作者:LoRexxar'@知道创宇404实验室
    时间:2019年7月23日
    英文版本:https://paper.seebug.org/990/

    2019年4月15号,gitea曾爆出过一个漏洞,恰逢当时对这个漏洞比较好奇就着手去研究了一下,漏洞的描述是这样的:

    models/repo_mirror.go in Gitea before 1.7.6 and 1.8.x before 1.8-RC3 mishandles mirror repo URL settings, leading to remote code execution.

    在和朋友@hammer的一同研究下,成功控制了git config的内容,但是在从git config到RCE的过程遇到了困难,就暂时搁置了,在过了几个月之后,偶然得到@Lz1y和@x1nGuang两位大佬的启发,成功复现了这个漏洞,下面我们就来仔细研究下这个问题。

    分析补丁

    首先根据cve的信息,确定漏洞1.7.6和1.8.0-rc3上修复

    根据漏洞文件为repo_mirror.go这个信息锁定更新的commit,commit主要为 #6593和#6595

    根据patch可以大致锁定问题的关键点

    /models/repo_mirror.go

    当仓库为mirror仓库时,settings页面会显示关于mirror的配置

    patch中将原来的修改配置文件中的url选项修改为NewCommand。很容易理解,将写入文件更改为执行命令,这种修复方式一定是因为写入文件存在无法修复这个问题的窘境,那么这也就说明url这里可以通过传入%0d%0a来换行,导致修改config中的其他配置。

    控制 gitconfig

    跟随前面的逻辑,首先我们新建一个mirror仓库。

    抓包并修改mirror_address为相应的属性。

    可以传入各种配置,可以控制config文件的内容。

    比较有趣的是,如果你更新同步设置时,服务端还会格式化配置。

    进一步利用

    而重要的是如何从config文件可控到下一步利用。

    首先,git服务端只会保留.git里的内容,并不是完整的类似我们客户端使用的git仓库。所以很难引入外部文件。否则就可以通过设置hook目录来实现RCE,这种思路的关键点在于找到一个可控的文件写入或者文件上传。

    其次,另外一种思路就是寻找一个能够执行命令的配置,并寻找一个能够触发相关配置的远程配置。

    通过写文件配合 githook path RCE

    在git中,存在一个叫做Git Hook的东西,是用于在处理一些操作的时,相应的hook就会执行相应的脚本。

    在web界面,只有gitea的管理员才能管理git hook,所以对于普通用户来说,我们就不能直接通过编辑git hook来修改脚本。

    但我们却可以通过控制git config来修改hook存放的目录。

    当我们构造发送

    服务端的config文件变为

    这样我们只要能在服务端的任意位置能够写入文件或者创建文件,我们就可以设置hookspath到那里,并触发git hook来执行命令。

    在经过我们的仔细研究之后,我们发现,在漏洞存在的版本1.7.5版本以下,如果编辑服务端的文件,那么服务端的文件就会保存在gitea的运行目录下生成。

    而这个文件在不重启gitea的情况下不会清除,而这个repo_id可以从其他的api处挖掘到。

    具体详细利用链可以看

    值得注意的是,这种方式需要知道服务端运行的位置,虽然我们可以认为go的路径都是比较形似的,也有部分人会在当前编译目录下执行。但可以说这种方式还是不算靠谱。

    通过控制 git config 配置来 RCE

    在@x1nGuang大佬的帮助下,我重新审视了和git config相关的一些配置。

    gitProxy

    gitProxy是用来针对git协议需要fetch等操作时,需要执行的命令。是一个用来应对特殊场景的配置选项。一般是应用于,在git请求时,可能的需要使用代理应用的场景。

    这里我们设置服务端

    然后需要注意,同步的url必须为git开头

    但问题在于,由于gitProxy在git设计中,就是执行一个代理应用,所以无论输入什么,都会被当作一个应用执行,也就没办法带参数。

    这样一来,在实际的利用场景中就又受到了很大的局限,这里可以尝试用普通项目中的上传文件功能来上传一个bin,然后抓包获取文件路径,最后通过gitProxy来执行后门。

    但同样的是,这种思路仍旧受限于gitea的运行目录,不过比起之前的利用方式来说,1.8.0版本也可以利用这种方式来RCE。

    sshCommand

    在git的文档中,还有一个配置是sshCommand。

    这是一个在git中允许通过特殊的配置,使git fetch/git push 通过ssh来连接远端的系统。在@Lz1y大佬的博客中也提到了这种利用方式。

    我们设置sshCommand为指定的命令

    然后设置协议为ssh保存,并点击同步。

    而与gitProxy不同的是,这里可以跟参数

    写在最后

    这是一个很特别的关于git类平台的漏洞例子,由于我在研究git config利用方式的时候遭遇了很多困难,导致这篇文章断断续续的复现了很久。整个漏洞利用链和git的特性都有强依赖,还算是挺有趣的体验,有机会再仔细分析一下gitea、gogs和gitlab的代码,希望也能挖一个有趣的洞...


    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/989/

    作者:吴烦恼 | Categories:安全研究 | Tags:
  • Redis 基于主从复制的 RCE 利用方式

    2019-07-15

    作者:LoRexxar'@知道创宇404实验室
    时间:2019年7月9日

    在2019年7月7日结束的WCTF2019 Final上,LC/BC的成员Pavel Toporkov在分享会上介绍了一种关于redis新版本的RCE利用方式,比起以前的利用方式来说,这种利用方式更为通用,危害也更大,下面就让我们从以前的redis RCE利用方式出发,一起聊聊关于redis的利用问题。

    https://2018.zeronights.ru/wp-content/uploads/materials/15-redis-post-exploitation.pdf

    通过写入文件 GetShell

    未授权的redis会导致GetShell,可以说已经是众所周知的了。

    而这种方式是通过写文件来完成GetShell的,这种方式的主要问题在于,redis保存的数据并不是简单的json或者是csv,所以写入的文件都会有大量的无用数据,形似

    这种主要利用了crontab、ssh key、webshell这样的文件都有一定容错性,再加上crontab和ssh服务可以说是服务器的标准的服务,所以在以前,这种通过写入文件的getshell方式基本就可以说是很通杀了。

    但随着现代的服务部署方式的不断发展,组件化成了不可逃避的大趋势,docker就是这股风潮下的产物之一,而在这种部署模式下,一个单一的容器中不会有除redis以外的任何服务存在,包括ssh和crontab,再加上权限的严格控制,只靠写文件就很难再getshell了,在这种情况下,我们就需要其他的利用手段了。

    通过主从复制 GetShell

    在介绍这种利用方式之前,首先我们需要介绍一下什么是主从复制和redis的模块。

    Redis主从复制

    Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。但如果当把数据存储在单个Redis的实例中,当读写体量比较大的时候,服务端就很难承受。为了应对这种情况,Redis就提供了主从模式,主从模式就是指使用一个redis实例作为主机,其他实例都作为备份机,其中主机和从机数据相同,而从机只负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式。

    这里我们开两台docker来做测试

    然后通过slaveof可以设置主从状态

    这样一来数据就会自动同步了

    Redis模块

    在了解了主从同步之后,我们还需要对redis的模块有所了解。

    在Reids 4.x之后,Redis新增了模块功能,通过外部拓展,可以实现在redis中实现一个新的Redis命令,通过写c语言并编译出.so文件。

    编写恶意so文件的代码

    https://github.com/RicterZ/RedisModules-ExecuteCommand

    利用原理

    Pavel Toporkov在2018年的zeronights会议上,分享了关于这个漏洞的详细原理。

    https://2018.zeronights.ru/wp-content/uploads/materials/15-redis-post-exploitation.pdf

    在ppt中提到,在两个Redis实例设置主从模式的时候,Redis的主机实例可以通过FULLRESYNC同步文件到从机上。

    然后在从机上加载so文件,我们就可以执行拓展的新命令了。

    复现过程

    这里我们选择使用模拟的恶意服务端来作为主机,并模拟fullresync请求。

    https://github.com/LoRexxar/redis-rogue-server

    然后启用redis 5.0的docker

    为了能够更清晰的看到效果,这里我们把从服务端执行完成后删除的部分暂时注释掉。

    然后直接通过脚本来攻击服务端

    然后我们链接上去就可以执行命令

     

    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/975/

    作者:吴烦恼 | Categories:安全研究技术分享 | Tags:
  • Linux 内核 TCP MSS 机制详细分析

    2019-07-15

    作者:Hcamael@知道创宇 404 实验室
    时间:2019 年 6 月 26 日
    英文版本:https://paper.seebug.org/967/

    前言

    上周Linux内核修复了4个CVE漏洞[1],其中的CVE-2019-11477感觉是一个很厉害的Dos漏洞,不过因为有其他事打断,所以进展的速度比较慢,这期间网上已经有相关的分析文章了。[2][3]

    而我在尝试复现CVE-2019-11477漏洞的过程中,在第一步设置MSS的问题上就遇到问题了,无法达到预期效果,但是目前公开的分析文章却没对该部分内容进行详细分析。所以本文将通过Linux内核源码对TCP的MSS机制进行详细分析。

    测试环境

    1. 存在漏洞的靶机

    操作系统版本:Ubuntu 18.04

    内核版本:4.15.0-20-generic

    地址:192.168.11.112

    内核源码:

    带符号的内核:

    关闭内核地址随机化(KALSR):

    装一个nginx,供测试:

    2. 宿主机

    操作系统:MacOS

    Wireshark:抓流量

    虚拟机:VMware Fusion 11

    调试Linux虚拟机:

    编译gdb:

    gdb进行远程调试:

    3. 攻击机器

    自己日常使用的Linux设备就好了

    地址:192.168.11.111

    日常习惯使用Python的,需要装个scapy构造自定义TCP包

    自定义SYN的MSS选项

    有三种方法可以设置TCP SYN包的MSS值

    1. iptable

    2. route

    3. 直接发包设置

    PS:使用scapy发送自定义TCP包需要ROOT权限

     

    flags选项S表示SYN,A表示ACK,SA表示SYN, ACK

    scapy中TCP可设置选项表:

    但是这个会有一个问题,在使用Python发送了一个SYN包以后,内核会自动带上一个RST包,查过资料后,发现在新版系统中,对于用户发送的未完成的TCP握手包,内核会发送RST包终止该连接,应该是为了防止进行SYN Floor攻击。解决办法是使用iptable过滤RST包:

    对于MSS的深入研究

    关于该漏洞的细节,别的文章中已经分析过了,这里简单的提一下,该漏洞为uint16溢出:

    所以在mss_now小于等于8时,才能发生整型溢出。

    深入研究的原因是因为进行了如下的测试:

    攻击机器通过iptables/iproute命令将MSS值为48后,使用curl请求靶机的http服务,然后使用wireshark抓流量,发现服务器返回的http数据包的确被分割成小块,但是只小到36,离预想的8有很大的差距

    这个时候我选择通过审计源码和调试来深入研究为啥MSS无法达到我的预期值,SYN包中设置的MSS值到代码中的mss_now的过程中发生了啥?

    随机进行源码审计,对发生溢出的函数tcp_set_skb_tso_segs进行回溯:

    随后对tcp_current_mss函数进行分析,关键代码如下:

     

    看完这部分源码后,我们对MSS的含义就有一个深刻的理解,首先说一说TCP协议:

    TCP协议包括了协议头和数据,协议头包括了固定长度的20字节和40字节的可选参数,也就是说TCP头部的最大长度为60字节,最小长度为20字节。

    __tcp_mtu_to_mss函数中的mss_now为我们SYN包中设置的MSS,从这里我们能看出MSS最小值是48,通过对TCP协议的理解和对代码的理解,可以知道SYN包中MSS的最小值48字节表示的是:TCP头可选参数最大长度40字节 + 数据最小长度8字节。

    但是在代码中的mss_now表示的是数据的长度,接下来我们再看该值的计算公式。

    tcphdr结构:

     

     

    该结构体为TCP头固定结构的结构体,大小为20bytes

    变量tcp_sk(sk)->tcp_header_len表示的是本机发出的TCP包头部的长度。

    因此我们得到的计算mss_now的公式为:SYN包设置的MSS值 - (本机发出的TCP包头部长度 - TCP头部固定的20字节长度)

    所以,如果tcp_header_len的值能达到最大值60,那么mss_now就能被设置为8。那么内核代码中,有办法让tcp_header_len达到最大值长度吗?随后我们回溯该变量:

     

     

    所以在Linux 4.15内核中,在用户不干预的情况下,内核是不会发出头部大小为60字节的TCP包。这就导致了MSS无法被设置为最小值8,最终导致该漏洞无法利用。

    总结

    我们来总结一下整个流程:

    1. 攻击者构造SYN包,自定义TCP头部可选参数MSS的值为48
    2. 靶机(受到攻击的机器)接收到SYN请求后,把SYN包中的数据保存在内存中,返回SYN,ACK包。
    3. 攻击者返回ACK包

    三次握手完成

    随后根据不同的服务,靶机主动向攻击者发送数据或者接收到攻击者的请求后向攻击者发送数据,这里就假设是一个nginx http服务。

    1. 攻击者向靶机发送请求:GET / HTTP/1.1

    2. 靶机接收到请求后,首先计算出tcp_header_len,默认等于20字节,在内核配置sysctl_tcp_timestamps开启的情况下,增加12字节,如果编译内核的时候选择了CONFIG_TCP_MD5SIG,会再增加18字节,也就是说tcp_header_len的最大长度为50字节。

    3. 随后需要计算出mss_now = 48 - 50 + 20 = 18

    这里假设一下该漏洞可能利用成功的场景:有一个TCP服务,自己设定了TCP可选参数,并且设置满了40字节,那么攻击者才有可能通过构造SYN包中的MSS值来对该服务进行Dos攻击。

    随后我对Linux 2.6.29至今的内核进行审计,mss_now的计算公式都一样,tcp_header_len长度也只会加上时间戳的12字节和md5值的18字节。

    ----- 2019/07/03 UPDATE -----

    经过@riatre大佬的指正,我发现上述我对tcp_current_mss函数的分析中漏了一段重要的代码:

     

    tcp_established_options函数的代码中,除了12字节的时间戳,20字节的md5,还有对SACK长度的计算,在长度不超过tcp可选项40字节限制的前提下,公式为:size = 4 + 8 * opts->num_sack_blocks

     

    所以凑齐40字节的方法是:12字节的时间戳 + 8 * 3(opts->num_sack_blocks)

    变量opts->num_sack_blocks表示从对端接受的数据包中丢失的数据包数目

    所以在这里修改一下总结中后三步的过程:

    1. 攻击者向靶机发送一段正常的HTTP请求
    2. 靶机接收到请求后,会发送HTTP响应包,如上面的wireshark截图所示,响应包会按照36字节的长度分割成多分
    3. 攻击者构造序列号带有缺漏的ACK包(ACK包需要带一些数据)
    4. 服务器接收到无序的ACK包后,发现产生了丢包的情况,所以在后续发送的数据包中,都会带上SACK选项,告诉客户端,那些数据包丢失,直到TCP链接断开或者接收到响应序列的数据包。

    效果如下图所示:

    因为算上时间戳,TCP SACK选项里最多只能包含3段序列编号,所以只要发送4次ACK包,就能把MSS设置为8。

    部分scapy代码如下:

    因为现在已经能满足mss_now=8的前提,后续将会对该漏洞进行进一步的分析。

    参考

    1. https://github.com/Netflix/security-bulletins/blob/master/advisories/third-party/2019-001.md
    2. https://paper.seebug.org/959/
    3. https://paper.seebug.org/960/

     

    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/966/

    作者:吴烦恼 | Categories:安全研究 | Tags:
  • Vim/Neovim 基于 modeline 的多个任意代码执行漏洞分析(CVE-2002-1377、CVE-2016-1248、CVE-2019-12735)

    2019-06-27

    作者:fenix@知道创宇 404 实验室
    日期:2019 年 6 月 11 日
    英文版本:https://paper.seebug.org/956/

    前言

    Vim 是从 vi 发展出来的一个文本编辑器。代码补全、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用,和 Emacs 并列成为类 Unix 系统用戶最喜欢的文本编辑器。Neovim 是一个基于 vim 源代码的重构项目。

    2019 年 06 月 04 日,Vim & neovim 被曝出任意代码执行漏洞。攻击者通过诱使受害者使用 vim 或者 neovim 打开一个精心制作的文件,可以在目标机器上执行任意命令。

    该漏洞是由于启用了 modeline 模式导致的,Vim & neovim 历史上也多次曝出和 modeline 相关的漏洞。

    原作者已经分析的很清楚了,本文权当总结一下,顺便对历史曝出的多个漏洞做一次完整的分析。(在 vim 环境下,neovim 类似)

    modeline 详解

    既然都是和 modeline 相关的漏洞,那就有必要知道 modeline 是什么。

    vim 一共有 4 种模式:正常模式、插入模式、命令模式、可视模式。

    在正常模式中,按下 : 键,就可以进入命令模式。在命令模式中可以执行一些输入并执行一些 vim 或插件提供的指令,就像在 shell 里一样。这些指令包括设置环境、文件操作、调用某个功能、执行命令等等。例如设置不显示行号:

    如果有很多偏好设置,每次打开文件都手动设置就会显得很繁琐,这时候 .vimrc 就派上用场了,在启动 vim 时,当前用户根目录下的 .vimrc 文件会被自动加载。

    .vimrc 中的设置会对打开的所有文件生效,不便于对单个文件作个性化设置,modeline 应运而生。

    vim 的 modeline 可以让你针对每个文件进行文件级别的设置,这些设置是覆盖当前用户的 .vimrc 中的设置的。vim 默认关闭了 modeline,在 .vimrc 末尾追加 set modeline 即可打开。

    如果 modeline 打开,vim 在打开文件时会解析文件开头及末尾符合一定格式的设置行。

    格式一:

    格式二:

    为了安全考虑,在 modeline 的设置中只支持 set 命令。

    特殊的,foldexpr,formatexpr,includeexpr,indentexpr,statusline,foldtext 等选项的值可以是一个表达式,如果选项是在 modeline 中设置,表达式在沙箱中执行。沙箱实质上就是对表达式所能实现的功能做了限制,如在沙箱中不能执行 shell 命令、不能读写文件、不能修改缓冲区等等,如下:

    vim 对于沙箱的实现也很简单。

    沙箱检查函数 check_secure():

    在 libcall、luaeval 等危险指令的开头进行沙箱检查,如果发现在沙箱中调用,直接 return 掉。

    历史曝出的几个 rce 漏洞中,CVE-2002-1377 和 CVE-2019-12735 都是由于存在部分指令没有检查沙箱,导致在 modeline 模式中被滥用从而任意命令执行。下面将一一分析。

    CVE-2002-1377

    2002 年曝出的 vim 任意代码执行漏洞,影响 6.0、6.1 版本。太过古老,环境难以重现,简单说下原理。PoC 如下:

    利用 libcall 指令调用 libc 库中的 system 函数实现任意命令执行。

    现在添加了沙箱检查,modeline 下已经用不了 libcall 了:

    CVE-2016-1248

    8.0.0056 之前的 vim 未正确验证 filetype、syntax 、keymap 选项的值,受害者在 modeline 开启下打开特制的文件,则可能导致执行任意代码。

    从 github 克隆代码,checkout 到 v8.0.0055 分支,编译安装。.vimrc 的配置如下:

    验证 PoC :

    set verbose=20开启所有日志,看下调用链:

    autocommand 即“自动命令”,在发生某些事件时自动执行,类似于钩子函数。

    比如我们在命令模式中输入 :set syntax=python, vim 就会在相应目录中寻找和 python syntax 相关的 vmscript 并加载。

    如果我们在 modeline 中设置了 filetype 或者 syntax,会执行 au! FileType * exe "set syntax=" . expand("<amatch>") 自动完成上述过程。首先删除所有和 FileType 相关联的自动命令,然后调用 exe (即 execute) 执行 set syntax=filetype。execute 用于执行一个表达式字符串,由于未对 filetype 过滤,造成了命令注入。

    相关代码在 /usr/local/share/vim/vim80/syntax/syntax.vim:

    patch 8.0.0056 增加了对名称的校验。

    CVE-2019-12735

    最近刚曝出来,影响 Vim < 8.1.1365,Neovim < 0.3.6。和 CVE-2002-1377 原理类似,找到了一个新的绕过沙箱执行命令的点。source 指定的定义如下:

    :so! filepath 可以从一个文件加载 vim 命令。

    构造 PoC,将待执行的命令放在 text 部分,so! % 加载当前文件。

    [text]{white}{vi:|vim:|ex:}[white]{options}

    补丁对 source 指令添加了沙箱检查。

    总结

    Windows 记事本都任意代码执行了,Vim 怎么能被比下去 … 漏洞无处不在,谨慎打开任何来历不明文件。

    参考链接

    https://github.com/numirias/security/blob/master/doc/2019-06-04_ace-vim-neovim.md

    https://github.com/vim/vim/commit/d0b5138ba4bccff8a744c99836041ef6322ed39a


    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/952/

    作者:吴烦恼 | Categories:安全研究 | Tags:
  • Mybb 18.20 From Stored XSS to RCE 分析

    2019-06-27

    作者:LoRexxar'@知道创宇404实验室
    日期:2019年6月12日
    英文版本:https://paper.seebug.org/954/

    2019年6月11日,RIPS团队在团队博客中分享了一篇MyBB <= 1.8.20: From Stored XSS to RCE,文章中主要提到了一个Mybb18.20中存在的存储型xss以及一个后台的文件上传绕过。

    其实漏洞本身来说,毕竟是需要通过XSS来触发的,哪怕是储存型XSS可以通过私信等方式隐藏,但漏洞的影响再怎么严重也有限,但漏洞点却意外的精巧,下面就让我们一起来详细聊聊看...

    漏洞要求

    储存型xss

    • 拥有可以发布信息的账号权限
    • 服务端开启视频解析
    • <=18.20

    管理员后台文件创建漏洞

    • 拥有后台管理员权限(换言之就是需要有管理员权限的账号触发xss)
    • <=18.20

    漏洞分析

    在原文的描述中,把多个漏洞构建成一个利用链来解释,但从漏洞分析的角度来看,我们没必要这么强行,我们分别聊聊这两个单独的漏洞:储存型xss、后台任意文件创建。

    储存型xss

    在Mybb乃至大部分的论坛类CMS中,一般无论是文章还是评论又或是的什么东西,都会需要在内容中插入图片、链接、视频等等等,而其中大部分都是选择使用一套所谓的“伪”标签的解析方式。

    也就是说用户们通过在内容中加入[url][img]等“伪”标签,后台就会在保存文章或者解析文章的时候,把这类“伪”标签转化为相应的<a><img>,然后输出到文章内容中,而这种方式会以事先规定好的方式解析和处理内容以及标签,也就是所谓的白名单防御,而这种语法被称之为bbcode

    这样一来攻击者就很难构造储存型xss了,因为除了这些标签以外,其他的标签都不会被解析(所有的左右尖括号以及双引号都会被转义)。

    正所谓,有人的地方就会有漏洞。

    在这看似很绝对的防御方式下,我们不如重新梳理下Mybb中的处理过程。

    /inc/class_parse.php line 435 的 parse_mycode函数中就是主要负责处理这个问题的地方。

    当服务端接收到你发送的内容时,首先会处理解析[ img ]相关的标签语法,然后如果开启了$this->options['allow_videocode'](默认开启),那么开始解析[ video ]相关的语法,然后是[list]标签。在488行开始,会对[url]等标签做相应的处理。

    我们把上面的流程简单的具象化,假设我们在内容中输入了

    后台会首先处理[ video ],然后内容就变成了

    然后会处理[url]标签,最后内容变成

    乍一看好像没什么问题,每个标签内容都会被拼接到标签相应的属性内,还会被htmlspecialchars_uni处理,也没办法逃逸双引号的包裹。

    但假如我们输入这样的内容呢?

    首先跟入到函数/inc/class_parse.php line 1385行 mycode_parse_video

    链接经过parse_url处理被分解为

    然后在1420行,各个参数会被做相应的处理,由于我们必须保留=号以及/ 号,所以这里我们选择把内容放在fragment中。

    在1501行case youtube中,被拼接到id上

    最后id会经过一次htmlspecialchars_uni,然后生成模板。

    当然这并不影响到我们上面的内容。

    到此为止我们的内容变成了

    紧接着再经过对[url]的处理,上面的内容变为

    我们再把前面的内容简化看看,链接由

    变成了

    由于我们插入在iframe标签中的href被转变成了<a href="http://onload=alert();//">, 由于双引号没有转义,所以iframe的href在a标签的href中被闭合,而原本的a标签中的href内容被直接暴露在了标签中,onload就变成了有效的属性!

    最后浏览器会做简单的解析分割处理,最后生成了相应的标签,当url中的链接加载完毕,标签的动作属性就可以被触发了。

    管理员后台文件创建漏洞

    在Mybb的管理员后台中,管理员可以自定义论坛的模板和主题,除了普通的导入主题以外,他们允许管理员直接创建新的css文件,当然,服务端限制了管理员的这种行为,它要求管理员只能创建文件结尾为.css的文件。

    看上去好像并没有什么办法绕过,但值得注意的是,代码中先将文件名先写入了数据库中。

    紧接着我们看看数据库结构

    我们可以很明显的看到name的类型为varchar且长度只有30位。

    如果我们在上传的xml文件中构造name为tttttttttttttttttttttttttt.php.css时,name在存入数据库时会被截断,并只保留前30位,也就是tttttttttttttttttttttttttt.php.

    紧接着我们需要寻找一个获取name并创建文件的地方。

    在/admin/modules/style/themes.php 的1252行,这个变量被从数据库中提取出来。

    theme_stylesheet 的name作为字典的键被写入相关的数据。

    $mybb->input['do'] == "save_orders"时,当前主题会被修改。

    在保存了当前主题之后,后台会检查每个文件是否存在,如果不存在,则会获取name并写入相应的内容。

    可以看到我们成功的写入了php文件

    完成的漏洞复现过程

    储存型xss

    找到任意一个发送信息的地方,如发表文章、发送私信等....

    发送下面这些信息

    然后阅读就可以触发

    管理员后台文件创建漏洞

    找到后台加载theme的地方

    构造上传文件test.xml

    需要注意要勾选 Ignore Version Compatibility。

    然后查看Theme列表,找到新添加的theme

    然后保存并访问相应tid地址的文件即可

    补丁

    储存型xss

    这里的iframe标签的链接被encode_url重新处理,一旦被转义,那么[url]就不会被继续解析,则不会存在问题。

    管理员后台文件创建漏洞

    在判断文件名后缀之前,加入了字符数的截断,这样一来就无法通过数据库字符截断来构造特殊的name了。

    写在最后

    整个漏洞其实说到实际利用来说,其实不算太苛刻,基本上来说只要能注册这个论坛的账号就可以构造xss,由于是储存型xss,所以无论是发送私信还是广而告之都有很大的概率被管理员点击,当管理员触发之后,之后的js构造exp就只是代码复杂度的问题了。

    抛开实际的利用不谈,这个漏洞的普适性才更加的特殊,bbcode是现在主流的论坛复杂环境的解决方案,事实上,可能会有不少cms会忽略和mybb一样的问题,毕竟人才是最大的安全问题,当人自以为是理解了机器的一切想法时,就会理所当然得忽略那些还没被发掘的问题,安全问题,也就在这种情况下悄然诞生了...

     

    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/949/

     

    作者:吴烦恼 | Categories:安全研究 | Tags:
  • MIMIC Defense CTF 2019 final writeup

    2019-05-31

    作者:LoRexxar'@知道创宇404实验室

    上周有幸去南京参加了强网杯拟态挑战赛,运气比较好拿了第二名,只是可惜是最后8分钟被爆了,差一点儿真是有点儿可惜。

    有关于拟态的观念我会在后面讲防火墙黑盒攻击的 writeup 时再详细写,抛开拟态不谈,赛宁这次引入的比赛模式我觉得还蛮有趣的,白盒排位赛的排名决定你是不是能挑战白盒拟态,这样的多线并行挑战考验的除了你的实际水平,也给比赛本身平添了一些有趣的色彩(虽然我们是被这个设定坑了),虽然我还没想到这种模式如何应用在普通的ctf赛场上,但起码也是一个有趣的思路不是吗。

    Web 白盒

    sqlcms

    这题其实相对比赛中的其他题目来说,就显得有些太简单了,当时如果不是因为我们是第一轮挑战白盒的队伍,浪费了 30 分钟时间,否则抢个前三血应该是没啥问题。

    简单测试就发现,过滤了以下符号

    此外还有一些字符串的过滤

    还有一些躺枪的(因为有or)

    总结起来就是,未知表名、不能使用逗号、不能截断的时间盲注。其实实际技巧没什么新意,已经是玩剩下的东西了,具体直接看 exp 吧

    ezweb

    这题觉得非常有意思,我喜欢这个出题思路,下面我们来一起整理下整个题目的思路。

    首先是打开页面就是简单粗暴的登录,用户名只把.换成了_,然后就直接存入了 session 中。

    当我们在用户名中插入/的时候,我们就会发现爆了无法打开文件的错误,/被识别为路径分割,然后 sqlite 又没有太高的权限去创建文件夹,所以就报错了,于是我们就得到了。

    如果用户名被直接拼接到了数据库名字中,将.转化为_

    直接访问相应的路径,就可以下载到自己的 db 文件,直接本地打开就可以看到其中的数据。

    image.png-103.9kB

    数据库里很明显由 filename 做主键,后面的数据是序列化之后的字符串,主要有两个点,一个是 file_type ,这代表文件上传之后,服务端会检查文件的类型,然后做相应的操作,其次还会保存相应的文件路径。

    抛开这边的数据库以后,我们再从黑盒这边继续分析。

    当你上传文件的时候,文件名是 md5(全文件名)+最后一个.后的后缀拼接。

    对于后缀的检查,如果点后为 ph 跟任何字符都会转为 mimic 。

    多传几次可以发现,后端的 file_type 是由前端上传时设置的 content-type 决定的,但后端类型只有4种,其中 text 会直接展现文件内容, image 会把文件路径传入 img 标签展示出来,zip 会展示压缩包里的内容,other 只会展示文件信息。

    其中最特别的就是 zip ,简单测试可以发现,不但会展示 zip 的内容,还会在uploads/{md5(filename)}中解压 zip 中的内容。

    测试发现,服务端限制了软连接,但是却允许跨目录,我们可以在压缩包中加入../../a,这个文件就会被解压到根目录,但可惜文件后缀仍然收到之前对 ph 的过滤,我们没办法写入任何 php 文件。

    按照常规思路来说,我们一般会选择上传.htaccess.user.ini,但很神奇的是,.htaccess因为 apache 有设置无法访问,不知道是不是写进去了。.user.ini成功写入了。但是两种方式都没生效。

    于是只能思考别的利用方式,这时候我们会想到数据被储存在sqlite中。

    如果我们可以把 sqlite 文件中数据修改,然后将文件上传到服务端,我们不就能实现任意文件读取吗。

    image.png-142.6kB

    这里我直接读了 flag ,正常操作应该是要先读代码,然后反序列化 getshell

    最后拿到 flag

    拟态防火墙

    两次参加拟态比赛,再加上简单了解过拟态的原理,我大概可以还原目前拟态防御的原理,也逐渐佐证拟态防御的缺陷。

    下面是我在攻击拟态防火墙时,探测到的后端结构,大概是这样的(不保证完全准确):

    image.png-33.8kB

    其中 Web 服务的执行体中,有 3 种服务端,分别为 nginx、apache 和 lighttpd 这3 种。

    Web 的执行体非常简陋,其形态更像是负载均衡的感觉,不知道是不是裁决机中规则没设置还是 Web 的裁决本身就有问题。

    而防火墙的执行体就更诡异了,据现场反馈说,防火墙的执行体是开了2个,因为反馈不一致,所以返回到裁决机的时候会导致互判错误...这种反馈尤其让我疑惑,这里的问题我在下面实际的漏洞中继续解释。

    配合防火墙的漏洞,我们下面逐渐佐证和分析拟态的缺点。

    我首先把攻击的过程分为两个部分,1是拿到 Web 服务执行体的 webshell,2是触发修改访问控制权限(比赛中攻击得分的要求)。

    GetShell

    首先我不得不说真的是运气站在了我这头,第一界强网杯拟态挑战赛举办的时候我也参加了比赛,当时的比赛规则没这么复杂,其中有两道拟态 Web 题目,其中一道没被攻破的就是今年的原题,拟态防火墙,使用的也是天融信的 Web 管理界面。

    一年前虽然没日下来,但是幸运的是,一年前和一年后的攻击得分目标不一致,再加上去年赛后我本身也研究过,导致今年看到这个题的时候,开局我就走在了前面。具体可以看下面这篇 wp 。

    https://mp.weixin.qq.com/s/cfEqcb8YX8EuidFlqgSHqg

    由于去年我研究的时候已经是赛后了,所以我并没有实际测试过,时至今日,我也不能肯定今年和去年是不是同一份代码。不过这不影响我们可以简单了解架构。

    https://github.com/YSheldon/ThinkPHP3.0.2_NGTP

    然后仔细阅读代码,代码结构为 Thinkphp3.2 架构,其中部分代码和远端不一致,所以只能尝试攻击。

    在3.2中,Thinkphp 有一些危险函数操作,比如 display,display 可以直接将文件include 进来,如果函数参数可控,我们又能上传文件,那么我们就可以 getshell。

    全局审计代码之后我们发现在/application/home/Controller/CommonControler.class.php

    image.png-289.1kB

    如果我们能让 type 返回为 html ,就可以控制 display 函数。

    搜索 type 可得$this->getAcceptType();

    只要将请求头中的 accept 设置好就可以了。

    然后我们需要找一个文件上传,在UserController.class.php moduleImport函数里

    这里的上传只能传到/tmp目录下,而且不可以跨目录,所以我们直接传文件上去。

    紧接着然后使用之前的文件包含直接包含该文件

    上传文件的时候要注意 seesion 和 token ,token 可以从首页登陆页面获得。

    至此我们成功获得了 webshell 。这里拿到 webshell 之后就会进入一段神奇的发现。

    首先,服务端除了/usr以外没有任何的目录,其中/usr/中除了3个服务端,也没有任何多余的东西。换言之就是没有/bin,也就是说并没有一个linux的基本环境,这里我把他理解为执行体,在他的外层还有别的代码来联通别的执行体。

    由于没有/bin,导致服务端不能执行system函数,这大大影响了我的攻击效率,这可能也是我被反超的一个原因...

    继续使用php eval shell,我们发现后端3个执行体分别为nginx\apache\lighthttpd,实际上来说都是在同一个文件夹下

    由于 Web 的服务器可以随便攻击,有趣的是,在未知情况下,服务端会被重置,但神奇的是,一次一般只会重置3个服务端的一部分,这里也没有拟态裁决的判定,只要单纯的刷新就可以进入不同的后端,其感觉就好像是负载均衡一样。

    这样我不禁怀疑起服务端的完成方式,大概像裁决机是被设定拼接在某个部分之前的,其裁决的内容也有所设定,到这里我们暂时把服务端架构更换。

    image.png-31.9kB

    阅读服务端代码

    在拿到 shell 之后,主办方强调 Web 服务和题目无关,需要修改后端的访问控制权限,由于本地的代码和远程差异太大,所以首先要拿到远端的代码。

    /conf/menu.php中可以获得相应功能的路由表。

    其中设置防火墙访问控制权限的路由为?c=Policy/Interview&a=control_show',

    然后直接读远端的代码/Controller/Policy/interviewController.class.php

    其操作相关为

    直接访问这个路由发现权限不够,跟入getPrivilege

    一直跟到 checklogin

    发现对 cookie 中的 loginkey 操作直接对比了 auth_id ,id 值直接盲猜为1,于是绕过权限控制

    添加相应的 cookie ,就可以直接操作访问控制页面的所有操作,但是后端有拟态防御,所以访问 500.

    至此,我无意中触发了拟态扰动...这完全是在我心理预期之外的触发,在我的理解中,我以为是我的参数配置错误,或者是这个 api 还需要添加策略组,然后再修改。由于我无法肯定问题出在了哪,所以我一直试图想要看到这个策略修改页面,并正在为之努力。(我认为我应该是在正常的操作功能,不会触发拟态扰动...)

    ps:这里膜@zsx和@超威蓝猫,因为我无法加载 jquery ,所以我看不到那个修改配置的页面是什么样的,但 ROIS 直接用 js 获取页面内容渲染...

    在仔细分析拟态的原理之后,我觉得如果这个功能可以被正常修改(在不被拟态拦截的情况下),那么我们就肯定触发了所有的执行体(不可能只影响其中一台)。

    那么我们反向思考过来,既然无法修改,就说明这个配置在裁决机背设置为白名单了,一旦修改就会直接拦截并返回 500!

    所以我们当时重新思考了拟态防火墙的结构...我们发现,因为Web服务作为防火墙的管理端,在防火墙的配置中,至少应该有裁决机的 ip ,搞不好可以直接获取防火墙的 ip 。

    image.png-45.3kB

    这时候如果我们直接向后端ip构造socket请求,那么我们就能造成一次降维打击

    只是可惜,因为没有 system shell ,再加上不知道为什么蚁剑和菜刀有问题,我们只能花时间一个一个文件去翻,结果就是花了大量的时间还没找到(远程的那份代码和我本地差异太大了),赛后想来,如果当场写一个脚本说不定就保住第一了2333

    关于拟态

    在几次和拟态防御的较量中,拟态防御现在的形态模式也逐渐清晰了起来,从最开始的测信道攻击、ddos攻击无法防御,以及关键的业务落地代价太大问题。逐渐到业务逻辑漏洞的防御缺陷。

    拟态防御本身的问题越来越清晰起来,其最关键的业务落地代价太大问题,在现在的拟态防御中,逐渐使用放弃一些安全压力的方式来缓解,现在的拟态防御更针对倾向于组件级安全问题的防御。假设在部分高防需求场景下,拟态作为安全生态的一环,如果可以通过配置的方式,将拟态与传统的Waf、防火墙的手段相结合,不得不承认,在一定程度上,拟态的确放大了安全防御中的一部分短板。拟态防御的后续发展怎么走,还是挺令人期待的。


    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/932/

    作者:吴烦恼 | Categories:技术分享 | Tags:
  • 如何打造自己的PoC框架-Pocsuite3-框架篇

    2019-05-15

    作者:w7ay@知道创宇404实验室
    English version: https://paper.seebug.org/914/
    相关阅读:如何打造自己的PoC框架-Pocsuite3-使用篇

    本节笔者将按照Pocsuite框架结构以及工程化实践,来实现一款自己的PoC框架。为了开一个好头,我们先取一个好听的名字,想威武霸气一些可以取上古神器之类的,诸如轩辕夏禹赤霄干将,若怀着对游戏的热爱也可以有山丘之王(Mountain King)剑圣(BladeMaster)月之女神(Priess Of the moon)。由于笔者比较懒,我们就取一个朴素的名字:AirPoc,中文名叫它"空气炮"吧。

    名称取好了,我们还要幻想一下大饼。这里请充分发挥想象力,幻想它的功能,你要记住,没有我们实现不了的功能,如果有,打死产品manager即可。

    这里不妨开下脑洞,为了组建兔子安全联盟,我们计划开发一款基于区块链的PoC验证框架AirPoc,限定只对"兔子安全联盟”范围内的网站进行安全检查,由一个AirPoc节点检查出了存在漏洞的地址,将URL和PoC共享到区块中,再由随机的其他节点验证,验证成功则获得"空气币",而被检测到的网站所有者则需要支付"空气币"作为报酬。

    虽然只是暂时的幻想,但是产品小哥哥也略带激动整理出了我们需要的功能。

    1. 使用简单,不要有太多的命令,可以跨平台使用
    2. 人多力量大,能让更多人参与进来的
    3. 能简单操作就能内置到其他产品上
    4. 验证速度与验证准确率极高!
    5. 我也不知道什么好,总之你跑起来能出东西就行!

    当然,这位产品小哥哥可能怕被打,没有将分布式,区块链的概念加入进来。

    具体细节

    下面就由笔者来具体实现由笔者兼职的产品manager随便一想(挖坑)的东西。我们逐一分析问题,并给出最后的解决方案。

    说到使用简单,我们就任性的选择使用Python了,不信你看看Python之父的头发。在安装了Python之后,也可以一份代码多处使用,但为了足够的简单与原生,我们决定尽量少使用Python的第三方包。而目前Python最新版为3.7,我们就以此为例。

    国外的众多开源安全项目都有不少人参与,像Metasploit

    image-20190425142853330

    Sqlmap

    image-20190425142829862

    Routersploit

    image-20190425142732368

    能贡献一份代码到上面可能是安全研究人员最想做的事情吧。

    所以笔者有个想法是AirPoc的PoC仓库可以开源到GitHub,并且能够在线调用上面的PoC,这样也不会为了PoC的更新而烦恼了。

    内置到其他产品也更是容易,如果是Python类的软件,可以直接把AirPoc当做包来调用,如果其他软件,AirPoc可以开放一个RPC接口提供使用,如果不想要Python的环境,也可以通过pyinstaller之类的工具打包,我们的设计原则是尽量不依赖其他第三方库,所以也会避免很多奇奇怪怪的问题。

    想要实现验证速度与验证准确率极高,我们要做好多线程或协程的并发模型,这里我们会在后面在详细叙述。

    最后,"我也不知道什么好,总之你跑起来能出东西就行!",如果上面的事情我们都做好了,这个应该就是水到渠成的了~

    AirPoc的框架

    在完成这个"宏伟计划"之前,我们也需要设计一下整体的代码框架。作为一名代码洁癖患者,一个良好的代码结构,是万里长征的第一步。我们建立如下的目录结构,env是虚拟环境,建立两个目录libpocslib用于存储之后的相关核心文件,pocs用于存储poc文件,和一个文件main.py用作初始入口。

    就像盖大楼需要打好地基,接下来完成基础框架,我们可以先不用写具体的功能,但是了解作为"地基"的函数的意义。如下,在main.py文件中如下代码,一个初始的框架就完成了。

     

    image-20190429110505553

    但是,正如你所见,版本号和我的比特币钱包的数字竟然差不多,我们还要给它加些料。

    单例模式

    在我们软件的初始化的工程中,我们需要得到很多环境相关的信息。比如当前执行的路径是哪?poc目录在哪?我们输出结果文件输出到哪个路径等等。

    它们有一个共同的特定是,它们只需要加载一次,在后面使用中直接拿来用就行了。这种模式在软件设计模式中有一个单独的名词,"单例模式"。

    幸运的是python的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。

    我们在lib目录里面新建一个data.py用于存储这些信息。同时将版本信息也放到这里来。

     

    为了更好的来表示这些常量,我们用PEP8标准里的规范,统一约定用大写和下划线来表示常量。为了说明与之前的区别,我们象征性的将VERSION减一个0,来表达我们的比特币又增长了10倍。

    动态加载

    在解决完我们相关的环境问题后,我们在看看如何动态加载模块。在具体细节里我们说过,我们期望PoC能够从本地或者远程网站(如GitHub)上加载。

    这里又得分成两种情况,如果是通过文件路径加载动态加载的模块,可以直接用__import__()来加载,但是如果要远程加载,可能就又会复杂一点,根据python的相关文档,我们要自己实现"查找器"与"加载器" https://docs.python.org/zh-cn/3/reference/import.html

    当然,你也可以从远程保存到本地后,按照本地加载模式进行加载。但是Pocsuite已经有完整的加载器代码了,我们可以直接拿来用。

    新建lib/loader.py文件

     

    具体如何实现的我们可以不用关心,我们只需要知道,其中我们可以用load_string_to_module来从源码中加载模块了。如果你有兴趣了解具体的实现,可以参考上面的python官方文档。

    规则的制定

    从文件或者远程加载好模块后,就可以准备运行的相关事宜了。我们需要对PoC做一个规则的统一约定,让程序更好的调用它们。

    你可以将规则定义的详细,也可以一切从简,主要是看使用场景。而前面也提到,为了保护"安全联盟"的安全问题,所以我们需要PoC更够比较简单的快速编写。

    同时我们还需要考虑如果PoC需要多个参数如何处理?笔者的规则是这样定义的。

     

    在PoC文件中定义一个verify函数用作验证使用,arg作为普通的参数传递,当需要传递较多的参数时,从kwargs中接收。在PoC验证成功后,也只需要返回一个字典即可,如果验证失败,返回FalseNone即可。字典内容由PoC编写者制定,给予编写者最大的灵活空间。

    但是注意!PoC的质量就需要依靠编写者的维护。

    V0.01

    我们最终要实现的目标是,设置好目标,程序自动加载指定的一个或多个PoC或全部的PoC,逐个检测目标。剩下的部分就是怎样将这些功能串联在一起了。

    前面我们已经实现了AirPoc的基础框架,现在只需要在其基础上具体实现功能即可。

    为了测试的方便,我们先在pocs目录下按照之前定义的规则建立两个简陋的PoC。

    image-20190429164121695

    image-20190429164143909

    现在,main.py中的代码如下

     

    我们的版本也来到了0.01,它已经是一个"成熟的”能自己跑PoC的框架了。

    image-20190429172118745

    多线程模型

    为了让我们的框架运行得更快一点,我们使用多线程来处理每个PoC,因为我们处理的任务大多是I/O密集型任务,所以我们也不用太纠结python是不是伪线程这个问题。

    多线程模型中最简单的一种是生产者/消费者的模型,启动多个线程来共同消费一个队列就行了。新建lib/threads.py

     

    值得注意的一点是,我们并没有使用Python线程中推荐的join()来阻塞线程,因为使用join()的话,python将无法响应用户输入的消息了,会导致Ctrl+C退出时没有任何响应,所以以while循环的方式来阻塞线程。

    接着将主程序改造成多线程的模式,将原start()中的"消费者"提取出来,单独用作一个函数,用队列接收数据即可。如下

     

    另外,线程数量是我们可配置的,我们将它改成从配置中读取。

     

    再次运行,会发现比以前快很多!

    image-20190430102952036

    统一网络请求

    这是我们整个框架的最后一个部分,如何来统一网络请求。有时我们需要让我们的PoC框架发出的网络请求中统一一下代理,UA头等等的设置,这需要我们框架进行统一的处理。在实现我们的目的之前,我们还需要在框架里做一个约定,约定我们的网络请求都需要统一使用requests来进行发包。开始时我们说到,我们会尽量不使用第三方模块,但是requests模块实在太好用了,我们将它排除在外...

    Python语言动态的机制,我们可以很容易在使用一个函数之前Hook它,将它原始的方法重定向到我们自定义的方法中,这是我们能够统一网络请求的一个前提。

     

    image-20190430112557108

    通过hook一个函数来达到我们自己的目的。

    像sqlmap这类工具,基于python内置的urllib模块,但是有大量的代码都在处理在了网络请求方面,甚至为了处理chunked发包的问题,hook重写了更底层的httplib库。

    pocsuite为了统一调度网络请求,hook了requests模块的相关方法。我们可以具体参考其中的代码。

    pocsuite3/lib/request/patch/__init__.py代码很清晰的说明了hook的函数

     

    如果你看过requests的源码,会知道这里面的重点是看它如何hook seesion函数的。

    pocsuite3/lib/request/patch/hook_request.py

     

    它重写了session_request函数的方法,让其中可以自定义我们自定义的文件头等信息。上述代码可能需要你看过requests才会对他有所理解,不过没关系,我们还是以拿来主义的精神直接用即可。

    为了达到此目的以及更好的优化框架结构,我们还需要做一些小调整。

    新建lib/requests.py

     

    同时在config中预留requests的接口

    image-20190430115617047

    以及init的时候执行我们的hook。

    image-20190430115652364

    我们新编写一个PoC,用这个网站测试一下 最后的效果 http://www.httpbin.org/get

    pocs/poc.py

     

    image-20190430130417913

    效果很好,但是如果加上https的网站,就有一个警告信息。

    image-20190430130730241

    同样参考Pocsuite的方法禁用掉warning信息

     

    最后有仪式感的将版本号变更为0.1,AirPoc的框架部分大体完成了。

    最后

    AirPoc的很多结构思想都来源于Pocsuite,如果直接阅读Pocsuite,也许能收获很多东西。目前AirPoc v0.1基础框架已经差不多完成了,已经可以从本地加载一个或多个PoC,进行批量测试。后面我们再尝试些更好玩的,如何验证无回显的情况,如何生成shellcode,以及如何操作回连的shell,敬请期待下节《功能篇》~。

    AirPoc下载:https://images.seebug.org/archive/airpoc.zip


    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/913/

    作者:吴烦恼 | Categories:安全研究技术分享 | Tags:
  • WebLogic RCE(CVE-2019-2725)漏洞之旅

    2019-05-05

    作者:Badcode@知道创宇404实验室
    时间:2019年4月30日
    English version: https://paper.seebug.org/910/

    417

    2019年4月17日,CNVD 发布《关于Oracle WebLogic wls9-async组件存在反序列化远程命令执行漏洞的安全公告》,公告指出部分版本WebLogic中默认包含的wls9_async_response包,为WebLogic Server提供异步通讯服务。由于该WAR包在反序列化处理输入信息时存在缺陷,攻击者可以发送精心构造的恶意 HTTP 请求,获得目标服务器的权限,在未授权的情况下远程执行命令。

    418

    2019年4月18日,开始应急。因为这个漏洞当时属于0day,也没有补丁可以参考,只能参考公告内容一步一步来看了。首先看到公告里提到的wls9_async_response.war包,看下web.xml里的url。

    看到/AsyncResponseService,尝试访问一下,404。之后看到weblogic.xmlweblogic-webservices.xml

    访问下_async/AsyncResponseService

    可以正常访问,再结合公告中的漏洞处置建议,禁止 /_async/* 路径的URL访问,可以大概率猜测,漏洞入口在这里。

    weblogic-webservices.xml中有一个类,weblogic.wsee.async.AsyncResponseBean,跟进去这个类,发现在wseeclient.jar里面

    而后我在这个类里面的方法下断点,然后构造一个普通的SOAP消息,发送。

    断点没有debug到。最后我把wsee/async所有类的所有方法都下了断点,重新发送消息,成功在AsyncResponseHandler类中的handleRequest拦截到了。

    继续流程,String var2 = (String)var1.getProperty("weblogic.wsee.addressing.RelatesTo");这个步骤一直取不到值,导致流程结束。为了解决这个问题,翻了不少资料,最后找到一个类似的例子,可以使用<ads:RelatesTo>test</ads:RelatesTo>weblogic.wsee.addressing.RelatesTo赋值。

     

    之后流程就能够继续下去了,我一直以为漏洞的关键点在这里,因为这个wsee.async下面的几个类中有readObject方法,我一直尝试着通过AsyncResponseHandler跳到readObject方法,而后就卡在这里,后面的流程就不写了,对这个漏洞来说是错的,上面写的这些猜测和流程都是正确的。

    419

    2019年4月19日,和我一起应急的师傅给我发了一张截图。

    看到这截图里面的RelatesTo,我还以为之前的推测没有错,只是没有构造好。

    全局搜索UnitOfWorkChangeSet这个类,之后在这个类中下断点。

    根据截图,构造一个类似的,然后发送

    在这个类中debug到了。

    看到了日思夜想的readObject,有了反序列的点,自然要找利用链了,目前 WebLogic 下面 commoncollections 相关的利用链已经是无法使用了,WebLoigc 依赖的common-collections版本已经升级了,先找个Jdk7u21测试一下,将生成的 payload 转换成 byte,发送。

    可以看到,成功地执行了命令。但是这个利用链限制太大了,基本没啥用。我想起去年应急过的一个WebLogic 反序列漏洞,CVE-2018-3191,既然jdk7u21都不受黑名单限制,想来CVE-2018-3191也是一样可以利用的。

    猜测没有错误,CVE-2018-3191也是能够利用的,这个漏洞也终于有点"危害"了。和 pyn3rd 师傅讨论一下有没有其他利用链,仔细翻下黑名单,除了CVE-2018-3191,就只有新的jython利用链(CVE-2019-2645)了,由 Matthias Kaiser大佬提交的,但是目前这个还有没有公开,所以这个利用链也没法使用。

    有了正确答案,就可以看下之前的猜测哪里出了问题。

    回到AsyncResponseHandler类中的handleRequesthandleRequest的上一步,HandlerIterator类中的handleRequest方法

    会遍历this.handlers,然后调用每个handlerhandleRequest去处理用户传入的SOAP Message。

    可以看到,AsyncResponseHandler仅仅只是21个handler之中的一个,而weblogic.wsee.addressing.RelatesTo的赋值就是在ServerAddressingHandler中完成的,有兴趣的可以去跟一下。这里面有一个非常重要的handler--WorkAreaServerHandler,看名字可能觉得眼熟,看到里面的handleRequest方法可能就不淡定了。

    之后的流程就和CVE-2017-10271是一样的了,关于这个漏洞的分析可以参考廖师傅的文章

    跟到这里就可以看出来了,这个url只是CVE-2017-10271漏洞的另外一个入口而已。这也是后期导致假PoC泛滥的一个原因。整个流程大概如下:

    那么问题来了,这个PoC是如何绕过CVE-2017-10271的黑名单的呢?

    首先来看一下CVE-2017-10271的补丁,会将传入的数据先调用validate校验,通过之后才交给XMLDecoder

     

    可以看到,objectnewmethod这些标签都被拦截了,遇到直接抛出错误。void标签后面只能跟indexarray标签后面可以跟class属性,但是类型只能是byte类型的。其中,过滤object标签是CVE-2017-3506的补丁,剩下的过滤是针对CVE-2017-10271的补丁。

    如果仔细看了黑名单的,就不难发现,外面流传的很多PoC都是假的,就是新url入口+老的payload,这样的组合是没有办法绕过这个黑名单的。

    绕过这个黑名单的关键是class标签,可以从官方的文档来了解一下这个标签。

    class标签可以表示一个类的实例,也就是说可以使用class标签来创建任意类的实例。而class标签又不在WebLogic 的黑名单之内,这才是这个漏洞最根本的原因。4月26日,Oracle 发布这个漏洞的补丁,过滤了class标签也证实了这点。

    既然漏洞的原因是绕过了CVE-2017-10271的黑名单,那么wls-wsat.war也是应该受影响的。

    测试一下,没有问题。

    这说明,CNVD的公告写的影响组件不全,漏洞处置建议也写的不全面,要通过访问策略控制禁止 /_async/* 及 /wls-wsat/* 路径的URL访问才行,之后我们也同步给了CNVD,CNVD发了第二次通告

    421

    2019年4月21日,准备构造出这个漏洞的检测PoC,能够使用class标签来创建类的实例,我首先考虑的是构造java.net.Socket,这也引出了一个JDK版本的坑。我测试的是jdk6,参考之前的PoC,可以这么构造

    ceye成功接收到请求,也说明Socket实例创建成功了。

    我把上面的检测PoC在 jdk 7上测试,竟然失败了,一直爆找不到java.net.Socket这个类错误,让我一度以为这个漏洞只能在 jdk 6 下面触发,后来仔细对比,发现是换行符的问题,也就是这样写才对。

    不带换行符的在6和7下面都能生成实例。其实这个问题在最早测试 CVE-2018-3191 payload的时候就已经发生过,pyn3rd师傅问我xml payload是怎么生成的,我说用的拼接,直接System.out.println输出的,都带了换行符,我因为当时跑weblogic的jdk是jdk6,所以没有问题,但是 pyn3rd 师傅的环境是 jdk7 的,没测试成功,只觉得是PoC写法不同造成的问题,后来师傅自己解决了,这里也没沟通,埋下了一个大坑,导致我后面踩进去了。

    422

    2019年4月22日,pyn3rd 师傅测试 WebLogic 12.1.3没成功,发现是12的版本没有oracle.toplink.internal.sessions.UnitOfWorkChangeSet这个类,所以没办法利用。尝试着构造新的exp,目前的情况是,能够创建类的实例,但是调用不了方法。自然想起com.sun.rowset.JdbcRowSetImpl这个类。

    这个是CVE-2017-10271的一种触发方法。之前的黑名单提过,void标签后面只能跟index,所以上面这个payload肯定会被黑名单拦截。尝试使用class标签重写上面的payload。

    构造的过程中,在跟底层代码的时候,发现 jdk 6和 jdk 7处理标签的方式不同。

    jdk 6使用的是com.sun.beans.ObjectHandler

    能用的有stringclassnullvoidarrayjavaobject和一些基本类型标签(如int)。

    jdk7 使用的是com.sun.beans.decoder.DocumentHandler

    可以看到,和jdk6差异不小,例如,jdk 6不支持newproperty等标签。

    我在用jdk 6 的标签构造的时候,一直没构造成功,直到我看到jdk 7 的源码里面的property,这不就是我想要的么,而且这个标签还不在 WebLogic 的黑名单内。所以重写上面的payload如下

    可以看到,没有触发黑名单,成功的执行了命令,而且没有依赖 WebLogic 内部的包,10.3.6和12.1.3都可以通用。遗憾的是,这个payload的打不了 jdk 6的,因为 jdk 6 不支持 property标签。期望有大佬能写出6也能用的。

    423

    2019年4月23日,在CNVD发出通告,各大安全公司发出漏洞预警之后,之前提过的新url+老payload的这种模式的PoC和exp纷纷出炉。不仅是国内,国外也很热闹,很多人表示测试成功,但是都是在无补丁的情况下测试的。Oracle 官网下载的 WebLogic 都是没有安装补丁的,Oracle的补丁是单独收费的,如果安装了 CVE-2017-10271 的补丁,这些PoC和exp都是没有办法触发的,绕过不了黑名单。

    426

    2019年4月26日,Oracle 官方发布紧急补丁,并为该漏洞分配编号CVE-2019-2725。

    427

    2019年4月27日,pyn3rd 师傅说12.1.3版本的exp也有人弄出来了,用的是org.slf4j.ext.EventData

     

    看下这个类的构造方法,直接将传入的xml交给XMLdecoder处理,太粗暴了...

    相当于经过了两次XMLdecode,所以外层用<class>绕过,内层直接标记为纯文本,绕过第一次过滤,第二次 XMLdecode不经过WebLogic 黑名单,直接被JDK解析反序列化执行。

    这种exp也是最完美的,没有jdk版本限制,不需要外连,可惜的是只能打12.1.3版本。

    430

    2019年4月30日,在其他大佬手中看到了这个漏洞的其他利用方式,没有 weblogic和 jdk的版本限制,比上面的几种利用方式都更完善。这种利用方式我之前也看到过,就是Tenable 发的演示视频,当时没想明白,看了大佬的利用方式之后,才明白自己忽略了什么。构造方式可以参考CVE-2017-17485,我之前构造exp的时候也没有往这方面想,这或许就是黑哥说的积累不够吧。

    总结

    • 针对这次漏洞,Oracle 也是打破了常规更新,在漏洞预警后不久就发布了补丁,仍然是使用黑名单的方式修复。(吐槽一下,这么修复,这个功能还能用么?)
    • 此次的漏洞事件中,也看到了安全圈的乱象,漏洞都没有经过完全的验证,就直接发错误的分析文章和假PoC,误导大众。
    • 在这个漏洞应急的过程中,从无到有,从缺到圆,踩了很多坑,也学习到了很多姿势,也看到了自己和大佬的差距。最后感谢漏洞应急过程中几位师傅的交流和指点。

    参考链接

     

    Paper

    本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/909/

    作者:吴烦恼 | Categories:安全研究技术分享 | Tags:
  • WebLogic CVE-2019-2647、CVE-2019-2648、CVE-2019-2649、CVE-2019-2650 XXE漏洞分析

    2019-05-05

    作者:Longofo@知道创宇404实验室
    时间:2019年4月26日

    Oracle发布了4月份的补丁,详情见链接(https://www.oracle.com/technetwork/security-advisory/cpuapr2019-5072813.html#AppendixFMW)

    @xxlegend 在《Weblogic CVE-2019-2647等相关XXE漏洞分析》分析了其中的一个XXE漏洞点,并给出了PoC。刚入手java不久,本着学习的目的,自己尝试分析了其他几个点的XXE并构造了PoC。下面的分析我尽量描述自己思考以及PoC构造过程,新手真的会踩很多莫名其妙的坑。感谢在复现与分析过程中为我提供帮助的小伙伴@Badcode,没有他的帮助我可能环境搭起来都会花费一大半时间。

    补丁分析,找到漏洞点

    根据JAVA常见XXE写法与防御方式(参考https://blog.spoock.com/2018/10/23/java-xxe/),通过对比补丁,发现新补丁以下四处进行了setFeature操作:

    应该就是对应的四个CVE了,其中ForeignRecoveryContext@xxlegend大佬已经分析过了,这里就不再分析了,下面主要是分析下其他三个点

    分析环境

    • Windows 10
    • WebLogic 10.3.6.0
    • Jdk160_29(WebLogic 10.3.6.0自带的JDK)

    WsrmServerPayloadContext 漏洞点分析

    WsrmServerPayloadContext修复后的代码如下:

    可以看到进行了setFeature操作防止xxe攻击,而未打补丁之前是没有进行setFeature操作的

    readExternal在反序列化对象时会被调用,与之对应的writeExternal在序列化对象时会被调用,看下writeExternal的逻辑:

    var1就是this.formENdpt,注意var5.serialize可以传入三种类型的对象,var1.getEndptElement()返回的是Element对象,先尝试新建一个项目构造一下PoC:

    结构如下

    test.xml内容如下,my.dtd暂时为空就行,先测试能否接收到请求:

    运行PoC,生成的反序列化数据xxe,使用十六进制查看器打开:

    发现DOCTYPE无法被引入

    我尝试了下面几种方法:

    • 在上面说到var5.serialize可以传入Document对象,测试了下,的确可以,但是如何使getEndptElement返回一个Document对象呢?
      • 尝试了自己创建一个EndpointReference类,修改getEndptElement返回对象,内容和原始内容一样,但是在反序列化时找不到我创建的类,原因是自己建的类package与原来的不同,所以失败了
      • 尝试像Python那样动态替换一个类的方法,貌似Java好像做不到...
    • 尝试了一个暴力的方法,替换Jar包中的类。首先复制出Weblogic的modules文件夹与wlserver_10.3\server\lib文件夹到另一个目录,将wlserver_10.3\server\lib\weblogic.jar解压,将WsrmServerPayloadContext.class类删除,重新压缩为weblogic.Jar,然后新建一个项目,引入需要的Jar文件(moduleswlserver_10.3\server\lib中所有的Jar包),然后新建一个与WsrmServerPayloadContext.class同样的包名,在其中新建WsrmServerPayloadContext.class类,复制原来的内容进行修改(修改只是为了生成能触发xml解析的数据,对readExternal反序列化没有影响)。WsrmServerPayloadContext.class修改的内容如下:
    • 经过测试第二种方式是可行的,但是好像过程略复杂。然后尝试了下新建一个与原始WsrmServerPayloadContext.class类同样的包名,然后进行修改,修改内容与第二种方式一样测试这种方式也是可行的,比第二种方式操作起来方便些

    构造新的PoC:

     

    查看下新生成的xxe十六进制:

    DOCTYPE被写入了

    测试下,使用T3协议脚本向WebLogic 7001端口发送序列化数据:

    漂亮,接收到请求了,接下来就是尝试下到底能不能读取到文件了

    构造的test.xml如下:

    my.dtd如下(my.dtd在使用PoC生成反序列化数据的时候先清空,然后,不然在dbBuilder.parse时会报错无法生成正常的反序列化数据,至于为什么,只有自己测试下才会明白):

    运行PoC生成反序列化数据,测下发现请求都接收不到了...,好吧,查看下十六进制:

    %dtd;%send;居然不见了...,可能是因为DOM解析器的原因,my.dtd内容为空,数据没有被引用。

    尝试debug看下:

    可以看到%dtd;%send;确实是被处理掉了

    测试下正常的加载外部数据,my.dtd改为如下:

    gen.xml为:

    debug看下:

    可以看到%dtd;%send;被my.dtd里面的内容替换了。debug大致看了xml解析过程,中间有一个EntityScanner,会检测xml中的ENTITY,并且会判断是否加载了外部资源,如果加载了就外部资源加载进来,后面会将实体引用替换为实体申明的内容。也就是说,我们构造的反序列化数据中的xml数据,已经被解析过一次了,而需要的是没有被解析过的数据,让目标去解析。

    所以我尝试修改了十六进制如下,使得xml修改成没有被解析的形式:

    运行PoC测试下,

    居然成功了,一开始以为反序列化生成的xml数据那块还会进行校验,不然反序列化不了,直接修改数据是不行的,没想到直接修改就可以了

    UnknownMsgHeader 漏洞点分析

    WsrmServerPayloadContext差不多,PoC构造也是新建包然后替换,就不详细分析了,只说下类修改的地方与PoC构造

    新建UnknownMsgHeader类,修改writeExternal

    PoC如下:

     

     

    运行PoC测试下(生成的步骤与第一个漏洞点一样),使用T3协议脚本向WebLogic 7001端口发送序列化数据:

    WsrmSequenceContext 漏洞点分析

    这个类看似需要构造的东西挺多的,readExternalwriteExternal的逻辑也比前两个复杂些,但是PoC构造也很容易

    新建WsrmSequenceContext类,修改

    PoC如下: