Created
April 24, 2025 13:55
-
-
Save 0xilis/153f12a87a5183c182da9bed1c860cb2 to your computer and use it in GitHub Desktop.
Patch CVE-2024-27876 libAppleArchive bug on jailbroken iOS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Please credit me (Snoolie K / 0xilis) for this patch */ | |
%hookf(int, concatExtractPath, char *dest, size_t dest_size, const char *dir, const char *path) { | |
size_t dir_len = strlen(dir); | |
size_t path_len = strlen(path); | |
if (!dir_len) { | |
pc_log_error(__FILE__, __func__, 0x173, 3, 0, "invalid dir: %s", dir); | |
return -1; | |
} | |
if (path_len + dir_len + 1 >= dest_size) { | |
pc_log_error(__FILE__, __func__, 0x174, 3, 0, "dir/path too long: %s", dir); | |
return -1; | |
} | |
strlcpy(dest, dir, dest_size); | |
/* If there's no path to append, we're done */ | |
if (!path_len) { | |
return 0; | |
} | |
size_t offset = 0; | |
while (offset < path_len) { | |
const char *current = path + offset; | |
char *next_slash = strchr(current, '/'); | |
size_t segment_end = next_slash ? (next_slash - path) : path_len; | |
size_t segment_len = segment_end - offset; | |
/* Validate path segment */ | |
if (segment_len == 1 && current[0] == '.') { | |
/* Single dot, skip if this is the last segment */ | |
if (!next_slash) { | |
return 0; | |
} | |
} else if (segment_len == 2 && current[0] == '.' && current[1] == '.') { | |
/* Double dot / path traversal, invalid path */ | |
pc_log_error(__FILE__, __func__, 0x18d, 3, 0, "invalid path: %s", path); | |
return -1; | |
} else if (segment_len == 0) { | |
/* Empty segment - invalid */ | |
pc_log_error(__FILE__, __func__, 0x186, 3, 0, "invalid path: %s", path); | |
return -1; | |
} | |
/* | |
* Here is the main symlink check. | |
* This was originally problematic since | |
* a process can just replace the file | |
* after the check with a symlink, a classic | |
* TOCTOU! We add a mkdir() check to prevent this. | |
* Now, technically I know some will say this is still | |
* problematic since a process can do it | |
* after the mkdir() call, but this should prevent | |
* the cases where libAppleArchive races with itself, | |
* and on iOS that's really all that can happen anyway | |
* due to sandbox, so since we only care about iOS | |
* this is fine. | |
*/ | |
struct stat st; | |
int stat_result = lstat(dest, &st); | |
if (offset == 0) { | |
if (stat_result != 0) { | |
pc_log_error(__FILE__, __func__, 0x194, 3, 0, | |
"dir doesn't exist, or is invalid: %s", dir); | |
return -1; | |
} | |
if (!S_ISDIR(st.st_mode)) { | |
pc_log_error(__FILE__, __func__, 0x194, 3, 0, | |
"dir doesn't exist, or is invalid: %s", dir); | |
return -1; | |
} | |
} else { | |
if (stat_result != 0) { | |
if (mkdir(dest, 0755) != 0) { /* We now check for mkdir() failure which will happen on self race */ | |
pc_log_error(__FILE__, __func__, 0x199, 3, 0, | |
"failed to create directory: %s", dest); | |
return -1; | |
} | |
} else if (!S_ISDIR(st.st_mode)) { | |
pc_log_error(__FILE__, __func__, 0x199, 3, 0, | |
"a parent of path is not a directory: %s", path); | |
return -1; | |
} | |
} | |
if (dest[dir_len - 1] != '/') { | |
dest[dir_len] = '/'; | |
dir_len++; | |
dest[dir_len] = '\0'; | |
} | |
memcpy(dest + dir_len, current, segment_len); | |
dir_len += segment_len; | |
dest[dir_len] = '\0'; | |
if (!next_slash) { | |
return 0; | |
} | |
offset = segment_end + 1; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment