For level 2, there are two programs that can be used to advance, and we have access to source code! Let’s check the first one.
The source code for level02.c is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
int atoi(const char *str);
Converts the string argument to an integer. The string must contain a valid integer number. If this is not the case, the returned value is undefined.
The number can be terminated by any character that cannot be part of an integer number. This includes white space, punctuation, and characters.
Returns 0 if the string can’t be converted to a valid integer.
int abs (int n);
Returns the absolute value of an integer.
Trying to take the absolute value of the most negative integer is not defined.
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
Sets a function as a handler for the signal. That function will be called when the program receives the signal.
The function can be a specific function or one of the following macros:
SIG_DFL Default signal handling: the default action associated with the signal occurs.
SIG_IGN Ignore the signal
According to POSIX, the behavior of a process is undefined after it ignores a SIGFPE, SIGILL, or SIGSEGV signal that was not generated by kill(2) or raise(3). Integer division by zero has undefined result. On some architectures it will generate a SIGFPE signal. (Also dividing the most negative integer by -1 may generate SIGFPE.) Ignoring this signal might lead to an endless loop.
The effects of signal() in a multithreaded process are unspecified.
SIGFPE (Signal Floating-Point Exception) Erroneous arithmetic operation, such as zero divide or an operation resulting in overflow (not necessarily with a floating-point operation).
We can see from the code that the program is looking for 2 arguments, with the second being a valid atoi converted integer, and it returns the absolute value of the result of the division between the first and second arguments. The catcher function is registered as handler for the SIGFPE signal, and we want it to get called. At first glance, this should be possible by doing a zero division. But we can’t use 0 for our second argument, because the program checks for atoi returning 0.
From the man page, we know that SIGFPE may be generated if we divide the most negative integer by -1, so let’s try that:
1 2 3 4 5 6
It worked! Now let’s look at the alternate way. This is the source for level02_alt.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
double strtod(const char *str, char **endptr);
Converts the string to double and returns the result. Any character that cannot be part of a floating-point number will end the parsing. A pointer to the rest of the string after the last valid character is stored in the object pointed by endptr. If no conversion is performed, 0 is returned and the value of str is stored in the location referenced by endptr.
The program will spawn a shell if we provide it with the exact match for answer. But it’s not as straightforward as passing 3.141593 to it, when I did that, I got the message that it was too low. I added a printf to the code, and I couldn’t see any difference in rounding, but since this is a no-go, there has to be something else. Looking more closely at the manpage:
The expected form of the (initial portion of the) string is optional leading white space as recognized by isspace(3), an optional plus (‘+’) or minus sign (‘–’) and then either (i) a decimal number, or (ii) a hexadecimal number, or (iii) an infinity, or (iv) a NAN (not-a-number).
A NAN is “NAN” (disregarding case) optionally followed by ‘(’, a sequence of characters, followed by ‘)’. The character string specifies in an implementation-dependent way the type of NAN.
Passing it nan gives us the shell:
1 2 3
Your temporary financial embarrassment will be relieved in a surprising manner.