Handlebars Tempelate Injection and RCE

mahmoudsec.blogspot.com · devanshbatham/Awesome-Bugbounty-Writeups · 4 hours ago · vulnerability
0 net
AI Summary

A researcher discovered a zero-day Server-Side Template Injection (SSTI) vulnerability in the Handlebars template engine used in Shopify's Return Magic app, achieving Remote Code Execution by exploiting Object.prototype methods and the Function constructor to bypass sandbox restrictions. The exploit leverages the 'with' helper and Object.prototype.defineProperty() to inject arbitrary code through email workflow templates.

Entities
Handlebars Shopify Return Magic HackerOne H1-514 Synack TrendMicro Matias
Handlebars template injection and RCE in a Shopify app Skip to main content Handlebars template injection and RCE in a Shopify app Get link Facebook X Pinterest Email Other Apps April 04, 2019 TL;DR We found a zero-day within a JavaScript template library called handlebars and used it to get Remote Code Execution in the Shopify Return Magic app. The Story: In October 2018, Shopify organized the HackerOne event "H1-514" to which some specific researchers were invited and I was one of them. Some of the Shopify apps that were in scope included an application called "Return Magic" that would automate the whole return process when a customer wants to return a product that they already purchased through a Shopify store. Looking at the application, I found that it has a feature called Email WorkFlow where shop owners can customize the email message sent to users once they return a product. Users could use variables in their template such as {{order.number}} , {{email}} ..etc. I decided to test this feature for Server Side Template injection and entered {{this}} {{self}} then sent a test email to myself and the email had [object Object] within it which immediately attracted my attention. So I spent a lot of time trying to find out what the template engine was, I searched for popular NodeJs templates and thought the template engine was mustache (wrong), I kept looking for mustache template injection online but nothing came up as Mustache is supposed to be a logicless template engine with no ability to call functions which made no sense as I was able to call some Object attributes such as {{this.__proto__}} and even call functions such as {{this.constructor.constructor}} which is the Function constructor. I kept trying to send parameters to this.constructor.constructor() but failed. I decided that this was not vulnerable and moved on to look for more bugs. Then the fate decides that this bug needs to be found and I see a message from Shopify on the event slack channel asking researchers to submit their "almost bugs" so if someone found something and feels it's exploitable, they would send the bug to Shopify security team and if the team manages to exploit it the reporter will get paid as if they found it. Immediately I sent my submission explaining what I have found and at the impact section I wrote "Could be a Server Side template injection that can be used to take over the server ¯\_(ツ)_/¯". Two months passed and I got no response from Shopify regarding my "almost bug" submission, then I was invited to another hacking event in Bali hosted by Synack. There I met the Synack Red Team and after the Synack event has ended, I was supposed to travel back to Egypt, but only 3 hours before the flight I decided to extended my stay for three more days then fly from Bali to Japan where I was supposed to participate in the TrendMicro CTF competition with my CTF team. Some of the SRT also decided to extend their stay in Bali. One of those was Matias so I contacted him to hangout together. After swimming in the ocean and enjoying the beautiful nature of Bali, we went to a restaurant for dinner where Matias told me about a bug he found in a bug bounty program that had something to do with JavaScript sandbox escape so we spent all night missing with objects and constructors, but unfortunately we couldn't escape the sandbox. I couldn't take constructors out of my head and I remembered the template injection bug I found in Shopify. I looked at the HackerOne report and thought that the template can't be mustache so I installed mustache locally and when I parsed {{this}} with mustache it actually returns nothing which is not the case with the Shopify application. I searched again for popular NodeJs template engines and I found a bunch of them, I looked for those that used curly brackets {{ }} for template expressions and downloaded them locally, one of the libraries was handlebars and when I parsed {{this}} it returned [object Object] which is the same as the Shopify app. I looked at handlebars documentation and found out that it's also supposed to not have much logic to prevent template injection attacks. But knowing that I can access the function constructor I decided to give it a try and see how I can pass parameters to functions. After reading the documentation, I found out that in handlebars developers can register functions as helpers in the template scope. We can pass parameters to helpers like this {{helper "param1" "param2" ...params}} . So the first thing I tried was {{this.constructor.constructor "console.log(process.pid)"}} but it just returned console.log(process.pid) as a string. I went to the source code to find out what was happening. At the runtime.js file, there was the following function: So what this function does is that it checks if the current object is of type 'function' and if so it just calls it using current.call(context) where context is the template scope, otherwise, it would just return the object itself. I looked further in the documentation of handlebars and found out that it had built in helpers such as "with", "blockHelperMissing", "forEach" ...etc After reading the source code for each helper, I had an exploitation in mind using the "with" helper as it is used to shift the context for a section of a template by using the built-in with block helper. So I would be able to perform curren.call(context) on my own context. So I tried the following: Basically that should pass console.log(process.pid) as the current context, then when the handlebars compiler reaches this.constructor.constructor and finds that it's a function, it should call it with the current context as the function argument. Then using {{#with this}} we call the returned function from the Function constructor and console.log(process.pid) gets executed. However, this did not work because function.call() is used to invoke a method with an owner object as an argument, so the first argument is the owner object and other arguments are the parameters sent to the function being called. So if the function was called like current.call(this, context) , the previous payload would have worked. I spent two more nights in Ubud then flew to Tokyo for the TrendMicro CTF. Again in Tokyo, I couldn't take objects and constructors out of my mind and kept trying to find a way to escape the sandbox. I had another idea of using Array.map() to call Function constructor on my context, but it didn't work because the compiler always passes an extra argument to any function I call which is an object containing the template scope which causes an error as my payload is considered a function argument not the function body. There seemed to be many possible ways to escape the sandbox but I had one big problem facing me which is that whenever a function is called within the template, the template compiler sends the template scope Object as the last parameter. For example, if I try to call something like constructor.constructor("test","test") , the compiler will call it like constructor.constructor("test", "test", this) and this will be converted to a string by calling Object.toString() and the anonymous function created will be: which will cause an error. I tried many other things but still no luck, then I decided to open the JavaScript documentation for Object prototype and look for something that could help escape the sandbox. I found out that I could overwrite the Object.prototype.toString() function using Object .prototype. defineProperty() so that it calls a function that returns a user controlled string (my payload). Since I can't define functions using the template, all I have to do is to find a function that is already defined within the template scope and returns a user controlled input. For example, the following nodejs application should be vulnerable: test.js example.html Now if you run this template, console.log(process.pid) gets executed. I reported that to Shopify and mentioned that if there was a function within the scope that returns a user controlled string, it would have been possible to get RCE. Later, when I met Ibrahim (@the_st0rm) I told him about my idea and he told me that I can use bind() to create a new function that when called will return my RCE payload. From JavaScript documentation: The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called. So now the idea is to create a string with whichever code I want to execute then bind its toString() to a function using bind() after that overwrite the Object.prototype.toString() function with that function. I spent a lot of time trying to apply this using handlebars templates, and eventually during my flight back to Egypt I was able to get a fully working PoC with no need to use functions defined in the template scope. Basically, what the template above does is: And when I tried it with Shopify, I got: Matias also texted me with an exploitation that he got which is much simpler than the one I used: With that said, I was able to get RCE on Shopify's Return Magic application as well as some other websites that used handlebars as a template engine. The vulnerability was also submitted to npm security and handlebars pushed a fix that disables access to constructors. The advisory can be found here: https://www.npmjs.com/advisories/755 In a nutshell You can use the following to inject Handlebars templates: Matias also had his own exploitation that is much simpler: Sorry for the long post, if you have any questions please drop me a tweet @Zombiehelp54 javascript nodejs sandbox Get link Facebook X Pinterest Email Other Apps Comments Mahmoud NourEldin April 4, 2019 at 11:13 AM Wow, very interesting blog Reply Delete Replies Reply Ahiezer April 4, 2019 at 1:22 PM Wow, fantastic word. Reply Delete Replies Reply NINAD SARANG April 6, 2019 at 10:18 AM nice finding. keep it up and keep posting such interesting issues Reply Delete Replies Reply Unknown April 7, 2019 at 8:05 PM the 2nd `{{#with (string.sub.apply 0 codelist)}}` its right? i think its should be `{{#with (this.apply 0 codelist)}}` Reply Delete Replies Mahmoud Gamal April 8, 2019 at 10:06 AM When the template compiler calls `apply()` it calls it on the current context, which in this case is `conslist[0]` which is the function constructor. So the way you call `apply()` doesn't really matter as the compiler will always call it on the current context. Matias used `#each` helper to shift the context for the section where `apply()` is called to the Function constructor. Btw it wouldn't have been possible to use `#with` as the with helper has the following line in its implementation: ``` if (_utils.isFunction(context)) { context = context.call(this); } ``` Basically what this does is that it checks if the context is a function, and if so it calls it and sets the context to whichever the function returns and in that case it will be an anonymous function returned by the function constructor rather than the Function constructor itself. In my exploit, I used `#blockHelperMissing` which does the same thing as `#with` except that it doesn't have the `_utils.isFunction(context)` check. Thanks for the question. Delete Replies Reply Mahmoud Gamal April 8, 2019 at 10:08 AM Your comment actually made me rethink about how I used `apply()` to call `bind()` in my initial exploit. I should have just used `bind()` the same way `apply()` was used (by shifting the context to `str.toString`). I've modified the exploit so it becomes more clean and easy to understand. Thanks again, Unknown person! Delete Replies Reply Balla May 26, 2020 at 5:49 AM Hi bro ... i found a company using handlebars with this same version ..can u send me your poc pls.. Delete Replies Reply Reply aymen azer April 8, 2019 at 2:35 PM السلام عليكم اخي انا اريد ان اصبح باحث امني ارجو ان تساعدني باجوبة بسيطة اريد مسدر لتعلم الثغرات البرمجية و ما هي لغات البرمجة التي احتاجها مع العلم اني عندي خلفية بسيطة في الجافا و c++ و الاسامبلي Reply Delete Replies Reply c0d3r.b0y June 10, 2019 at 3:26 AM Great finding. I have been recently finding a most secure template engine for node.js. I went through all and most of them are not sandboxed i.e. allows RCE easily. Is it fixed by handlebars ? Reply Delete Replies Reply Unknown July 16, 2019 at 5:34 AM it'll be nice to know which versions you've been referring to. RCE and XSS are not new to handlebars; were they using an outdated version? The link you referred to dates back to 2016, but your blog is in 2019. Great post btw! Thanks for sharing the details! Reply Delete Replies Mahmoud Gamal July 17, 2019 at 4:35 AM Versions of handlebars prior to 4.0.14 were vulnerable. Also, the link date is Feb 14th, 2019. Delete Replies Reply Reply MS Dynamics November 6, 2019 at 1:34 AM Thanks for posting. Its an Important topic to be read. Node JS training in hyderabad Reply Delete Replies Reply Data science November 28, 2019 at 1:49 AM This is great information and all relevant to me. I know when I engage with my readers on my blog posts, not only does it encourage others to leave comments, but it makes my blog feel more like a community – exactly what I want! Data Science Training in Hyderabad Hadoop Training in Hyderabad Java Training in Hyderabad Python online Training in Hyderabad Tableau online Training in Hyderabad Blockchain online Training in Hyderabad informatica online Training in Hyderabad devops online Training Reply Delete Replies Reply Priya December 14, 2019 at 3:17 AM I like your post very much. It is very much useful for everyone. I hope you will share more info about this. Node JS Online training Node JS training in Hyderabad Reply Delete Replies Reply rtusharkumarrastogi January 13, 2020 at 9:13 PM Welcome to the world of develop for MSK clinic.Best services provider in clinic. We provide genuine likes, followers and views to your Ultrasound guided injection. Regards Ultrasound guided injection Reply Delete Replies Reply Atul February 6, 2020 at 11:32 PM It was great experience after reading this. thanks for sharing such good stuff with us. JavaScript Institute in Delhi Reply Delete Replies Reply Pankaj February 7, 2020 at 2:15 AM This comment has been removed by the author. Reply Delete Replies Reply Buy Adderall Online-Adderall Online(Generic Adderall Online)) February 15, 2020 at 1:59 AM If you are feeling very depressed, have any panic disorders, Mental Stress, Anxiety Disorders visit the page and resolve all these problams and also New Year Offer begain offer limited period. Call Now For Buy Anxiety Medicines at very cheap price as compair to other dealer: +1-850-424-1335 Website: https://redditpharma.com/product/adderall-dosage/ https://redditpharma.com/product/ambien-dosage/ https://redditpharma.com/buy-alprazolam-online/ https://redditpharma.com/buy-hydrocodone-online/ https://redditpharma.com/buy-oxycodone-online/ https://redditpharma.com/buy-codeine-online/ Reply Delete Replies Reply seojonsmith February 24, 2020 at 1:08 AM Buy highest quality generic drugs, xanax online, SSD chemical solution Online, with fast & free services. Reply Delete Replies Reply Mexicanpills February 24, 2020 at 2:17 AM Amazing post. Thanks Buy Xanax Online Reply Delete Replies Reply Buy Adderall Online-Adderall Online(Generic Adderall Online)) February 26, 2020 at 4:11 AM topmedsreview: Provide the best information about anxiety, panic disorder, mental stress, depression, Hyperactivitys issuse. Top meds review is a social platform providing the best medical information, medicine review, and health guidance. Our website provides credible information, depth reference material about health subjects that matter to everyone. Anxiety Medicines Soma Addiction Types Of Xanax Bars Reply Delete Replies Reply SaiRangaTracedeals March 16, 2020 at 4:38 AM Thanks for sharing this article. Save you huge money & time. Download the Tracedeals Online Deals & Coupons&Sign In to get daily alerts from your favorite stores Offers,Coupons & Deals App Coupons and Offers App for Shopping Online Online Shopping Deals & Coupons App Online Shopping Offers & Coupons App Reply Delete Replies Reply dhramik March 22, 2020 at 12:12 AM The New Modern JavaScript Boot camp Course (2020) ProgrammingFree course Reply Delete Replies Reply kazzouzi May 20, 2020 at 4:56 AM to execute shell command use {{this.push "return require('child_process').execSync('ls -la');"}} Reply Delete Replies Reply mors July 4, 2020 at 9:11 PM عااااااااااااااااااااااااااااااااااااااش Reply Delete Replies Reply Add comment Load more... Post a Comment Popular posts from this blog SQL Injection: Utilizing XML Functions in Oracle and PostgreSQL to bypass WAFs February 13, 2023 TL;DR. In this blog post we will be discussing how built-in XML functions in Oracle and PostgreSQL database management systems can be used to bypass web application firewalls (WAFs). I will be presenting two real-life examples from private bug bounty programs where traditional methods for bypassing WAFs were not effective. Introduction It's really frustrating when you find a valid SQL injection vulnerability, but there isn't much to do because of a WAF blocking most of your payloads. Many WAF rules can be bypassed using character case switching, comments, splitting the payload into multiple parameters, double URL encoding and many other methods that depend on how the target application and the WAF handle your requests. However, In the cases we are discussing in this blog, I was not able to bypass the WAF using common WAF bypass methods. Case 1: SQL Injection in an Oracle database - WAF bypass using REGEXP_LIKE() and DBMS_XMLGEN.GETXMLTYPE() *This is a private bug bounty prog... Read more Exploiting Out Of Band XXE using internal network and php wrappers August 06, 2019 Hello hackers, A couple of weeks ago I tweeted about exploiting an out of band XXE vulnerability with a firewall blocking all outgoing requests including DNS lookups, so here is the full story: This is a private bug bounty program so I won't be mentioning who the vendor is. As usual during a hacking night while navigating the target application I came across an endpoint that took a parameter called xml but its value was encrypted. Later I found out that XML data sent to the backend is encrypted in the client side before sent in HTTP requests which means that XML data might not be properly validated in the backend, so I wanted to modify it to be able to inject my own XXE payload. So what I first tried was to find the JavaScript function used to encrypt the XML and do the same for my custom XML payload, however, the application's JavaScript was minimized with WebPack which made it very hard to read and trace functions. To avoid the hassle of finding the JavaScript encrypting... Read more Mahmoud Gamal Visit profile Archive 2023 1 February 1 2019 2 August 1 April 1 Handlebars template injection and RCE in a Shopify... 2018 1 July 1 2017 2 June 1 February 1 2015 2 November 1 September 1 Show more Show less Labels google hackerone hacking javascript nodejs sandbox security SQL Injection WAF WAF ByPass xss Show more Show less Report Abuse