<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Manuel Benz</title>
    <description>The latest articles on DEV Community by Manuel Benz (@mbenz89).</description>
    <link>https://dev.to/mbenz89</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F495262%2F486e1c7b-a635-469e-923c-759f7eedd9ad.jpeg</url>
      <title>DEV Community: Manuel Benz</title>
      <link>https://dev.to/mbenz89</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mbenz89"/>
    <language>en</language>
    <item>
      <title>How to Prevent Code Injection Vulnerabilities in Serverless Applications (Part 2/2)</title>
      <dc:creator>Manuel Benz</dc:creator>
      <pubDate>Thu, 11 Feb 2021 10:14:01 +0000</pubDate>
      <link>https://dev.to/mbenz89/how-to-prevent-code-injection-vulnerabilities-in-serverless-applications-part-2-2-j5k</link>
      <guid>https://dev.to/mbenz89/how-to-prevent-code-injection-vulnerabilities-in-serverless-applications-part-2-2-j5k</guid>
      <description>&lt;p&gt;This article is &lt;strong&gt;Part 2&lt;/strong&gt; of a series. Here, I’m going to explain &lt;strong&gt;how to secure Serverless applications from injection attacks using AWS Web Application Firewalls (WAF)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 1&lt;/strong&gt; I introduced ServerlessGoat, an intentionally vulnerable serverless MS-Word &lt;em&gt;.doc&lt;/em&gt; to text converter service. I explained how to exploit its code injection vulnerability and how to fix the vulnerability by replacing an invocation of the command-line tool &lt;code&gt;cat&lt;/code&gt; with a proper JS library to download the MS-Word document. Furthermore, I shortly introduced input validation with denylists, i.e., excluding inputs that contain specific characters or match a certain format, and allowlists, i.e., only allowing inputs with a certain format or input that matches an item in a given list of constants. Let’s dive deep into how AWS WAF can help us implementing allow-/denylists for input validation!&lt;/p&gt;

&lt;p&gt;If you missed out on &lt;strong&gt;Part 1,&lt;/strong&gt; feel free to &lt;a href="https://codeshield.io/blog/2020/11/25/prevent_ci_part1/"&gt;catch up&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Input Validation using AWS WAF
&lt;/h2&gt;

&lt;p&gt;Input validation can of course be implemented in the serverless function directly — and should be according to &lt;a href="https://www.jeremydaly.com/event-injection-protecting-your-serverless-applications/"&gt;security best practices&lt;/a&gt;. However, serverless infrastructures often contain a plethora of different functions that might all profit from similar input validation in which case it makes sense to use a central point for additional pre-sanitization.&lt;/p&gt;

&lt;p&gt;Fortunately, with Web Application Firewalls (WAF) AWS proposes a means to conduct input validation for multiple functions even before they are invoked at all. WAFs can be attached to API gateways and Load Balancers and already allow the use of &lt;a href="https://s3.us-east-2.amazonaws.com/awswaf-owasp/owasp_10_base.yml"&gt;pre-configured rules for the OWASP Top-Ten&lt;/a&gt; as described in the &lt;a href="https://d0.awsstatic.com/whitepapers/Security/aws-waf-owasp.pdf"&gt;corresponding whitepaper&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementing a Denylist with AWS WAF&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using WAF, it is straight forward to implement a basic denylist approach and attach it to the ServerlessGoat API gateway. WAF specifications can be created with the AWS console, CLI, and REST API. For the sake of simplicity, I’m going to explain how to setup WAF in the AWS console. This allows for easier testing and playing around with WAF using a proper graphical user interface.&lt;/p&gt;

&lt;p&gt;After &lt;a href="https://eu-central-1.console.aws.amazon.com/lambda/home?region=eu-central-1#/create/app?applicationId=arn:aws:serverlessrepo:eu-central-1:217096764149:applications/Serverless-Goat-Java"&gt;deploying the ServerlessGoat application&lt;/a&gt;, let’s follow the &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-control-access-aws-waf.html"&gt;documentation&lt;/a&gt; to set up an Access Control List (ACL) for WAF in the created API gateway. Like in traditional firewalls, ACLs are lists that allow/prevent access from specific sources to specific assets based on rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Open the API Gateway console for the &lt;em&gt;serverless-goat&lt;/em&gt; API -&amp;gt; go to &lt;em&gt;Stages&lt;/em&gt; -&amp;gt; open &lt;em&gt;Prod&lt;/em&gt; stage -&amp;gt; create a new &lt;em&gt;Web ACL&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WvZW_U1f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rxscj1fjk5hh8rvmm6y4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WvZW_U1f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rxscj1fjk5hh8rvmm6y4.png" class="img-fluid"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; In the Web ACLs console, press &lt;em&gt;Create Web ACL.&lt;/em&gt; Fill-in metadata for the ACL and press &lt;em&gt;Add AWS resources&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cqalHmkU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/khwtnhegguj68wpczjn7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cqalHmkU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/khwtnhegguj68wpczjn7.png" class="img-fluid"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Select the &lt;em&gt;Prod&lt;/em&gt; and &lt;em&gt;Stage&lt;/em&gt; stages of the &lt;em&gt;serverless-goat&lt;/em&gt; API gateway -&amp;gt;Press &lt;em&gt;ADD -&amp;gt;&lt;/em&gt; Press &lt;em&gt;Next&lt;/em&gt; to add rules to the ACL&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KIbGjaOm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1ftudmtcwjge00t40yne.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KIbGjaOm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1ftudmtcwjge00t40yne.png" class="img-fluid"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; In the &lt;em&gt;Add rules and rule groups&lt;/em&gt; view, click on &lt;em&gt;Add rules&lt;/em&gt; and then select &lt;em&gt;Add my own rules and rule groups&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5:&lt;/strong&gt; Use the visual rule editor to create a rule that applies if a semicolon (;) character is contained in the &lt;em&gt;document_url&lt;/em&gt; query string parameter -&amp;gt; Since we are building a denylist, make sure to block the request if the rule applies -&amp;gt; Add the rule&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lxEUmfif--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fw71f1f3c9wcmofuy1xr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lxEUmfif--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fw71f1f3c9wcmofuy1xr.png" class="img-fluid"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6:&lt;/strong&gt; Make sure to allow all requests that do not match any rule! We want to only exclude requests where the &lt;em&gt;document_url&lt;/em&gt; query string parameter contains a semicolon. -&amp;gt; Press &lt;em&gt;Next -&amp;gt; Next -&amp;gt; Next -&amp;gt;&lt;/em&gt; Create the web ACL&lt;/p&gt;

&lt;p&gt;AWS will take some time to set up the ACL and attach it to the API gateway stages. After the creation is completed, we can test if our denylist behaves as expected.&lt;/p&gt;

&lt;p&gt;Browse to the &lt;em&gt;Prod&lt;/em&gt; stage view of the &lt;em&gt;serverless-goat&lt;/em&gt; API gateway. You can now see the ACL being correctly assigned to the stage. Expand the &lt;em&gt;/api/convert&lt;/em&gt; endpoint to acquire the endpoint URL of the stage:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y1ZoXoOB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/e1zm3ii94l7znzbmcrhc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y1ZoXoOB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/e1zm3ii94l7znzbmcrhc.png" class="img-fluid"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Using this URL, we can now test our denylist. If we propose a valid MS-Word document to the &lt;em&gt;document_url&lt;/em&gt; parameter, we get back the contained text as plaintext:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cmukeh7p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xfykgalch82dbh1y4ua8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cmukeh7p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xfykgalch82dbh1y4ua8.png" class="img-fluid"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Trying to attack the Goat with &lt;a href="https://foobar;"&gt;&lt;code&gt;https://foobar;&lt;/code&gt;&lt;/a&gt;&lt;code&gt;cat /etc/passwd #&lt;/code&gt; now results in &lt;em&gt;Forbidden&lt;/em&gt;, as our WAF has successfully detected the semicolon char in the input parameter:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qdAIbcos--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/npe780c53eqwor0naab5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qdAIbcos--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/npe780c53eqwor0naab5.png" class="img-fluid"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pitfalls of Denylists&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Are we secure now? &lt;strong&gt;NO! 😦&lt;/strong&gt; We just prevented the specific attack where commands are chained with a semicolon. However, bash at least allows for a bunch of other possible solutions to chain and inject additional commands into the OS command, e.g., using &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; or &lt;code&gt;||&lt;/code&gt; (to only name a few).&lt;/p&gt;

&lt;p&gt;If we now propose &lt;a href="https://www.puresec.io/hubfs/document.doc"&gt;&lt;code&gt;https://www.puresec.io/hubfs/document.doc&lt;/code&gt;&lt;/a&gt;&lt;code&gt;&amp;gt; /dev/null &amp;amp;&amp;amp; cat /etc/passwd #&lt;/code&gt; as &lt;em&gt;document_url,&lt;/em&gt; where we throw away the output of the curl command by directing it to &lt;code&gt;/dev/null&lt;/code&gt; and then chain the invocation of &lt;em&gt;cat&lt;/em&gt; with the &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; (and) operator, we are still able to exploit the command injection vulnerability to read &lt;code&gt;/etc/passwd&lt;/code&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ivy48l1k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8zfco5enq20gxscctijj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ivy48l1k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8zfco5enq20gxscctijj.png" class="img-fluid"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;This boils down to a general problem with denylists: It is very hard to come up with all possibly problematic inputs and specifically filter out requests that contain those. It’s almost guaranteed that an attacker will be able to find a proper input that circumvents the rules of a denylist by using specific publicly accessible enumeration tools. But how to properly validate the input then? Use &lt;strong&gt;allowlists&lt;/strong&gt; to only allow very specific input formats and deny everything else!&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing an Allowlist with AWS WAF
&lt;/h2&gt;

&lt;p&gt;Implementing allowlists with WAF is as easy as implementing denylists. For our allowlist, we want to come up with a pattern that is as specific as possible to only match a proper input document. Let’s, therefore, create a regular expression to match a valid &lt;em&gt;document_url.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For this, we again open the &lt;em&gt;&lt;a href="https://console.aws.amazon.com/wafv2/homev2"&gt;AWS Firewall Manager&lt;/a&gt; -&amp;gt;&lt;/em&gt; Select the &lt;em&gt;Regex pattern sets&lt;/em&gt; view -&amp;gt; Press &lt;em&gt;Create regex pattern set&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Yo79sCxa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2ojizobjyrte92cv9c26.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Yo79sCxa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2ojizobjyrte92cv9c26.png" class="img-fluid"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;We now create a regular expression in our region that only matches URLs starting with &lt;em&gt;http(s),&lt;/em&gt;  ending with &lt;em&gt;.doc,&lt;/em&gt; and nothing in between but plain words and slashes (&lt;em&gt;/).&lt;/em&gt; Thereby, we especially prevent an attacker from adding any bogus input before, after, or in between the provided URL:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vSqSKQcW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qzawxj890e3wge09v2ve.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vSqSKQcW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qzawxj890e3wge09v2ve.png" class="img-fluid"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Now, we want to add the previously created regular expression as a filter to our ACL:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jLV88PQt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6tmgh1bk7kowjc9yjtvc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jLV88PQt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6tmgh1bk7kowjc9yjtvc.png" class="img-fluid"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Go to &lt;em&gt;Rules&lt;/em&gt; tab -&amp;gt; &lt;em&gt;Add rules&lt;/em&gt; -&amp;gt; &lt;em&gt;Add my own rules and rule groups&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The setup is similar as in Step 5. This time, however, we are using our previously created regular expression as matcher:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5y_NAQ7Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4k68mqqc2llyb4cnc6rr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5y_NAQ7Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4k68mqqc2llyb4cnc6rr.png" class="img-fluid"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Remember, we are building an &lt;strong&gt;allowlist.&lt;/strong&gt; Thus, make sure that this time, to allow requests where the &lt;em&gt;document_url&lt;/em&gt; query parameter matches this rule.&lt;/p&gt;

&lt;p&gt;To finalize the allowlist, we now have to make sure that our previously created rule from the denylist is not active anymore and we only allow requests that match our newly created &lt;em&gt;Allow_Valid_URLs&lt;/em&gt; rule. Therefore, after creating the new rule, delete the old &lt;em&gt;Deny_Command_Chaining&lt;/em&gt; rule and set the &lt;em&gt;default web ACL action for requests that don’t match any rules&lt;/em&gt; to &lt;strong&gt;Block.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The rule section of our ACL should now look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Fy47wi04--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/dft6bui1l14bl6b1wxw6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Fy47wi04--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/dft6bui1l14bl6b1wxw6.png" class="img-fluid"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Since we edited the existing ACL that is already bound to our API gateway, we do not have to conduct any further steps. Let’s test our allowlist:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3wMHM_NT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/c9tjxhntcee5q0qsw9se.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3wMHM_NT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/c9tjxhntcee5q0qsw9se.png" class="img-fluid"&gt;&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;As we can see, none of our attacks is working anymore and we are still able to use the service with a valid &lt;em&gt;document_url&lt;/em&gt;. 👍&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are we secure now?&lt;/strong&gt; Well, ServerlessGoat has quite some more issues waiting to get fixed — at least we secured our app from Command Injection Attacks now! 🛡&lt;/p&gt;

&lt;p&gt;Keep in mind, however: Realistic apps are much harder to overlook and you can never be sure that your WAF restricts bogus data to flow to your function through every possible channel. Therefore, in addition to a WAF, never trust the inputs to your lambda functions and implement additional counter measurements in your code!&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing the vulnerability is one thing. But how to find it in the first place?
&lt;/h2&gt;

&lt;p&gt;We develop &lt;a href="https://codeshield.io/"&gt;CodeShield&lt;/a&gt; to help developers finding and fixing vulnerabilities in serverless applications. Check out our &lt;a href="https://dashboard.codeshield.io/"&gt;product tour&lt;/a&gt; to try it yourself!&lt;/p&gt;

&lt;h2&gt;
  
  
  About
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Manuel Benz is co-founder of &lt;a href="https://codeshield.io/"&gt;CodeShield&lt;/a&gt;, a novel static security testing tool focusing on in-depth program analysis of Microservice architectures and Serverless applications. Prior to the start-up, Manuel worked as a researcher on combinations of static and dynamic program analysis for vulnerability detection at the Secure Software Engineering group at Paderborn University. Manuel is still actively maintaining the &lt;a href="https://github.com/soot-oss/soot"&gt;Soot static program analysis framework for Java&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>security</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to Prevent Code Injection Vulnerabilities in Serverless Applications (Part 1/2)</title>
      <dc:creator>Manuel Benz</dc:creator>
      <pubDate>Thu, 03 Dec 2020 13:01:46 +0000</pubDate>
      <link>https://dev.to/mbenz89/how-to-prevent-code-injection-vulnerabilities-in-serverless-applications-part-1-2-4ca4</link>
      <guid>https://dev.to/mbenz89/how-to-prevent-code-injection-vulnerabilities-in-serverless-applications-part-1-2-4ca4</guid>
      <description>&lt;p&gt;Recently Serverless application architectures are a &lt;a href="https://www.serverless.com/blog/2018-serverless-community-survey-huge-growth-usage/"&gt;trending&lt;/a&gt; model for web application development. This is hardly surprising since serverless backends can help to save huge costs for hosting and maintaining web applications. Instead of having full-blown servers continuously running and producing costs, serverless functions can be fired only &lt;em&gt;on-demand&lt;/em&gt;, when there is load for the server to process, and are &lt;em&gt;paid-per-use&lt;/em&gt; only for the time of their execution. &lt;/p&gt;

