Adding a remote MySQL user

First, log onto the local Mysql server (add the ‘-p’ switch if you have a root password assigned, or ‘-h hostname’ if it’s a remote server):

mysql -u root

Now add the user and update the privileges. To restrict to a single remote IP instead of any, replace ‘%’ with ‘1.2.3.4’, ect.

GRANT ALL PRIVILEGES ON *.* TO remoteuser@'%' IDENTIFIED BY 'remotepassword';
flush privileges;

Install ffmpeg, flvtool2, mplayer, mencoder and ffmpeg-php in Cpanel, CentOS, RHEL

This is by far the easiest way to get up and running with ffmpeg and assorted tools on a Cpanel / RHEL / CentOS server. I can’t take credit for all of it, however the original site where I found it has long since been offline with nothing similar replacing it. Hopefully these instructions help others as they have helped me.

First, install rpmforge so you don’t have to compile a bunch of components from scratch:

cd /usr/src
wget http://dag.wieers.com/rpm/packages/rpmforge-release/rpmforge-release-0.3.6-1.el5.rf.x86_64.rpm
rpm -ivh rpmforge-release-0.3.6-1.el5.rf.x86_64.rpm

Now, edit ‘/etc/yum.conf’ and remove ‘ruby*’ from the excludes list. This is just to get past the next step; we must restore it later.

Now, install the required packages:

yum install ffmpeg ffmpeg-devel flvtool2 mplayer mencoder lame libogg libvorbis libtheora swftools amrnb amrwb transcode x264

Now, add ‘ruby*’ back to the excludes list in ‘/etc/yum.conf’ to keep Cpanel happy.

Install Mplayer directly from source:

wget http://www.mplayerhq.hu/MPlayer/releases/codecs/essential-20071007.tar.bz2
tar xjf essential-20071007.tar.bz2
mv essential-20071007 /usr/lib/codec
mkdir /usr/local/include/ffmpeg/
cp -par /usr/include/lib{avcodec,avdevice,avformat,avutil,swscale} /usr/local/include/ffmpeg/
cp -pa /usr/include/lib{avcodec,avdevice,avformat,avutil,swscale}/*.h /usr/local/include/ffmpeg/

Same with the ffmpeg PHP module:

wget http://downloads.sourceforge.net/sourceforge/ffmpeg-php/ffmpeg-php-0.6.0.tbz2
tar xjf ffmpeg-php-0.6.0.tbz2
cd ffmpeg-php-0.6.0
phpize
./configure && make
make install

Add after the ‘extension_dir’ and ‘zend_extension’ entries in the php.ini:

extension="ffmpeg.so"

Test your installation with the following command. I’ve included the raw shell output so you know what you should be seeing:

root@server [~]# php -i | grep ffmpeg
ffmpeg
ffmpeg-php version => 0.6.0-svn
ffmpeg-php built on => Apr 28 2010 15:40:49
ffmpeg-php gd support  => enabled
ffmpeg libavcodec version => Lavc52.20.0
ffmpeg libavformat version => Lavf52.31.0
ffmpeg swscaler version => SwS0.7.1
ffmpeg.allow_persistent => 0 => 0
ffmpeg.show_warnings => 0 => 0

Update

In certain situations, you may end up with the following error:

/usr/src/ffmpeg-php-0.6.0/ffmpeg_frame.c: In function âzim_ffmpeg_frame_toGDImageâ:
/usr/src/ffmpeg-php-0.6.0/ffmpeg_frame.c:336: error: âPIX_FMT_RGBA32â undeclared (first use in this function)
/usr/src/ffmpeg-php-0.6.0/ffmpeg_frame.c:336: error: (Each undeclared identifier is reported only once
/usr/src/ffmpeg-php-0.6.0/ffmpeg_frame.c:336: error: for each function it appears in.)
/usr/src/ffmpeg-php-0.6.0/ffmpeg_frame.c: In function âzim_ffmpeg_frame_ffmpeg_frameâ:
/usr/src/ffmpeg-php-0.6.0/ffmpeg_frame.c:421: error: âPIX_FMT_RGBA32â undeclared (first use in this function)
make: *** [ffmpeg_frame.lo] Error 1

To resolve this, search for all instance of “PIX_FMT_RGBA32″ and replace with “PIX_FMT_RGB32″ in ffmpeg_frame.c and recompile:

perl -pi -e "s/PIX_FMT_RGBA32/PIX_FMT_RGB32/g" ffmpeg_frame.c
make clean
phpize
./configure && make
make install

Finding PHP shell scripts and PHP exploits

Exploits love to hide their evil code using random combination’s of base64_encode, gzdeflate, ect. Although you’re going to get plenty of false positives using this method, by using common sense and this simple command you can weed out most popular exploits which are either standalone files or embedded into existing files. I sometimes update this when I find new exploits so check back.

Replace the path below (.) with the absolute path of the directory you want to recursively scan. For example, you could recursively scan from the working directory:

grep '((eval.*(base64_decode|gzinflate|\$_))|\$[0O]{4,}|FilesMan|GLOBALS.*exit|JGF1dGhfc|IIIl|die\(PHP_OS|posix_getpwuid|Array\(base64_decode|document\.write\("\\u00|sh(3(ll|11)))' . -lroE --include=*.php*

Path to replace . which will all public-facing web folders on a Cpanel box:

/home/*/public_html/

