Khalil Shreateh specializes in cybersecurity, particularly as a "white hat" hacker. He focuses on identifying and reporting security vulnerabilities in software and online platforms, with notable expertise in web application security. His most prominent work includes discovering a critical flaw in Facebook's system in 2013. Additionally, he develops free social media tools and browser extensions, contributing to digital security and user accessibility.

Get Rid of Ads!


Subscribe now for only $3 a month and enjoy an ad-free experience.

Contact us at khalil@khalil-shreateh.com

 

 

Samsung QuramDNG Type Confusion Detector Vulnerability Scanner
Samsung QuramDNG Type Confusion Detector Vulnerability Scanner
Samsung QuramDNG Type Confusion Detector Vulnerability Scanner

=============================================================================================================================================
| # Title Samsung QuramDNG Type Confusion Detector Vulnerability Scanner

=============================================================================================================================================
| # Title : Samsung QuramDNG Type Confusion Detector Vulnerability Scanner |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.1 (64 bits) |
| # Vendor : https://www.samsung.com/us/ |
=============================================================================================================================================

[+] References : https://packetstorm.news/files/id/213328/ & CVE-2025-58478

[+] Summary : This C++ scanner analyzes DNG (Digital Negative) files for the CVE-2025-58478 type confusion vulnerability in the libimagecodec.quram.so library used on Samsung devices.

[+] Affected Versions :

Samsung One UI 6.0 to One UI 8.0

Android 14, 15, and 16

Observed on Samsung Galaxy S22, S23, S24, and S25 series

[+] Key features :

Validates DNG/TIFF headers (Little/Big Endian).

Parses complete IFD chains and entries safely.

Inspects high?risk tags including OpcodeList1, BitsPerSample, and SampleFormat.

Detects type confusion scenarios where 8?bit unsigned image data is misinterpreted as 32?bit floating?point data by the QuramDNG decoder.

Flags malformed opcode structures that may lead to out?of?bounds memory access and potential code execution.

Produces a clear vulnerability report with exploit logic, affected components, and mitigation status.

[+] Fix Status :

Patched in Samsung December 2025 Security Update

This tool is intended for defensive security analysis and forensic validation of DNG files on Samsung platforms.

[+] POC :

#include <iostream>
#include <fstream>
#include <vector>
#include <cstdint>
#include <cstring>
#include <iomanip>
#include <algorithm>
#include <unordered_map>
#include <sstream>

