<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Virtuous Code &#187; dreamhost</title>
	<atom:link href="http://devblog.avdi.org/tag/dreamhost/feed/" rel="self" type="application/rss+xml" />
	<link>http://devblog.avdi.org</link>
	<description>&#34;The three virtues of a programmer: laziness, impatience, and hubris&#34; -- Larry Wall</description>
	<lastBuildDate>Thu, 23 May 2013 18:31:20 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=</generator>
		<item>
		<title>Back in business</title>
		<link>http://devblog.avdi.org/2012/02/25/back-in-business/</link>
		<comments>http://devblog.avdi.org/2012/02/25/back-in-business/#comments</comments>
		<pubDate>Sat, 25 Feb 2012 05:00:00 +0000</pubDate>
		<dc:creator>Avdi Grimm</dc:creator>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[dreamhost]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://virtuouscode.com/?p=2047</guid>
		<description><![CDATA[An update on the site intrusion which took out this blog for several days, and the steps I've taken to repair the damage and prevent future attacks. <a href="http://devblog.avdi.org/2012/02/25/back-in-business/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>If you tried to read one of my posts in the last few days and got a maintenance page instead, sorry about that. While I was down in Virginia <a href="http://www.cvreg.org/2012/2/9/february-meeting-avdi-grimm-object-on-rails">visiting CVREG</a>, my hosting was cracked, and I&#8217;ve been doing damage control and recovery every since. I haven&#8217;t finished rebuilding the site, but all the old posts should be available once again. </p>
<div id="outline-container-1" class="outline-2">
<h2 id="sec-1">Incident report</h2>
<div class="outline-text-2" id="text-1">
<p>   For anyone curious, here are some details about the compromise. </p>
<p>   I use Dreamhost shared hosting for my blog hosting. I realize this   is strictly amateur-hour by geek standards, but it&#8217;s cheap and   spacious and it has met my needs. And with the right caching in   place it doesn&#8217;t fall over when it gets Reddited, unlike what a   similarly-priced VPS might do. Also, they make it dirt-simple to   deploy WordPress and keep it up-to-date. I realize WordPress is   old-and-busted in this day of Octopress, but I like the plugin   ecosystem and being able to schedule posts into the future. </p>
<p>   Just before I gave my talk at CVREG, someone tweeted to me that they   were being redirected to a Bing search whenever they tried to view   one of my posts. As soon as I got home, I investigated. I discovered   that my site had indeed been cracked, and that the compromise was   extensive. </p>
<p>   The initial exploit was via world-writable directories in two of my   blogs. One directory was in a plugin (Extended Comment Options), and   one was a cache directory inside a theme. I&#8217;m not sure why either   directory was world-writable; I assume because of badly-written   PHP.  </p>
<p>   You can all laugh at me for using WordPress now. </p>
<p>   Another account on the same shared server wrote backdoor PHP files   to those world-writable directories. This account was probably just   another in a chain of compromised accounts, not that of the original   attacker. In all cases the files were named &#8220;r.php&#8221;. They contained   obfuscated PHP code, but from looking at the logs and from   researching related exploits I surmise that they enabled arbitrary   commands to be executed in response to POST requests from the web. </p>
<p>   You can all laugh at me for using a shared host now. </p>
<p>   These backdoors were set up on the 14th of February. On the 21st, an   unknown attacker used one or more of the backdoors to rewrite every   .php file in my account. Each file had an obfuscated block of PHP   inserted at the beginning. The block&#8217;s purpose is to redirect   visitors to malware/scam sites. <a href="http://blog.sucuri.net/2010/05/new-attack-today-against-wordpress.html">Here&#8217;s a post on the attack</a>,   which apparently originated back in 2010. </p>
<p>   Dreamhost makes it possible to create multiple users, but I had   lazily run most of my sites under my main login. As a result, the   attack infected this blog, <a href="http://wideteams.com">wideteams.com</a>, and other personal sites.   Once I notified Dreamhost of the issue their automated scan was able   to furnish me with a handy list of all the affected files, which   clued me in to the extent of the attack. </p>
<p>   You can all laugh at me for using a single user for most of my sites   now. </p>
<p>   The good news was, the inserted code was pretty obvious once I knew   what to look for. In addition, PHP files were apparently the only   assets targeted. I found no altered HTML or JavaScript files, and   the database tables were apparently unaffected. </p>
</p></div>
</p></div>
<div id="outline-container-2" class="outline-2">
<h2 id="sec-2">Cleanup</h2>
<div class="outline-text-2" id="text-2">
<p>   The first thing I did was set up site-wide redirects to a maintenance   page. </p>
<p>   A review of access logs around the time that the files were   modified, as well as the Dreamhost security report, clued me in to   the backdoor files. I deleted them, after tarballing them up in case   I wanted to take a closer look at them later. </p>
<p>   I also tarballed up all the compromised sites, so that I could   safely make modifications without accidentally losing any important   data. </p>
<p>   I changed all of my Dreamhost user account passwords, including my   Dreamhost panel password, although I had no reason to think that had   been compromised. I changed every MySQL user password as well. I   wiped out and re-created my <code>.ssh</code>- in case that had been tampered   with. </p>
<p>   While it had become clear that the attack probably had nothing to do   with weak or stolen passwords, I decided to take the opportunity to   finally go through and change every single one of my online accounts   to use a unique, random password, managed by <a href="http://lastpass.com">LastPass</a>. I had already   done this for the really important stuff like email and banking   accounts, but I still had quite a few accounts that I&#8217;d used one of   my &#8220;standard&#8221; passwords on. I used the LastPass <a href="https://lastpass.com/index.php?securitychallenge=1&amp;fromwebsite=1&amp;lpnorefresh=1">Security Challenge</a>   to hunt down the accounts with duplicate and/or weak passwords. </p>
<p>   I wrote a trivial script to clean the affected PHP files, and ran it   on the cracked site directories. In theory, I could have just   returned the sites to service at this point. But since I had no   pristine copy to compare them with (silly me), I wasn&#8217;t comfortable   doing that. And in any case, I wanted to set things up Right, or at   least better, this time. </p>
<p>   Up until this happened, I had been running this blog in a   sub-directory of avdi.org. I did this because I read somewhere that   it was better Google juice to have a blog as a sub-directory of your   main site than as a subdomain. However, running it out of a   sub-directory severely limited my options for isolating the blog   hosting, or, for that matter, for moving the blog to a separate host   like <a href="http://wpengine.com">WPEngine</a>. I decided that flexibility trumps Google   juice. Formerly <a href="http://devblog.avdi.org">http://devblog.avdi.org</a> and <a href="http://virtuouscode.com">http://virtuouscode.com</a>   had simply redirected to <a href="http://avdi.org/devblog">http://avdi.org/devblog</a>. I switched my   setup to have Dreamhost fully host <a href="http://virtuouscode.com">http://virtuouscode.com</a>   separately from <a href="http://avdi.org">http://avdi.org</a>, with <a href="http://devblog.avdi.org">http://devblog.avdi.org</a> as a   mirror domain. </p>
<p>   In addition, I created a new user dedicated to the virtuouscode.com   domain. All PHP code in this domain runs under the dedicated user   account, so that any future attacks of this nature will be isolated   to a single blog. </p>
<p>   I used the Dreamhost one-click installer to set up a bare-bones   WordPress installation in the newly hosted domain. As soon as I was   done doing the initial set-up, I initialized a git repo for the site   and added everything&mdash;PHP, HTML, etc.&mdash;to it. Then I set up a   private GitHub repo and pushed the files to it. </p>
<p>   I wrote a simple script, suitable for automation with cron, to   bundle up any changes to the site and push them to GitHub. I was   careful to make the script log everything it did locally, and then   email the log to me when finished. </p>
<p>   I had initially hoped that I would be able to simply point the new   blog at the existing database tables. This turned out to be   nontrivial. I had recent backups of the DB, so I then thought I   would import the backups. However, the tool that I&#8217;d used to back up   the tables did simple SQL dumps, which the &#8220;official&#8221; WordPress   import/export tool didn&#8217;t understand. And for various reasons it   wasn&#8217;t as simple as just importing the old tables into the new ones   with MySQL admin tools. </p>
<p>   Rather than spend all night fiddling with MySQL imports, I opted to   fire up the old (cleaned) site long enough to do a &#8220;proper&#8221;   WordPress export. I then imported the tables into the new   installation, which worked out quite nicely, even setting up missing   users so the posts wouldn&#8217;t be &#8220;orphaned&#8221;. </p>
<p>   Then it was a matter of reinstalling and configuring my theme, as   well as a base set of plugins. Notably, I wasn&#8217;t about to go live   again without <a href="http://wordpress.org/extend/plugins/wp-super-cache/">WP SuperCache</a> configured. While I was at it, I added   some new optimization plugins: <a href="http://wordpress.org/extend/plugins/use-google-libraries/">Use Google Libraries</a>, which   substitutes the Google hosted versions of various popular JS libs   like jQuery; and <a href="http://wordpress.org/extend/plugins/bwp-minify/">Better WordPress Minify</a>, which bundles and minifies   CSS and JavaScript assets. With these plugins in place, the front   page gets a respectable 81 on <a href="https://developers.google.com/pagespeed/">Google Page Speed Online</a>.  </p>
<p>   I reinstalled various other plugins, like the <a href="http://wordpress.org/extend/plugins/embed-github-gist/">Embed GitHub Gist</a>   plugin (which does exactly what you&#8217;d expect), and the <a href="http://getclicky.com">Clicky</a> plugin   (for live site stats). After each plugin, I committed and pushed the   changes to the Git repo. If there&#8217;s one thing this whole fiasco has   taught me, it&#8217;s the importance of being able to roll back my   WordPress install to a previous state. </p>
<p>   Finally, I added a <a href="http://httpd.apache.org/docs/current/mod/mod_rewrite.html">rewrite rule</a> in the old avdi.org site to   permanently redirect requests for anything formerly hosted at   avdi.org/devblog to the devblog.avdi.org domain. Aside: nothing   makes me feel like a complete n00b again quite like Apache rewrite   rules. </p>
</p></div>
</p></div>
<div id="outline-container-3" class="outline-2">
<h2 id="sec-3">Lessons</h2>
<div class="outline-text-2" id="text-3">
<p>   What have I learned? </p>
<ul>
<li>It can never be said too many times: backup, backup, backup. And     in the context of WordPress, backup not only the data tables, but     the site files. I&#8217;ll be adding my GitHub push script to my daily     crontab shortly. </li>
<li>A backup is useless unless you&#8217;ve gone through a restore scenario     and verified it works. I was lucky that I didn&#8217;t have to use my     SQL-dump backups. I&#8217;m going to have to look into a better     solution for automated WordPress data backups, one that dumps to a     format which can be trivially re-imported. Perhaps something like     <a href="http://vaultpress.com/">VaultPress</a> is a good investment. </li>
<li>Separate accounts for separate sites, always. </li>
<li>World-writable directories are very bad news on a shared     host. Shortly I&#8217;ll be writing a cron job to scan for them, fix     them, and notify me. </li>
<li>WordPress has a big red target painted on it as a result of its     ubiquity. I still like the WordPress ecosystem, but I&#8217;ll continue     to consider moving to static blog hosting software like     Octopress. For now, though, I have a publishing setup which works     well for me, and I&#8217;m not quite ready to give that up, despite the     recent difficulties. </li>
</ul></div>
</p></div>
<div id="outline-container-4" class="outline-2">
<h2 id="sec-4">Conclusion</h2>
<div class="outline-text-2" id="text-4">
<p>   I don&#8217;t expect many of you have read this far, but I figured there   might be one or two people interested in the details of what   happened and how I dealt with it. If nothing else, maybe someone in   a similar boat will google this some day and find some useful   information here. </p>
<p>   If you have any recommendations for better WordPress security &#8211;   plugins, hosts, tools, scripts, backup services, anything&mdash;please   feel free to post them in the comments. I&#8217;m always interested in   improving my setup. Now more than ever. </p>
</p></div>
</p></div>
]]></content:encoded>
			<wfw:commentRss>http://devblog.avdi.org/2012/02/25/back-in-business/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
	</channel>
</rss>
