WordPress Injected Advertisements

I’ve debated on writing this up because I don’t understand a lot about exactly what happened, but I’ll post as much information here as I can.

The problem: I took over the management of a simple WordPress site several months ago, and haven’t had any problems.  I don’t host it or own the domain, so most of what I have to do involves content.  A few weeks ago an ad appeared above the menu (we’re using the Twenty Eleven theme) and I couldn’t pinpoint it.  The text and link varied, and a look at the header code in the theme revealed nothing.  Still, the HTML showed up in the rendered page (and across the site).  The latest was related to “viagra from india” or something like that.  A rude markup shows where the content was injected:

wordPressHack01

My client finally called me and was quite worried, which was justified.  I had looked into it but honestly hadn’t put in the time to dig in.  I sat down a couple of days ago and did a little searching, and came up with a few things to try – one of which was addressing the possibility that a plugin used on the site had been compromised.

Looking through the installed plugins, I noticed one that didn’t look like it was necessary and involved disabling menu items.  I disabled the plugin, refreshed the page, and the ad was gone.  Solved!

No it wasn’t.

My client called yesterday and said the ad was still there.  I pulled up the site and didn’t see it.   Thinking that maybe it had something to do with the content filter at work, I told him I’d do some research and get back to him.

I got home last night, pulled up the site, and the ad was displayed.  I logged in to the admin panel, poked around a bit, and went back to the site – no ad.  Turns out the ads don’t display if you have a cookie saved from the site’s admin panel.

More Googling: A couple more searches led me to this post at StackExchange, revealing something I wasn’t looking for but had seen earlier when looking at the site’s files.  Specifically, it was a line in the theme’s functions.php file that looked like this:

<?php $wp_function_initialize = create_function('$a',strrev(';)a$(lave')); $wp_function_initialize(strrev(';))"=owOpICcoB3Xu9Wa0Nmb1Z2XrNWYixGbhNmIoQ...

[...a very long string - 6416 characters in my case...]

...Q9QnblRnbvNGJ7IiI9QnblRnbvNGJ7lCbyVHJokTO58FbyV3X0V2Zg42bpR3YuVnZ"
(edoced_46esab(lave'));?>

I’d seen this before but didn’t pay it much attention (I was looking for offending code in the theme’s display files.)  That StackExchange post really helped me realize that the code in functions.php didn’t belong.  If you look closely you might notice that the code uses PHP’s strrev() function, which reverses a string.  If you reverse the string in the first strrev() call:

strrev(';)a$(lave')

You’ll see that it returns the string eval($a);.  PHP’s eval() function executes a string as PHP code (and is highly discouraged, by the way).  That eval() statement is handed to create_function(), which creates an anonymous function (in this case the function’s name is $a).  There are so many layers to this hack – stay with me here…

The next strrev() call takes that very large string that starts with ;))”=owOpI and ends with (edoced_46esab(lave (hint: that’s eval(base64_decode) reversed.  A clue…).  I copied that string and threw it in my Python terminal to reverse it, and upon discovering that I had a base64-encoded string I decided to decode it and have a look.  My Python code:

import base64
s = '=owOpICcoB3Xu [...] 2Zg42bpR3YuVnZ'
r = s[::-1]
d = base64.b64decode(r)
print(d)

This script takes the string and reverses it, decodes it with the base64 decoder, and prints it.  Out comes over 200 lines of PHP code that does the following (from what I had the energy to decipher):

  • Tries, through several methods, to determine methods by which it can access a URL
  • Detects Google, Bing, and Yahoo robots
  • Checks for the existence of certain cookies (this is how it hides from WP site admins)
  • Accesses one of two different URLs to get content
  • Inserts that content into the site

Once found, it was very easy to get rid of.  At first I simply commented the line in the functions.php and verified problem resolution, and then deleted the line.  Going through the code, while not necessary, was an interesting 120 minutes.  I even went so far as to manually browse to one of the URLs it contacted, and it only spit out a base64 string to the browser.

Curious, I decoded that string with Python, which unsurprisingly revealed this:

</div>||||||</div>|||<a href="http://lakeshorewinecellars.com/buy-viagra-from-india/">buy viagra from india</a>

And there you have it.  Happy hunting!

Mounting Windows Shares in Linux

I’ve needed to mount CIFS shares (Windows network shares) from Linux before, and it’s usually not a problem. It’s as easy as the mount command with a few options – for example:

mount -t cifs //windowsbox/sharename /mnt/point

That command will use the caller’s username – since root is typically the only one allowed to mount file systems, it will try to authenticate with the Windows machine with the username “root.” You’ll be prompted for a password as well.

To specify a username to use for Windows authentication, use the ‘-o’ flag with a string of arguments, without spaces:

mount -t cifs -o username=ben //windowsbox/sharename /mnt/point

You can also throw the password in there, separating it from the username with a comma:

mount -t cifs -o username=ben,password=myP@ss\&ord //windowsbox/sharename /mnt/point

Note that you will need to escape any reserved characters from this command, such as the ampersand ‘&’ I have escaped in the example above.

However, if you wanted to mount this automatically, you’d have to add a new line to /etc/fstab to include the filesystem you want to mount. The line in /etc/fstab to mount the filesystem mentioned above would be something like

//windowsbox/sharename  /mnt/point  cifs  username=ben,password=myP@ss&ord  0  0

This will work all day long. However, there’s a problem: your password is in plain text in /etc/fstab, which is a file any user on the system can read. The best way to solve this is to put your credentials in a text file readable only by root, and then having /etc/fstab refer to this credentials file rather than including the username and password in the fstab line. According to the man page, the credentials file can have three lines in it:

username=value
password=value
domain=value

Another note: you don’t need to escape reserved characters in fstab or in the credentials file. You might want to store the file somewhere in root’s home directory or somewhere else that isn’t globally readable. Ownership should be root:root and permissions would be best at 600. If I stored the file at /root/creds.crd, the fstab line would then be

//windowsbox/sharename  /mnt/point  cifs  credentials=/root/creds.crd  0  0

Standard users would be able to see where the credentials were stored, but they would not be able to see those credentials.

Of course, if I missed anything you’ll let me know, right?

xargs Reminder

I remember using xargs at a previous workplace for pasting a multi-line text list (say, from a text file on my desktop) to generate a space-delimited line of text containing the items in the pasted list. For the life of me this morning I couldn’t remember how to do it, and most examples don’t use xargs in this way. Here’s my reminder that I hope might help others.

Say I had this list of students in a text file that looked like:

mary
bob
susan
jim
jennifer
xavier

and I needed to generate a quick Bash one-liner to process something for them on the command line.  If I wanted to use a for loop on the command line, I’d supply the for loop with a list of things separated by a space.  For example:

for x in bob sue larry
> do
> echo "Hi $x"
> done

and the output will be:

Hi bob
Hi sue
Hi larry

On to xargs: say those names are in a text file on my desktop, with one name per line:

mary
bob
susan
jim
jennifer
xavier

If I copy the text from the file and paste it into the Linux command window, each name will be on a separate line and be interpreted as a command:

$ mary
-bash: mary: command not found
$ bob
-bash: bob: command not found
$ susan
-bash: susan: command not found
$ jim
-bash: jim: command not found
$ jennifer
-bash: jennifer: command not found
$ xavier
-bash: xavier: command not found

Using xargs, I can paste the list into the window and get all the names on one line, each separated by a space.  I will type xargs <<EOF and press enter, then paste my text.  The ‘EOF’ part of the command specifies the end-of-file string, which is needed to tell xargs when I’m finished with the input.  After I paste the list, I enter EOF to finish the command:

$ xargs <<EOF
> mary
> bob
> susan
> jim
> jennifer
> xavier
> EOF

And the output:

mary bob susan jim jennifer xavier

I can then copy this single line and paste it in the for loop.  Sure, it’s not really efficient with three items, but it sure makes things easier when you’re working with 100 server names.

A Few DSGet Commands

DSGet is a Windows command-line tool used to interact with Active Directory Domain Services (AD DS). I came across some commands I’d written down earlier this year and thought I’d post them here for later recall.

To list direct members of a group:

DSGet group "CN=GroupName, OU=OrganizationalUnit, DC=domain, DC=tld" -members

To list all of the group’s members (including members of a group that is a member of the group in question):

DSGet group "CN=GroupName, OU=OrganizationalUnit, DC=domain, DC=tld" -members -expand

To list the direct group membership of a user:

DSGet user "CN=UserName, OU=OrganizationalUnit, DC=domain, DC=tld" -memberof

To list full group membership of a user (like a recursive list, so-to-speak):

DSGet user "CN=UserName, OU=OrganizationalUnit, DC=domain, DC=tld" -memberof -expand

And a PowerShell command (cmdlet) to show the sAMAccountName(s) of the members of a group:

Get-ADGroupMember "GroupName" -recursive | Select sAMAccountName

I know this is far from an exhaustive list of commands, but at least now I can throw that ratty sheet of notebook paper away.

Clearing out a MySQL Database with One Long Bash Command

IMG_2196The need arose yesterday to drop all tables from a database so I could re-create a WordPress installation.  Sure, I could have dropped the database and re-created the entire thing, but that wouldn’t have been much of a challenge and I wanted to easily keep the same credentials and permissions on the database.

However, the number of tables exceeded my laziness.  There was a Drupal installation in addition to an old, unrelated WordPress site – perhaps around forty tables total.  I found a script that would do this, but it was also too involved.  I ended up with a fairly simple bash loop that did the trick. 

I’m using CentOS Linux and connecting to another CentOS Linux server running MySQL 5.1.69 for this article. Here’s how it worked in a not-so-brief explanation:

Overall, I simply needed to connect to the database and drop every table.  There isn’t any DROP-ALL-TABLES sort of command that I know of, so they must be dropped one-by-one.  Let’s get a list of tables to drop, shall we?

$ mysql -h db01 -u user -pP@ssword wordpress -e 'SHOW TABLES;'
+-----------------------+
| Tables_in_wordpress   |
+-----------------------+
| wp_commentmeta        |
| wp_comments           |
| wp_links              |
| wp_options            |
| wp_postmeta           |
| wp_posts              |
| wp_term_relationships |
| wp_term_taxonomy      |
| wp_terms              |
| wp_usermeta           |
| wp_users              |
+-----------------------+

There – that did it, but we don’t need all those pipe symbols in the output.  Pipe it through awk:

$ mysql -h db01 -u user -pP@ssword wordpress -e 'SHOW TABLES;' | awk '{ print $1}'
Tables_in_wordpress
wp_commentmeta
wp_comments
wp_links
wp_options
wp_postmeta
wp_posts
wp_term_relationships
wp_term_taxonomy
wp_terms
wp_usermeta
wp_users

And voila!  A simple list of all table names in this database.  You could easily filter this output to capture only a subset of table names with different prefixes (‘wp_’, ‘blog_’, etc..) if you didn’t need to list all of the tables.  The top of this list still includes the output column header, so we can just grep -v it right on out of there…

$ mysql -h db01 -u user -pP@ssword wordpress -e 'SHOW TABLES;' | awk '{ print $1}' \
| grep -v "^Tables_"
wp_commentmeta
wp_comments
wp_links
wp_options
wp_postmeta
wp_posts
wp_term_relationships
wp_term_taxonomy
wp_terms
wp_usermeta
wp_users

…and the list is clean.  Now we just have to drop each of those tables.  If we surround that entire command with tick marks (`) bash will run the command and capture the output.  We can use that output as an iterable list.  The For loop knows how to use this list – we just need to tell it what to do with each item.  So here we go:

$ for x in `mysql -h db01 -u user -pP@ssword wordpress -e 'SHOW TABLES;' \
| awk '{ print $1}' | grep -v "^Tables_"`
> do
> echo $x
> done
wp_commentmeta
wp_comments
wp_links
wp_options
wp_postmeta
wp_posts
wp_term_relationships
wp_term_taxonomy
wp_terms
wp_usermeta
wp_users

The previous example just prints each item in the list – this is useful to us in that we know exactly what’s in the list.  With this knowledge we can put each item (each table name, that is) in an SQL statement to drop the tables.  Let’s make sure we can do that by printing some SQL statements:

$ for x in `mysql -h db01 -u user -pP@ssword wordpress -e 'SHOW TABLES;' \
| awk '{ print $1}' | grep -v "^Tables_"`
> do
> echo 'DROP TABLE $x;'
> done
DROP TABLE $x;
DROP TABLE $x;
DROP TABLE $x;
DROP TABLE $x;
DROP TABLE $x;
DROP TABLE $x;
DROP TABLE $x;
DROP TABLE $x;
DROP TABLE $x;
DROP TABLE $x;
DROP TABLE $x;

Uh-oh!  See what happened there?  The literal string ‘$x’ was printed instead of the variable’s value.  That’s an important difference between using single-quoted (‘) and double-quoted (“) strings.  Single-quoted strings will literally print what you put in them, and double-quoted strings will allow you to substitute variables.  Let’s change the single-quotes to double-quotes:

$ for x in `mysql -h db01 -u user -pP@ssword wordpress -e 'SHOW TABLES;' \
| awk '{ print $1}' | grep -v "^Tables_"`
> do
> echo "DROP TABLE $x;"
> done
DROP TABLE wp_commentmeta;
DROP TABLE wp_comments;
DROP TABLE wp_links;
DROP TABLE wp_options;
DROP TABLE wp_postmeta;
DROP TABLE wp_posts;
DROP TABLE wp_term_relationships;
DROP TABLE wp_term_taxonomy;
DROP TABLE wp_terms;
DROP TABLE wp_usermeta;
DROP TABLE wp_users;

Success!  We now have everything we need to drop the tables.  Just change the echo statement in the loop to a mysql statement with the proper credentials and bash will run those commands:

$ for x in `mysql -h db01 -u user -pP@ssword wordpress -e 'SHOW TABLES;' \
| awk '{ print $1}' | grep -v "^Tables_"`
> do
> mysql -h db01 -u user -pP@ssword wordpress -e "DROP TABLE $x;"
> done

There won’t be any output unless there is an error. For each of the table names, an SQL DROP statement will be issued to the database.

If you would like to read more on the commands I used, feel free to visit some of the documentation for them:

It goes without saying that you should be careful with these operations and most database maintenance should be handled by a knowledgeable database administrator – especially in a production environment. My purpose here was only to experiment with bash and do something I hadn’t done before, and the database I was working with wasn’t currently in production.  I highly recommend against trying these things on a database your organization depends on.

Out with the Hallway Promotions

It seems that too many people think that Information Technology is simply a job that anyone can do.
“Jim, I got an e-mail from the system that says the backups failed last night.”
“Did you call Frank?”
“Frank is out this week on a cruise.”
“Oh, then just get Cynthia from accounting to do it.”
As if Cynthia knows what to do.

Been a While

I’ve been thinking about how over the years I haven’t lived up to so many promises about posting here. I should change that by deciding on the type of things I post. In the past (say, 2004-or-so) I was at least partially opinionated about politics, social issues, and technology. My posts were sometimes rants and other times contributions to a larger conversation. The problem with those kinds of posts is that no one gives a crap about what I think; I’m not that influential. In recent years I don’t very much care about politics (they’re all full of shit) or television (they’re all full of shit, too). I am simply not passionate anymore concerning the popular.
What I have done for the past ten years is talk about, study, toy with, and teach technology. Being a full-time instructor, I’ve become even more fond lately of creating tutorials with screenshots and screencasts. For the last few months I’ve had a daily reminder on my calendar to write a post here, and every day when I look at it I have nothing to contribute. This evening I came across the weblog of a fellow IT worker, Jeffrey B. Murphy, who appears to write simple yet concise how-to’s and tech notes that help him through his days as a system administrator. This, as I see it, does two things: it provides him with some sort of searchable knowledge base he can reference and it offers the World Wide Web some sensible and usable information. I’ve done a few posts for similar reasons, and they happen to be among the more popular here.
I’ve been hesitant to write a post if it’s not very long; all the SEO folks say it’s bad to publish fewer than 300 words. From now on I won’t care how long the post is – if I can share accurate and helpful information, so be it. I’ll post even the simplest things in hoping that it may start a conversation. That’s what this thing is for, right?

Wait – did I get down to my point? I had one when I started writing…

Either way, here it is in fewer words: I plan on posting helpful how-to articles and tutorial-type screencast videos here. As often as I can. I’ve been writing here for just over ten years and it’s high time I made more use of it.