class DNGVulnerabilityScanner {
private:
std::vector<uint8_t> fileData;
bool littleEndian;
bool isTiffValid;
bool vulnerabilityFound;

public:
DNGVulnerabilityScanner() : littleEndian(true), isTiffValid(false), vulnerabilityFound(false) {}

bool loadFile(const std::string& filename) {
std::ifstream file(filename, std::ios::binary);
if (!file) {
std::cerr << "[-] Failed to open file: " << filename << std::endl;
return false;
}

file.seekg(0, std::ios::end);
size_t size = file.tellg();
file.seekg(0, std::ios::beg);

if (size == 0) {
std::cerr << "[-] File is empty" << std::endl;
return false;
}

fileData.resize(size);
file.read(reinterpret_cast<char*>(fileData.data()), size);

std::cout << "[+] Loaded file: " << filename
<< " (" << size << " bytes)" << std::endl;
return true;
}

bool isVulnerable() const {
return vulnerabilityFound;
}

bool isValidOffset(size_t offset, size_t size = 1) const {
return offset < fileData.size() && (offset + size) <= fileData.size();
}

uint16_t readU16(size_t offset) const {
if (!isValidOffset(offset, 2)) return 0;
if (littleEndian) {
return static_cast<uint16_t>(fileData[offset]) |
(static_cast<uint16_t>(fileData[offset+1]) << 8);
}
return (static_cast<uint16_t>(fileData[offset]) << 8) |
static_cast<uint16_t>(fileData[offset+1]);
}

uint32_t readU32(size_t offset) const {
if (!isValidOffset(offset, 4)) return 0;
if (littleEndian) {
return static_cast<uint32_t>(fileData[offset]) |
(static_cast<uint32_t>(fileData[offset+1]) << 8) |
(static_cast<uint32_t>(fileData[offset+2]) << 16) |
(static_cast<uint32_t>(fileData[offset+3]) << 24);
}
return (static_cast<uint32_t>(fileData[offset]) << 24) |
(static_cast<uint32_t>(fileData[offset+1]) << 16) |
(static_cast<uint32_t>(fileData[offset+2]) << 8) |
static_cast<uint32_t>(fileData[offset+3]);
}

float readFloat(size_t offset) const {
uint32_t intValue = readU32(offset);
float result;
std::memcpy(&result, &intValue, sizeof(float));
return result;
}

std::string getTagName(uint16_t tag) const {
static const std::unordered_map<uint16_t, std::string> tagNames = {
{254, "NewSubFileType"},
{256, "ImageWidth"},
{257, "ImageLength"},
{258, "BitsPerSample"},
{259, "Compression"},
{262, "PhotometricInterpretation"},
{277, "SamplesPerPixel"},
{279, "StripByteCounts"},
{282, "XResolution"},
{283, "YResolution"},
{284, "PlanarConfiguration"},
{296, "ResolutionUnit"},
{339, "SampleFormat"},
{322, "TileWidth"},
{323, "TileLength"},
{324, "TileOffsets"},
{325, "TileByteCounts"},
{50706, "DNGVersion"},
{50707, "DNGBackwardVersion"},
{50708, "UniqueCameraModel"},
{50709, "LocalizedCameraModel"},
{50710, "CFAPlaneColor"},
{50711, "CFALayout"},
{50712, "LinearizationTable"},
{50713, "BlackLevelRepeatDim"},
{50714, "BlackLevel"},
{50715, "BlackLevelDeltaH"},
{50716, "BlackLevelDeltaV"},
{50717, "WhiteLevel"},
{50718, "DefaultScale"},
{50719, "DefaultCropOrigin"},
{50720, "DefaultCropSize"},
{50827, "ActiveArea"},
{50828, "MaskedAreas"},
{50829, "AsShotNeutral"},
{50931, "OriginalRawFileName"},
{50932, "OriginalRawFileData"},
{50964, "AsShotICCProfile"},
{50965, "AsShotPreProfileMatrix"},
{50966, "CurrentICCProfile"},
{50967, "CurrentPreProfileMatrix"},
{51008, "OpcodeList1"},
{51009, "OpcodeList2"},
{51022, "NoiseProfile"},
};

auto it = tagNames.find(tag);
if (it != tagNames.end()) return it->second;

std::stringstream ss;
ss << "UnknownTag(0x" << std::hex << std::setw(4) << std::setfill('0') << tag << ")";
return ss.str();
}

std::string getTypeName(uint16_t type) const {
switch(type) {
case 1: return "BYTE";
case 2: return "ASCII";
case 3: return "SHORT";
case 4: return "LONG";
case 5: return "RATIONAL";
case 6: return "SBYTE";
case 7: return "UNDEFINED";
case 8: return "SSHORT";
case 9: return "SLONG";
case 10: return "SRATIONAL";
case 11: return "FLOAT";
case 12: return "DOUBLE";
default: return "UNKNOWN(" + std::to_string(type) + ")";
}
}

size_t getTypeSize(uint16_t type) const {
switch(type) {
case 1: case 2: case 6: case 7: return 1;
case 3: case 8: return 2;
case 4: case 9: case 11: return 4;
case 5: case 10: case 12: return 8;
default: return 1; // Default to 1 byte for unknown types
}
}

bool validateTIFFHeader() {
if (fileData.size() < 8) {
std::cerr << "[-] File too small for TIFF header" << std::endl;
return false;
}

if (fileData[0] == 0x49 && fileData[1] == 0x49) {
littleEndian = true;
std::cout << "[+] Byte Order: Little Endian (II)" << std::endl;
} else if (fileData[0] == 0x4D && fileData[1] == 0x4D) {
littleEndian = false;
std::cout << "[+] Byte Order: Big Endian (MM)" << std::endl;
} else {
std::cerr << "[-] Invalid TIFF byte order marker" << std::endl;
return false;
}

uint16_t magic = readU16(2);
if (magic != 42) {
std::cerr << "[-] Invalid TIFF magic number: " << magic << std::endl;
return false;
}

std::cout << "[+] Valid TIFF magic number: " << magic << std::endl;
isTiffValid = true;
return true;
}

bool parseIFDChain(uint32_t firstIFDOffset) {
uint32_t currentIFDOffset = firstIFDOffset;
int ifdNumber = 0;
bool foundInAnyIFD = false;

while (currentIFDOffset != 0 && ifdNumber < 10) {
bool foundInThisIFD = parseIFD(currentIFDOffset, ifdNumber);
if (foundInThisIFD) {
foundInAnyIFD = true;
}

if (!isValidOffset(currentIFDOffset, 2)) {
break;
}

uint16_t entryCount = readU16(currentIFDOffset);
size_t nextIFDOffsetPos = currentIFDOffset + 2 + (entryCount * 12);

if (!isValidOffset(nextIFDOffsetPos, 4)) {
break;
}

currentIFDOffset = readU32(nextIFDOffsetPos);
ifdNumber++;
}

return foundInAnyIFD;
}

bool parseIFD(uint32_t ifdOffset, int ifdNumber = 0) {
if (!isValidOffset(ifdOffset, 2)) {
std::cerr << "[-] IFD" << ifdNumber << " offset out of bounds: 0x"
<< std::hex << ifdOffset << std::dec << std::endl;
return false;
}

uint16_t entryCount = readU16(ifdOffset);
std::cout << "\n[+] IFD" << ifdNumber << " at offset 0x" << std::hex << ifdOffset
<< " has " << std::dec << entryCount << " entries" << std::endl;

size_t entryOffset = ifdOffset + 2;
bool foundVulnerabilityInThisIFD = false;

for (uint16_t i = 0; i < entryCount; i++) {
if (!isValidOffset(entryOffset, 12)) {
std::cerr << "[-] IFD entry " << i << " out of bounds" << std::endl;
break;
}

uint16_t tag = readU16(entryOffset);
uint16_t type = readU16(entryOffset + 2);
uint32_t count = readU32(entryOffset + 4);

std::cout << "\n [" << i << "] " << getTagName(tag)
<< " (0x" << std::hex << tag << ")" << std::dec << std::endl;
std::cout << " Type: " << getTypeName(type) << " (" << type << ")" << std::endl;
std::cout << " Count: " << count << std::endl;

bool isVulnerable = handleIFDEntryValue(tag, type, count, entryOffset + 8, ifdNumber);
if (isVulnerable) {
foundVulnerabilityInThisIFD = true;
vulnerabilityFound = true;
}

entryOffset += 12;
}

return foundVulnerabilityInThisIFD;
}

bool handleIFDEntryValue(uint16_t tag, uint16_t type, uint32_t count,
size_t valueOffset, int ifdNumber) {
size_t typeSize = getTypeSize(type);
uint64_t totalSize = static_cast<uint64_t>(count) * static_cast<uint64_t>(typeSize);
bool isVulnerable = false;

if (totalSize <= 4) {

std::cout << " Value: ";
printValue(tag, type, count, valueOffset, static_cast<size_t>(totalSize), false);
} else {

uint32_t dataOffset = readU32(valueOffset);
std::cout << " Data at offset: 0x" << std::hex << dataOffset
<< " (size: " << std::dec << totalSize << " bytes)" << std::endl;

if (totalSize > 1048576) {
std::cout << " [WARNING] Data size suspiciously large: "
<< totalSize << " bytes" << std::endl;
}

if (isValidOffset(dataOffset, static_cast<size_t>(std::min(totalSize, static_cast<uint64_t>(4096))))) {

std::cout << " First " << std::min(static_cast<size_t>(32), static_cast<size_t>(totalSize))
<< " bytes: ";
printValue(tag, type, std::min(count, 8u), dataOffset,
std::min(static_cast<size_t>(totalSize), static_cast<size_t>(32)), true);

if (tag == 51008) {
isVulnerable = analyzeOpcodeList1(dataOffset, count);
} else if (tag == 258) {
analyzeBitsPerSample(dataOffset, count);
} else if (tag == 339) {
analyzeSampleFormat(dataOffset, count);
} else if (tag == 324) {
analyzeTileOffsets(dataOffset, count);
} else if (tag == 325) {
analyzeTileByteCounts(dataOffset, count);
}
} else {
std::cout << " [WARNING] Data offset/size out of bounds" << std::endl;
}
}

return isVulnerable;
}

void printValue(uint16_t tag, uint16_t type, uint32_t count,
size_t offset, size_t totalSize, bool isExternal) {
if (!isValidOffset(offset, std::min(totalSize, static_cast<size_t>(256)))) {
std::cout << "[OUT OF BOUNDS]" << std::endl;
return;
}

switch(type) {
case 1:
case 6:
case 7:
if (count == 1 && totalSize == 1) {
std::cout << std::hex << "0x" << static_cast<int>(fileData[offset]) << std::dec;
} else {
std::cout << count << " " << getTypeName(type) << " values";
if (count <= 8) {
std::cout << " [";
for (uint32_t i = 0; i < count && i < 8; i++) {
if (i > 0) std::cout << " ";
std::cout << std::hex << static_cast<int>(fileData[offset + i]) << std::dec;
}
if (count > 8) std::cout << " ...";
std::cout << "]";
}
}
break;

case 2:
if (count <= 4 && !isExternal) {
std::cout << "\"";
for (uint32_t i = 0; i < count; i++) {
unsigned char c = static_cast<unsigned char>(fileData[offset + i]);
std::cout << (c >= 32 && c < 127 ? static_cast<char>(c) : '.');
}
std::cout << "\"";
} else {
std::string str;
size_t printCount = std::min(count, static_cast<uint32_t>(64));
for (uint32_t i = 0; i < printCount; i++) {
unsigned char c = static_cast<unsigned char>(fileData[offset + i]);
if (c == 0) break;
str += (c >= 32 && c < 127 ? static_cast<char>(c) : '.');
}
std::cout << "\"" << str << "\"";
if (str.length() < count) std::cout << " ...";
}
break;

case 3:
case 8:
if (count == 1) {
uint16_t val = readU16(offset);
std::cout << val << " (0x" << std::hex << val << ")" << std::dec;
} else {
std::cout << count << " " << getTypeName(type) << " values";
if (count <= 4) {
std::cout << " [";
for (uint32_t i = 0; i < count; i++) {
if (i > 0) std::cout << ", ";
uint16_t val = readU16(offset + i * 2);
std::cout << val;
}
std::cout << "]";
}
}
break;

case 4:
case 9:
if (count == 1) {
uint32_t val = readU32(offset);
std::cout << val << " (0x" << std::hex << val << ")" << std::dec;
} else {
std::cout << count << " " << getTypeName(type) << " values";
if (count <= 2) {
std::cout << " [";
for (uint32_t i = 0; i < count; i++) {
if (i > 0) std::cout << ", ";
uint32_t val = readU32(offset + i * 4);
std::cout << val;
}
std::cout << "]";
}
}
break;

case 5:
case 10:
std::cout << count << " " << getTypeName(type) << " values";
break;

case 11:
if (count == 1) {
float val = readFloat(offset);
std::cout << val;
} else {
std::cout << count << " FLOAT values";
if (count <= 2) {
std::cout << " [";
for (uint32_t i = 0; i < count; i++) {
if (i > 0) std::cout << ", ";
float val = readFloat(offset + i * 4);
std::cout << val;
}
std::cout << "]";
}
}
break;

case 12: // DOUBLE
std::cout << count << " DOUBLE values";
break;

default:
std::cout << count << " values of unknown type " << type;
std::cout << " (assuming " << getTypeSize(type) << " bytes each)";
break;
}
std::cout << std::endl;
}

bool analyzeOpcodeList1(size_t offset, uint32_t count) {
std::cout << " [ANALYZING OPCODE LIST]" << std::endl;

if (count < 16) {
std::cout << " [WARNING] OpcodeList1 too small: " << count << " bytes" << std::endl;
return false;
}

uint32_t dataSize = readU32(offset + 12);
uint64_t totalOpcodeSize = static_cast<uint64_t>(16) + static_cast<uint64_t>(dataSize);

if (totalOpcodeSize > count) {
std::cout << " [WARNING] Opcode data size (" << dataSize
<< ") exceeds available space (" << count << ")" << std::endl;
return false;
}

if (!isValidOffset(offset, static_cast<size_t>(std::min(totalOpcodeSize, static_cast<uint64_t>(count))))) {
std::cout << " [WARNING] Opcode data out of bounds" << std::endl;
return false;
}

uint32_t opcodeId = readU32(offset);
uint32_t version = readU32(offset + 4);
uint32_t flags = readU32(offset + 8);

std::cout << " Opcode ID: " << opcodeId;
if (opcodeId == 1) std::cout << " (ScalePerRow)";
else if (opcodeId == 2) std::cout << " (LookupTable)";
else if (opcodeId == 3) std::cout << " (MapTable)";
else if (opcodeId == 4) std::cout << " (DeltaPerRow)";
else if (opcodeId == 5) std::cout << " (ScalePerCol)";
else if (opcodeId == 6) std::cout << " (DeltaPerCol)";
std::cout << std::endl;

std::cout << " Version: 0x" << std::hex << version << std::dec << std::endl;
std::cout << " Flags: 0x" << std::hex << flags << std::dec;

bool isVulnerable = false;

if (flags == 0x41414141 || flags == 0x42424242 || flags == 0x43434343 ||
flags == 0x44444444 || flags == 0x45454545) {
std::cout << " [MALICIOUS - Common exploit pattern]" << std::endl;
isVulnerable = true;
} else if ((flags & 0xF0F0F0F0) == 0x40404040) {
std::cout << " [SUSPICIOUS - May cause unexpected behavior]" << std::endl;
isVulnerable = true;
} else {
std::cout << std::endl;
}

std::cout << " Data Size: " << dataSize << " bytes" << std::endl;

if (opcodeId == 1) {
std::cout << " [POTENTIAL TYPE CONFUSION] ScalePerRow opcode detected" << std::endl;

if (dataSize >= 36) {
uint32_t planes = readU32(offset + 16 + 16); // planes field
std::cout << " Planes: " << planes << std::endl;
}

isVulnerable = true;
}

return isVulnerable;
}

void analyzeBitsPerSample(size_t offset, uint32_t count) {
std::cout << " [BITS PER SAMPLE ANALYSIS]" << std::endl;

size_t maxToDisplay = std::min(count, static_cast<uint32_t>(16));
bool has8Bit = false;
bool has32Bit = false;

for (uint32_t i = 0; i < maxToDisplay; i++) {
if (!isValidOffset(offset + i * 2, 2)) break;

uint16_t bits = readU16(offset + i * 2);

if (i < 4) { // Display first 4 values
std::cout << " Plane " << i << ": " << bits << " bits";

if (bits == 8) {
std::cout << " [WARNING: 8-bit with ScalePerRow may cause type confusion]";
has8Bit = true;
} else if (bits == 32) {
std::cout << " [32-bit - compatible with float]";
has32Bit = true;
} else if (bits != 16 && bits != 32) {
std::cout << " [NOTE: Non-standard bit depth]";
}
std::cout << std::endl;
}

if (bits == 8) has8Bit = true;
if (bits == 32) has32Bit = true;
}

if (count > 4) {
std::cout << " ... and " << (count - 4) << " more planes" << std::endl;
}

if (has8Bit && !has32Bit) {
std::cout << " [HIGH RISK] 8-bit samples without 32-bit option" << std::endl;
} else if (has8Bit) {
std::cout << " [MEDIUM RISK] Mixed 8-bit and 32-bit samples" << std::endl;
}
}

void analyzeSampleFormat(size_t offset, uint32_t count) {
std::cout << " [SAMPLE FORMAT ANALYSIS]" << std::endl;

size_t maxToDisplay = std::min(count, static_cast<uint32_t>(16));
bool hasUnsigned = false;
bool hasFloat = false;

for (uint32_t i = 0; i < maxToDisplay; i++) {
if (!isValidOffset(offset + i * 2, 2)) break;

uint16_t format = readU16(offset + i * 2);
std::string formatName;

switch(format) {
case 1: formatName = "Unsigned Integer"; hasUnsigned = true; break;
case 2: formatName = "Signed Integer"; break;
case 3: formatName = "Floating Point"; hasFloat = true; break;
default: formatName = "Unknown(" + std::to_string(format) + ")"; break;
}

if (i < 4) { // Display first 4 values
std::cout << " Plane " << i << ": " << formatName << " (" << format << ")";

if (format == 1) {
std::cout << " [WARNING: Unsigned with ScalePerRow will cause type confusion]";
} else if (format != 3) {
std::cout << " [NOTE: Non-float with ScalePerRow may cause issues]";
}
std::cout << std::endl;
}
}

if (count > 4) {
std::cout << " ... and " << (count - 4) << " more planes" << std::endl;
}

if (hasUnsigned && !hasFloat) {
std::cout << " [HIGH RISK] Unsigned format without float option" << std::endl;
} else if (hasUnsigned) {
std::cout << " [MEDIUM RISK] Mixed unsigned and float formats" << std::endl;
}
}

void analyzeTileOffsets(size_t offset, uint32_t count) {
std::cout << " [TILE OFFSETS ANALYSIS]" << std::endl;

size_t maxToDisplay = std::min(count, static_cast<uint32_t>(4));
bool hasInvalidOffset = false;

for (uint32_t i = 0; i < maxToDisplay; i++) {
if (!isValidOffset(offset + i * 4, 4)) {
hasInvalidOffset = true;
break;
}

uint32_t tileOffset = readU32(offset + i * 4);
std::cout << " Tile " << i << " offset: 0x" << std::hex << tileOffset << std::dec;

if (tileOffset >= fileData.size()) {
std::cout << " [INVALID - out of file bounds]";
hasInvalidOffset = true;
} else if (tileOffset < 100) {
std::cout << " [SUSPICIOUS - very small offset]";
}
std::cout << std::endl;
}

if (count > 4) {
std::cout << " ... and " << (count - 4) << " more tiles" << std::endl;
}

if (hasInvalidOffset) {
std::cout << " [WARNING] Invalid tile offsets detected" << std::endl;
}
}

void analyzeTileByteCounts(size_t offset, uint32_t count) {
std::cout << " [TILE BYTE COUNTS ANALYSIS]" << std::endl;

size_t maxToDisplay = std::min(count, static_cast<uint32_t>(4));
uint64_t totalBytes = 0;
bool hasLargeCount = false;

for (uint32_t i = 0; i < maxToDisplay; i++) {
if (!isValidOffset(offset + i * 4, 4)) break;

uint32_t byteCount = readU32(offset + i * 4);
totalBytes += byteCount;
std::cout << " Tile " << i << " size: " << byteCount << " bytes";

if (byteCount > 10485760) { // 10MB
std::cout << " [SUSPICIOUS - very large tile]";
hasLargeCount = true;
} else if (byteCount == 0) {
std::cout << " [WARNING - zero-sized tile]";
}
std::cout << std::endl;
}

if (count > 4) {
std::cout << " ... and " << (count - 4) << " more tiles" << std::endl;
}

std::cout << " Total tiles size: " << totalBytes << " bytes" << std::endl;

if (hasLargeCount) {
std::cout << " [WARNING] Very large tile sizes detected" << std::endl;
}
}

bool scanForVulnerability() {
if (!validateTIFFHeader()) {
return false;
}

// Read IFD0 offset from TIFF header
uint32_t ifd0Offset = readU32(4);
std::cout << "[+] First IFD at offset: 0x" << std::hex << ifd0Offset << std::dec << std::endl;

bool foundVulnerability = parseIFDChain(ifd0Offset);

return foundVulnerability;
}

void printReport() {
if (vulnerabilityFound) {
std::cout << "\n" << std::string(60, '!') << std::endl;
std::cout << "!! VULNERABLE DNG FILE DETECTED !!" << std::endl;
std::cout << std::string(60, '!') << std::endl;

printExploitInfo();
} else {
std::cout << "\n" << std::string(60, '=') << std::endl;
std::cout << "SAFE DNG FILE" << std::endl;
std::cout << std::string(60, '=') << std::endl;
std::cout << "No obvious CVE-2025-58478 vulnerability detected." << std::endl;
std::cout << "Note: This doesn't guarantee the file is completely safe." << std::endl;
}
}

void printExploitInfo() {
std::cout << "\n[EXPLOIT DETAILS]" << std::endl;
std::cout << "Vulnerability: Type Confusion in QuramDng library" << std::endl;
std::cout << "CVE: CVE-2025-58478" << std::endl;
std::cout << "Affected Library: libimagecodec.quram.so" << std::endl;
std::cout << "\nExploit Mechanism:" << std::endl;
std::cout << "1. DNG file specifies 8-bit Unsigned data (BitsPerSample=8, SampleFormat=1)" << std::endl;
std::cout << "2. But includes ScalePerRow opcode that expects 32-bit Float data" << std::endl;
std::cout << "3. Library treats uint8_t* as float* leading to OOB read/write" << std::endl;
std::cout << "4. Results in memory corruption and potential code execution" << std::endl;
std::cout << "\nAffected Apps:" << std::endl;
std::cout << "- com.samsung.ipservice (auto-decodes after MEDIA_SCANNER_SCAN_FILE)" << std::endl;
std::cout << "- com.samsung.gallery3d (Samsung Gallery)" << std::endl;
std::cout << "\nAffected Devices:" << std::endl;
std::cout << "- Samsung Galaxy S22, S23, S24, S25 series" << std::endl;
std::cout << "- One UI 6.0 through 8.0 (Android 14-16)" << std::endl;
std::cout << "\nFixed in: Samsung December 2025 Security Update" << std::endl;
std::cout << "Patch: https://security.samsungmobile.com/securityUpdate.smsb?year=2025&month=12" << std::endl;
}
};

