Pocket Poster 3.3.0: worse_query port for iOS 26-27.2
- Replace bad_query with forcequitOS/worse_query (App Group based) - Read the App Group from the code signature entitlements - Add pathleak container enumeration and iOS 27 system container lookup - Route descriptor/CarPlay writes through worse_query - Add sign_personal.sh and POCKET_POSTER_SELFTEST diagnostics - Keep bad_query as an iOS 26 fallback
This commit is contained in:
1 parent
2472fdc48b
commit
4c7d968d49
18 files changed
+1026
-355
No files matched your search
@@ -139,16 +139,16 @@ class CarPlayManager {
|
||||
}
|
||||
|
||||
let cacheRel = "\(appHash)/Library/Caches/MappedImageCache/com.apple.CarPlayApp.wallpaper-images"
|
||||
if SymHandler.prefersBadQuery {
|
||||
let destPath = BadQuery.carPlayCachePath(appHash: appHash)
|
||||
if SymHandler.prefersSandboxEscape {
|
||||
let destPath = SandboxEscape.carPlayCachePath(appHash: appHash)
|
||||
do {
|
||||
try SymHandler.writeFilesViaBadQuery(toDirectory: destPath, files: toRemove)
|
||||
try SymHandler.writeFilesViaEscape(toDirectory: destPath, files: toRemove)
|
||||
// remove local staging copies after successful write
|
||||
for imgURL in toRemove {
|
||||
try? FileManager.default.removeItem(at: imgURL)
|
||||
}
|
||||
} catch {
|
||||
print("bad_query CarPlay failed, falling back to symlink: \(error)")
|
||||
print("worse_query CarPlay failed, falling back to symlink: \(error)")
|
||||
let _ = try SymHandler.createAppSymlink(for: cacheRel)
|
||||
for imgURL in toRemove {
|
||||
try FileManager.default.trashItem(at: imgURL, resultingItemURL: nil)
|
||||
|
||||
@@ -51,11 +51,11 @@ class PosterBoardManager: ObservableObject {
|
||||
return false
|
||||
}
|
||||
|
||||
/// Wipe PosterBoard custom descriptor collections using bad_query (real delete).
|
||||
/// Wipe PosterBoard custom descriptor collections using worse_query (real delete).
|
||||
/// Falls back to language-toggle trick on older exploit path.
|
||||
func resetCollections(appHash: String) throws {
|
||||
if SymHandler.prefersBadQuery {
|
||||
try wipeDescriptorsViaBadQuery(appHash: appHash)
|
||||
if SymHandler.prefersSandboxEscape {
|
||||
try wipeDescriptorsViaEscape(appHash: appHash)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -69,18 +69,23 @@ class PosterBoardManager: ObservableObject {
|
||||
}
|
||||
|
||||
/// Delete everything under each PRBPosterExtensionDataStore/*/Extensions/*/descriptors.
|
||||
private func wipeDescriptorsViaBadQuery(appHash: String) throws {
|
||||
private func wipeDescriptorsViaEscape(appHash: String) throws {
|
||||
let ver = SymHandler.getExtensionVersion()
|
||||
let extensionsRoot = BadQuery.applicationContainerPath(appHash: appHash)
|
||||
let extensionsRoot = SandboxEscape.applicationContainerPath(appHash: appHash)
|
||||
+ "/Library/Application Support/PRBPosterExtensionDataStore/\(ver)/Extensions"
|
||||
|
||||
// Open parent with sandbox extension
|
||||
let rootHandle = try BadQuery.consume(path: extensionsRoot, create: true)
|
||||
// Open parent with sandbox extension. A missing path simply means there
|
||||
// is nothing to wipe yet.
|
||||
guard let rootHandle = try? SandboxEscape.consume(path: extensionsRoot, create: true) else {
|
||||
if !SandboxEscape.isAvailable {
|
||||
throw SandboxEscapeError.unavailable
|
||||
}
|
||||
return
|
||||
}
|
||||
defer { rootHandle.release() }
|
||||
|
||||
let fm = FileManager.default
|
||||
guard fm.fileExists(atPath: extensionsRoot) else {
|
||||
// Nothing to wipe — treat as success (collections already empty / never set up)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -92,7 +97,7 @@ class PosterBoardManager: ObservableObject {
|
||||
.appending("/descriptors")
|
||||
|
||||
// Ensure we hold an extension on the descriptors dir itself
|
||||
let descHandle = try? BadQuery.consume(path: descriptorsPath, create: true)
|
||||
let descHandle = try? SandboxEscape.consume(path: descriptorsPath, create: true)
|
||||
defer { descHandle?.release() }
|
||||
|
||||
guard fm.fileExists(atPath: descriptorsPath) else { continue }
|
||||
@@ -273,9 +278,9 @@ class PosterBoardManager: ObservableObject {
|
||||
SymHandler.cleanup()
|
||||
}
|
||||
|
||||
let useBadQuery = SymHandler.prefersBadQuery
|
||||
if useBadQuery {
|
||||
UIApplication.shared.change(title: NSLocalizedString("Applying Wallpapers...", comment: ""), body: "bad_query sandbox escape…")
|
||||
let useSandboxEscape = SymHandler.prefersSandboxEscape
|
||||
if useSandboxEscape {
|
||||
UIApplication.shared.change(title: NSLocalizedString("Applying Wallpapers...", comment: ""), body: "worse_query sandbox escape…")
|
||||
}
|
||||
|
||||
for (ext, descriptorsList) in extList {
|
||||
@@ -289,13 +294,13 @@ class PosterBoardManager: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
if useBadQuery {
|
||||
// iOS 26/27: direct write via container_query sandbox extension
|
||||
if useSandboxEscape {
|
||||
// iOS 26/27: direct write via the container_query sandbox extension
|
||||
do {
|
||||
try SymHandler.writeDescriptorsViaBadQuery(appHash: appHash, ext: ext, descriptorFolders: foldersToWrite)
|
||||
try SymHandler.writeDescriptorsViaEscape(appHash: appHash, ext: ext, descriptorFolders: foldersToWrite)
|
||||
} catch {
|
||||
// Fall back to legacy .Trash symlink if bad_query path fails
|
||||
print("bad_query apply failed, falling back to symlink: \(error)")
|
||||
// Fall back to legacy .Trash symlink if the escape path fails
|
||||
print("worse_query apply failed, falling back to symlink: \(error)")
|
||||
try applyDescriptorsViaSymlink(appHash: appHash, ext: ext, folders: foldersToWrite)
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -34,9 +34,9 @@ class SymHandler {
|
||||
return getLCDocumentsDirectory().appendingPathComponent(".Trash", conformingTo: .symbolicLink)
|
||||
}
|
||||
|
||||
/// Prefer bad_query (iOS 26/27 sandbox escape); fall back to .Trash symlink exploit.
|
||||
static var prefersBadQuery: Bool {
|
||||
BadQuery.isAvailable
|
||||
/// Prefer worse_query (iOS 26/27 sandbox escape); fall back to .Trash symlink exploit.
|
||||
static var prefersSandboxEscape: Bool {
|
||||
SandboxEscape.isAvailable
|
||||
}
|
||||
|
||||
// MARK: Symlink Creation (legacy exploit for older iOS)
|
||||
@@ -69,19 +69,20 @@ class SymHandler {
|
||||
return try createAppSymlink(for: "\(appHash)/Library/Application Support/PRBPosterExtensionDataStore/\(extVer)/Extensions/\(ext)/descriptors")
|
||||
}
|
||||
|
||||
// MARK: Direct write via bad_query
|
||||
|
||||
/// Copy descriptor folders into PosterBoard descriptors using sandbox escape.
|
||||
static func writeDescriptorsViaBadQuery(appHash: String, ext: String, descriptorFolders: [URL]) throws {
|
||||
let destPath = BadQuery.descriptorsPath(appHash: appHash, ext: ext)
|
||||
print("bad_query writing to \(destPath)")
|
||||
|
||||
// Ensure descriptors directory exists (open parent chain if needed)
|
||||
try BadQuery.ensureDirectory(at: destPath)
|
||||
|
||||
let handle = try BadQuery.consume(path: destPath, create: true)
|
||||
// MARK: Direct write via worse_query
|
||||
|
||||
/// Copy descriptor folders into PosterBoard descriptors using the sandbox escape.
|
||||
static func writeDescriptorsViaEscape(appHash: String, ext: String, descriptorFolders: [URL]) throws {
|
||||
let destPath = SandboxEscape.descriptorsPath(appHash: appHash, ext: ext)
|
||||
print("worse_query writing to \(destPath)")
|
||||
|
||||
// Make sure the extension data store exists (PosterBoard creates it on boot).
|
||||
let containerRoot = SandboxEscape.applicationContainerPath(appHash: appHash)
|
||||
try SandboxEscape.ensureDirectory(at: destPath, containerRoot: containerRoot)
|
||||
|
||||
let handle = try SandboxEscape.consume(path: destPath, create: true)
|
||||
defer { handle.release() }
|
||||
|
||||
|
||||
let fm = FileManager.default
|
||||
for descr in descriptorFolders {
|
||||
guard descr.lastPathComponent != "__MACOSX" else { continue }
|
||||
@@ -93,13 +94,13 @@ class SymHandler {
|
||||
try fm.copyItem(at: descr, to: destURL)
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy files into an absolute directory under an app container via bad_query.
|
||||
static func writeFilesViaBadQuery(toDirectory destPath: String, files: [URL]) throws {
|
||||
try BadQuery.ensureDirectory(at: destPath)
|
||||
let handle = try BadQuery.consume(path: destPath, create: true)
|
||||
|
||||
/// Copy files into an absolute directory under an app container via worse_query.
|
||||
static func writeFilesViaEscape(toDirectory destPath: String, files: [URL]) throws {
|
||||
try SandboxEscape.ensureDirectory(at: destPath)
|
||||
let handle = try SandboxEscape.consume(path: destPath, create: true)
|
||||
defer { handle.release() }
|
||||
|
||||
|
||||
let fm = FileManager.default
|
||||
for file in files {
|
||||
let destURL = URL(fileURLWithPath: destPath).appendingPathComponent(file.lastPathComponent)
|
||||
|
||||
Reference in new issue
Block a user