Kévin Subileau

Espace personnel


Local file disclosure vulnerability in Crayon Syntax Highlighter

I discovered a local file disclosure vulnerability affecting all versions before 2.7.0 of Crayon Syntax Highlighter, a popular syntax highlighter built in PHP and jQuery. According to wordpress.org, the vulnerable versions of this WordPress plugin are installed on more than 40,000 websites.

This critical vulnerability allows remote attackers to read arbitrary files on server's file system, even outside the web root. This includes PHP source code, configuration files and system files as /etc/passwd or wp-config.php for example. Furthermore, as you will see below, authentication is generally not required to exploit this vulnerability. Of course, the web server must have read access on the target files.

I tested a few versions between the old 1.10 to the latest 2.6.10, with a freshly installed WordPress 4.1 and the default configuration, and all tested versions were vulnerable. This means that the vulnerability exists since at least 3 years.

Technical analysis

Crayon Syntax Highlighter can highlight from a URL, inline code, or a local file. Here are some examples of normal markup that could be put inside post content in order to highlight code from these three source types:

<!-- Inline code -->
<pre class="lang:php">
    <?php //code to highlight here ?>

<!-- URL -->
<pre class="lang:java" data-url="http://example.com/class.java"></pre>

<!-- Local file - the interesting case -->
<pre class="lang:java" data-url="/java_sample/class.java"></pre>

In the local file case, by default, the path given in the data-url attribute is relative to the WordPress root directory. In the plugin settings page, the user can also specify a sub-folder from which this relative path should start. But, as you can see below in this piece of code extracted from crayon, the problem is that there is absolutely no control over the file type or the path given (stored in the variable $url).

// Try to replace the site URL with a path to force local loading
if (strpos($url, $site_http) !== FALSE || strpos($url, $site_path) !== FALSE ) {
    $url = str_replace($site_http, $site_path, $url);
    // Attempt to load locally
    $local = TRUE;
    $local_url = $url;
} else if (empty($scheme)) {
    // No url scheme is given - path may be given as relative
    $local_url = preg_replace('#^((\/|\\\\)*)?#', $site_path . $this->setting_val(CrayonSettings::LOCAL_PATH), $url);
    $local = TRUE;
// Try to find the file locally
if ($local == TRUE) {
    if ( ($contents = CrayonUtil::file($local_url)) !== FALSE ) {
    } else {
        // [...]

The given path is simply prepended with the base path at line 79, and then the file's content is loaded at line 84 (the method CrayonUtil::file just calls the function file_get_contents). So, for example, assuming that the administrator hasn't modified the standard WordPress file structure and with the default settings, it's possible to get the database credentials and WordPress secret keys simply by putting the code below inside a post or page content:

<pre data-url="/wp-config.php"></pre>

You can also use a directory traversal attack to reach the root directory and get the content of /etc/passwd for example:

<pre data-url="/../../../../../../../../../../../../../../../../../etc/passwd"></pre>

Of course it's also possible to get the content of theme's source files, .htaccess and .htpasswd files, log files, and other files that could contain highly sensitive information.

But so far, I told you that these exploits must be put inside a post or page, so normally this requires authentication. But the real issue is that unauthenticated visitors can also exploit the vulnerability simply by posting a comment containing the same malicious markup (except when an alternative comment system is used, as Disqus). It's because Crayon can also highlight inside comments, and this feature is enabled by default. This is why I say that authentication is generally not required to exploit this vulnerability.

Even if comments are moderated before publication, the issue remains the same because WordPress display the comment to its author before moderation with a message like "Your comment is awaiting moderation.". So the file content is revealed before the administrator can delete the malicious comment.

You can see bellow a screen capture showing the exploit in action, both inside the post content and a comment before moderation:

Disclosure of /etc/passwd and wp-config.php (click to enlarge)

Disclosure of /etc/passwd and wp-config.php (click to enlarge)

Vendor response

I sent the initial report of the vulnerability to the plugin author on January 4. He replied quickly and confirmed the issue.

On April 5, he informed me that he had patched the issue. Finally, on April 13, he released the version 2.7.0, which includes this patch.

I thank him for his cooperation and his excellent work.


The preferred solution is to update quickly to the latest version (>= 2.7.0), which fix the vulnerability by removing support of local file highlighting.

In the event where you really cannot install the update, an alternative way to limit risks is to disable Crayon in comments, in order to prevent attacks from unauthenticated visitor. But it will still be possible to exploit the vulnerability through a post or page content...

Disable Crayons in comments to prevent unauthenticated attacks

Disable Crayons in comments to prevent unauthenticated attacks

CVSS Score

I evaluate the CVSS Score at 7.8 (AV:N/AC:L/Au:N/C:C/I:N/A:N).


Secunia : #63998.
WPVDB : #7904.
OSVDB : #121278.

About me

For non-French speakers that can't read others pages about me on this blog (sorry, it's currently primarly intended to French speakers), I will introduce me briefly in English here so that you can know who am I. I'm Kevin Subileau, 23 years old. I'm not a professional security researcher (yet), but I'm a passionate in computing science, junior system & network engineer by day, and web developer by night, also interested by computing security. You can contact me by leaving a comment below (in English or French), on Twitter or by filling this form (fields are, top to bottom, name / email / subject / comment).


Vulnérabilité mfunc dans W3 Total Cache et WP Super Cache

Le week-end dernier, j'ai reçu simultanément trois commentaires plutôt originaux à modérer sur ce site. En effet, ces derniers tentaient d'exploiter une vulnérabilité connue sur les deux plugins de cache les plus populaires, W3 Total Cache et WP Super Cache. Cette faille de sécurité a été reportée il y a un an environ par kisscsaby sur le forum WordPress, et a fort heureusement été totalement corrigée depuis. J'ai tout de même souhaité vous en parler car je l'ai trouvée intéressante à étudier et aussi parce qu'elle reste active sur les nombreux sites non mis à jour...


Voilà un commentaire plus que suspect...

Cette attaque s'appuie sur plusieurs fonctions (mfunc, mclude, et dynamic-cached-content) présentes sur les versions vulnérables (W3TC ≤ ou WPSC ≤ 1.2), et permet d'exécuter un code PHP arbitraire sur le serveur. Autant dire que l'on peut tout faire ! Pour comprendre, il faut savoir qu'un site WordPress avec une version vulnérable d'un de ces plugins va exécuter le code PHP situé à l'intérieur de la balise ouvrante mfunc. Par exemple, le code <!--mfunc echo PHP_VERSION; -–><!–-/mfunc-–> va afficher la version de PHP utilisé.

Cette fonctionnalité permet initialement au développeur du site d'indiquer les éléments qui ne doivent pas être mis en cache par le plugin, comme par exemple la date ou le temps de génération de la page. Le code compris dans cette balise est alors exécuté à chaque fois, tandis que le reste de la page est mis en cache.

Lire la suite →