BROP Attack之Nginx远程代码执行漏洞分析及利用
信息安全公益宣传,信息安全知识启蒙。
加微信群回复公众号:微信群;QQ群:16004488
Blind ROP是一种很有意思的攻击方式,其实很多国外文章,以及原来乌云知识库中的一篇文章都有介绍,我把这些参考文章都放在结尾位置,感兴趣的小伙伴可以一起学习交流一下。作为Flappy pig战队的脑残粉,我也会时时关注CTF的动态,听说Blind ROP也出现在了今年的HCTF的pwn题中,文后我会附上z神的github,里面有HCTF的这道BROP pwn题。
最近也跟着joker师傅和muhe师傅一起看了看关于Blind ROP的东西,这是个非常有意思的利用方式,虽然比较复杂,但同时也很佩服这个利用的脑洞,攻防对抗就是不断在这种精彩的利用和缓解中提升的。
对于CTF我不是特别了解,但是在学习的过程中,通过一个Nginx的老洞,总算“认识”了Blind ROP,受益匪浅,同时也感谢joker师傅,muhe师傅,swing师傅在学习过程中的讨论和指导。
下面我将就Nginx漏洞原理,以及这个Nginx漏洞的Exploit来全方位浅析BROP这种利用方式,关于这个漏洞的原理,网上有详细说明,这里我将结合Exploit的利用来讲解整个过程。文中有失误的地方还请师傅们多多包含,多多批评指正(毕竟通读2000+行ruby太痛苦T.T)。
Nginx漏洞分析(CVE-2013-2028)
这是一个Nginx的栈溢出漏洞,我的分析环境是在x86下,而利用是在x86_64下,我本来是不想这样的,但是之前在x86下用msf复现了Nginx这个漏洞,顺道进行了分析,然后拿到Exploit的时候又在x86_64下进行了BROP的利用研究,不过这个系统版本不影响我们对漏洞的理解,利用的分析。
关于漏洞环境以及Nginx安装搭建这里我就不多说了,文后的参考文章中,我会提供一个搭建环境,按那个搭没错,这里漏洞分析我的环境是Ubuntu 13.04 x86,利用分析环境是Kali 2.0。
搭建好环境之后用“#/usr/local/nginx/nginx”运行nginx服务,有的环境下是“#/usr/local/nginx/sbin/nginx”,运行服务后,用gdb attach方法附加,之后通过msf方法发送Exploit,这里我碰到过一种情况,就是msf发送Exploit的时候,会提示ERRCONNECT,这时候可以通过set target 0的方法设置好目标对象,而不用auto target,应该就可以了。发送Exploit之后,首先我们来看一下发送的数据包。
一共发送了两个GET包,在后面的畸形字符串前,包含了一个Encoding字段,值为chunked,而第一个数据包,同样也包含了一个值为chunked,但不带畸形数据,后面会解释为什么要发送两个,这时Nginx捕获到了崩溃。
崩溃状况下,通过bt的方法回溯一下堆栈调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
这里问题出在stack_chk_fail,也就是canary的检查失败了,导致了程序异常中断,这个bt回溯内容很长,这里我就不讲述回溯过程了,我们直接来正向动静结合分析一下整个漏洞的成因。首先我们发送的数据包包含chunked字段。这样会进行一次if语句比较,然后给一个r在结构体的headers_in的chunked成员变量赋值。
src/http/ngx_http_request.c:1707:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
如果看之前的bt回溯可以看到,这个r结构体经常会作为参数被引用到,这个r结构体是一个Nginx HTTP请求的结构体,其定义如下:
1 |
|
通过这种定义,我们能直接用p命令来打印整个结构体的内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
|
这里我列举了成员变量headers_in的内容,除了这个成员变量还有很多,感兴趣的小伙伴可以直接在源码中找到,在这个漏洞中,我们只关心headers_in中的部分成员,因此后续分析中,我只列举关键的成员变量值,结构体名字太长,后面都称之为r结构体。。
回到刚才if语句位置,在ngx_http_process_request_header函数下断点进行单步跟踪。
1 2 3 4 5 6 7 8 9 10 |
|
命中函数入口位置,这个时候查看r结构体中的chunked对象。
1 2 3 4 |
|
这个值还是0x0,接下来开始单步跟踪。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
单步跟踪到0x8074314地址位置,调用了strncasecmp做比较,比较的两个值就是chunked,这时候数据包包含chunked,所以会进入刚才源码中的if语句处理,处理后再看r结构体的chunked变量已经被赋值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
随后,程序会进入漏洞触发的关键函数ngx_http_discard_request_body,这个函数中会有一处对chunked的值进行判断,如果chunked的值为1,则会进入到if语句内部处理逻辑,会调用另一个函数ngx_http_discard_request_body_filter。
1 2 3 4 5 6 7 8 9 10 11 12 |
|
在执行完ngx_http_discard_request_body离开if语句的逻辑之后,会执行ngx_http_read_discarded_request_body,也就是刚才在回溯过程中stack_chk所在的最内层漏洞函数,首先我们来看一下ngx_http_discard_request_body_filter函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
注意在for循环中会进行三个if语句逻辑处理,分别对应三种NGX状态,在rc==NGX_AGAIN的时候,会对content_length_n成员变量进行赋值。NGX_AGAIN就是要第二次接收时才会触发,因此这就解释了在之前我们提到的为什么要发两个数据包的问题。
在这个函数下断点进行单步跟踪。首先会判断if语句中NGX状态。
1 2 3 4 5 |
|
这里判断通过,会进入到这个if语句中进行处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
▼ 点击阅读原文,查看更多精彩文章。