Don’t forget something as simple as ‘clamscan’ (if you’ve got ClamAV installed) can also find some PHP shells. Replace the path below with the absolute path of the directory you want to recursively scan. For example, you could scan all public HTML folders on a Cpanel server for various exploits and certain phishing sites:

nice -n 19 clamscan /home/*/public_html -r -i | grep " FOUND"

Installing Ksplice – The Easy Way

With the recent high-profile exploits like CVE-2010-3301 and CVE-2010-3081, keeping your Linux boxes secure is becoming more and more important. Enter the world of Ksplice.

Ksplice is a tool which automatically applies live Kernel updates to a running system so you never have to reboot, ever again. Imagine staying secure with each kernel release and never having to reboot your Linux machine…and yes, it really works!

When you’ve got dozens or more CentOS boxes to install Ksplice on, this single cut-and-paste method lets you do it in one click. Remember to replace ACTUAL_KEY with your Ksplice access key.

wget https://www.ksplice.com/yum/uptrack/centos/ksplice-uptrack-release.noarch.rpm
rpm -i ksplice-uptrack-release.noarch.rpm
yum -y install uptrack
perl -pi -e 's/INSERT_ACCESS_KEY/ACTUAL_KEY/' /etc/uptrack/uptrack.conf
perl -pi -e 's/autoinstall = no/autoinstall = yes/' /etc/uptrack/uptrack.conf
uptrack-upgrade -y

The code could be easily modified for other flavors of Linux.

Check out www.ksplice.com.

Using TCPdump and Wireshark to monitor network traffic

Here are a few shortcuts for common monitoring tasks with TCPdump. I will update this post over time as new needs arise.

Monitor traffic to/from a single port:

tcpdump -n -i eth0 'port 53'

Monitor and decode traffic to/from or regarding a particular domain or IP (there may be a better way, this is quick and dirty). You can replace the IP with a domain name if needed:

tcpdump -nvvA | grep 188.92.72.128 -A 2

Monitor POST payload for a particular domain (useful for tracing hack attempts and other abuse):

tshark -V -T text -R 'http.request and http.host == "djlab.com" and http.request.method == "POST"' -x > post_log_djlab.com.txt

Monitor POST payload for all domains on a machine:

tshark -V -T text -R 'http.request and http.request.method == "POST"' -x > post_log_all_domains.txt

IIS6 Directory Listing Timeout with Windows Firewall

This issue comes up every so often. No matter what, even if you manually define all the passive ports in Windows firewall, add an exception for inetinfo.exe, the works, certain directory listings still hang on the client end. This issue is apparent only with the Firewall enabled — disabling the firewall allows the listings to complete, but leaves the server at risk.

The symptom

Status:	Connection established, waiting for welcome message...
Response:	220 Microsoft FTP Service
Command:	USER xxxxxxxx
Response:	331 Password required for xxxxxxxx.
Command:	PASS ***********
Response:	230 User xxxxxxxx logged in.
Status:	Connected
Status:	Retrieving directory listing...
Command:	CWD /productos
Response:	250 CWD command successful.
Command:	TYPE I
Response:	200 Type set to I.
Command:	PASV
Response:	227 Entering Passive Mode (x,x,x,x,19,137).
Command:	LIST
Response:	125 Data connection already open; Transfer starting.
Response:	226 Transfer complete.
Error:	Connection timed out
Error:	Failed to retrieve directory listing

The Fix

Go to Start -> Run… (or a command prompt) and paste the following:

%SystemRoot%\System32\inetsrv\inetmgr.exe

Go to FTP Sites (right click) -> Properties

1. Under the ‘FTP Site’ tab:

Change ‘Connection timout (in seconds)’ to 300.

2. Under the ‘Home Directory’ Tab:

Change ‘Directory listing style’ from MS-DOS to UNIX.

You can now open those problematic directories:

Status:	Connection established, waiting for welcome message...
Response:	220 Microsoft FTP Service
Command:	USER xxxxxxxx
Response:	331 Password required for xxxxxxxx.
Command:	PASS ***********
Response:	230 User xxxxxxxx logged in.
Status:	Connected
Status:	Retrieving directory listing...
Command:	CWD /productos
Response:	250 CWD command successful.
Command:	TYPE I
Response:	200 Type set to I.
Command:	PASV
Response:	227 Entering Passive Mode (x,x,x,x,19,139).
Command:	LIST
Response:	125 Data connection already open; Transfer starting.
Response:	226 Transfer complete.
Status:	Directory listing successful

In step 1, some folder listings were fixed, even though the listings are generated in well under 2 seconds. In step 2, the rest were fixed. How the heck could Windows Firewall have anything to do with these settings? Please let me know.

Creating Forensic Copies and Backups of Any Media

Using almost any flavor of GNU Linux it’s possible to make forensic quality bit-copies of hard drives and almost any other type of media, such as USB flash drives or memory cards. This procedure is useful for law enforcement preserving data for analysis, and for making high quality, personal backups of your own media. Best of all, this example uses completely free software and is very easy to do once you get the hang of it.

In the following example, I’m making a forensic copy of my office desktop PC’s main hard drive to a USB hard drive before I attempt to clean up a recent malware infection. By taking an exact copy of the disk in a working condition, I can start over as many times as needed until it is clean of the infection.

Note: Make sure the network connection is unplugged as soon as you notice any strange activity on your PC indicating virii/malware infection. This is the ONLY way to ensure your private data isn’t leaked out to bad guys.

First, download and burn a recent copy of the Knoppix live boot CD. Knoppix is a complete Linux boot CD with the necessary Kernel modules to boot most hardware, read/write NTFS, and works very well with most types of removable media.

Download Link

Determining the device IDs of the source and destination disk

With the external storage disconnected, boot the Knoppix disk and select ‘Shell’ from the main menu.

First we need to determine what device ID is assigned to your primary disk. In this example we’re looking for a standard ATA (IDE) disk. If you had a SCSI disk you would grep SCSI instead of ATA. Type the following:

dmesg | grep ATA

The output:

hda: WDC WD400BB-53AUA1, ATA DISK drive
hdc: CRD-8482B, ATAPI CD/DVD-ROM drive
hdd: WDC WD400BB-32DEA0, ATA DISK drive

In this case, ‘hda’ is the disk we’re backing up. Write this down.

Now, plug in a USB hard drive and wait about 15-20 seconds for Linux to detect and assign it a device ID. Type the following:

dmesg | grep SCSI

In this case, ‘/dev/sdb1’ is the primary partition of the USB disk we’re storing the backup on. Write this down.

Mount the external disk:

mkdir usbhdd
mount /dev/sdb1 usbhdd

Note: To unmount the USB disk cleanly, use ‘umount usbhdd’. Not now though!

Create a backup image

This process can take quite a long time depending on the size of the source disk and the speed of your disks. A 250Gb HDD typically takes 4-5 hours using this method. We’re going to use the ‘dd’ command to create the backup copy of the disk. The noerror and sync options mean that dd will continue in the event a bad block is discovered and keep the backup image in sync by writing zeros where the unreadable data occurs. These options are critical in creating an exact image.

dd if=/dev/hda of=usbhdd/backup.img conv=noerror,sync bs=64K

There will be no progress indication while creating the image, so you’ll just have to be patient. If you really want to see what’s going on, you can open a new shell by pressing Ctl-Alt-F2 and issuing the ‘vmstat 1’ command to monitor IO activity or monitor the dd process for status. Press Ctl-Alt-F1 to return to the primary shell. When done, verify the backup image by comparing the geometry of the physical HDD and the backup image using fdisk. The output of the following two commands should be identical:

fdisk -l usbhdd/backup.img
fdisk -l /dev/hda

Restoring the backup

Use dd again to restore your backup to the physical disk — take note that that ‘if’ and ‘of’ (input and output) are simply reversed:

dd if=usbhdd/backup.img of=/dev/hda conv=noerror,sync bs=64k

Browsing the backup

For browsing the backup image from a Windows machine, I recommend WinImage. It allows you to open the image and browse files using an interface similar to Windows Explorer. For browsing the image under Linux, you can simply mount it using the ‘-o loop -r’ options. Make sure to always mount it read-only using the ‘-r’ switch so no changes are made to the image by accident.

WinImage is available Here. I recommend anyone involved in IT have this app in their toolkit.

Batch PHP Thumbnail Generator

The following script will scan all images in the same directory and create thumbnails in the ‘thumbs’ folder of each image.  You can run it as many times as you wish, it will only create thumbnails for images that don’t exist in the ‘thumbs’ folder.  For example, you can easily set it up as a cron job.  Feel free to tweak the settings or any other part of this script however you wish.

<?php
/*############################################
Batch PHP-based thumbail creator by Randy M. (rsm@fastserv.com)
Classes borrowed from hiege Iseng Resize Class (http://shiege.com/scripts/thumbnail/)
----------------------------------------------
Note :
- GD must Enabled
- Autodetect file extension (.jpg/jpeg, .png, .gif, .wbmp)
  but some server can't generate .gif / .wbmp file types
- If your GD not support 'ImageCreateTrueColor' function,
  change one line from 'ImageCreateTrueColor' to 'ImageCreate'
  (the position in 'show' and 'save' function)
############################################*/