&lt;p&gt;Like usual web applications, serverless applications are equally affected by security vulnerabilities. Indeed, the &lt;em&gt;OWASP Foundation&lt;/em&gt; has published an interpretation of the &lt;em&gt;OWASP Top-10 list of Web Application Security Risks&lt;/em&gt; &lt;a href="https://owasp.org/www-project-serverless-top-10/"&gt;specifically for serverless&lt;/a&gt;. Alike, OWASP has published &lt;a href="https://github.com/OWASP/Serverless-Goat"&gt;ServerlessGoat&lt;/a&gt;, a&lt;br&gt;
serverless version of their (in-)famous &lt;a href="https://owasp.org/www-project-webgoat/"&gt;WebGoat&lt;/a&gt; application. &lt;a href="https://github.com/OWASP/Serverless-Goat"&gt;ServerlessGoat&lt;/a&gt; is an intentionally vulnerable serverless application containing instances of eight of the ten most critical serverless application vulnerabilities.&lt;/p&gt;

&lt;p&gt;With ServerlessGoat, OWASP not only ships an application to showcase how &lt;strong&gt;not&lt;/strong&gt; to build a serverless application but also proposes a great and in-depth &lt;a href="https://github.com/OWASP/Serverless-Goat/blob/master/LESSONS.md"&gt;explanation on how to attack&lt;/a&gt; it. Unfortunately, the authors miss the opportunity to explain how to fix or (better) even prevent these vulnerabilities in the first place.&lt;/p&gt;

&lt;p&gt;This article is &lt;strong&gt;Part 1&lt;/strong&gt; of a series. Here, I’m going to explain &lt;strong&gt;how to exploit the OS Command Injection vulnerability in ServerlessGoat&lt;/strong&gt; and &lt;strong&gt;how to generally fix it&lt;/strong&gt;. In &lt;a href="https://codeshield.io/blog/2021/02/08/prevent_ci_part2/"&gt;&lt;strong&gt;Part 2&lt;/strong&gt;&lt;/a&gt; of the series, I then show &lt;strong&gt;how to secure applications from injection attacks using AWS Web Application Firewalls (WAF)&lt;/strong&gt;.&lt;/p&gt;


&lt;h3&gt;
  
  
  Exploiting ServerlessGoat code injection
&lt;/h3&gt;

&lt;p&gt;ServerlessGoat implements an MS-Word &lt;em&gt;.doc&lt;/em&gt; to text converter service. For this, the app accepts a user-supplied URL to an MS-Word document and processes as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Download the document via the supplied URL using &lt;code&gt;curl&lt;/code&gt; OS-command (line 3)&lt;/li&gt;
&lt;li&gt; Convert it to text using the Linux &lt;code&gt;catdoc&lt;/code&gt; tool (line 3)&lt;/li&gt;
&lt;li&gt; Store the resulting text in an S3 bucket (line 8–14)&lt;/li&gt;
&lt;li&gt; Respond with an URL to the generated text in the S3 bucket (line 16–21)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A user can then access the plain-text conversion of her supplied MS-Word document via the returned S3 URL.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Main Logic of ServerlessGoat




&lt;p&gt;Looking at the implementation of this procedure, one quickly  notices that the proposed &lt;code&gt;document_url&lt;/code&gt; query string parameter is directly used in an OS-command invocation (Line 3) without any means of proper input validation.&lt;/p&gt;

&lt;p&gt;As described in &lt;a href="https://github.com/OWASP/Serverless-Goat/blob/master/LESSONS.md#lesson-2-reverse-engineering-the-lambda-function"&gt;Lesson 2&lt;/a&gt; of the author’s exploitation guideline, an attacker can thus craft a &lt;code&gt;document_url&lt;/code&gt; query string parameter that leads to completely ignoring the piped &lt;code&gt;catdoc&lt;/code&gt; invocation and instead execute an arbitrary OS-command. Even more, the attacker can then also acquire the output of the executed OS-command by accessing the proposed S3 bucket URL.&lt;/p&gt;

