Open Redirect (Bypass Of #59d7c660-744c-4fee-88b7-6117b6846aea) in sissbruecker/linkding

Valid

Reported on

Mar 26th 2022


Description

Hello everyone,

I found an Open Redirect on linkding on remove a bookmark functionality, it is a bypass of a previously submitted report, when users are tricked into visiting the vulnerable link, they will immediately redirected to arbitrary hosts.

Proof of Concept

- Just visit the following link: https://demo.linkding.link/bookmarks/67940/remove?return_url=//evil.com

Impact

Open Redirect is used mostly on phishing campaigns ...

Occurrences

Now let's see the actual vulnerable code:

def get_safe_return_url(return_url: str, fallback_url: str):
    # Use fallback if URL is none or URL is not on same domain
    if not return_url or not return_url.startswith('/'):
        return fallback_url
    return return_url

what happens here is that the return_url variable is checked using startswith('/') method, it's definitely vulnerable as the attacker doesn't need to supply HTTP OR HTTPS in front of a url to redirect a user, he will simply use two forwarded slahes //evil.com and since it starts with a slash / it passes the check and the browser still understand that //evil.com is a URI that he should navigate to ...

Remediation:

I think that using a proper regex should fix the issue, but the better solution in this case is implementing a whitelist of pre-defined values, so the attacker's can't bypass the restriction, for example look for this snippet:

def get_safe_return_url(return_url: str, fallback_url: str):
    # Use fallback if URL is none or URL is not on same domain
   allowed_values = ['/', 'about', 'home', 'anythingelse']
    if not return_url in allowed_values:
        return fallback_url
    return return_url

so here the if statement will check if the supplied return_url parameter is one of the allowed values, If it's not the fallback_url will be returned.

The first occurrence of this issue is withing remove() function defined in bookmarks.py view:

def remove(request, bookmark_id: int):
    try:
        bookmark = Bookmark.objects.get(pk=bookmark_id, owner=request.user)
    except Bookmark.DoesNotExist:
        raise Http404('Bookmark does not exist')

    bookmark.delete()
    return_url = get_safe_return_url(request.GET.get('return_url'), reverse('bookmarks:index'))
    return HttpResponseRedirect(return_url)

as we can see it checks first if the bookmarks is exist and the user is authorized to delete it, it removes it using delete() method. Now he will get the return_url GET parameter and passes it to get_safe_return_url() function which is the actual vulnerable function, after that it uses HttpReponsesRedirect() function to redirect the user.

We are processing your report and will contact the sissbruecker/linkding team within 24 hours. 2 months ago
Sascha Ißbrücker validated this vulnerability 2 months ago
Moad Akhraz has been awarded the disclosure bounty
The fix bounty is now up for grabs
Sascha Ißbrücker confirmed that a fix has been merged on 3906d9 2 months ago
The fix bounty has been dropped
bookmarks.py#L139-L147 has been validated
utils.py#L100-L104 has been validated
Moad Akhraz
2 months ago

Researcher


Hi @admin,

I see that this issue didn't awarded a bounty ... the same issue was awarded a bounty in a previous report.

Best Regards,

Moaad

Jamie Slome
2 months ago

Admin


Hello @Moad - seeing as this is quite a unique and rare case, where this report was made after the initial report and is considered to be a duplicate, would you be up for splitting the bounty with the researcher, and we can mark both reports as valid?

Moad Akhraz
2 months ago

Researcher


Hi @admin, this is one of the kindest replies I encountered every in bug bounty platforms !

No worries, He can enjoy the bounty, I would like to suggest some additional features to the platform like flags, although we have Valid flag, can you please work on other flags like N/A, Informative, Duplicated !

Best Regards,

Mooad

Jamie Slome
2 months ago

Admin


@mdakh404 - we are very grateful for your patience and for your understanding here.

I will zero out the bounties here, and we will treat this report as a duplicate of the other report with the other report being treated as the initial report.

We are actually very soon to release new flags including N/A, Informative and Duplicate as well as Spam.

If you have any future feature requests, feel free to create an issue on our public roadmap repository, here.

to join this conversation