$thumbs_folder="thumbs"; // folder where thumbs will go
$thumbs_width=""; // set width for thumbnail, or
$thumbs_height=""; // set height for thumbnail, or
$thumbs_autosize="220";  // set the biggest width or height for thumbnail
$thumbs_quality="75";  // [OPTIONAL] set quality for jpeg only (0 - 100) (worst - best), default = 75

$d = opendir(".");
while (false !== ($file = readdir($d))) {
  if ( filetype($file) == 'file' ){
    $type = ereg_replace(".*\.(.*)$","\\1",$file);
    if ( preg_match("/^(png|gif|jpg|jpeg)$/i",$type) ) {
      if ( !file_exists  ($thumbs_folder."/".$file) ) {
        echo "Resizing: ".$file."<br>";
        $thumb=new thumbnail($file);// generate image_file, set filename to resize
        if ($thumbs_width) {$thumb->size_width($thumbs_width);}
        if ($thumbs_height) {$thumb->size_width($thumbs_height);}
        if ($thumbs_autosize) {$thumb->size_auto($thumbs_autosize);}
        if ($thumbs_quality) {$thumb->jpeg_quality($thumbs_quality);}
        $thumb->save($thumbs_folder."/".$file);
      }
    }
  }
}


class thumbnail
{
    var $img;

    function thumbnail($imgfile)
    {
        //detect image format
        $this->img["format"]=ereg_replace(".*\.(.*)$","\\1",$imgfile);
        $this->img["format"]=strtoupper($this->img["format"]);
        if ($this->img["format"]=="JPG" || $this->img["format"]=="JPEG") {
            //JPEG
            $this->img["format"]="JPEG";
            $this->img["src"] = ImageCreateFromJPEG ($imgfile);
        } elseif ($this->img["format"]=="PNG") {
            //PNG
            $this->img["format"]="PNG";
            $this->img["src"] = ImageCreateFromPNG ($imgfile);
        } elseif ($this->img["format"]=="GIF") {
            //GIF
            $this->img["format"]="GIF";
            $this->img["src"] = ImageCreateFromGIF ($imgfile);
        } elseif ($this->img["format"]=="WBMP") {
            //WBMP
            $this->img["format"]="WBMP";
            $this->img["src"] = ImageCreateFromWBMP ($imgfile);
        } else {
            //DEFAULT
            echo "Not Supported File";
            return;
        }
        @$this->img["lebar"] = imagesx($this->img["src"]);
        @$this->img["tinggi"] = imagesy($this->img["src"]);
        //default quality jpeg
        $this->img["quality"]=75;
    }

    function size_height($size=100)
    {
        //height
        $this->img["tinggi_thumb"]=$size;
        @$this->img["lebar_thumb"] = ($this->img["tinggi_thumb"]/$this->img["tinggi"])*$this->img["lebar"];
    }

