• Browse topics
Login
Sign up
Login

SNYK LEARN LOGIN

OTHER REGIONS

For Snyk Enterprise customers with regional contracts. More info

Sign up

Uncaught exception

Catch 'em all: protect your app from uncaught exceptions

JavaScript

Uncaught exception: the basics

What is an uncaught exception?

Uncaught exceptions are a common problem in JavaScript programming that can cause unexpected crashes and other types of vulnerabilities in web applications. These errors occur when an exception is thrown but not properly handled by the code, leading to undefined behavior and potential security vulnerabilities. To prevent these issues, you should always handle returned errors, use error-handling techniques such as try-catch blocks, and implement data validation to prevent input errors.

About this lesson

In this lesson, you will learn how uncaught exceptions work and how to protect your applications against them. We will begin by exploiting an uncaught exception vulnerability in a simple application. Then we will analyze the vulnerable code and explore some options for remediation and prevention.

FUN FACT

uncaughtException event

In Node.js you can catch uncaught exceptions using the uncaughtException event. However, this last resort shouldn’t be used to catch errors and continue running the application.

Node.js says the following about this: "The correct use of uncaughtException is to perform synchronous cleanup of allocated resources before shutting down the process. It is not safe to resume normal operation after uncaughtException."

This means an uncaught exception will always bring your app down, even if you still manage to catch it using the uncaughtException event.

Uncaught exception in action

Testing for uncaught exceptions

  • STEP 1
  • STEP 2
  • STEP 3
  • STEP 4
  • STEP 5
  • STEP 6

Setting the stage

A pentester has configured their app to crawl websites and for each endpoint, replay the request with a variety of modifications made to the request.

Target XYZ.svg

Uncaught exception under the hood

What actually happened? Why did we get an error message exposing an API key?

Let's look at the code behind the /waitlist endpoint.

As you can see, the waitlist endpoint performs a request to the API of the website. At first sight, this code looks rather good. But how did you and William manage to leak the API key?

The problem sits within the error handling of the request. Or actually, the problem is that there is no error handling at all.

The API URL is constructed using the host-header from the request. The reason is that the code has to work out of the box on multiple domains (target.xyz, target.abc, etc.). This is common practice, but not entirely recommended, as the host-header is essentially user input.

Since no validation is done on this value, we can craft an invalid URL and trigger an exception from within the request function. This exception is then, by default, written to the response, exposing the target.net API key within the request URL that was supposed to stay private.

The difference between supplying an empty host-header in this scenario vs a host header containing a non-existing host is the following:

Using a host-header with the value “i-do-not-exist.abc”, the application tries to make a request to “i-do-not-exist.abc” which results in a DNS resolution error:

events.js:292
throw er; // Unhandled 'error' event
^
Error: getaddrinfo ENOTFOUND i-do-not-exist.abc
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:66:26)
Emitted 'error' event on Request instance at:
at Request.onRequestError (/home/target/node_modules/request/request.js:877:8)
at ClientRequest.emit (events.js:315:20)
at TLSSocket.socketErrorListener (_http_client.js:432:9)
at TLSSocket.emit (events.js:315:20)
at emitErrorNT (internal/streams/destroy.js:84:8)
at processTicksAndRejections (internal/process/task_queues.js:84:21) {
errno: -3008,
code: 'ENOTFOUND',
syscall: 'getaddrinfo',
hostname: 'i-do-not-exist.abc'
}

The application terminates because this is an uncaught exception, that isn’t being caught by Express.

However, when we use an empty value as host-header, we construct an invalid URL, and an error is thrown that is actually caught by Express. Just not by the custom target.xyz code, but rather by default Express code, which decided that it is best to print it to the response as well as to stderr.

Because target.xyz didn’t handle errors, an exception slipped through which, unintendedly, exposed sensitive information that was deemed private and secure.

Impacts of an uncaught exception

The impacts of uncaught exceptions depend on what exception is thrown and in which context. That is why the impact is generally called “unintended behavior”.

Below are some examples of this unintended behavior.

Cross-site scripting

User input within uncaught exceptions are not filtered or sanitized. For example, a function that converts an integer as string to an integer of type int32.

If the string is user-supplied and contains <h1>Hello World</h1>, then the convert function will throw an error saying that <h1>Hello World</h1> is not a number.

Denial-of-service

Most of the time, uncaught exceptions end up crashing the application. Even if an exception can be caught by a global exception handler, continuing running is not advised and can lead to undefined behavior.

Information exposure

As demonstrated in the scenario with William and target.xyz, uncaught exceptions can expose sensitive information. This can include system information that can be used in further attacks but also personal information. For example, information about other users that are using the same application.

FUN FACT

Can you spot the SSRF vulnerability in the code?

By supplying a host-header containing our own domain (e.g. attacker.com) and then setting the IP address of that domain (A-record) to a private IP address (e.g. 192.168.178.1), we can send GET-requests to services running on the internal network.

Even the /v1/waitlist path can be overwritten by using a host-header value such as attacker.com/new/path/#. By utilizing this SSRF, we could have leaked the API key as well, by monitoring the logs of attacker.com and extracting the api_key parameter sent with the request.

Scan your code & stay secure with Snyk - for FREE!

Did you know you can use Snyk for free to verify that your code
doesn't include this or other vulnerabilities?

Scan your code

Uncaught exception mitigation

Preventing uncaught exceptions in JavaScript has been getting easier and easier over the years. Many functions in libraries now return error variables that contain a possible error that happened within the function.

For example, in the waitlist code of target.xyz, the request function can be extended to handle errors:

This checks the error variable for a possible error and then handles the error without showing the stacktrace to the user.

If this error variable is not available, a try…catch statement can be used to catch errors happening within the request function:

Even though this seems like a solution that would catch all errors, this isn’t true. The request function is an asynchronous function that returns immediately and does not throw an error when it encounters an invalid host. Instead, it triggers an "error" event on the returned request object.

To catch this error, we need to add an event listener for the "error" event on the request object and handle it appropriately.

Luckily, these last two solutions are not needed in the scenario of target.xyz because the first solution is sufficient enough in catching all types of errors stemming from the request function.

While the errors are now handled, it would be even better to not have those errors occur in the first place. Input validation could have played a big role here in preventing the API from leaking.

For example, the domains on which the application would run were known beforehand (target.xyz, target.abc and target.lmn). It is also known that the host-header should always contain a value and can not be empty.

Implementing a simple hostname whitelist for the host-header would result in no errors being thrown from the DNS resolution and no errors being thrown from the URI parser.

Of course, unexpected errors such as a timeout from the API can still happen. That is why input validation alone isn’t enough.

Quiz

Test your knowledge!

Quiz

How can the vulnerability of uncaught exceptions be effectively mitigated in applications?

Keep learning

To learn more about uncaught exceptions, check out more about this CWE here: https://cwe.mitre.org/data/definitions/248.html

Congratulations

You have taken your first step into learning what an uncaught exception is, how it works, what the impacts are, and how to protect your own applications. We hope that you will apply this knowledge to make your applications safer.Ma ke sure to check out our lessons on other common vulnerabilities.