Half-Elf on Tech

Thoughts From a Professional Lesbian

Author: Ipstenu (Mika Epstein)

  • Evaluating Evil

    Evaluating Evil

    Credit: EvalBlog
    Credit: EvalBlog
    One of the things I do at DreamHost is help with hacked sites. This means when WP is hacked, I look at it, figure out how, and explain to the person how to fix it, or how to tell their tech folks what needs doing. There are occasions where I’ll delete things for them, but usually that happens when there’s a folder or file with weird permissions.

    We have a lot of tricks with what we look for, like base64, but recently I started to find files that missed my scan, but not my “Hey, wait, wp-mai1.php isn’t a WordPress file…” check. Files like this:

    $a51a0e6bb0e53a=str_rot13('tmhapbzcerff');$a51a0e6bb0e5e4=str_rot13(strrev('rqbp rq_46rfno'));
    eval($a51a0e6bb0e53a($a51a0e6bb0e5e4('eF6dWFtv6kYQ/it9qMQ5UlWBCVGtKg+JWozQaSrcU9+qKvIlBIGh6BBCyK/[...]')));
    eval($a51a0e6bb0e53a($a51a0e6bb0e5e4('eF7tW1uvotqW/ivnYSe1d85JignSvcxJPXgDtQCXKNdO50TAJSqop7xw6fR/7zHm1CUqqGW91EMnK1FgzHG/[...]')));
    

    Now obviously I can just add str_rot13 to my checklist (nothing in WordPress core uses it), but .. how do I look for those eval strings?

    Eval is a funny thing. In JavaScript: The Good Parts, Douglas Crockford states “eval is Evil: The eval function is the most misused feature of JavaScript. Avoid it” but he’s taking JS and I’m looking at php files. So with the (current) assumption that I can ignore js I can try this(I also use ack for this half the time, depends on my mood)(You can leave out ‘exclude SVN’ stuff if you want to. Most users don’t have it.):

    grep -R --exclude-dir="\.svn" --exclude="*.js" "eval" .
    

    That gets me a lot of files, though, and I don’t want to parse what I don’t need to. By the way, there’s one and only one file in all of WP that uses eval() in a ‘nefarious’ way, and that’s ./wp-admin/js/revisions-js.php, which is the WordPress easter egg. That’s also the only place you’ll see p,a,c,k,e,r code. But clearly I want to look for eval( or even eval($ because that’s more exact, and that should give me a better result.

    This is a two edged sword, of course. If I’m too precise, I will miss some of their shenanigans. If I’m not close enough to what I’m looking for, I get too much. And worst of all, I don’t always know what I’m looking for. Quite a lot of finding new hacks is a world where “I’ll know it when I see it.” So let’s take it down and say I want to find no JS, nothing in .svn, and anything with eval and a paren:

    grep -R --exclude-dir="\.svn" --exclude="*.js" -e 'eval(' .
    

    That’s a lot better, and in fact, this is a good start! But it’s hard to read because of how long the lines are:

    ./foo.php:eval($a51a0e6bb0e53a($a51a0e6bb0e5e4('eF6dWFtv6kYQ/it9qMQ5UlWBCVGtKg+JWozQaSrcU9
    ./foo.php:eval($a51a0e6bb0e53a($a51a0e6bb0e5e4('eF7tW1uvotqW/ivnYSe1d85JignSvcxJPXgDtQCXKN
    ./wp-admin/includes/class-pclzip.php://      eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);');
    ./wp-admin/js/revisions-js.php:eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('6(4(){2 e=6(\\'#Q\\').v();2 i=\\'\\\\\\',.R/=\\\\\\\\S-;T"<>U?+|V:W[]X{}\\'.u(\\'\\');2 o=\\'Y[]\\\\\\\\Z;\\\\\\'10,./11{}|12:"13<>?-=14+\\'.u(\\'\\');2 5=4(s){r=\\'\\';6.15(s.u(\\'\\'),4(){2 t=16.D();2 c=6.17(t,i);r+=\\'\$\\'==t?n:(-1==c?t:o[c])});j r};2 a=[\\'O.E[18 e.y.19.1a\\',\\'1b 1c. 1d .1e.,1f 1g\\',\\'O.E e.1h 1i 8\\',\\'9\\',\\'0\\'];2 b=[\\'<1j. 1k \$1l\\',\\'1m. 1n 1o 1p\\',\\'1q, 1r. ,1s. 1t\\'&#93;;2 w=&#91;&#93;;2 h=6(5(\\'#1u\\'));6(5(\\'1v\\')).1w(4(e){7(1x!==e.1y){j}7(x&amp;&amp;x.F){x.F();j G}1z.1A=6(5(\\'#1B\\')).1C(\\'1D\\');j G});2 k=4(){2 l=a.H();7(\\'I\\'==J l){7(m){2 c={};c&#91;5(\\'1E\\')&#93;=5(\\'1F\\');c&#91;5(\\'1G\\')&#93;=5(\\'1H..b\\');6(5(\\'1I 1J\\')).1K(c);p();h.v().1L({1M:1},z,\\'1N\\',4(){h.K()});d(m,L)}j}w=5(l).u(\\'\\');A()};2 A=4(){B=w.H();7(\\'I\\'==J B){7(m){h.M(5(\\'1O 1P\\'));d(k,C)}N{7(a.P){d(p,C);d(k,z)}N{d(4(){p();h.v()},C);d(4(){e.K()},L)}}j}h.M(B.D());d(A,1Q)};2 m=4(){a=b;m=1R;k()};p=4(){2 f=6(\\'p\\').1S(0);2 g=6.1T(f.q).1U();1V(2 g=f.q.P;g>0;g--){7(3==f.q[g-1].1W||\\'1X\\'==f.q[g-1].1Y.1Z()){f.20(f.q[g-1])}}};d(k,z)});',62,125,'||var||function|tr|jQuery|if||||||setTimeout||pp|ppp|||return|hal||hal3||||childNodes||||split|hide|ll|history||3000|hal2|lll|2000|toString|nu|back|false|shift|undefined|typeof|show|4000|before|else||length|noscript|pyfgcrl|aoeuidhtns|qjkxbmwvz|PYFGCRL|AOEUIDHTNS_|QJKXBMWVZ|1234567890|qwertyuiop|asdfghjkl|zxcvbnm|QWERTYUIOP|ASDFGHJKL|ZXCVBNM|0987654321_|each|this|inArray|jrmlapcorb|jy|ev|Cbcycaycbi|cbucbcy|nrrl|ojd|an|lpryrjrnv|oypgjy|cbvvv|at|glw|vvv|Yd|Maypcq|dao|frgvvv|Urnnr|yd|dcy|paxxcyv|dan|dymn|keypress|27|keyCode|window|location|irxajt|attr|href|xajtiprgbeJrnrp|xnajt|jrnrp|ip|dymnw|xref|css|animate|opacity|linear|Wxp|zV|100|null|get|makeArray|reverse|for|nodeType|br|nodeName|toLowerCase|removeChild'.split('|'),0,{}))
    ./wp-admin/press-this.php:		var my_src = eval(
    ./wp-admin/press-this.php:			var my_src = eval(
    ./wp-admin/press-this.php:							eval(data);
    ./wp-includes/class-json.php: * Javascript, and can be directly eval()'ed with no further parsing
    ./wp-includes/functions.php:		if ( doubleval($bytes) >= $mag )
    

    Okay, lets get smarter!

    grep -R --exclude-dir="\.svn" --exclude="*.js" -e 'eval(' .|cut -c -80
    

    Now I’m telling it to cut up after 80 characters, because it’s easier to pick out the bad with just that much. Look:

    ./foo.php:eval($a51a0e6bb0e53a($a51a0e6bb0e5e4('eF6dWFtv6kYQ/it9qMQ5UlWBCVGtKg+J
    ./foo.php:eval($a51a0e6bb0e53a($a51a0e6bb0e5e4('eF7tW1uvotqW/ivnYSe1d85JignSvcxJ
    ./wp-admin/includes/class-pclzip.php://      eval('$v_result = '.$p_options[PCLZ
    ./wp-admin/js/revisions-js.php:eval(function(p,a,c,k,e,r){e=function(c){return(c
    ./wp-admin/press-this.php:		var my_src = eval(
    ./wp-admin/press-this.php:			var my_src = eval(
    ./wp-admin/press-this.php:							eval(data);
    ./wp-includes/class-json.php: * Javascript, and can be directly eval()'ed with n
    ./wp-includes/functions.php:		if ( doubleval($bytes) >= $mag )
    

    Part of the reason this works is I know what I’m looking for. WordPress, in general, doesn’t encrypt content. Passwords and security stuff, yes, but when it does that, it uses variables so you would get eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);');, which remains totally human readable. By that I mean I can see clear words that are easy to search for in a doc, or via grep or awk without being forced to copy/paste. I can remember “PCLZIP underscore CB…”

    RandomCharacters_320Those random characters are not human readable at all. That’s how I know they’re bad. Of course, if someone got clever-er, they would start naming those variables things that ‘make sense’ in the world of WP, and I have a constant fear that by pointing out how I can tell this is a hack, I give them ideas on how to do evil-er things to us.

    It’s for reasons like this that I, when faced with a hack or asked to clean one up, always perform Scorched Earth Security. I delete everything and reinstall it. I look for PHP and JS files in wp-content/uploads, or .htaccess files anywhere they shouldn’t be (in clean WP, you have two at most: at the root of your site and in akismet). I make sure I download my themes and plugins from known clean locations. I’m careful. And I always change my passwords. Heck, I don’t even know what mine are right now!

    But none of this is static enough for me to say “This is the fix forever and ever” or “this is how you will always find the evil…” By the time we’ve codified and discussed best methods, the hackers have moved on. The logic of what to look for now may not last long, but the basic concept of looking for wrong and how to search for it should remain a good starting point for a while yet.

    Do you have special tricks you use to find the evil? Like what Topher did to clean up a hack?

  • Recovering Your Cape

    Recovering Your Cape

    One of the odder “hacks” out there is one where the person, once they get in, de-frocks your Super Admin on a Multisite. This isn’t always a hack, sometimes it’s just a simple mistake.

    To quote my friend Jen Mylo:

    1. People give away admin rights like logo-encrusted keychains at a car show and then the new admins abuse the power.
    2. Someone who has admin rights deservedly but doesn’t know code makes a mistake.

    […]

    Some people make bad decisions about who to give admin roles.

    There’s an extra level of problems with making everyone and their pet monkey a super-admin on Multisite. You may think it’s a great thing, because now someone else can add new users, install plugins, edit themes and plugins, and of course, use iframes and PHP and such in widgets.

    tumblr_md8hekGkk31qc184to1_500We run a Multisite at work, and they let me ‘secure it up’ recently. The first thing I did was demote pretty much everyone except five of us to ‘Editor.’ I told them all that I’d done this, and if they found something they couldn’t do, tell me, and I’d fix it. At this point, I’ve changed only three people to Admin, and dropped even more to ‘Author.’ Why? Because they don’t need to have high levels of access to do what they need to do! The admins on the site can tweak theme settings, play with widgets, and add ‘existing users.’ Everyone else? They just write content. Heck, most of them don’t even need to be Editors, but we gave them that level so they could help us copy-edit other posts. Two people complained “I need Super Admin access!” and I gave them my best Enda: NO CAPES.

    Limit your admins, and there is less of a chance someone will accidentally remove access from the wrong person.

    So now that that’s out of the way, how do you get it back?

    Normally, reinstating an admin account is pretty easy. You go in via mySQL, pop open the wp_usermeta table, find your ID, and toss this in for meta_value for wp_capabilities: a:1:{s:13:"administrator";b:1;} That won’t restore all the roles, if you happen to be using Role Scoper, or some other management tool, but if you’ve got that, you can do anything. If you’re using WP-CLI, wp user update 1 --role=administrator (assuming you’re user ID 1).

    Screen Shot 2013-06-21 at 11.34.20 AM

    There’s a sidebar/caveat to this. Sometimes this doesn’t work, and it happens if you change your DB prefix. So normally you have the prefix wp_ and the table wp_options. In that table you have a option named wp_user_roles and everything works. But then you make a new site, and you pick a different DB prefix, maybe you heard it was more secure, or maybe you wanted both tables in the same DB. Either way, now you have wp_wdssrr_options instead, and when you copy over your old options content, no one can log in. It’s because you have to rename that option to wp_wdssrr_user_roles

    Screen Shot 2013-06-25 at 10.00.51 AM

    I just had a site with this problem last week.

    NomadTrip_7091 On the other hand, getting back Super Admin access is less straightforward, but by no means is it impossible.

    1. Go into wp_sitemeta and look for site_admins.
    2. In there you will see something like this: a:1:{i:0;s:7:"Ipstenu";}
    3. If your userID is ‘superman’ then it would be a:1:{i:0;s:8:"superman";}

    Capitalization and stringlength matter. Add one user, and use that to correctly restore power to the others.

    Can you do this via WP-CLI? Yes, if you’re on the latest versions. Kind of. You can get a list of super admins via wp network-meta get 1 site_admins and in theory wp network-meta update 1 site_admins USERNAME would work except that the data is serialized. I opened a ticket with WP-CLI, and it’s a ‘plugin territory’ issue right now, so I’ll have to see if I can code it myself.

  • Shortcode: MLB.TV

    Shortcode: MLB.TV

    I like baseball, I like the Indians. I like embedding content. Why MLB.tv likes to make their stuff not easily embeddable is beyond me. I think [mlbtv id=28142247] is way easier to deal with if I’m using the visual editor. I grabbed the default sizes from their settings.

    <?php 
    // MLB.TV Shortcode - Better code by Konstantin Kovshenin
    // &#91;mlbtv id="###" width="480" height="270"&#93;
    function mlbtv_shortcode( $attr, $content = '' ) {
        $attr = shortcode_atts( array(
            'id' => null,
            'width' => 400,
            'height' => 224,
        ), $attr );
     
        $attr = array_map( 'absint', $attr );
        $url = add_query_arg( array(
            'content_id' => $attr['id'],
            'width' => $attr['width'],
            'height' => $attr['height'],
            'property' => 'mlb',
        ), 'http://wapc.mlb.com/shared/video/embed/embed.html' );
     
        return sprintf( '<iframe src="%s" width="%d" height="%d" frameborder="0">Your browser does not support iframes.</iframe>', esc_url( $url ), $attr['width'], $attr['height'] );
    }
    add_shortcode( 'mlbtv', 'mlbtv_shortcode' );
    

    [mlbtv id=28142247]

    Looks just fine.

  • Speaking at WordCamp San Francisco – 2013

    Speaking at WordCamp San Francisco – 2013

    It’s true! I’ll be speaking at WordCamp San Francisco again this year.

    Screen Shot 2013-06-21 at 12.39.45 PM

    Last year I talked about supporting WordPress and how you can give back. This year, the subject of my talk is “Don’t Use WordPress Multisite.” I wrote a post here about that in 2011, and while quite a lot of it remains true, I still see hundreds of people who think Multisite is the cure to all that ails ’em.

    I love it, but it’s not everything and a side of fries. But I’m not going to give away the talk here, and it’s not just going to be a copy of that post. So feel free to read it, offer comments, and meet me where I left my harp: Sam Frank’s Disco.(This is a very bad joke I’ll tell you if you want.)

  • To Fork Or Not To Fork

    To Fork Or Not To Fork

    ForkinRoad Sometimes the question you have to ask yourself is if you should fork. With a plugin, this is a fairly easy conversation. You want functionality, the original author doesn’t, you fork. But when you look at a theme, you start getting into messier territory.

    In many ways, a theme is ‘simpler’ than a plugin. Theme devs, don’t shoot me! What I mean is that a plugin can be or do anything, but a theme is always a theme. While it may change how your site looks in amazing ways (and I am constantly in envy of people who can visualize like that), it really is just a theme. This is why reviewing themes is easier to monitor and manage than plugins. But that’s another conversation. The point here is that when you want to extend a theme, you make a child theme. Done.

    What happens when you’re already using a child theme, though? StudioPress, a theme shop I love, makes child themes and sells those. This site is using a child theme, though it’s currently one of my own devising. Previously, it was using a child theme called Streamline, however, and it had a lot of changes.

    So when do you make a child theme, and when do you extend it in other ways? It really depends on what you’re doing. While I sat and tried to figure out where my personal breakpoints were, I realized that since the best themes know they are a theme, and not a plugin, it was really easy. A great theme lets you seamlessly use a plugin to add in functionality. They may even tell you what the best ones are. Recently, Coen Jacobs wrote about how good themes never bundle plugins and he’s right. A plugin is a plugin, a theme is a theme. Keep ’em separate, keep ’em safe.

    I’ve done it in three different ways for three different sites, and I’ll explain my rational below.

    The Simple

    Fork+in+the+roadOne site is simple. Every ounce of functionality I needed, and more, was in the core code of StudioPress, not even a child theme was needed. All I wanted was to change some colors and add in a couple CPTs and shortcodes. That was easily done via Jetpack, which has a CSS editor, and a couple plugins. Actually, 99% was the CSS, once I sat down and looked at it. All the weird stuff was normal plugins.

    Sometimes simple is a teeny bit complicated, and when that happens, I may make a custom mu-plugin or two, but in general, not so much needed unless I want a Custom Post Type. And anyway, I never put a CPT in my theme if I can help it.

    The Complicated

    On the other hand, one site is hella complex. I found a theme I really, really, liked. Almost. And worse, this was a child theme. It was exactly what I was afraid of. There was no way I would get what I wanted out of this theme unless I edited the functions.php file, a lot of the CSS, and a ton of the images. Could I still have done this via CSS and a couple plugins? No. Well, the CSS yes, but not the code. I had need for some crazy functions, a total re-write of comments, and the list went on.

    In this case, I forked. I ripped out the small amount I never wanted. I added in the medium amount I did want. I directly edited the theme’s CSS, added in a post template, and changed all the images to my chose color. I even added in a different font. Could I have re-written from scratch? Of course, but the theme had 90% of what I already wanted to do.

    There actually is a step before forking, and that would be using plugins. I know I mentioned plugins before, with the simple themes, but actually most theme frameworks are extra special. They often have custom plugins like Simple Hooks, which fundamentally lets me do everything I might do in a functions.php file for that theme. This means most of the time, I don’t fork. But. This theme really was so complex that I needed more than just the simple hooks. Genesis Complex Hooks may have done it, or another plugin to make a transient functions.php (ala CSS editing). And that would have done it except for when I wanted to change a lot of images, add in JS, and then a custom page template …

    Well you see how it goes. The point here, however, is that I sat and thought about it, studied my setup, and made a long term decision. Originally I was using the plugin way, but when it stopped being extendable, I decided to do this, and I regret nothing.

    Fork_In_The_Road

    The Original

    The last option I had was I theme I kind of liked, but it was old. It was pre-HTML5, it didn’t have microformats, but more-over, it wasn’t aging as well as I would have liked. I made a list of what I liked about it, what I didn’t, and what I really wanted. While the list was short, it was also clearly not going to just be CSS. I wanted a custom front page, an extra page template, images, and Genericons built in. In short, I wanted this theme to be something that could carry me onward, regardless of a plugin, even one I wrote.

    I have not made my own, 100% from scratch, child-theme in a long while, and this one may not really count since I was designing it to look like something else. This took me an afternoon to bang out the basics, and a couple days of minor fixes here and there to perfect them. Release and iterate, as they say. Certainly, I could have taken someone else’s theme, but it was going to be a surprising amount of work to do that. Instead, I made a list of my needed features and my desired options, and went to town. It’s still a pretty simple child-theme (which speaks well to the inherent extensibility of the parent), but now it’s mine, and it’s easy to extend it and expand it.

    The Rest…

    What about you? What do you think about when deciding how to handle a theme that needs changing?

  • DreamPress: We Went There

    DreamPress: We Went There

    Not the most flattering picture of me...
    Not the most flattering picture of me…
    One of the projects I’ve been involved with since I started here at DreamHost has been a secret. Actually two of them were, but we released one already. Anyway. The one we pushed today is DreamPress!

    What is DreamPress? Here’s marketing:

    What is DreamPress? That’s a good question! Think of WordPress. You got it? Cool. Now, think of that WordPress install, but on steroids! Picture it running on a specialized, optimized, virtual private server. Yeah, this is far from your average WordPress hosting package, isn’t it? DreamPress comes pre-configured by our in-house WordPress experts for maximum security and performance under traffic load. This is our call-out to all entrepreneurs, website designers, developers, and bloggers to give it a test-drive today!

    And here’s me:

    If you did your job really well, one day you woke up to a downed website becuase you swamped it with traffic. I’ve been there, you’ve been there. There’s a point where your host reaches out to you and says “Honey, sweetie pie, you’re too big for shared hosting.” And as horrible as this is, it’s a good thing! You’re popular! Of course you’ll try caching and changing themes and plugins, but there’s just that moment when, damn it, you’ve got to step up and go up to a VPS.

    And you know what? A VPS is scary. I started this blog when I got my VPS because I wanted to learn how to manage it. But you see, I’m weird. I know it, you know it, that’s okay. I’m strange and I like me, just the way I are, and this works for me. It doesn’t work for everyone, so when you find yourself faced with something new and different, maybe you don’t want to learn it. Maybe it’s not your thing. So looking at that VPS is a great big bag of nope.

    Screen Shot 2013-06-04 at 3.37.07 PMThis is where managed hosting steps in. Hosts take on the job of managing your server. We handle upgrades (which yes, we do anyway), but we also optimize and customize the server. All the times you hear me talk about ‘Edit your httpd.conf’ or ‘installing PageSpeed…’ is something we’ve already done for you. Need more memory? We’ll take care of it without bothering you. It’s a VPS without the responsibility. PHP memory, server power, even caching is taken care of. Oh yes, you no longer need any caching plugins, because we’ve got Varnish in front for you.

    Of course, this isn’t perfect for everyone. Hard-core code jockeys may want that VPS (or dedicated) to play with the nuts and bolts, install extra add-ones, and just go to town. But if that isn’t for you, come check out DreamPress. I promise we’ll have a magic button to migrate things soon!

    Nifty articles on DreamPress:

    I know there are a lot of hosts out there. I work for one, I use others, and I think the more of us who get into this supporting the app that needs supporting, the better experience for everyone. Bring it on, baby!