Open Bug 1266789 Opened 9 years ago Updated 3 years ago

Hang when loading URL with hundreds of subdomains

Categories

(Core :: Networking, defect, P3)

defect

Tracking

()

People

(Reporter: mohitrawat08, Unassigned)

Details

(Keywords: csectype-dos, hang, sec-low, Whiteboard: [necko-backlog][has-patch][needs-tests])

Attachments

(3 files, 1 obsolete file)

Attached video Firefox.avi
User Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0 Build ID: 20160407164938 Steps to reproduce: Tested on: Windows 8, Ubuntu 15.10 Firefox version: 45.0.2 URL: http://www.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com.google.com/ To reproduce the issue copy the crafted URL and paste it to to Firefox. Please see the video for better clarity. https://www.dropbox.com/s/la8llji032l48ng/Firefox.avi?dl=0 Actual results: I would like to report an bug/issue on latest version of Mozilla Firefox, I would like to update that the browser stops responding if we try opening a crafted URL on it. This issue has been tested on both Windows and Linux versions. An attacker can remotely exploit this vulnerability to crash down user’s browser as Firefox is unable to handle the request. Expected results: Exploitation Scenario: An attacker can use the shorten URL services like TinyURL to short the crafted URL like (http://tinyurl.com/zwgfqku) and post the url with a catchy title on a forum as the users starts clicking on the link their bowser starts getting crashed. This bug might lead to potentially exploitable critical issues
Severity: normal → critical
Summary: browser crashed with crafted url → browser hangs with crafted url
I can't reproduce this on either OS X or Windows. From the screencast, it seems you have lots of add-ons installed. Can you try if you can reproduce the issue in safe mode ( https://support.mozilla.org/en-US/kb/troubleshoot-and-diagnose-firefox-problems#w_3-restart-firefox-in-safe-mode ) ?
Group: firefox-core-security
Flags: needinfo?(mohitrawat08)
To reproduce the issue in the safe mode kindly increase the length of the URL. To check the issue visit http://factainment.xyz/poc/ and click the "Click here" hyperlink. As you open the link,your browser stops responding.
Caught this in Visual Studio: xul.dll!nsTHashtable<nsCStringHashKey>::s_HashKey(const void * aKey) Line 373 C++ xul.dll!mozilla::DataStorage::Get(const nsCString & aKey, mozilla::DataStorageType aType) Line 442 C++ xul.dll!nsSiteSecurityService::IsSecureHost(unsigned int aType, const char * aHost, unsigned int aFlags, bool * aResult) Line 1032 C++ xul.dll!nsSiteSecurityService::IsSecureURI(unsigned int aType, nsIURI * aURI, unsigned int aFlags, bool * aResult) Line 895 C++ xul.dll!mozilla::net::nsHttpHandler::SpeculativeConnectInternal(nsIURI * aURI, nsIInterfaceRequestor * aCallbacks, bool anonymous) Line 2236 C++ xul.dll!IOServiceProxyCallback::OnProxyAvailable(nsICancelable * request, nsIChannel * channel, nsIProxyInfo * pi, nsresult status) Line 1890 C++ xul.dll!nsAsyncResolveRequest::DoCallback() Line 253 C++ xul.dll!nsAsyncResolveRequest::OnQueryComplete(nsresult status, const nsCString & pacString, const nsCString & newPACURL) Line 215 C++ xul.dll!ExecuteCallback::Run() Line 87 C++ xul.dll!nsThread::ProcessNextEvent(bool aMayWait, bool * aResult) Line 995 C++ xul.dll!NS_ProcessNextEvent(nsIThread * aThread, bool aMayWait) Line 290 C++ xul.dll!mozilla::ipc::MessagePump::Run(base::MessagePump::Delegate * aDelegate) Line 98 C++ xul.dll!MessageLoop::Run() Line 204 C++ xul.dll!nsBaseAppShell::Run() Line 158 C++ xul.dll!nsAppShell::Run() Line 262 C++ xul.dll!nsAppStartup::Run() Line 285 C++ xul.dll!XREMain::XRE_mainRun() Line 4347 C++ xul.dll!XREMain::XRE_main(int argc, char * * argv, const nsXREAppData * aAppData) Line 4451 C++ xul.dll!XRE_main(int argc, char * * argv, const nsXREAppData * aAppData, unsigned int aFlags) Line 4559 C++ firefox.exe!do_main(int argc, char * * argv, char * * envp, nsIFile * xreDirectory) Line 220 C++ firefox.exe!NS_internal_main(int argc, char * * argv, char * * envp) Line 362 C++ firefox.exe!wmain(int argc, wchar_t * * argv) Line 138 C++ Build was off m-c rev https://hg.mozilla.org/mozilla-central/rev/9ce31e9f90cb0e534611b0f617c5bbc232ffe748
Component: Untriaged → Networking
Product: Firefox → Core
Version: 45 Branch → unspecified
Reproduced with the steps from comment 2 on Firefox 45.0.2, Firefox 46 and Nightly 49 (Build ID 20160426044609) using Windows 8.1 and Ubuntu 15.04.
Status: UNCONFIRMED → NEW
Ever confirmed: true
Also worth noting that this hangs the parent process on trunk.
I am seeing a few thousand calls to nsPermissionManager::GetPermissionHashKey(nsIPrincipal*, unsigned int, bool) on the stack. I assume this happens because we check permissions for each individual subdomain.
Flags: needinfo?(michael)
Summary: browser hangs with crafted url → Hang when loading URL with hundreds of subdomains
I've profiled the error, and cleopatra shows the hang occuring within nsSiteSecurityService::IsSecureHost. This method performs a hash table lookup for every segment in the input URL. The time appears to be dominated by the sheer number of hashtable lookups. I'm not sure what change could be made to that method to avoid the performance problem caused by these hashtable lookups. nsPermissionManager::GetPermissionHashKey also performs a large number of hashtable lookups, as it has to check for permissions on every subdomain. I've thrown together a patch which removes some of the non-hashtable work from GetPermissionHashKey's loop, and I'll attach it, but it's not a solution to the whole problem (and I can't even tell if it improves the situation, as even mousing over the link causes the hang due to the predictor calling IsSecureHost). cleopatra link: https://cleopatra.io/#report=c156acba9a0ff147596b2fca32ca657910b584f9
Flags: needinfo?(michael)
This is the patch (which doesn't fix the problem, but I figured I should attach it in case it becomes useful later) which hopefully will improve the performance of the GetPermissionHashKey loop.
Whiteboard: [necko-backlog]
Hi, Is there any update regarding the issue?
:keeler, it looks like you have touched code near the problematic hashtable lookups in nsSiteSecurityService::IsSecureHost before. Any idea as to what we can do to fix this?
Flags: needinfo?(dkeeler)
Well, we're never going to actually store something in that table with a key longer than 256 characters, so we could just check for that and early-return.
Flags: needinfo?(dkeeler)
Hi, Is there any update regarding the issue?
I have had this patch sitting around forever, and for some reason I just stumbed across this bug again, and realized I hadn't submitted this patch yet. I'm going to do that now...
Attachment #8779829 - Flags: review?(dkeeler)
Comment on attachment 8779829 [details] [diff] [review] Don't try to look up subdomains which have keys longer than MAX_KEY_LENGTH in nsSiteSecurityService Review of attachment 8779829 [details] [diff] [review]: ----------------------------------------------------------------- Thanks for the patch. This is the right idea, but I think the details need to be fixed (unless I'm misunderstanding this code). Also, it would be good to have some tests for this (particularly for the scenario I mention below) - you can work off of existing tests in https://dxr.mozilla.org/mozilla-central/source/security/manager/ssl/tests/unit/ ::: security/manager/ssl/DataStorage.h @@ +91,5 @@ > > public: > + // The maxumum length of the key passed to a function storing or retrieving > + // data from this structure. Validated by DataStorage::ValidateKeyAndValue. > + enum { MAX_KEY_LENGTH = 256 }; Maybe let's just make this a static const uint32_t? Also, let's update any documentation in this file or DataStorage.cpp that references "256" to say "MAX_KEY_LENGTH (currently 256)" or something. ::: security/manager/ssl/nsSiteSecurityService.cpp @@ +1029,5 @@ > const char *subdomain; > > uint32_t offset = 0; > + // If the host is too long, we can save some processing time by not looking at > + // the subdomains which are longer than MAX_KEY_LENGTH characters, as they won't be found nit: keep lines <80 characters, please @@ +1032,5 @@ > + // If the host is too long, we can save some processing time by not looking at > + // the subdomains which are longer than MAX_KEY_LENGTH characters, as they won't be found > + // within our DataStorage. > + if (host.Length() > DataStorage::MAX_KEY_LENGTH) { > + offset = DataStorage::MAX_KEY_LENGTH; I'm not sure this is correct. Imagine host is 300 characters long. This will make the code start looking for ancestor domains 256 characters into host (so domains less than 300 - 256 = 44 characters long), and we could potentially miss an ancestor domain that is less than 256 characters but also longer than 44 characters. I think what we want here is `offset = host.Length() - DataStorage::MAX_KEY_LENGTH`, right? Also, let's avoid the "subdomain" terminology in comments, since we're really looking for ancestor domains (the name of the variable pointing to the host data is perhaps not the best).
Attachment #8779829 - Flags: review?(dkeeler) → review-
Attachment #8779829 - Attachment is obsolete: true
Assignee: nobody → michael
Comment on attachment 8780255 [details] [diff] [review] Don't try to look up subdomains which have keys longer than MAX_KEY_LENGTH in nsSiteSecurityService Review of attachment 8780255 [details] [diff] [review]: ----------------------------------------------------------------- Looks good, but it still needs some xpcshell tests. Let me know if some pointers on that would be helpful. ::: security/manager/ssl/nsSiteSecurityService.cpp @@ +1012,5 @@ > + // If the host is too long, we can save some processing time by not looking at > + // the ancestor domains which are longer than MAX_KEY_LENGTH characters, as > + // they won't be found within our DataStorage. > + if (host.Length() > DataStorage::MAX_KEY_LENGTH) { > + offset = host.Length() - DataStorage::MAX_KEY_LENGTH; We should document that this is more of a heuristic than an exact optimization because when we actually turn the ancestor domain into a lookup key, we append ":HSTS" or ":HPKP" onto it. I don't think it's worth the added complexity to take this into account in code.
Attachment #8780255 - Flags: review?(dkeeler)
Unfortunately, I don't know enough about how this component works to come up with a coherent strategy for testing the new changes, other than generating a link with 1000+ subdomains and mousing over / clicking on it, timing whether or not the system becomes unresponsive. i'd love some pointers as to how I can verify that this doesn't change existing behavior.
Flags: needinfo?(dkeeler)
Well, I don't know of a way to deterministically test that the hang has been made less severe (note that we may still have ~128 lookups if the domain is something like a.a.a.a.a.a.....), but it would be good to ensure that if there's a domain in storage that's just at the length limit, a subdomain is properly identified as HSTS when we do a lookup on it. https://dxr.mozilla.org/mozilla-central/source/security/manager/ssl/tests/unit/test_sts_fqdn.js might be a helpful starting point. Another thing to do would be to manually verify that this change makes a difference in practice.
Flags: needinfo?(dkeeler)
Priority: -- → P1
Priority: P1 → P3

Unassigning as I don't have time to work on this.

Assignee: nika → nobody
Whiteboard: [necko-backlog] → [necko-backlog][has-patch][needs-tests]

Redirect a needinfo that is pending on an inactive user to the triage owner.
:dragana, since the bug has high severity, could you have a look please?

For more information, please visit auto_nag documentation.

Flags: needinfo?(mohitrawat08) → needinfo?(dd.mozilla)
Flags: needinfo?(dd.mozilla)

In the process of migrating remaining bugs to the new severity system, the severity for this bug cannot be automatically determined. Please retriage this bug using the new severity system.

Severity: critical → --

I don't think this happens anymore. I couldn't reproduce it with comment 0, and comment 2 doesn't work anymore.
I'm going to leave this open for now.

Severity: -- → S3
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: