81

Even though sometimes software bugs and vulnerabilities are deemed as the same concept, there must be at least one distinct aspect between them, and I think the most prominent one is exploitability (the latter one having the property).

What I'm curious about is, even after seeing many cases that divide-by-zero bugs are reported as software problems, I can hardly come up with any attack (other than DoS) using divide-by-zero bugs. I know not all kinds of bugs have the same impact upon a system in terms of security, but is there any attack method that uses divide-by-zero bugs to achieve something different than DoS, like privilege escalation for example?

Gwangmu Lee
  • 859
  • 1
  • 5
  • 7
  • 4
    I have a vague memory of a CVE from many years ago that was at its core a divide by zero, but was a remote root arbitrary code bug. It was *probably* something like what John Deters described, but I don't remember enough to risk giving an answer. – Ed Grimm Mar 04 '19 at 06:13
  • 20
    I’m assuming you’re talking about *integer* division by zero? Because IEEE 754 floating point division by zero is well-defined, and should therefore be unproblematic (but parts of what applies in the scenario of John Deters’ answer would also apply here). – Konrad Rudolph Mar 04 '19 at 12:39
  • 2
    Another takeaway I get from this question is that code review should ensure that the denominator is vetted prior to every divide operation. A zero denominator indicates some kind of error that needs to be investigated. – EvilSnack Mar 04 '19 at 13:39
  • 7
    i was on a project where we had to report a user-controllable divide-by-zero as a major security hole to the US government. users could craft packets that would cause a mod 0 operation, which causes divide by 0. the thing that got killed was an intrusion prevention system. so, you could turn it off with a crafty packet, just before you began an attack. of course this is a very strange and specific example. – Rob Mar 05 '19 at 05:17
  • @Rob Ohhh that's very interesting. I've never thought of a case where DoS straight up opens other holes. – Gwangmu Lee Mar 05 '19 at 05:52
  • 2
    There's been several cases of [screensavers crashing](https://www.jwz.org/blog/2015/04/i-told-you-so-again/) due to things like this, leaving the system unlocked and unprotected – that other guy Mar 05 '19 at 21:19
  • @thatotherguy: I've never bothered how screensavers actually work because that's been such a superfluous thing since at least 20 years. But if it really works in such a way that crashing the screensaver may leave the computer unlocked, then the whole design is seriously stupid, from bottom to top -- a divide by zero is the smallest problem in that case. My baseline expectation would be that the _desktop environment_ locks the user out (and disables hardware such as 1394/USB), and then invokes some _toy program_ which can (ideally) do little more than draw silly lines on the screen. – Damon Mar 08 '19 at 09:27

13 Answers13

106

At issue is that an exception handler will be invoked to handle the division by zero. In general, attackers know that exception handlers are not as well-tested as regular code flows. Your main logic flow might be sound and thoroughly tested, but an exception handler can be triggered by interrupts occurring anywhere in the code within its scope.

int myFunction(int a, int b, SomeState state) {

    state(UNINITIALIZED);
    try {
        state.something(a/b);
        state(NORMAL);
    }
    catch () {
        state.something(b/a);
        state(INVERTED);
    }
    return retval;
}

This horrible pseudocode sort of illustrates one way the flaw could be exploited. Let's say that an uninitialized state is somehow vulnerable. If this routine is called, the state is first uninitialized. If b is zero, it catches the exception and tries to do some other logic. But if both a and b are zero, it throws again, leaving state uninitialized.

The division by zero itself wasn't the vulnerability, it's the bad code around it that's possible to exploit.

John Deters
  • 33,650
  • 3
  • 57
  • 110
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackexchange.com/rooms/90763/discussion-on-answer-by-john-deters-is-divide-by-zero-a-security-vulnerability). – Rory Alsop Mar 08 '19 at 09:36
50

To add another contrived but based on real example:

Many (many) moons ago, my high school was running Novell Netware and had it configured so that students couldn't directly run an dos prompt (which was annoying if you e.g. needed to format a floppy disk). However, it was discovered that if you entered in a password more than X characters (i.e. just held down a key for a while), this would crash netware and drop the system back to ... a dos prompt. Most likely this was a buffer overrun, but the principle is the same: if you're dealing with a system (especially an embedded one) which handles at least some unexpected crashes by dropping you into a maintenance mode, then there is a security concern.

