Open Redirect (Bypass Of #59d7c660-744c-4fee-88b7-6117b6846aea) in sissbruecker/linkding
Mar 26th 2022
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
Open Redirect is used mostly on phishing campaigns ...
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 ...
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.
I see that this issue didn't awarded a bounty ... the same issue was awarded a bounty in a previous report.
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?
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 !
@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
Duplicate as well as
If you have any future feature requests, feel free to create an issue on our public roadmap repository, here.