一个经典的由整数溢出导致栈溢出的漏洞。下面感觉写的有点乱。
栈的基础知识:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/stack-intro-zh/
栈溢出原理:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/stackoverflow-basic-zh/
基础的ROP:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/basic-rop-zh/
一步一步学rop(蒸米rop):
参考资料
原理简述
CVE-2013-2028是nginx的一个整数溢出导致栈溢出的漏洞。
从触发漏洞的关键函数ngx_http_discard_request_body开始分析。这个函数中,会对chunked的值进行判断,如果chunked的值为1,则会进入到if语句内部处理逻辑,会调用另一个函数ngx_http_discard_request_body_filter
ngx_http_discard_request_body(ngx_http_request_t *r)
{
if (size || r->headers_in.chunked) {
rc = ngx_http_discard_request_body_filter(r, r->header_in);
if (rc != NGX_OK) {
return rc;
}
if (r->headers_in.content_length_n == 0) {
return NGX_OK;
}
}
ngx_http_read_discarded_request_body
在执行ngx_http_discard_request_body离开if语句逻辑后,会执行ngx_http_read_discarded_request_body。首先先看if里的ngx_http_discard_request_body_filter函数。
ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b)
{
for ( ;; ) {
rc = ngx_http_parse_chunked(r, b, rb->chunked);
if (rc == NGX_OK) {
/* a chunk has been parsed successfully */
}
if (rc == NGX_DONE) {
/* a whole response has been parsed successfully */
}
if (rc == NGX_AGAIN) {
/* set amount of data we want to see next time */
r->headers_in.content_length_n = rb->chunked->length;
break;
}
注意在for循环中会进行三个if语句逻辑处理,分别对应三种NGX状态,在rc==NGX_AGAIN的时候,会对content_length_n成员变量进行赋值。NGX_AGAIN就是要第二次接收时才会触发,所以要发两个数据包才会触发该漏洞。
通过动态调试会知道content_length_n的值成了一个极大值0xfdffbbffa8afed92。随后进入ngx_http_read_discarded_request_body函数处理。
src/http/ngx_http_request_body.c:676
static ngx_int_t
ngx_http_read_discarded_request_body(ngx_http_request_t *r)
{
size_t size;
ssize_t n;
ngx_int_t rc;
ngx_buf_t b;
u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http read discarded body");
ngx_memzero(&b, sizeof(ngx_buf_t));
b.temporary = 1;
for ( ;; ) {
if (r->headers_in.content_length_n == 0) {
r->read_event_handler = ngx_http_block_reading;
return NGX_OK;
}
if (!r->connection->read->ready) {
return NGX_AGAIN;
}
size = (size_t) ngx_min(r->headers_in.content_length_n,
NGX_HTTP_DISCARD_BUFFER_SIZE);//key!!
n = r->connection->recv(r->connection, buffer, size);//key!!
在22行,会调用ngx_min函数进行比较,会取content_length_n和NGX_HTTP_DISCARD_BUFFER_SIZE里较小的值。由于content_length_n为一个负数,这里的比较是进行有符号比较,因此,最小值是content_length_n。由于在比较时,ecx存放后8位,edx存放前8位,随后会将ecx的值交给size,这个size是极大值,比buffer的4096大很多,在进行recv的时候,就会接收一个超过buffer大小的数据,造成栈溢出。
总而言之就是,一个整数溢出导致的栈溢出。
获取nginx源码
sudo apt-get update -y
sudo apt-get install -y libssl-dev gcc make wget tar
wget https://github.com/nginx/nginx/archive/release-1.4.0.tar.gz && tar xfv release-1.4.0.tar.gz
build
Without stack cookies:
./auto/configure --without-http_rewrite_module --without-http_gzip_module
vim Makefile
# Add '-fno-stack-protector' to the CFLAGS
make -j4
sudo make install
With stack cookies:
./auto/configure --without-http_rewrite_module --without-http_gzip_module
make -j4
sudo make install
run
sudo /usr/local/nginx/sbin/nginx -g "daemon off;"
在浏览器输入localhost,可以看到如下界面,说明nginx服务开启成功了
参考:https://github.com/m4drat/CVE-2013-2028-Exploit
这时访问localhost:8081可以出现上面一样的界面,因为docker-compose里将80端口映射到8081了。
搜索漏洞
search cve-2013-2028
使用exp
use exploit/linux/http/nginx_chunked_size
这里没写全,可以看msf的文档复现。
可以参考这个链接来触发漏洞:
https://github.com/mudongliang/LinuxFlaw/tree/master/CVE-2013-2028
github:
exploit db: