2012/03/31

I forgot to remove debug code and updated last changes to production


we moved here http://radzserg.com/2012/03/31/i-forgot-to-remove-debug-code-and-updated-last-changes-to-production/


Sometimes you need to debug some code and you do something like this

if ($service->canBeUpdated) {
    // $service->canBeUpdated returns false 
    // and you need to debug this place 
}
 
// and you do simple (very simple :) stub
if (true || $service->canBeUpdated()) {

}


:D ooh everything works and you can easily debug this place. You've finished with that task and did some other tasks and finally updated everything to live.

Oh crap.. I forgot to remove debug code and updated last changes to production (or some customer complains that smth is wrong and this is even worst)

So I use this simple component in order to prevent such situations. Because because to be honest I faced with such problems not one time.



<?php

/**
 *
 * User: radzserg
 * Date: 3/6/12
 */
 
class App_Debug 
{

    /**
     * @static
     * @param $val
     * @return bool
     */
    public static function alwaysTrueForDebug($val)
    {
        if (APPLICATION_ENV == 'development') {
            return true;
        } else {
            return $val;
        }
    }

    public static function setVarForDebug(&$var, $value)
    {
        if (APPLICATION_ENV == 'development') {
            $var = $value;
        }
    }

}
// Now I can do it in this way 
if (App_Debug::alwaysTrueForDebug($service->canBeUpdated)) {
// or
App_Debug::setVarForDebug($user['state'], 'active') 

// first expression will be true and $user['state'] will be set 
// if only APPLICATION_ENV == 'development'


2012/03/30

Create copy of db strucutre. Simple yii command


we moved here http://radzserg.com/2012/03/30/create-copy-of-db-strucutre-simple-yii-command/

Continue to publish some of components that I usually use in different projects. Today I'll tell you about how easily create copy of db structure from production scheme to test db.

As usual I don't care about test DB until you starts your test and you get smth like table {my_funny_new_table} doesn't exist or smth like undefined field for {my_funny_table}. And you remember that you haven't updated structure for test DB.

So I use this simple command in my yii projects (and similar in not yii projects)

<?php

/**
 * Create test db
 * @author radzserg
 *
 */
class CreateTestDbCommand extends CConsoleCommand
{
    public function run($args)
    {
        echo "Creating test DB\n\n";

        $mainConfig = require dirname(__FILE__) . '/../config/main.php';
        $testConfig = require dirname(__FILE__) . '/../config/test.php';

        $mainDbConfig = $mainConfig['components']['db'];
        $testDbConfig = $testConfig['components']['db'];

        $m = array();
        preg_match('~mysql:host=(.+);dbname=(.+)~is', $testDbConfig['connectionString'], $m);
        $testDbConfig['host'] = $m[1];
        $testDbConfig['dbname'] = $m[2];

        $m = array();
        preg_match('~mysql:host=(.+);dbname=(.+)~is', $mainDbConfig['connectionString'], $m);
        $mainDbConfig['host'] = $m[1];
        $mainDbConfig['dbname'] = $m[2];

        $dumpPath = ROOT_PATH . '/protected/data/schema.sql';

        $mysqlTestDbCreator = new MysqlTestDbCreater($mainDbConfig, $testDbConfig, $dumpPath);
        $mysqlTestDbCreator->run();

        echo "Testing Db was successfully created\n\n";
    }
}

/**
 * Create test db as copy of real db
 *
 */
class MysqlTestDbCreater
{
    private $_productionConfig = NULL;
    private $_testConfig = NULL;
    private $_dumpPath = NULL;

    public function __construct($productionConfig, $testConfig, $dumpPath)
    {
        $this->_productionConfig = $productionConfig;
        $this->_testConfig = $testConfig;
        $this->_dumpPath = $dumpPath;
    }

    public function run()
    {
        $this->_makeDump();
        $this->_createTestDb();
    }

    protected function _createTestDb()
    {
        $config = $this->_testConfig;
        $loginOptions = "--user={$config['username']} --password={$config['password']} "
            . "--host={$config['host']}";

        // truncate database
        $command = "echo \"SET FOREIGN_KEY_CHECKS = 0;\"; mysqldump {$loginOptions} --add-drop-table --no-data {$config['dbname']} | grep ^DROP";
        $delete_commands = array();
        $return = false;
        exec($command, $delete_commands, $return);
        mysql_connect($config['host'], $config['username'], $config['password']);
        mysql_select_db($config['dbname']);
        foreach ($delete_commands as $command) {
            mysql_query($command);
        }    
            
        $command = "mysql {$config['dbname']} {$loginOptions} < "  . $this->_dumpPath;
        
        $output = array();
        $return = false;
        exec($command, $output, $return);
        $output = implode("\n", $output);
        echo $output; 
    }

    protected function _makeDump()
    {
        $config = $this->_productionConfig;
        $loginOptions = "--user={$config['username']} --password={$config['password']} "
            . "--host={$config['host']} {$config['dbname']}";
        $command = "mysqldump $loginOptions --opt --no-data > " . $this->_dumpPath;
  
        $output = array();
        $return = false;
        exec($command, $output, $return);
        $output = implode("\n", $output);
  echo $output;
    }
} 
good testing

2012/03/22

SQL Injections by binary search

we moved here http://radzserg.com/2012/03/22/sql-injections-by-binary-search/


Yesterday someone tried to hack one of the projects I'm working on. Yesterday I was busy all the day. When I started to work it was about 4 PM. And what did I see in error logs

2012-03-21T04:20:27-05:00 CRIT (2): Exception information:
Message: CURL error (http://myproject.com/path_to_api.php): The requested URL returned error: 500
Stack trace:
....
Request Parameters:
Array
(
    [id] => 11874362999999.9' or Length((select distinct column_name from `information_schema`.columns where table_schema=0x123(I changed real name) and table_name=0x321(I changed real name) limit 0,1))<8 and 'x'='x
)

At first I did not even gave special importance to this. I thought somebody try to check sql injection to urls. I knew that all he can see it's just an error page. I started to check logs.

Then I found such query
[id] => 11874362999999.9' or ascii(substring((select concat(system_settings.val,0x5e,system_settings.code,0x5e,system_settings.id) from {our_scheme_name}.system_settings limit 17,1),19,1))<118 and 'x'='x

And it looks really scary, because hacker already knew our DB structure. (Then I understood he has known it in first query, I was confusing by encoded names i.e. 0x123 hexadecimal number) I've immediately blocked him by IP. And started investigation.

All such errors was from one page, it became clear that we have a hole here. I started to check code, we did a api call. I went thought this call and what did I find in one very old class?

'select * from some_table where id=\''.$id.'\';

classic... :(

So how did it work? He added his query but he got only error page, without any valuable info. And let's see how it works

select *
from some_table
where id= 11874362999999.9'
    or Length((select distinct column_name from `information_schema`.columns where table_schema=0x123(I changed real name) and table_name=0x321(I changed real name) limit 0,1))<8 and 'x'='x'


if his query was succeed he got standard page. If it was failed he got error message. Then he use binary search, look at that query

ascii(substring((select concat(system_settings.val,0x5e,system_settings.code,0x5e,system_settings.id) from {our_scheme_name}.system_settings limit 17,1),19,1))<118

He picked up the values ​​character by character by binary search. Initially he got DB structure(looks like he used some automatic tool) then he started works with tables, making more clever requests.

To get DB structure you need to perform thousands of queries.

Fortunately we stopped him soon and closed the hole and he couldn't get valuable info. But it was very unpleasant.

But be careful! Those freaks don't sleep. Follow the classic simple rules.
- Do not use this shit  'select * from some_table where id=\''.$id.'\';
- Do not store passwords as plain text
- Analyze suspicious user behavior (I'm gonna wright about that last 2 months :))


And finally, I found this cheatsheet - read it even if you are familiar with most of the examples, in order to refresh the memories.
http://ferruh.mavituna.com/sql-injection-cheatsheet-oku/