int main(int argc, char* argv[]) {
if (argc != 2) {
std::cout << "Samsung QuramDng Vulnerability Scanner" << std::endl;
std::cout << "CVE-2025-58478 - Type Confusion in libimagecodec.quram.so" << std::endl;
std::cout << "======================================================" << std::endl;
std::cout << "Usage: " << argv[0] << " <dng_file>" << std::endl;
std::cout << "\nReturn Codes:" << std::endl;
std::cout << " 0 - File is safe or scanner completed successfully" << std::endl;
std::cout << " 1 - File is vulnerable to CVE-2025-58478" << std::endl;
std::cout << " 2 - Error reading or parsing file" << std::endl;
std::cout << "\nExamples:" << std::endl;
std::cout << " " << argv[0] << " exploit.dng" << std::endl;
std::cout << " " << argv[0] << " test_image.dng" << std::endl;
return 2;
}

std::cout << "Samsung QuramDng Vulnerability Scanner " << std::endl;
std::cout << "CVE-2025-58478 - Type Confusion in libimagecodec.quram.so" << std::endl;
std::cout << "======================================================" << std::endl;

DNGVulnerabilityScanner scanner;

if (!scanner.loadFile(argv[1])) {
return 2;
}

try {
bool isVulnerable = scanner.scanForVulnerability();
scanner.printReport();

if (isVulnerable) {
std::cout << "\n[!] WARNING: This DNG file contains the vulnerability" << std::endl;
std::cout << "[!] Do not open on vulnerable Samsung devices" << std::endl;
std::cout << "[!] Fixed in: Samsung December 2025 Security Update" << std::endl;
return 1; // Return 1 for vulnerable file
} else {
std::cout << "\n[+] File appears to be safe from CVE-2025-58478" << std::endl;
return 0; // Return 0 for safe file
}
} catch (const std::exception& e) {
std::cerr << "\n[-] Error during scanning: " << e.what() << std::endl;
return 2;
} catch (...) {
std::cerr << "\n[-] Unknown error during scanning" << std::endl;
return 2;
}
}

Greetings to :============================================================
jericho * Larry W. Cashdollar * r00t * Malvuln (John Page aka hyp3rlinx)*|
==========================================================================

Social Media Share