Estimated reading time: 6 minutes
TL;DR
A logic flaw existed in the CIccTagXmlProfileSequenceId::ParseXml function of the DemoIccMAX Project where the function unconditionally returned false and has been assigned CVE-2024-38427 and is part of the Bug Class known as Profile Bleed. Profile Bleed is saving a Color Profile that results with unintended information disclosure encoded into the binary blob.
CVSS 3.1 Base Score: 8.8 AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
DemoIccMAX Project Overview
The DemoIccMAX project, formerly known as RefIccMAX, offers an open-source set of libraries and tools for interacting with, manipulating, and applying iccMAX-based color management profiles. It supports the iccMAX profile specification and legacy ICC profiles defined by earlier ICC profile specifications.
DemoIccMAX serves as an industry starting point, offering a set of tools and libraries for handling XML and ICC color profiles.
Bug Details
- Patch Date: May 20, 2024
- Reporter: David Hoyt
- Method: Manual Source Code Review
- Patch via Pull Request: 66
Bug Type
CWE-253: Incorrect Check of Function Return Value
A logic flaw in the CIccTagXmlProfileSequenceId::ParseXml function of the DemoIccMAX project resulted in the unconditional return value of false. This flaw allows user-controllable inputs in the XML to be processed potentially leading to arbitrary code execution in the context of the user account credential. The code for CIccTagXmlProfileSequenceId::ParseXml was committed in 2015 with approximately 100K lines of code (LoC) and widely reused.
PoC

Prior Art
Over the past 3 years there have been many similar reports of color profile processing resulting in overflow conditions and memory corruption.
Fix
Commit ed4ee6.
Build
cd /tmp
wget https://github.com/InternationalColorConsortium/DemoIccMAX/archive/f891074a0f1c9d61a3dfa53749265f8c14ed4ee6.zip
unzip f891074a0f1c9d61a3dfa53749265f8c14ed4ee6.zip
cd DemoIccMAX-f891074a0f1c9d61a3dfa53749265f8c14ed4ee6/Build
cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-g -fsanitize=address,undefined -fno-omit-frame-pointer -Wall" -Wno-dev Cmake
make -j$(nproc) 2>&1 | grep 'error:'
Call Graph for CIccTagXmlProfileSequenceId::ParseXml
When the return value is set to false unconditionally, the ParseXml helper function does not complete its intended parsing process.

Understanding the Call Graph Nodes
The nodes in the call graph represent function calls made within CIccTagXmlProfileSequenceId::ParseXml.
Functions & Purposes:
CIccUTF16String::c_strRetrieves a C-style string from a UTF-16 string.icGetSigVal: Extracts a signature value.icXmlAttrValue: Retrieves an attribute value from an XML node.icXmlFindAttr: Finds an attribute within an XML node.icXmlFindNode: Finds a child node within an XML node.icXmlGetHexData: Converts hex data from a string to a binary format.CIccTagMultiLocalizedUnicode::SetText: Sets a localized text value.CIccLocalizedUnicode::SetText: Sets text for a localized Unicode object.
Call Graph for CIccTagXmlDict::ParseXml
When ParseXml returns true correctly, the profile is fully parsed, and tags are processed. The Validate function can assess the profile, producing meaningful warnings and errors.

Finding a Logical Flaw
Manual Code Review
Commit 889db62 was the entry point for code review. The codebase is approximately 100K lines of code, which is significant and requires a deep dive to understand the reference implementation.
I used basic command-line analysis tools like ctags and cscope with vim, and Doxygen with interactive SVG images to review the source code and call graphs. Knowing that any code utilizing ParseXml would be vulnerable, I started by setting up a ctags database using:
ctags -R .
I setup cscope database using:
find . -type f \( -name "*.c" -o -name "*.cpp" -o -name "*.h" \) > cscope.files
cscope -b -k
Search for [ParseXml:
cscope -L -0 ParseXml > cscope_output.txt
Contents of cscope_output.txt
...
IccXML/IccLibXML/IccMpeXml.h <global> 390 virtual bool ParseXml(xmlNode *pNode, std::string &parseStr);
IccXML/IccLibXML/IccProfileXml.h <global> 89 bool ParseXml(xmlNode *pNode, std::string &parseStr);
IccXML/IccLibXML/IccTagXml.cpp <global> 98 bool CIccTagXmlUnknown::ParseXml(xmlNode *pNode, std::string &parseStr)
IccXML/IccLibXML/IccTagXml.cpp <global> 255 bool CIccTagXmlText::ParseXml(xmlNode *pNode, std::string &parseStr)
IccXML/IccLibXML/IccTagXml.cpp <global> 266 bool CIccTagXmlUtf8Text::ParseXml(xmlNode *pNode, std::string &parseStr)
IccXML/IccLibXML/IccTagXml.cpp <global> 277 bool CIccTagXmlZipUtf8Text::ParseXml(xmlNode *pNode, std::string &parseStr)
IccXML/IccLibXML/IccTagXml.cpp <global> 302 bool CIccTagXmlZipXml::ParseXml(xmlNode *pNode, std::string &parseStr)
...
Functions that Indirectly Call into CIccTagXmlProfileSequenceId::ParseXml:
Total .cpp and .h file references: 226
Unique .cpp and .h files: 9
File-wise occurrence count:
IccXML/IccLibXML/IccMpeXml.cpp: 30
IccXML/IccLibXML/IccMpeXml.h: 19
IccXML/IccLibXML/IccProfileXml.cpp: 4
IccXML/IccLibXML/IccProfileXml.h: 1
IccXML/IccLibXML/IccTagXml.cpp: 58
IccXML/IccLibXML/IccTagXml.h: 47
IccXML/IccLibXML/build/Release/usr/local/include/IccMpeXml.h: 19
IccXML/IccLibXML/build/Release/usr/local/include/IccProfileXml.h: 1
IccXML/IccLibXML/build/Release/usr/local/include/IccTagXml.h: 47
CIccTagXmlProfileSequenceId::ParseXml
bool CIccTagXmlProfileSequenceId::ParseXml(xmlNode *pNode, std::string &parseStr)
...
return false;
}
The critical point is the return false. This unconditional return value passes unsanitized XML to the caller, potentially leading to arbitrary code execution in the context of the user credential. Let’s examine how the code handles data with this unconditional return value and compare it to the corrected return value of true.
Confirm false is incorrect unconditional return value
Taking a look at program execution flow, this is with the function CIccTagXmlProfileSequenceId::ParseXml and the return value == false stepping thru the code in lldb.
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
frame #0: 0x000000010121a900 libIccProfLib2.2.dylib`CIccProfile::Validate(this=0x00007ff7bfefede0, sReport="Warning! - Unknown NULL: Unregistered CMM signature.\r\nWarning! - spectralViewingConditionsTag::>illuminantXYZ - XYZNumber appears to be normalized! Y value should reflect absolute luminance.\r\nWarning! - spectralViewingConditionsTag::>surroundXYZ - XYZNumber appears to be normalized! Y value should reflect absolute luminance.\r\n", sSigPath="") const at IccProfile.cpp:2818:10
2815 rv = icMaxStatus(rv, i->pTag->Validate(sSigPath + icGetSigPath(i->TagInfo.sig), sReport, this));
2816 }
2817
-> 2818 return rv;
2819 }
2820
2821 /**
Key Observations:
- Incomplete Parsing: The profile may not be fully parsed, leading to missing or partially processed tags.
In earlier commits, the unconditional return value set to false caused significant issues during the execution of the CreateAllProfiles.sh script. The incorrect return value triggered heap-buffer-overflows, which were captured by AddressSanitizer. Below is a sample command and its resulting error:
+ iccFromXml CMYK-3DLUTs.xml CMYK-3DLUTs.icc
ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62b00001b04f at pc 0x0001054798fb bp 0x7ff7bb347010 sp 0x7ff7bb347008
READ of size 1 at 0x62b00001b04f thread T0
#0 0x1054798fa in CIccXmlArrayType<unsigned short, (icTagTypeSignature)1969828150>::ParseText(unsigned short*, unsigned int, char const*) IccUtilXml.cpp:1003
Changed Behavior with Correct Return Value true:
This change ensures the function processes the XML elements correctly and gracefully handles errors without causing memory corruption. The same command now results in a more controlled and descriptive output:
iccFromXml CMYK-3DLUTs.xml CMYK-3DLUTs.icc
Number of items parsed: 16
Number of items parsed: 4096
Number of items parsed: 4096
Number of items parsed: 4096
Number of items parsed: 2
Error parsing GridPoints.
Unable to parse element of type CIccMpeXmlCLUT starting on line 61
Unable to parse element of type CIccMpeXmlCalculator
Unable to parse element (CalculatorElement) starting on line 36
Unable to Parse "multiProcessElementType" (multiProcessElementType) Tag on line 33
Unable to Parse 'CMYK-3DLUTs.xml'
We can see the additional code paths with ctags and cscope too:
Total .cpp and .h file references: 240
Unique .cpp and .h files: 9
File-wise occurrence count:
IccXML/IccLibXML/IccMpeXml.cpp: 37
IccXML/IccLibXML/IccMpeXml.h: 21
IccXML/IccLibXML/IccProfileXml.cpp: 4
IccXML/IccLibXML/IccProfileXml.h: 1
IccXML/IccLibXML/IccTagXml.cpp: 59
IccXML/IccLibXML/IccTagXml.h: 48
IccXML/IccLibXML/build/Release/usr/local/include/IccMpeXml.h: 21
IccXML/IccLibXML/build/Release/usr/local/include/IccProfileXml.h: 1
IccXML/IccLibXML/build/Release/usr/local/include/IccTagXml.h: 48
Now, we see 240 references indicating that the updated return value exercises additional code paths.
CIccTagXmlProfileSequenceId::ParseXml return true
Confirm true is correct unconditional return value
Stepping through the updated code with lldb confirms the fix:
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x00000001000027cf iccFromXml`main + 927
iccFromXml`main:
-> 0x1000027cf <+927>: callq 0x100002c95 ; symbol stub for: CIccProfile::Validate(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>) const
0x1000027d4 <+932>: movl %eax, %r14d
0x1000027d7 <+935>: movq %r15, %r12
0x1000027da <+938>: testb $0x1, -0xc0(%rbp)
With the corrected return value of (true), the ICC profile parsing, validation, and saving processes complete successfully without errors. The program performs all necessary memory cleanup operations, preventing leaks, and new parsing errors with XML data.
Analysis of XML Unit Test Errors
There are unit test errors that indicate issues with parsing specific XML elements and types.
Import and File Parsing Failures
Example: Failed to parse import RefEstimationImport.xml file.
Tag Member Parsing Failures
Example: Failed to parse tag member float16NumberType.
Tag Parsing Failures
Example: Unable to parse float16ArrayType (float16NumberType) tag.
Element Parsing Failures
Example: Unable to parse element (CalculatorElement) tag.
Implication: Specific elements within the XML files could not be parsed. This could indicate the inability to process element types.
General Parsing Failures
Example: Unable to parse CMYK-3DLUTs.xml file.
Sample Parser Output
+ ../iccFromXml LCDDisplay.xml LCDDisplay.icc
Number of items parsed: 256
Number of items parsed: 256
Number of items parsed: 256
Number of items parsed: 401
Number of items parsed: 1203
Number of items parsed: 401
Number of items parsed: 401
Number of items parsed: 1203
Number of items parsed: 401
Number of items parsed: 256
Number of items parsed: 256
Number of items parsed: 256
Number of items parsed: 9
Number of items parsed: 3
Number of items parsed: 9
Number of items parsed: 3
Number of items parsed: 1203
Number of items parsed: 401
Number of items parsed: 401
Profile parsed and saved correctly
+ ../iccFromXml LaserProjector.xml LaserProjector.icc
Number of items parsed: 256
Number of items parsed: 256
Number of items parsed: 256
Number of items parsed: 401
Number of items parsed: 2
Error parsing GridPoints.
Unable to parse element of type CIccMpeXmlEmissionCLUT
Unable to parse element (EmissionCLutElement) starting on line 131
Unable to Parse "multiProcessElementType" (multiProcessElementType) Tag on line 24
Detailed Report
**Files Affected:**
- `RefEstimationImport.xml`
- `17ChanPart1.xml`
- `17ChanWithSpots-MVIS.xml`
- `18ChanWithSpots-MVIS.xml`
- `CMYK-3DLUTs.xml`
- `CMYK-3DLUTs2.xml`
- `CMYKOGP-MVIS-Smooth.xml`
- `ISO22028-Encoded-bg-sRGB.xml`
- `ISO22028-Encoded-sRGB.xml`
- `LaserProjector.xml`
- `NamedColor.xml`
- `RefDecC.xml`
- `RefDecH.xml`
- `RefIncW.xml`
- `argbRef.xml`
- `calcExercizeOps.xml`
- `sRgbEncodingOverrides.xml`
- `srgbCalc++Test.xml`
- `srgbCalcTest.xml`
- `srgbRef.xml`
- Parsing Issues:
- Entire files could not be parsed.
- Tag Member Parsing FailuresTags Affected:
float16NumberTypefloat32NumberType
- Specific tag members could not be parsed.
- Tag Parsing Failures Tags Affected:Tag Parsing:
- Specific tags could not be parsed.
- Element Parsing FailuresElements Affected:
- CalculatorElement
- EmissionCLutElement
- CIccMpeXmlCLUT
- CIccMpeXmlCalculator
- CIccMpeXmlEmissionCLUT
- CIccMpeXmlExtCLUT
- CIccMpeXmlTintArray
- Specific elements could not be parsed.
Observations
The use of using convert_type = std::codecvt_utf8<wchar_t>; and its related functionality in the DemoIccMAX Project is significant and potentially non-trivial to refactor comprehensively.
Details
File: DemoIccMAX-master/IccProfLib/IccTagDict.cpp:88:27
Code: using convert_type = std::codecvt_utf8<wchar_t>;
File: DemoIccMAX-master/IccProfLib/IccTagDict.cpp:216:24 ‘convert_type’ is deprecated
Code: std::wstring_convert<convert_type, wchar_t> converter;
Knowledgebase
- https://bugs.chromium.org/p/project-zero/issues/detail?id=2225
- https://bugs.chromium.org/p/project-zero/issues/detail?id=2226
My Prior Research
- https://srd.cx/cve-2022-26730/
- https://srd.cx/cve-2023-32443/
- https://xss.cx/2023/10/29/src/demomax-clang-static-analysis/
My Current CVE’s for the DemoIccMAX Project
- CVE-2023-46602 https://nvd.nist.gov/vuln/detail/CVE-2023-46602
- CVE-2023-46603 https://nvd.nist.gov/vuln/detail/CVE-2023-46603
- CVE-2023-46866 https://nvd.nist.gov/vuln/detail/CVE-2023-46866
- CVE-2023-46867 https://nvd.nist.gov/vuln/detail/CVE-2023-46867
- CVE-2023-47249 https://nvd.nist.gov/vuln/detail/CVE-2023-47249
- CVE-2023-48736 https://nvd.nist.gov/vuln/detail/CVE-2023-48736
- CVE-2024-38427 https://nvd.nist.gov/vuln/detail/CVE-2024-38427
DORKs for IccXmlLib or IccProfLib
Finding Open Source Repositories
inurl:github.com "CIccTagXmlProfileSequenceId"
inurl:gitlab.com "CIccTagXmlProfileSequenceId"
Identifying Web Applications and Services:
"ICC profile XML parsing" inurl:app
"embed ICC profile into images" inurl:service
"extract ICC profile from images" inurl:service
Locating Documentation and Tutorials
"CIccTagXmlProfileSequenceId::ParseXml" intitle:documentation
"ICC profile parsing tutorial" intitle:guide
Finding Vulnerable Instances
"XML parsing error" "ICC profile" inurl:log
"XML parsing failure" "ICC profile" inurl:error
intext:"libiccxml" OR intext:"iccproflib" "International Color Consortium" filetype:pdf OR filetype:txt OR filetype:md OR filetype:xml OR filetype:txt OR filetype:cpp
"Libiccxml" OR "iccproflib"
"iccxmllib" OR "iccproflib"


You must be logged in to post a comment.