ShellShock, What I've Understood

Disclaimer: I'm not a security specialist; if you are running a webserver, please consider asking a qualified technician.

If last week you have been to Mars, maybe you haven't heard about the new security issue named ShellShock (CVE-2014-6271), claimed to be even more dangerous than HeartBleed.

In few words, it's a vulnerability in the Burn Again Shell (bash) in all Unix-derived systems (Linux, BSD and MacOS) [and maybe Windows too] that can be triggered via a remote call to a web server that uses CGIs. The following is a simple method to understand if your system is vulnerable (taken from here):

env X="() { :;} ; echo busted" /bin/sh -c "echo completed"

env X="() { :;} ; echo busted" `which bash` -c "echo completed"

Run these two commands from a shell; if you read "busted", your system is vulnerable.

Obviously, the preferred targets for attacks based on this vulnerability are the webservers directly connected to internet. And this don't mean only those that run large companies websites, but also embedded devices, such as routers and IP cams.

Embedded Systems: Not So Bad

Due to the limited power of the hardware, embedded systems usually don't have the standard bash binary but use the shell functions provided by BusyBox that is not vulnerable.

This is true for many embedded devices but not for all; for this reason, if you own one of those,  I suggest you to verify with the producer or by yourself, if you are skilled enough.

Webservers: Pretty Bad

I'll assume that your system is correctly configured: since this vulnerability does not involve privilege escalation, all the commands being executed by the exploiter will run with the same privileges of the webserver application and its CGIs. Of course, if Apache is running with root permissions, you are in big troubles even if your system is not vulnerable, but this is a different story.

That said, this vulnerability can be tapped in five ways.
  1. Making the server slow or unavailable, for example running busy loops or filling the memory with an infinite series of forks.
  2. Stealing your data (restricted pages on the website and databases).
  3. Deleting all data accessible from webserver and CGIs (webpages and databases): you can mitigate this risk with frequent backups.
  4. Being used for a DOS attack against other sites.
  5. Injecting malicious scripts or redirecting the visitors to malicious sites. This is the worst scenario: with sed or awk it is quite simple to change every HTML page (but also JavaScript files or Python CGIs) to inoculate code that can take advantage of some browser vulnerability - or simply deface your homepage.
To add some more fear, some of the above can be combined to increase the damage.

Conclusions

ShellShock is a vulnerability that should not be underestimated but probably it's not so bad as it has been reported. Many embedded systems, that are more likely to not receive an update, are not affected. And nowadays many technologies other than CGI are used.

Now, if you are asking if there is something that average people can do to mitigate the risk, you'll have to read this post for the answer.

0 Errors - 0 Warnings

Are you one of those that don't care about warnings when compiling? Well, if you write conditions like the following,  probably you are:

if (2 == foo)

At first, it seems reasonable: if you forget one "=" by mistake, the compiler will stop the build with an error, making it obvious that you did something wrong. If the condition was written in the opposite way, it only would present you a warning message. But this approach has a big pitfall: the false sense of security.

If you think that whenever your code compiles without errors it is OK and the warnings are just annoying messages that can be easily ignored, you are probably missing the following problem:

if (bar = foo)

Every decent compiler will warn you by telling that, if an assignment is what you really want to do, it's better to surround it with double parenthesis. But if you ignore warnings, you'll get an unexpected (for you) behavior.

In my opinion, compiler warnings are even more important than errors: an error is something that is illegal in the format of a program while a warning is telling you that something looks strange in the logic of your code.

Many warnings are similar to a question: "are you sure you want to do that?" Or better: "doing that you'll get this result: is it what you want?" From my personal experience, many times the answer was "No!"

Bottom line: if you want to be sure that your code won't compile if there are warnings, the flag -Werror of gcc is what you need.

C++ And goto Don't Match Together

As you can see by reading this blog, I'm a fan of goto when used in an appropriate way. Unfortunately it works for C only - not C++.

The issue is all in this short paragraph (6.7/3) of the C++ standard:
It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps from a point where a local variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has POD type (3.9) and is declared without an initializer (8.5).

This means that your C++ compiler will not build many C sources if you have used gotos to bypass initializations.

The solution to maintain the same functionality and the same structure of your code is to use exceptions. I'm sure you are thinking "why cannot I simply use some nested ifs?" My answer is "readability over all!"

An Example

