-
-
Notifications
You must be signed in to change notification settings - Fork 328
Open
Description
Description
A heap buffer overflow vulnerability exists in libplist when processing XML plist files containing entity escape sequences (such as <, >, &). The vulnerability is triggered by inconsistent length metadata after XML entity unescaping, leading to out-of-bounds memory access during OpenStep format conversion.
This bug was detected in fuzzing.
Root Cause
The vulnerability stems from a length metadata inconsistency between XML parsing and memory management:
- XML Parsing Phase: When parsing XML with entity escapes,
text_parts_get_contentcalculates memory allocation based on pre-unescaping length but returns the smaller post-unescaping length - Copy Phase:
plist_copy_nodepreserves the original (incorrect) length field but reallocates string memory usingstrdup, which only allocatesstrlen() + 1bytes based on the actual string content - Access Phase:
node_to_openstepuses the preserved length field to iterate, causing reads beyond thestrdup-allocated memory
Technical Details
Crash Location: /src/libplist/src/oplist.c:203:47 in node_to_openstep function
Vulnerability Type: Heap buffer overflow (1-byte read beyond allocated memory)
Trigger Condition: XML plist with entity escapes + plist copy + OpenStep conversion
Call Stack:
#0 node_to_openstep (/src/libplist/src/oplist.c:203:47) - out-of-bounds read
#1 plist_to_openstep (/src/libplist/src/oplist.c:469:11)
#2 Application code calling plist_to_openstep
Memory Allocation Info:
- Allocated: 45 bytes via
strdupinplist_copy_node(/src/libplist/src/plist.c:587:31) - Accessed: Attempt to read 46th byte, causing buffer overflow
Reproduction
Test Program
#include <plist/plist.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main() {
// Hex data from crash report that contains entity-escaped XML
unsigned char crash_data[] = {
0x3c,0x3f,0x87,0x3f,0x3f,0x3e,0x3c,0x6b,0x65,0x79,0x0,0x0,0x0,0xff,0x3e,
0xff,0xff,0xff,0xff,0xff,0xff,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,
0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,
0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x2,0x0,
0x0,0x0,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,
0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,
0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0x3c,0x2f,0x6b,0x65,0x79,0x0,0xe7,0xe0,0x2f,0x3e,0x3c,0x64
};
printf("Testing XML entity unescaping length inconsistency bug...\\n");
plist_t xml_plist = NULL;
if (plist_from_xml((const char*)crash_data, sizeof(crash_data), &xml_plist) == PLIST_ERR_SUCCESS && xml_plist) {
printf("XML parsed successfully\\n");
// This creates the length inconsistency
plist_t copied_plist = plist_copy(xml_plist);
if (copied_plist) {
printf("Plist copied successfully\\n");
char* openstep = NULL;
uint32_t openstep_len = 0;
// This triggers the buffer overflow
printf("Converting to OpenStep format...\\n");
if (plist_to_openstep(copied_plist, &openstep, &openstep_len, 1) == PLIST_ERR_SUCCESS) {
printf("Conversion successful\\n");
if (openstep) free(openstep);
}
plist_free(copied_plist);
}
plist_free(xml_plist);
}
return 0;
}Compilation
clang test.c -o test -fsanitize=address -I/usr/include/libplist-2.0 -lplist-2.0Simplified Example
// Create a plist with entity-escaped XML content
const char* xml_with_entities = "<?xml version=\\"1.0\\"?><plist><key>Test <String></key></plist>";
plist_t plist = NULL;
plist_from_xml(xml_with_entities, strlen(xml_with_entities), &plist);
// Copy creates the length inconsistency
plist_t copied = plist_copy(plist);
// OpenStep conversion triggers overflow
char* result = NULL;
uint32_t len = 0;
plist_to_openstep(copied, &result, &len, 0); // CRASHAddressSanitizer Report
ERROR: AddressSanitizer: heap-buffer-overflow on address 0x5040000000bd at pc 0x556a6c6ea140 bp 0x7ffef41eacb0 sp 0x7ffef41eaca8
READ of size 1 at 0x5040000000bd thread T0
#0 0x556a6c6ea13f in node_to_openstep /src/libplist/src/oplist.c:203:47
#1 0x556a6c6e810b in plist_to_openstep /src/libplist/src/oplist.c:469:11
0x5040000000bd is located 0 bytes after 45-byte region [0x504000000090,0x5040000000bd)
allocated by thread T0 here:
#0 0x556a6c6882da in strdup /src/llvm-project/compiler-rt/lib/asan/asan_interceptors.cpp:570:3
#1 0x556a6c6efd26 in plist_copy_node /src/libplist/src/plist.c:587:31
SUMMARY: AddressSanitizer: heap-buffer-overflow /src/libplist/src/oplist.c:203:47 in node_to_openstep
Metadata
Metadata
Assignees
Labels
No labels