Unmasking the User in PHP CLI Scripts

Who are you. Whoo whoo. Whoo whoo.

…The Who? Anyone? …Bueller?

So let’s say you need to know what user is running a script in PHP. If you’re like me, you may have tried get_current_user only to find that’s just for the current owner of the file. Well THAT’S deceivingly unhelpful! =\

Wait! Sec. One more moment of gripe: If what get_current_user in php does makes sense to you, I’d like to be the first to ask you: …How?!

Not only did that not help, but I also couldn’t find any solutions or hints in the right direction from the comments in the documentation (generally, I find them to be a good place for a next step). There was one suggesting to using posix functions like this: print_r(posix_getpwuid(posix_geteuid())); — however, no luck! If the user is invoking the script with sudo, we lose all trace of who done it. Also, this is technique can limit who can use it as it’s not uncommon for environments to disable posix_* functions for security.

So, Who Really Done It?!

Ranting aside… the answer to this eluded me! Low and behold, the it was inside of $_SERVER all along. Le sigh. I put together a quick function with a few fallbacks to figure it out. Let me know if this doesn’t work for you, but for me, does the trick in spades =]

function get_real_current_user()
{
    if (isset($_SERVER['SUDO_USER'])) {
        return $_SERVER['SUDO_USER'] . ' [priv]';
    }
    if (isset($_SERVER['LOGNAME'])) {
        return $_SERVER['LOGNAME'];
    }
    if (isset($_SERVER['HTTP_HOST'])) {
        // Could also use $_SERVER['REQUEST_URI'] rather than SCRIPT_NAME... up to you =]
        return "Web via: " . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'];
    }
    return "Unknown User";
}

I like this solution the best. Not only is this data already in the scope of our script already, but we don’t have to use posix or try crap like echo exec("who am i"); and parsing through that. #winning

Hope that helps someone out there in the ether!

Cheers!
Ryan

Adding Crontabs to your Coworkers Unlocked Computers

What’s a cron?

Alright, what we’re doing here is scheduling a mac to do something for us using cron. Cron is a program that checks to see if there is anything it needs to do every minute. Once something qualifies, it will execute the code it’s given. But what if the code we give it is…… shenaniganalous in nature? Mwah ha ha ha!!! The possibilities are limited only by your imagination =P

In this example, we’re going to make a job that will repeatedly say “nacho nacho nacho” every ten minutes until the end of time. Seem fair? Alright, the schedule when we’re done will look something like this:

*/10 * * * * ( /usr/bin/say "nacho nacho nacho" )

Ready. Set. Cron.

Here’s how it works on a mac:

  1. Open up a terminal on the victim’s computer. You can press the F4 key, type in “terminal” and press enter.
  2. Type in “crontab -e” (without quotes) to edit the current cron schedule for the user.
  3. To add the schedule above in Vim, press “i” – the bottom left corner should say -- INSERT --
  4. Paste in the schedule from above. You can change the text to say something else, but whatever you do, do not use exclamation points. It’s syntax in bash and will not work!
  5. Once you have your schedule pasted in and it’s perfectly crafted, press escape, then type “:x” (which you’ll see in the bottom left) and press enter — this will save your new schedule and exit Vim

It will tell you if it fails, otherwise, consider your plan in progress! Quit the terminal and wait.

Cleanup

Alright, so the joke is done, and if anyone hears your victim’s computer say “pee pee” one more time, …no wait it’s still hilarious. Ok, let’s say you want to remove it anyway, right? If they don’t have any crons they need to save, there’s a simple way to do it.

Open up the terminal again and type the following (WARNING: it will clear ALL CRONS on the machine!):

crontab -r

Now, if you’ve come across the rare person who has something in their crontab, you can simply edit their crontab again and remove the magnificent job you added like this:

  1. Open up the terminal and type crontab -e
  2. Using ‘j’ and ‘k’, you can navigate to the line that needs to be removed
  3. Once on the offending line, you can type ‘dd’ (yep, two of em) to delete the entire line
  4. Once the schedule is gone, type “:x” to save and quit vim and you’re set!
  5. Hit cmd+q and close the terminal as if nothing ever happened.
  6. Flex.

What else, Ryan?

…yes! A quick reference for you!

Great question! I’m so glad you asked! There is SO MUCH MORE we can do.

View current cron tasks
crontab -l (that’s an “L” to “List”)
Remove all current scheduled tasks
crontab -r
Schedule to open a google search for nachos every 10 minutes
*/10 * * * * ( /usr/bin/open "https://www.google.com/webhp?#q=nachos" )
…hey I get it. Sometimes, you just need some nachos.

More fun with Say

Say comes with a lot of different options… have a look using man say in your terminal to see the manual. Now when you look at this, if you have any questions on how to use some of these options, you can ask!

For instance, let’s say I’d like to use a different voice… but I don’t know which ones I can use. Ask yer mac like this: say -v ?

This will give us a massive list of voices to choose from. Now you can switch up your command to do things like:

14 * * * * ( /usr/bin/say -v Whisper "I can see you" )

The above schedule, on exactly the 14th minute of the hour, will creepily whisper “I can see you”.

Conclusion

Here we’ve explored three tools (crontab, say, and open) that in coordination can help you mess with unlocked computers. I give you this knowledge knowing full well it could be used against me……… but then again, I lock my computer =P

One or many

So, I get that there are probably better ways to handle this circumstance, but when working with legacy code, sometimes, it’s not up to you. I had a method that would be called with either one thing, or an array of these things. Approaching a context like this, the solution we came up with was (what I think is an) amazingly simple way to ensure if you’re sometimes receiving one thing or an array of things, you can handle both.

Check it out:


class Foo
{
    public function oneOrMany($input)
    {
        if ( is_array( $input ) ) {
            return array_map(array($this,'oneOrMany'), $input);
        }

        // Now do stuff to one $input only and return your results =]
        return $results;
    }
}

There it is. And now hopefully I’ll remember this better next time I need something quick.

Cheers!
Ryan

Blog Spammer Shootin’ Match

The Stage

For some reason….. this blog — THIS blog! …..gets a crap ton of spam. So I went looking. I figured I’d share the bulk of what I found.

mysql> update wp_comments set comment_approved='spam' where comment_approved='0' AND comment_content LIKE "%China%";
Query OK, 969 rows affected (0.46 sec)
Rows matched: 969  Changed: 969  Warnings: 0
 
mysql> update wp_comments set comment_approved='spam' where comment_approved='0' AND comment_content LIKE "%obama%";
Query OK, 119 rows affected (0.09 sec)
Rows matched: 119  Changed: 119  Warnings: 0
 
mysql> update wp_comments set comment_approved='spam' where comment_approved='0' AND comment_content LIKE "%nofollow%";
Query OK, 412 rows affected (0.09 sec)
Rows matched: 412  Changed: 412  Warnings: 0


Yep. If you’re getting crazy spam, watch out for China, obama, and nofollow. They. Will. Destroy. Your. Sanity.

The Apology

If you legitimately are trying to comment and noted something about any of the three mentioned above, I owe you an apology. I’m sorry I shot your comment in the spam box with glee and a special kind of whimsy.

Looking at the Data, Again!

I kinda enjoy using this as a psuedo “comment honeypot” because it’s just so darn entertaining. Here are some other fun ways to look at your comment spam:

By number of comments posted per IP:

mysql> select comment_author_IP, count(comment_author_IP) as count from wp_comments where comment_approved='0' group by comment_author_IP order by count desc;
+-------------------+-------+
| comment_author_IP | count |
+-------------------+-------+
| 123.45.67.89      |    15 |
| 123.45.67.89      |    14 |
| 123.45.67.89      |    10 |
| 123.45.67.89      |     3 |
| 123.45.67.89      |     3 |
| 123.45.67.89      |     2 |
| 123.45.67.89      |     2 |
| 123.45.67.89      |     2 |
| 123.45.67.89      |     1 |
| 123.45.67.89      |     1 |
+-------------------+-------+
10 rows in set (0.00 sec)

Original IP’s censored b/c I don’t wanna be a jerk to anyone but the guy at 123.45.67.89!

By my patented, foolproof, never wrong Offender Rating System®:

mysql> select comment_author_IP, (count(comment_author_IP)+SUM(LENGTH(comment_content))) as offender_rating from wp_comments where comment_approved='0' group by comment_author_IP order by offender_rating desc;
+-------------------+-----------------+
| comment_author_IP | offender_rating |
+-------------------+-----------------+
| 123.45.67.89      |           10051 |
| 123.45.67.89      |            3907 |
| 123.45.67.89      |            2367 |
| 123.45.67.89      |            1159 |
| 123.45.67.89      |             836 |
| 123.45.67.89      |             811 |
| 123.45.67.89      |             677 |
| 123.45.67.89      |             653 |
| 123.45.67.89      |             328 |
| 123.45.67.89      |             297 |
+-------------------+-----------------+
10 rows in set (0.00 sec)

Again, original IP’s censored b/c I don’t wanna be a jerk to anyone but the guy at 123.45.67.89!

…fin!

Wasn’t that fun?! =D

Don’t spam me, bro!

Getting a quick summary on your MySQL Database

Summary of yer tables

Sometimes, you just want to see what’s going on from a high level. “HOW CAN I GET A SUMMARY OF MY TABLES?!” you may have asked yourself…

To do this, you’ll need to do a simple query into the TABLES table in the information_schema database. Like this:


mysql> use information_schema;
Database changed

mysql> 

It’s a habit for me, but I always like to describe the table so I know what I’m looking at:


mysql> desc TABLES;
+-----------------+---------------------+------+-----+---------+-------+
| Field           | Type                | Null | Key | Default | Extra |
+-----------------+---------------------+------+-----+---------+-------+
| TABLE_CATALOG   | varchar(512)        | NO   |     |         |       |
| TABLE_SCHEMA    | varchar(64)         | NO   |     |         |       |
| TABLE_NAME      | varchar(64)         | NO   |     |         |       |
| TABLE_TYPE      | varchar(64)         | NO   |     |         |       |
| ENGINE          | varchar(64)         | YES  |     | NULL    |       |
| VERSION         | bigint(21) unsigned | YES  |     | NULL    |       |
| ROW_FORMAT      | varchar(10)         | YES  |     | NULL    |       |
| TABLE_ROWS      | bigint(21) unsigned | YES  |     | NULL    |       |
| AVG_ROW_LENGTH  | bigint(21) unsigned | YES  |     | NULL    |       |
| DATA_LENGTH     | bigint(21) unsigned | YES  |     | NULL    |       |
| MAX_DATA_LENGTH | bigint(21) unsigned | YES  |     | NULL    |       |
| INDEX_LENGTH    | bigint(21) unsigned | YES  |     | NULL    |       |
| DATA_FREE       | bigint(21) unsigned | YES  |     | NULL    |       |
| AUTO_INCREMENT  | bigint(21) unsigned | YES  |     | NULL    |       |
| CREATE_TIME     | datetime            | YES  |     | NULL    |       |
| UPDATE_TIME     | datetime            | YES  |     | NULL    |       |
| CHECK_TIME      | datetime            | YES  |     | NULL    |       |
| TABLE_COLLATION | varchar(32)         | YES  |     | NULL    |       |
| CHECKSUM        | bigint(21) unsigned | YES  |     | NULL    |       |
| CREATE_OPTIONS  | varchar(255)        | YES  |     | NULL    |       |
| TABLE_COMMENT   | varchar(2048)       | NO   |     |         |       |
+-----------------+---------------------+------+-----+---------+-------+
21 rows in set (0.00 sec)

mysql> 

Now, we can see what we’re looking for. Here’s what I generally like to do:


mysql> select TABLE_NAME, TABLE_ROWS, AVG_ROW_LENGTH, DATA_LENGTH, MAX_DATA_LENGTH, INDEX_LENGTH, AUTO_INCREMENT from TABLES where TABLE_SCHEMA='werd';
+-----------------------+------------+----------------+-------------+------------------+--------------+----------------+
| TABLE_NAME            | TABLE_ROWS | AVG_ROW_LENGTH | DATA_LENGTH | MAX_DATA_LENGTH  | INDEX_LENGTH | AUTO_INCREMENT |
+-----------------------+------------+----------------+-------------+------------------+--------------+----------------+
| wp_commentmeta        |          0 |              0 |           0 |  281474976710655 |         4096 |              1 |
| wp_comments           |          1 |            228 |         228 |  281474976710655 |         7168 |              2 |
| wp_hook_list          |          0 |              0 |       16384 |                0 |            0 |           NULL |
| wp_links              |          0 |              0 |           0 |  281474976710655 |         1024 |              1 |
| wp_options            |        154 |           2548 |      430244 |  281474976710655 |        13312 |            835 |
| wp_postmeta           |          9 |             44 |         404 |  281474976710655 |        10240 |             20 |
| wp_posts              |          8 |            298 |        2388 |  281474976710655 |        11264 |             17 |
| wp_term_relationships |          3 |             21 |          63 | 5910974510923775 |         3072 |           NULL |
| wp_term_taxonomy      |          1 |             40 |          40 |  281474976710655 |         4096 |              2 |
| wp_terms              |          1 |             40 |          40 |  281474976710655 |        11264 |              2 |
| wp_usermeta           |         19 |             61 |        1160 |  281474976710655 |        10240 |             20 |
| wp_users              |          1 |             92 |          92 |  281474976710655 |         4096 |              2 |
+-----------------------+------------+----------------+-------------+------------------+--------------+----------------+
12 rows in set (0.00 sec)

mysql> 

Yeah, even my schema names are trendy and clever.</sarcasm>

Anyways, now you can sort and/or select however you want! Grok yer data, amigo. =]

I realize my code blocks are not blocking well enough. Will fix.

Greppin’ Baller Style

So you wanna find something in yer code? Yep. Grep.

There comes a time when you really just want to find something, but either the grep is going to take way too long or you just like being efficient down to the microsecond. Either way, I’m about to show you how I grep. /flex

Anytime I grep, this is generally how I roll:
grep -IirFn "What I'm looking for" .
Let’s break it down now! We’re grepping, but what are all those options?

  • -I – ignore binary files (ie: don’t grep through my images, bro)
  • -i – case insensitive. I know I grepped for “What I’m looking for”, but maybe it’s not capitalized
  • -r – recursively grep through this directory and each one inside it… recursively. Ahem. Bad joke.
  • -F – search for my string literally, no regex!
  • -n – prepend each found line with the corresponding line number – such help! =]
  • "What I'm looking for" . – so what I’m looking for and the dot at the end is to tell grep to start looking here (and sneaky tip, include hidden files!)

Ludicrous speed, ENGAGE

Nerd Remix! Betcha Picard wished he said that once in a while. #nerdage

So let’s say this is all well and good, but we gotta keep on rollin’ through a huuuuuge codebase with lots and lots of stuuuuuuff. What then?!

This is generally when I like to FIND the context of the files I can get at and only grep through those:
find . -name "*.php" -type f -exec grep -FnH "DOING_AJAX" {} \;
Wait… that’s different…. and the arguments aren’t the same. What just happened?! D=

All great questions! Thank you for asking, me! =D

  • find . – find is the command, the dot here is for the same reason (start looking here and include hidden files!)
  • -name "*.php" – this tells find to go find files with a .php extension in the name. Keep in mind, if you want to not bother with case sensitivity, you can replace “-name” with “-iname” =]
  • -type f – this tells find to only worry about FILES (as opposed to directories, etc…)
  • -exec blah blah – this is pretty cool. You can tell find to execute any command upon the matched item. So here we told it to execute a grep. Make sure you end your exec with a fully escaped semicolon too!
  • grep -FnH – We know about the -Fn already, but the -H allows our grep to also prepend the matched grep with the filename also. So -nH gives us the filename:linenumber: match.

I hope that helps you get to what you’re looking for just a little quicker =]

Quick way to check for php syntax errors via CLI

Need a quick way to run through all php files in a directory (recursively) and see which ones have syntax errors? Here you are my friend:
find . -name "*.php" -type f -print | xargs -I file php -l file | grep -v "No syntax errors detected"

Another way to do this (which if you’re as familiar with find as I am — ie: not very) you could do this as well:
find . -name "*.php" -type f -exec php -l {} \; | grep -v "No syntax errors detected"

Rohjaynator::1665017975::24474306