Let's see a quick example, as I would write it in C.
int foo()
{
        int err = 0;
        X *x = NULL;
        Y *y = NULL;

        x = x_factory();
        if (x == NULL) {
                err = 1;
                goto clean_exit;
        }

        y = y_factory(x);
        if (y == NULL) {
                err = 2;
                goto clean_exit;
        }

        while ( /* condition */ ) {
                while ( /* other condition */ ) {
                        /* some code */

                        if ( /* critical error */ ) {
                                err = 3;
                                goto clean_exit;
                        }
                }
        }

        /* some other code */

clean_exit:
        if (x != NULL)
                x_free(x);
        if (y != NULL)
                y_free(y);

        return err;
}
The same code in C++ won't work, if in the code inside or after the loops there is some initialization. So, let's see how you would be tempted to rewrite it.
int foo()
{
        int err = 0;
        X *x = NULL;
        Y *y = NULL;

        x = x_factory();
        if (x != NULL) {
                y = y_factory(x);
                if (y != NULL){
                        while ( /* condition */ ) {
                                while ( /* other condition */ ) {
                                        /* some code */

                                        if ( /* critical error */ ) {
                                                err = 3;
                                                break;
                                        }
                                }

                                if (err != 0)
                                        break;
                        }

                        if (err != 0) {
                                /* some other code */
                        }
                } else {
                        err = 2;
                }
        } else {
                err = 1;
        }

        if (x != NULL)
                x_free(x);
        if (y != NULL)
                y_free(y);

        return err;
}
Too many elses and too indented, in my opinion. Below there is my version, using one of the most powerful constructs of C++: exceptions.
int foo()
{
        int err = 0;
        X *x = NULL;
        Y *y = NULL;

        try {
                x = x_factory();
                if (x == NULL)
                        throw(1);

                y = y_factory(x);
                if (y == NULL)
                        throw(2);

                while ( /* condition */ ) {
                        while ( /* other condition */ ) {
                                /* some code */

                                if ( /* critical error */ )
                                        throw (3);
                        }
                }

                /* some other code */
        } catch (int exception_code) {
                err = exception_code;
        }

        if (x != NULL)
                x_free(x);
        if (y != NULL)
                y_free(y);

        return err;
}
This is my opinion; what's your?

All You Need To Know About Software Development


While I was reading this (long) article, I've felt like all the different pieces of the puzzle in my head go in the right place.

This is the most complete and correct description of the software development best and worst practices I've ever read. Michael Dubakov covered every single aspect and analyzed each factor that can impact on the speed of a software project.

I've only to add a single small note about refactoring: I'm not sure that it is a non-value added activity. Generally speaking it may be so but often, after a refactoring I've found my code run faster and/or have a smaller memory footprint.

That said, it's definitely a great article. Take your time to read and re-read it.


You Are Not A Programmer


So you write code every day, maybe in a nerdy language like C or even in assembly. And a company is paying you for this job. When someone asks you "what do you do?", it's normal for you to reply "I'm a programmer", isn't it?

Well, let's see if you are a liar. This is a simple yes/no questionnaire about what you have done in the last two years.

The Real Programmer Test

  1. Have you studied a new programming language?

  2. Have you used a new technology?

  3. Have you spent some time to optimize your code?

  4. Have you programmed for your pleasure out of the working hours?

  5. Have you eaten at least 50 pizzas?

  6. Have you drunk at least 3 coffees every day?

  7. At least once did you choose to not use your favorite programming language because you thought it was not the best choice for a project?

  8. Have there been more happy days than sad days when doing your job?

If you replied "yes" at more than half of the above questions, congratulations, you are a real programmer!

Explanation of the Test

If you are not a real programmer, maybe you cannot understand how the above questions come from, so here there are some hints.

  • A programmer is curious by nature: he likes to learn new languages and technologies, even if they are not required by his job (questions 1 and 2).

  • A programmer knows that every code needs some refactoring at some point (question 3).

  • A programmer is happy when he can write code (questions 4 and 8).

  • A programmer is realistic: he knows that one-size-fits-all doesn't exists in computer science; in other words, for some purposes a language/technology can be better than another (question 7).

  • A programmer needs to have it's brain constantly fed by carbohydrates (pizza) and sometimes powered by caffeine (questions 5 and 6).

Having said that, you may argue that many of these characteristics are innate. Well, you are right! Many people write code because they think it's just like any other job but they are wrong. Programming needs passion, devotion and the right way of thinking. And over all (as I've read in a pizzeria):

If it were an easy job, everyone would be able to do it

Image by icudeabeach

Authors In The Open Source World

Last week, Seth Godin wrote another great post. This time the argument is the difference between companies and authors. No company would endorse a competitor while writers often suggest books written by others.

The implicit message is that culture is not a product.

Open source logo
Image by Andrew
For FOSS developers it works almost the same. If someone is creating a good software, his project will be not only praised but also improved by other developers. And the good part is that they share their work for free.

For these reasons I think that with the following sentence, Seth is describing a situation wider than he thought.

It's not a zero-sum game. It's an infinite game, one where we each seek to help ideas spread and lives change.