<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://asdf.foo/feed.xml" rel="self" type="application/atom+xml" /><link href="https://asdf.foo/" rel="alternate" type="text/html" /><updated>2026-08-13T12:29:14+00:00</updated><id>https://asdf.foo/feed.xml</id><title type="html">asdf.foo</title><subtitle>Mostly writeups of web and client side bugs.</subtitle><entry><title type="html">Using HMAC collisions to forge password reset tokens</title><link href="https://asdf.foo/2026/02/13/using-hmac-collisions-to-forge-password-reset-tokens.html" rel="alternate" type="text/html" title="Using HMAC collisions to forge password reset tokens" /><published>2026-02-13T00:00:00+00:00</published><updated>2026-02-13T00:00:00+00:00</updated><id>https://asdf.foo/2026/02/13/using-hmac-collisions-to-forge-password-reset-tokens</id><content type="html" xml:base="https://asdf.foo/2026/02/13/using-hmac-collisions-to-forge-password-reset-tokens.html"><![CDATA[<p>Back in September 2025 a friend reached out with a lead on a bug bounty target.
We didn’t manage to exploit the lead, but the site’s password-reset flow caught
my attention. After spending a good amount of time poking at it, I discovered an
issue that would allow an attacker to craft password reset tokens that can change
the password of a victim’s account under certain conditions.</p>

<h1 id="0-heres-the-tldr">0. Here’s the TL;DR</h1>
<p>The password reset token is a base64 encoded json object with user properties
and an HMAC to ensure the integrity of the token. The HMAC is computed over the
concatenated user properties of the token. This lets an attacker create a new
account with carefully chosen user properties that concatenate to the same
string as the victim’s account and therefore generates a matching HMAC. Besides the
HMAC value, the token also contains an expiration unix timestamp in seconds
which the attacker can obtain. Finally the token also contains a “SessionEpoch”
value in nanoseconds that the attacker needs to bruteforce. The attacker can
leak a very close estimate of this value, which makes the bruteforcing very
feasible.</p>

<h1 id="1-the-password-reset-flow-and-the-hmac-value">1. The password reset flow and the HMAC value</h1>
<h2 id="resetting-a-password">Resetting a password</h2>
<p>Let’s first quickly go over how the password reset flow works. When a user wants
to reset their password they enter their email and receive a password reset
link with a token. The link looks something like this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://app.example.com/resetpassword/eyJBY2NvdW50IjoiYXNkZmZvbyIsIkVtYWlsIjoiYXNkZkBhc2RmLmZvbyIsIkV4cGlyZXMiOjE3NjEyMjM1NjYsIkhtYWMiOiJCaHNJUm05UHBfS2RVZGI0VHd2LUVybFJEQk1pRTEwTFpFcVp0MWhxQ3hNPSIsIlNlc3Npb25FcG9jaCI6MTc2MTEzNzE2MjQxMTgxMzk5MSwiRmlyc3ROYW1lIjoiSGFyb2xkIiwiTGFzdE5hbWUiOiJGb29iYXIifQ==
</code></pre></div></div>
<p>The token base64 decodes to this object:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
    <span class="dl">"</span><span class="s2">Account</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">asdffoo</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">Email</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">asdf@asdf.foo</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">Expires</span><span class="dl">"</span><span class="p">:</span> <span class="mi">1761223566</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">Hmac</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">BhsIRm9Pp_KdUdb4Twv-ErlRDBMiE10LZEqZt1hqCxM=</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">SessionEpoch</span><span class="dl">"</span><span class="p">:</span> <span class="mi">1761137162411814000</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">FirstName</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Harold</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">LastName</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Foobar</span><span class="dl">"</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Visiting the reset link lets us choose a new password. If we attempt to
modify this token by changing the email for example, the link will return a
<code class="language-plaintext highlighter-rouge">403 Forbidden</code> response.</p>

<p>After choosing a new password, the page will make a POST request to the same
endpoint, setting the new password for the account.</p>

<h2 id="tampering-with-the-token-values">Tampering with the token values</h2>
<p>An <abbr title="hash-based message authentication code">HMAC</abbr>-function is
a function that takes a secret key and some data and spits out a message
authentication code (MAC) which is a kind of hash that ensures authenticity and
integrity of the data. In our case the <code class="language-plaintext highlighter-rouge">Hmac</code> value in the reset-token ensures that the
token values are authentic and can’t be tampered with. Or can they?…</p>

<p>A property of an HMAC-function is that it always produces the same output for
the same input. I found that the input to this HMAC-function is, at least
partially, constructed from values from this json object, and that these values
are simply concatenated together. I realized this by moving around the values
in the json and observing that the token was still valid (valid token returns
status codes 200 or 409, invalid token returns 403). For example the following
token returns <code class="language-plaintext highlighter-rouge">200 OK</code> but notice the <code class="language-plaintext highlighter-rouge">FirstName</code> and <code class="language-plaintext highlighter-rouge">LastName</code> have been
changed:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
    <span class="dl">"</span><span class="s2">Account</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">asdffoo</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">Email</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">asdf@asdf.foo</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">Expires</span><span class="dl">"</span><span class="p">:</span> <span class="mi">1761223566</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">Hmac</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">BhsIRm9Pp_KdUdb4Twv-ErlRDBMiE10LZEqZt1hqCxM=</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">SessionEpoch</span><span class="dl">"</span><span class="p">:</span> <span class="mi">1761137162411814000</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">FirstName</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">HaroldFooba</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">LastName</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">r</span><span class="dl">"</span>
<span class="p">}</span>
</code></pre></div></div>
<p>So now we know that the <code class="language-plaintext highlighter-rouge">FirstName</code> and <code class="language-plaintext highlighter-rouge">LastName</code> must be concatenated in the
input to the HMAC.</p>

<p>After some experimenting, I found out that in the input to the HMAC-function,
the following values must be concatenated directly.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>...|Account|Email|FirstName|LastName|...
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">SessionEpoch</code> value is not part of the HMAC input. The <code class="language-plaintext highlighter-rouge">Expires</code> value <em>is</em>
part of the HMAC input, but I don’t know where it appears.</p>

<h1 id="2-using-hmac-collisions-to-forge-password-reset-tokens">2. Using HMAC collisions to forge password reset tokens</h1>
<p>This gave me an idea. Can we create a user with values such that we can
generate a password reset token with a valid <code class="language-plaintext highlighter-rouge">Hmac</code> field that is valid for
<em>another</em> user?</p>

<p>To achieve this goal we have to overcome these challenges:</p>
<ul>
  <li>Figure out how to create an account so that the concatenated values of
<code class="language-plaintext highlighter-rouge">Account</code>, <code class="language-plaintext highlighter-rouge">Email</code>, <code class="language-plaintext highlighter-rouge">FirstName</code> and <code class="language-plaintext highlighter-rouge">LastName</code> matches a victim’s account.</li>
  <li>We need a way to control or predict the <code class="language-plaintext highlighter-rouge">Expires</code> value</li>
  <li>Figure out the value of <code class="language-plaintext highlighter-rouge">SessionEpoch</code></li>
</ul>

<h2 id="concatenation-collisions">Concatenation collisions</h2>
<p>Our first challenge is to choose account values so that when they’re
concatenated they match a victim account’s concatenated values. The first thing
we learn is that <code class="language-plaintext highlighter-rouge">Account</code> values are unique so we cannot choose the same
<code class="language-plaintext highlighter-rouge">Account</code> value as the victim. That’s fine and doesn’t stop us.</p>

<p>If we can register our own email on the same domain as the victim we can create
a collision like this:</p>

<table>
  <thead>
    <tr>
      <th>Property</th>
      <th>victim</th>
      <th>attacker</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Account</td>
      <td>victimaccount</td>
      <td>victimac</td>
    </tr>
    <tr>
      <td>Email</td>
      <td>john@victim.com</td>
      <td><mark>count</mark>john@victim.com</td>
    </tr>
    <tr>
      <td>FirstName</td>
      <td>john</td>
      <td>john</td>
    </tr>
    <tr>
      <td>LastName</td>
      <td>doe</td>
      <td>doe</td>
    </tr>
  </tbody>
</table>

<p>If we cannot register emails on <code class="language-plaintext highlighter-rouge">victim.com</code>, then maybe we can buy the domain
<code class="language-plaintext highlighter-rouge">victim.co</code>:</p>

<table>
  <thead>
    <tr>
      <th>Property</th>
      <th>victim</th>
      <th>attacker</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Account</td>
      <td>victimaccount</td>
      <td>victimac</td>
    </tr>
    <tr>
      <td>Email</td>
      <td>john@victim.com</td>
      <td><mark>count</mark>john@victim.co</td>
    </tr>
    <tr>
      <td>FirstName</td>
      <td>john</td>
      <td><mark>m</mark>john</td>
    </tr>
    <tr>
      <td>LastName</td>
      <td>doe</td>
      <td>doe</td>
    </tr>
  </tbody>
</table>

<p>Finding an available domain that is a prefix is in many cases not possible.
We’re much more likely to find a domain that <em>extends</em> the victim’s email domain.
A few examples:</p>

<table>
  <thead>
    <tr>
      <th>Domain</th>
      <th>Extending domains</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>victim.com</td>
      <td>victim.company, victim.community, victim.computer</td>
    </tr>
    <tr>
      <td>victim.org</td>
      <td>victim.organic</td>
    </tr>
    <tr>
      <td>victim.me</td>
      <td>victim.meme, victim.media, victim.memorial, victim.menu</td>
    </tr>
    <tr>
      <td>victim.ai</td>
      <td>victim.airforce</td>
    </tr>
  </tbody>
</table>

<p>victim.computer is available so with that domain we can create an account like this:</p>

<table>
  <thead>
    <tr>
      <th>Property</th>
      <th>victim</th>
      <th>attacker</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Account</td>
      <td>victimaccount</td>
      <td>victimac</td>
    </tr>
    <tr>
      <td>Email</td>
      <td>john@victim.com</td>
      <td><mark>count</mark>john@victim.com<mark style="background-color: #ffd6d6">puter</mark></td>
    </tr>
    <tr>
      <td>FirstName</td>
      <td>john</td>
      <td>john</td>
    </tr>
    <tr>
      <td>LastName</td>
      <td>doe</td>
      <td>doe</td>
    </tr>
  </tbody>
</table>

<p>But that does <em>not</em> give us the collision we need. We would need a way to change
the victim’s first name to be “puterjohn” for the collision to happen. It turns
out that in some cases, an attacker <em>can</em> in fact change the name of a victim’s
account.</p>

<p>This is possible when the attacker is an admin of an organization of which the
victim is also a member. Since users can be members of multiple organizations,
any user that is already a member of the attacker’s organization or any user that
accepts an invitation from the attacker can have their name changed by the attacker.</p>

<table>
  <thead>
    <tr>
      <th>Property</th>
      <th>victim</th>
      <th>attacker</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Account</td>
      <td>victimaccount</td>
      <td>victimac</td>
    </tr>
    <tr>
      <td>Email</td>
      <td>john@victim.com</td>
      <td><mark>count</mark>john@victim.computer</td>
    </tr>
    <tr>
      <td>FirstName</td>
      <td><mark style="background-color: #d6ffd6">puter</mark>john</td>
      <td>john</td>
    </tr>
    <tr>
      <td>LastName</td>
      <td>doe</td>
      <td>doe</td>
    </tr>
  </tbody>
</table>

<p>An attacker, with admin rights in one of the victim’s organizations, can change
the victim’s name so the collision happens. A successful compromise of the
victim account would give the attacker access to the victim’s other
organizations.</p>

<h2 id="getting-matching-expires-values-for-attackervictim-tokens">Getting matching “Expires” values for attacker/victim tokens</h2>
<p>To compromise the john@victim.com account (with the first name changed to “puterjohn”) we request a password reset to our email “countjohn@victim.computer”. We receive a link with a token that decodes to this:</p>
<pre>
{
    "Account": "victimac",
    "Email": "countjohn@victim.computer",
    "Expires": 1770383125,
    "Hmac": "FsPutMfy3ntfBmQB4ozqiSqv2ZVVDSCaau9_z0SdlNk=",
    "SessionEpoch": 1763122926978402369,
    "FirstName": "john",
    "LastName": "doe"
}</pre>
<p>We fix the property values to match a reset token for the victim’s account:</p>
<pre>
{
    "Account": "victimac<mark>count</mark>",
    "Email": "john@victim.com",
    "Expires": 1770383125,
    "Hmac": "FsPutMfy3ntfBmQB4ozqiSqv2ZVVDSCaau9_z0SdlNk=",
    "SessionEpoch": 1763122926978402369,
    "FirstName": "<mark>puter</mark>john",
    "LastName": "doe"
}
</pre>
<p>Now the token properties matches the victim’s account and the <code class="language-plaintext highlighter-rouge">Hmac</code> value is still valid for the token.
But the tokens still fail to reset the victim’s account! After some more testing I realized that the account has some state saved on the server side about the password reset, and a token with the correct user properties and a valid Hmac is not enough in itself.</p>