    function size_width($size=100)
    {
        //width
        $this->img["lebar_thumb"]=$size;
        @$this->img["tinggi_thumb"] = ($this->img["lebar_thumb"]/$this->img["lebar"])*$this->img["tinggi"];
    }

    function size_auto($size=100)
    {
        //size
        if ($this->img["lebar"]>=$this->img["tinggi"]) {
            $this->img["lebar_thumb"]=$size;
            @$this->img["tinggi_thumb"] = ($this->img["lebar_thumb"]/$this->img["lebar"])*$this->img["tinggi"];
        } else {
            $this->img["tinggi_thumb"]=$size;
            @$this->img["lebar_thumb"] = ($this->img["tinggi_thumb"]/$this->img["tinggi"])*$this->img["lebar"];
         }
    }

    function jpeg_quality($quality=75)
    {
        //jpeg quality
        $this->img["quality"]=$quality;
    }

    function show()
    {
        //show thumb
        @Header("Content-Type: image/".$this->img["format"]);

        /* change ImageCreateTrueColor to ImageCreate if your GD not supported ImageCreateTrueColor function*/
        $this->img["des"] = ImageCreateTrueColor($this->img["lebar_thumb"],$this->img["tinggi_thumb"]);
            @imagecopyresized ($this->img["des"], $this->img["src"], 0, 0, 0, 0, $this->img["lebar_thumb"], $this->img["tinggi_thumb"], $this->img["lebar"], $this->img["tinggi"]);

        if ($this->img["format"]=="JPG" || $this->img["format"]=="JPEG") {
            //JPEG
            imageJPEG($this->img["des"],"",$this->img["quality"]);
        } elseif ($this->img["format"]=="PNG") {
            //PNG
            imagePNG($this->img["des"]);
        } elseif ($this->img["format"]=="GIF") {
            //GIF
            imageGIF($this->img["des"]);
        } elseif ($this->img["format"]=="WBMP") {
            //WBMP
            imageWBMP($this->img["des"]);
        }
    }

    function save($save="")
    {
        //save thumb
        if (empty($save)) $save=strtolower("./thumb.".$this->img["format"]);
        /* change ImageCreateTrueColor to ImageCreate if your GD not supported ImageCreateTrueColor function*/
        $this->img["des"] = ImageCreateTrueColor($this->img["lebar_thumb"],$this->img["tinggi_thumb"]);
            @imagecopyresized ($this->img["des"], $this->img["src"], 0, 0, 0, 0, $this->img["lebar_thumb"], $this->img["tinggi_thumb"], $this->img["lebar"], $this->img["tinggi"]);

        if ($this->img["format"]=="JPG" || $this->img["format"]=="JPEG") {
            //JPEG
            imageJPEG($this->img["des"],"$save",$this->img["quality"]);
        } elseif ($this->img["format"]=="PNG") {
            //PNG
            imagePNG($this->img["des"],"$save");
        } elseif ($this->img["format"]=="GIF") {
            //GIF
            imageGIF($this->img["des"],"$save");
        } elseif ($this->img["format"]=="WBMP") {
            //WBMP
            imageWBMP($this->img["des"],"$save");
        }
    }
}
?> 

ConfigServer Firewall LFD Breaks tmpwatch on Linux servers

The issue was that old/stale tmp files are not getting cleaned by tmpwatch. This is because CSF/LFD regularly reads all tmp files scanning for trojans and other exploits, thus changing the ‘last access’ time so tmpwatch wouldn’t delete anything.

I wrote a script to manually clean out session that are over 7 days old. Other files will linger like sockets, and a few others, but the space consumed is negligible.

#!/bin/sh
# Clean out /tmp manually since CSF+LFD breaks tmpwatch.
find /tmp -mtime +7 -name *sess_* -type f -print0 | xargs -0 rm
find /tmp -mtime +7 -name php* -type f -print0 | xargs -0 rm

I set it up as a bash script and set up a daily cron:

0 0 * * * /root/cleantmp.sh > /dev/null 2>&1