&lt;p&gt;For example, providing &lt;code&gt;https://foobar; cat /var/task/index.js #&lt;/code&gt;as &lt;code&gt;document_url&lt;/code&gt; will make the shell first &lt;em&gt;curl&lt;/em&gt; &lt;code&gt;https://foobar&lt;/code&gt;, then &lt;em&gt;cat&lt;/em&gt; the source code of the lambda function. The remainder of the original command&lt;br&gt;
(&lt;code&gt;catdoc&lt;/code&gt;) is ignored due to the out-commenting (&lt;code&gt;#&lt;/code&gt;) of everything after the &lt;code&gt;cat&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;By reading the acquired contents of the &lt;em&gt;index.js&lt;/em&gt; file from the S3 bucket, an attacker is thus able to completely reverse-engineer the lambda function and search for further possibly exploitable weaknesses in the code. But that’s just&lt;br&gt;
one possible attack, an attacker could also exploit the vulnerability to spawn a reverse shell, get access to environment variables, read the &lt;code&gt;/etc/passwd&lt;/code&gt; file,&lt;br&gt;
run some privilege escalation attacks, etc.&lt;/p&gt;

&lt;h4&gt;
  
  
  Let’s try it!
&lt;/h4&gt;

&lt;p&gt;First, we &lt;a href="https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:761130837472:applications~serverless-goat"&gt;deploy&lt;/a&gt; ServerlessGoat in our AWS account. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;em&gt;Unfortunately, the official ServerlessGoat is currently not usable due to running on a deprecated NodeJS runtime. As an alternative, we published a &lt;a href="https://github.com/CodeShield-Security/Serverless-Goat-Java"&gt;Java version of the application&lt;/a&gt; which can be used as a replacement. You can deploy it &lt;a href="https://eu-central-1.console.aws.amazon.com/lambda/home?region=eu-central-1#/create/app?applicationId=arn:aws:serverlessrepo:eu-central-1:217096764149:applications/Serverless-Goat-Java"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Second, let’s try the service with a proper MS-Word document:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M7B3d0pt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ch31ui6hf1buggobtwt2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M7B3d0pt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ch31ui6hf1buggobtwt2.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Works! Let’s see if we can run a simple command injection attack to get some information about the users on the target machine by printing the &lt;code&gt;/etc/passwd&lt;/code&gt; file:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BwSbV2qK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/iyd49va74mn0sk8e720g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BwSbV2qK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/iyd49va74mn0sk8e720g.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perfect! By using &lt;code&gt;https://foobar ; cat /etc/passwd #&lt;/code&gt; as &lt;code&gt;document_url&lt;/code&gt;, we are able to see all groups and users on the system.&lt;/p&gt;

&lt;p&gt;Now that we know we can execute arbitrary commands on the machine, let’s try to list the directory tree of the server with &lt;code&gt;ls -LR&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IWEZbZLu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/p29ydhzhnjwzgovokf5o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IWEZbZLu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/p29ydhzhnjwzgovokf5o.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It seems that the server stores it’s used libraries in the &lt;code&gt;.lib&lt;/code&gt; directory.&lt;br&gt;
Having a list of all used libraries, we could search for more attack vectors by exploiting known vulnerabilities of those. A good starting point to learn more about possible vulnerabilities and exploits for those could be the &lt;a href="https://nvd.nist.gov/vuln/search"&gt;National&lt;br&gt;
Vulnerability Database (NVD)&lt;/a&gt; and &lt;a href="https://www.exploit-db.com/"&gt;exploit-db&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To find even more possible attack vectors, we could also download the server’s &lt;code&gt;.class&lt;/code&gt; files and decompile them to learn about its concrete implementation.&lt;/p&gt;

&lt;p&gt;Lastly, let’s see if we can learn something from the environment variables on the machine:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ceEeV3Wx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/hvbetget508c9rvrhkex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ceEeV3Wx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/hvbetget508c9rvrhkex.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ouch 😣…that’s a lot of sensitive information!&lt;/p&gt;




&lt;h3&gt;
  
  
  Fixing the Vulnerability
&lt;/h3&gt;

&lt;p&gt;Generally speaking, such injection vulnerabilities, e.g., OS Command Injection, SQL Injection, Code Injection, XSS, etc., stem from user input being used directly in sensitive operations.&lt;/p&gt;

&lt;p&gt;To prevent such vulnerabilities, there are two solutions, first, if possible, do not use user input in sensitive operations at all, and/or, second, validate that the user input has a proper format and is not able to exploit the sensitive operation.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Do not use an OS Command for Downloading the Document&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;In our ServerlessGoat example, the OS Command Injection attack is possible because the &lt;code&gt;document_url&lt;/code&gt; user input directly flows into an OS command invocation of &lt;code&gt;curl&lt;/code&gt;. Using &lt;code&gt;curl&lt;/code&gt; to download the MS-Word document seems to rather be a convenience-driven decision. By no means, it is necessary to use &lt;code&gt;curl&lt;/code&gt; to download a file. Instead, we can just use some JS native way to acquire a web-resource and store it in the file system, e.g., with &lt;a href="https://github.com/request/request#streaming"&gt;the request library&lt;/a&gt;, and afterward propose this downloaded file to the &lt;code&gt;catdoc&lt;/code&gt; command. Using this approach, no user input will directly flow into the OS command anymore. Even more, with such a fix, proposing some bogus &lt;code&gt;document_url&lt;/code&gt; as input will rather lead to the &lt;em&gt;request API&lt;/em&gt; failing fast due to not being able to acquire any proper web-resource.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;em&gt;Indeed, AWS has recently removed the &lt;code&gt;curl&lt;/code&gt; command from their &lt;code&gt;amazonlinux:2&lt;/code&gt; runtime. This certainly helps to prevent OS-Command Injections like the one in ServerlessGoat since users are rather forced to use a proper library for sending web requests instead of &lt;code&gt;curl&lt;/code&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;What if I need to pass User Input to a Sensitive Operation?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;In certain scenarios, one will not be able to completely prevent using user input in an OS Command, e.g., due to the need for a specific application which’s functionality cannot be mimicked by a third-party library or if the runtime-performance that only a binary application can provide is required.&lt;/p&gt;

&lt;p&gt;In such cases, it is important to properly validate the user input before it is used in the OS command. Such validation is usually done by using either denylists, i.e., excluding inputs that contain specific characters or match a certain format, or by using allowlists, i.e., only allowing inputs with a certain format or input that matches an item in a given list of constants.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 2&lt;/strong&gt; of this series, I will explain how to set up AWS Web Application Firewalls (WAF) to secure your serverless application from injection attacks.&lt;br&gt; The AWS WAF proposes a deny- and allowlist approach to conduct exactly such input validation for multiple functions even before they are invoked at all. 🛡&lt;/p&gt;




&lt;h3&gt;
  
  
  Fixing the vulnerability is one thing. But how to find it in the first place?
&lt;/h3&gt;

&lt;p&gt;We develop &lt;a href="https://codeshield.io"&gt;CodeShield&lt;/a&gt; to help developers finding and fixing vulnerabilities in serverless applications. Check out our &lt;a href="https://dashboard.codeshield.io"&gt;product tour&lt;/a&gt; to try it yourself!&lt;/p&gt;




&lt;h3&gt;
  
  
  About
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Manuel Benz is co-founder of &lt;a href="https://codeshield.io"&gt;CodeShield&lt;/a&gt;, a novel static security testing tool focusing on in-depth program analysis of Microservice architectures and Serverless applications. Prior to the start-up, Manuel worked as a researcher on combinations of static and dynamic program analysis for vulnerability detection at the Secure Software Engineering group at Paderborn University. Manuel is still actively maintaining the &lt;a href="https://github.com/soot-oss/soot"&gt;Soot static program analysis framework for Java&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>security</category>
      <category>aws</category>
      <category>microservices</category>
    </item>
  </channel>
</rss>