Foon
  • 601
  • 4
  • 3
  • 3
    Brings back happy memories... – speciesUnknown Mar 06 '19 at 10:11
  • 8
    Some day I'll have to tell the full story around exploiting a similar flaw in some Apple ][ bulletin board software. The software was originally written for integer BASIC but ported to floating point BASIC without much consideration. You could cause floating point BASIC to crash with an overflow just by entering "99e999" at any integer input prompt. This would drop you to a command prompt and you could, for example, read private emails or forums you weren't authorized to access. – David Schwartz Mar 07 '19 at 18:08
19

As the other answers are suggesting, it entirely depends on the whole system. That is not just the code, but the platform, language and compiler too.

For example in c++, division by zero on integers is undefined behaviour. One of the rules about that particular language is the compiler may assume that undefined behaviour will never occur, and is allowed to do anything if it does.

Anything includes crash with an error message and clean up, crash without an error message and leave everything in a weird state, or most terrifying try to keep going as if nothing has happened.

Now in practice most good modern compilers try to crash cleanly if they hit something like that, but this should make it clear why the assumption is that it is a vulnerability. If you just build your code with a different compiler or different settings, and don't even change the code, what the program does could go from a clean crash to wiping out your database.

Josiah
  • 1,818
  • 9
  • 14
  • 5
    The last paragraph is dependent on one's definition of "good". Some people would regard a compiler that optimizes `if (x != 0) launch_nuclear_missiles(); return 1/x;` by making the function call unconditional as superior to one that doesn't. I would say that quality compilers *which are designed to be suitable for tasks involving untrustworthy input* should either crash or do nothing in response to a division by zero (if code computes `z=y/x;` but never uses `z`, a compiler that ignores `x` entirely should not generally be viewed as inferior to one that traps if it's zero), but... – supercat Mar 04 '19 at 17:01
  • ...for tasks which will never involve untrustworthy input, looser semantics may be reasonable. – supercat Mar 04 '19 at 17:01
  • 2
    @supercat I think it would require a guarantee that `launch_nuclear_missles` always returns. – Solomon Ucko Mar 05 '19 at 00:01
  • 2
    @SolomonUcko: Why? If `x` is zero, the division by zero would occur whether or not the function would return if called. If `x` isn't zero, the function could return without invoking UB. – supercat Mar 05 '19 at 05:03
  • 1
    @supercat Oops, nevermind. The lack of curly braces and the fact that everything is on one line keeps tripping me up. – Solomon Ucko Mar 05 '19 at 11:43
  • @supercat: "I would say that quality compilers which are designed to be suitable for tasks involving untrustworthy input should either crash or do nothing in response to a division by zero" - I get the impression that you confuse compilers and interpreters. The input of a compiler is the program text; the output is an executable program. Untrustworthy input to the resulting executable cannot time travel to crash the compiler. As to "do nothing", that generally doesn't solve the problem. In `double x = y/0`, omitting the initialization would leave `x` uninitialized, causing UB later on. – MSalters Mar 05 '19 at 13:31
  • 1
    @MSalters: On most platforms, unless an implementation goes out of its way to cause some other behavior, an attempt to load an Indeterminate Value of type `double` will either load some arbitrary value or fire a trap whose default behavior will force an abnormal program termination. Likewise for most forms of UB other than those characterized as "Critical Undefined Behaviors" in (IIRC) Annex L. Although Annex L is written too imprecisely to really mean anything (to be meaningful, it should define a behavioral model and then specify that non-critical UB may behave in any fashion that is... – supercat Mar 05 '19 at 14:32
  • 1
    ...consistent with that model) the notion that implementations should interpret UB as an excuse to jump the rails *even when the underlying platform would allow an implementation to offer useful behavioral guarantees at a cost which is generally zero outside contrived scenarios*. C's reputation as being a fast language came from the fact that if on some particular platform a program's requirements could be met without checking for certain conditions in machine code, neither the programmer nor compiler would need to include such checks. If programmers have to include such checks... – supercat Mar 05 '19 at 14:37
  • 1
    ...even in cases where the environment's natural behavior could meet requirements without them, that will compel programmers to write code that cannot be processed as efficiently as would have been possible by exploiting the platform's behavior. – supercat Mar 05 '19 at 14:40
16

Altogether with a misconfigured webserver, a PHP environment could reveal the physical path to the script when a division by zero is caused (internal path leakage):

<?php
$denominator = $_GET['number'];
echo 1/$denominator;

Accessing script.php?number=0, we could see:

Warning: Division by zero in /var/www/html/script.php on line 3.

  • 2
    It might be worth adding to this that you an sometimes use it to get even more verbose errors: ("Error near Select * from passwords where logins = 1/0") 'Verbose errors' is a misconfiguration but this is a cheap way to exploit it. – JeffUK Mar 05 '19 at 11:20
  • 3
    That would be a very poorly configured PHP installation. Not that such installations don't exist, though. display_errors hasn't shipped ON by default for a LONG time. – Kevin_Kinsey Mar 05 '19 at 17:34
13

While I sometimes say in my workshops and lectures that all security issues in software are essentially bugs, the reverse is not automatically true.

I would not, however, go to exploitability as an argument. Just because you and me can't figure out a way to exploit doesn't mean someone more crafty won't one day. We would have to provide a formal proof that it is not exploitable. Good luck with formal proofs in current programming languages.

So to answer your question: A division by zero could be a vulnerability or a part of it. Typically it will lead to an exception and thus termination of the software. This by itself could be important information for an attacker (he now knows that some variable was zero).

It is unlikely that the division by zero alone is a security vulnerability, but in a larger context it can be a part of the problem.

So, to close the loop, you should always treat all bugs seriously. If you cannot be sure (e.g. your division uses a variable you don't control) then you need to catch and handle it, security or not. "yeah, it's a bug but I don't see how it could be exploited" is not a security-conscious mindset.

Tom
  • 10,124
  • 18
  • 51
  • "all security issues in software are essentially bugs" and some of them are PEBCAK =P – alo Malbarez Mar 08 '19 at 01:12
  • 2
    @aloMalbarez I routinely challenge people on that assumption. Heck, I've given a keynote speech on that. "users == losers" is the excuse lazy people use to not discuss their shortcomings in user interface design, visibility of consequences or plain old blame-shifting. – Tom Mar 08 '19 at 08:59
10

Division by zero is not inherently a security vulnerability.

However, if you can make an application server crash and stay offline by making it divide by zero, this may constitute a denial of service vulnerability.

  • 15
    This was already mentioned in the question though. – JAD Mar 04 '19 at 08:45
  • 4
    @JAD: There seem to be two related classes pf denial-attacks. One, provide a single input such that the server state gets persistently corrupted. Two, provide repeated inputs that repeatedly crash the server. This answer seems to cover the first scenario (_stay offline_) while the question appears to cover the second. But they're definitely related. – MSalters Mar 04 '19 at 12:03
  • @jad, in that respect the title and the question are inconsistent. Should probably change the title. – JeffUK Mar 05 '19 at 11:19
  • Basically, any bug in a program that can cause it to crash is potentially a security vulnerability, and a failure to prevent division by zero is a common such bug, but there's no inherent difference between that and other causes of crashing. For example an array-out-of-bounds error is just as dangerous. – Michael Kay Mar 07 '19 at 11:30
5

I think ultimately your answer’s going to come down to the individual system in play. How does the system handle trying to divide by 0? If it’s elegant, then your attack options are limited or nonexistent. If it does something funky you can probably get in there with something.

Basically, no standard attacks can come out of this - that I’m aware of anyway - but computers can always handle bugs badly, and bad handling of bugs is the source of many vulnerabilities.

securityOrange
  • 913
  • 4
  • 12
5

If a program will only receive trustworthy data, and if it would not have to meet any behavioral requirements when given maliciously-crafted data, then it may be possible to generate slightly more efficient code than would be required if there were some behavioral requirements that it had to meet for all data.

Because some tasks only involve processing trustworthy data, while others involve processing data from untrustworthy sources, the C Standard allows implementations that are intended only for the former tasks to make optimizations that would be inappropriate in those intended for the latter, and for those intended to be suitable for the latter tasks to offer guarantees that would unnecessarily impede optimizations that could be useful when processing the former.

Unfortunately, the Standard provides no means by which implementations can indicate what kinds of behavioral guarantees they will offer beyond those mandated by the Standard. When C89 was written, the authors expected that "the marketplace" could do a better job than the authors of the Standard of determining what kinds of implementations should support what "popular extensions" by behaving at least somewhat predictably in cases where the Standard imposed no requirements, and such attitude has not changed even though it no longer fits today's compiler marketplace.

Given something like:

if (x != 0) launch_nuclear_missiles();
... and then, possibly in another function
z=y/x;

some people would view a compiler that replaces the "if" with an unconditional call to launch_nuclear_missiles() as superior to one that only calls launch_nuclear_missiles if x is non-zero, but such behavior would only be appropriate when processing tasks that will never involve untrustworthy input.

If one knows that the compilers one is using will uphold the kinds of weak behavioral guarantees that general-purpose compilers used to offer as a matter of course, and which facilitate writing programs that could meet weak behavioral constraints even when given maliciously-crafted inputs, then division-by-zero might not pose security weaknesses. With aggressively-optimizing compilers that are not designed to be suitable for tasks involving untrustworthy input, however, it will be necessary to add enough safety-checking logic to negate any advantages such "optimizations" could have offered.

supercat
  • 2,029
  • 10
  • 10
  • @PeterCordes: If `x` isn't zero, the function could return without causing UB. If `x` is zero, then UB would occur regardless of what the function would do if called. – supercat Mar 05 '19 at 05:00
  • Oh yes, you're right. @SolomonUcko's argument doesn't hold up, because `x=0` would lead to `y/x` running *without* a call to a possibly-non-returning function, and thus can be assumed not to happen. Obviously this code has a bug, but yes some failure modes are worse than others. C is not a safe language. – Peter Cordes Mar 05 '19 at 05:19
  • @PeterCordes: It's a shame that it's fashionable for compilers to regard the "popular extensions" alluded to in the Rationale as "missed optimization opportunities", rather than recognizing that they allowed many programs to meet requirements more cheaply than would otherwise be possible. – supercat Mar 05 '19 at 05:29
4

but is there any attack method that uses divide-by-zero bugs to achieve something different than DoS, like privilege escalation for example?

Quick search on MITRE's CVE database will reveal that mostly division by zero causes denial of service, but one particular case CVE-2005-0998 allows for something else:

Description The Web_Links module for PHP-Nuke 7.6 allows remote attackers to obtain sensitive information via an invalid show parameter, which triggers a division by zero PHP error that leaks the full pathname of the server.

This itself doesn't allow for privilege escalation or specific attacks, but generally speaking sensitive information can be useful in crafting other attacks. In other words, by itself division by zero might be only a stepping stone.

3

I see where you are coming from on this. In it own right its difficult to see how just an arithmetic error could be used to cause anything that required data input (like code execution).

However, depending on the language it could cause flow control issues or selectively crash threads etc. This is the sort of thing that allows you to compound other issues or if you're really (un)lucky, directly lead to triggering a pathway that causes double something exploitable. Remember the data entry and the bug don't necessarily have to be the same.

This is a bit of a contrived, but say for example you moved an object already in a linked list by adding a copy to the head, and then iterating till you find it and remove the copy. If you do this is in a thread and for some reason (say someone puts a stats print statement in the iteration loop that's wrong), it's possible that will leave the list in a bad state with 2 copies of the same element. When this gets cleaned up: double free...

If you can control the contents of this new element, and can make the stat result in a divide by zero and still control enough of the contents, this could well be exploitable.

Not likely, but possible.

ANone
  • 230
  • 1
  • 4
2

The question might be better phrased as "Why is Denial of Service considered a security vulnerability?". When running Linux on an x86 processor, if the processor divides an integer by 0 (or the minimum signed integer value divided by -1, thanks supercat) the program receives a SIGFPE signal. In Windows it's an illegal operation. The C/C++ standard considers it undefined behavior which means you can't reliably know what will happen just by looking at the code.

Normally this, as with many but not all Denial of Service vulnerabilities, will result in a "crash". Here's some possibilities of what you can exploit when a program has abnormal termination:

  • It could allow another program to use the same resources as the first (like a port) and pretend to be the same program.
  • Crashing may circumvent any cleanup operations used by the program which could be exploited.
  • If the exit code of the program is being used in another program that isn't checking the signal state of the child process's PID it could think the program returned an error code. For instance on my Linux machine, the following 2 programs both set $? (the variable used in shell scripts to represent return code of the previous command) to 136: int main(int argc, char** argv) { return 1 / (argc - 1); } and int main() { return 136; }
  • Crashing may result in things like coredumps which may contain protected memory like security keys, passwords, and the recipe for Coca-Cola.

Additionally, because of optimizations, the compiler may choose to assume that the divisor could never be 0 and potentially optimize away code you might not have considered. This only applies to compiled languages like C and C++ that have undefined behavior (or its equivalent) for division by zero. I couldn't produce optimized code that does that using the examples in some other answers in clang or gcc, but there is nothing preventing them or other compilers from doing that either in the past or future.

Erroneous
  • 191
  • 3
  • Whether "crashing" would represent a security vulnerability is a function of the execution environment. If one is using a crazy-aggressive C implementation, however, any action whose behavior isn't mandated by the C Standard may cause security vulnerabilities even in an environment where "crashing" wouldn't. – supercat Mar 06 '19 at 21:33
  • @supercat I attempted to improve my answer based on your comments – Erroneous Mar 07 '19 at 14:04
  • You should say that if *machine code* running on Linux attempts an integer division by zero (or 32-bit integer division of -2147483648 by -1), it will raise a `SIGFPE`. A C program that attempts to perform such division would pose security flaws in cases where a SIGFPE might do so, but also in cases where a C optimizer's assumption that a program's inputs will never result in a division by zero may result in a compiler optimizing out safety code that would only be relevant if such inputs occur. – supercat Mar 07 '19 at 15:22
1

Here is an example of where it might make a difference.

It is 100% contrived - but many classes of exploits are identified by theoretical "contrived" examples, until specific examples are found.

It is pseudo-code.

try:
   get the average cpu usage per logged in user
   if this average is too high, tell the person logging on that we're busy.
   get login credentials
   verify login credentials
   if credentials are invalid, tell them to go away
   restrict access for this session to the correct access for this user
if there is an exception:
   do nothing

let the user in

In this case the average cpu per logged in user, might be calculated as "total cpu / number of logged in users" - if there are no logged in users, you get "divide by 0", and it skips directly to the "if there is an exception" clause. This includes restricting access - the new session might have access matching the last user to log in, or unlimited access.

While this example shows all the code together, often your main code will call something, which calls something else, and the two aspects that interact (in this case, division by 0, and the fact that the exception is ignored) may run in parts of the code which are a long way apart.

As many people have mentioned, division by zero (on it's own) is not a security bug, or indeed a bug at all. As far as I'm aware, most (maybe all!) programming languages have clearly documented information as to what happens when division by zero occurs. I think pretty much every language that has exceptions, has an exception for division by zero (I have seen a mechanical calculator get stuck in an endless loop).

AMADANON Inc.
  • 1,481
  • 9
  • 9
1

There are some very specific answers here. I would agree that a (unhandled) division by zero issue is primarily a functional issue - but that does not mean it cannot be exploited as a security issue. Indeed, there are so many bugs which can be exploited the classification of "functional" vs "security" when applied to bugs seems somewhat pointless.

You dismiss a category of Security issues (DOS) without any discussion of what you consider out of scope here. The most obvious scenario is that tripping the error will cause the instance of the program to stop. But there are more subtle effects - if it causes large amounts of data to be written to the filesystem (such as a core dump) it presents an avenue for a second type of DOS, by filling up the filesystem.

Jefrey mentions disclosure for a PHP application - but error messages, logs and core dumps provide rich picking grounds for people trying to extract information they should not have access to from programs written in any language.

If the premature termination of the program leads to some sort of response, then an attacker has means of triggering a specific program at a time of their choosing - they have some control over what your system is doing which can facilitate an attack / privilege escalation.

Even if the control system is manual, there is still a transition state for the system where security behaviours are less well defined and where assets such as pass-phrases, passwords and private keys are in flight.

symcbean
  • 18,278
  • 39
  • 73