<p>When trying to change the password the server not only checks if <code class="language-plaintext highlighter-rouge">Hmac</code> is valid, but also checks if the user is in a pending password reset state and if <code class="language-plaintext highlighter-rouge">Expires</code> and <code class="language-plaintext highlighter-rouge">SessionEpoch</code> values matches the server side state values…</p>

<p>The <code class="language-plaintext highlighter-rouge">Expires</code> value is just a Unix timestamp in seconds that indicate when the reset token expires. Since it’s in seconds we can just request a password reset token for both our attacker account and the victim account at the same time. Burp proxy has excellent tooling for sending 2 requests simultaneously. This way we can put the victim account in a pending password change state, and know the value of <code class="language-plaintext highlighter-rouge">Expires</code> since we will receive a token with this value on our countjohn@victim.computer email.</p>

<h2 id="brute-forcing-the-sessionepoch">Brute forcing the “SessionEpoch”</h2>
<p>The token still fails to change the victim’s password. Even with the victim
account in a pending password reset state and a token with the correct <code class="language-plaintext highlighter-rouge">Expires</code>
timestamp, a valid <code class="language-plaintext highlighter-rouge">Hmac</code> and all the user properties matching the victim’s
account. It seems like we have to figure out the value of <code class="language-plaintext highlighter-rouge">SessionEpoch</code> as well.</p>

<p>The <code class="language-plaintext highlighter-rouge">SessionEpoch</code> value is a unix timestamp in nano seconds. The timestamp
appears to be the last time the user signed out or their session expired.</p>

<p>Since this value is not part of the HMAC-function input, we can still generate a
token with a valid <code class="language-plaintext highlighter-rouge">Hmac</code> and <code class="language-plaintext highlighter-rouge">Expires</code> values, and just change the
<code class="language-plaintext highlighter-rouge">SessionEpoch</code> to try and guess the correct value. But there’s a billion (!)
nanoseconds per second so this is not feasible unless we can get a good idea
about when the victim last signed out.</p>

<p>Lucky for us we can use our admin account to send a PUT request to</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://app.example.com/api/user/[VICTIM_USER_ID]/change/admin
</code></pre></div></div>
<p>which will set the <code class="language-plaintext highlighter-rouge">SessionEpoch</code> of that account to the current timestamp.</p>

<p>If we use a “helper” account that we have access to, we can set the
<code class="language-plaintext highlighter-rouge">SessionEpoch</code> for both the victim account and our helper account with Burp’s
“single packet” feature. This way the 2 PUT requests will hit the server at
almost the exact same time, and we know that the <code class="language-plaintext highlighter-rouge">SessionEpoch</code> values must be
very close to each other. Since we can just request a password reset for the
helper account and read the <code class="language-plaintext highlighter-rouge">SessionEpoch</code> from the reset token, we have a
pretty good idea of what the <code class="language-plaintext highlighter-rouge">SessionEpoch</code> value is for the victim account and
we can now easily brute force the value.</p>

<p>With Burp’s TurboIntruder tool I easily got more than 1000 requests per second,
and this can probably be improved a lot more by playing around more with the
settings.</p>

<figure>
<img src="data:image/png;base64,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
" alt="Burp TurboIntruder RPS" />
<figcaption><em>Sending 1164 requests per second with TurboIntruder</em></figcaption>
</figure>

<p>In the POC I created and sent with the report the victim and helper accounts’ <code class="language-plaintext highlighter-rouge">SessionEpoch</code> values were about 2 million nanoseconds apart.</p>

<table>
  <thead>
    <tr>
      <th>SessionEpoch</th>
      <th style="text-align: right">Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">SessionEpoch</code><sub>victim</sub></td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">1761304073586371112</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">SessionEpoch</code><sub>helper</sub></td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">1761304073588332952</code></td>
    </tr>
    <tr>
      <td>Difference</td>
      <td style="text-align: right"><code class="language-plaintext highlighter-rouge">1961840</code></td>
    </tr>
  </tbody>
</table>

<p>Assuming 1000 requests per second we can guess 3.6 million values per hour:</p>

<math display="block">
  <mn>1000</mn>
  <mfrac>
    <mtext>requests</mtext>
    <mtext>second</mtext>
  </mfrac>
  <mo>&#xA0;</mo>
  <mo>×</mo>
  <mo>&#xA0;</mo>
  <mn>60</mn>
  <mfrac>
    <mtext>seconds</mtext>
    <mtext>minute</mtext>
  </mfrac>
  <mo>&#xA0;</mo>
  <mo>×</mo>
  <mo>&#xA0;</mo>
  <mn>60</mn>
  <mfrac>
    <mtext>minutes</mtext>
    <mtext>hour</mtext>
  </mfrac>
  <mo>&#xA0;</mo>
  <mo>=</mo>
  <mo>&#xA0;</mo>
  <mn>3,600,000</mn>
  <mfrac>
    <mtext>requests</mtext>
    <mtext>hour</mtext>
  </mfrac>
</math>
<p><br /></p>
<h1 id="3-performing-the-attack">3. Performing the attack</h1>
<p>When I reported this I also submitted a video of me performing the attack
against an account I had created myself. I created a victim account and bought
the domain intigriti.media for the attacker account:</p>

<table>
  <thead>
    <tr>
      <th>Property</th>
      <th>victim</th>
      <th>attacker</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Account</td>
      <td>lil_endian_victim</td>
      <td>lil_endian_vi</td>
    </tr>
    <tr>
      <td>Email</td>
      <td>lil_endian@intigriti.me</td>
      <td>ctimlil_endian@intigriti.media</td>
    </tr>
    <tr>
      <td>FirstName</td>
      <td>Foo</td>
      <td>Foo</td>
    </tr>
    <tr>
      <td>LastName</td>
      <td>Bar</td>
      <td>Bar</td>
    </tr>
  </tbody>
</table>

<p>I then</p>
<ol>
  <li>Used an admin account to change the first name of the victim account to “diaFoo”.</li>
  <li>Sent the PUT request to set the <code class="language-plaintext highlighter-rouge">SessionEpoch</code> for the victim account and a helper account.</li>
  <li>Requested a password reset for the helper account and extracted the <code class="language-plaintext highlighter-rouge">SessionEpoch</code> value from the reset token.</li>
  <li>Request a password reset for both the victim and attacker account using Burp’s single packet request. The 2 accounts now have pending password resets with the same <code class="language-plaintext highlighter-rouge">Expires</code> values.</li>
  <li>I adjust the values in the attacker’s reset token to match the victim’s account values instead.</li>
  <li>I use the approximate <code class="language-plaintext highlighter-rouge">SessionEpoch</code> value from the helper account as a starting point, and brute force the correct value for the victim’s token.</li>
  <li>I manage to find the correct token and use it to successfully change the victim account’s password.</li>
</ol>

<h1 id="4-program-response">4. Program response</h1>
<p>Both the Intigriti triagers and the program were a pleasure to work with on this
report.  The program had the issue resolved just 4 days after the report was
triaged. The report got accepted as <abbr title="CVSS:3.0/AV:N/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:N">High</abbr>, and awarded a bounty of $2250 + $500
bonus.</p>

<p>The company has read a draft of this writeup and I promised to mention:</p>
<ul>
  <li>No exploitation occurred prior to the report I submitted via Intigriti.</li>
  <li>The issue has been fully remediated prior to the publication of this writeup.</li>
</ul>]]></content><author><name></name></author><summary type="html"><![CDATA[Back in September 2025 a friend reached out with a lead on a bug bounty target. We didn’t manage to exploit the lead, but the site’s password-reset flow caught my attention. After spending a good amount of time poking at it, I discovered an issue that would allow an attacker to craft password reset tokens that can change the password of a victim’s account under certain conditions.]]></summary></entry></feed>