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

ASX/M3U/PLS On-Demand Playlist Generator

Using a single php file and a simple rewrite rule, it is possible to turn any standard website hosting account in to a on-demand stream server with automatic playlist generation (to launch specific players, ect).

Place this into a file called playlist.php in the root folder:

<?php
  // Auto Playlist Generator by R. McAnally 6/2010
  // To be used with proper rewrite rules
  $expire = "Expires: " . gmdate("D, d M Y H:i:s", time()) . " GMT";
  header("Cache-Control: max-age=86400, must-revalidate");
  header($expire);
  header("Content-Disposition: filename=".basename($_SERVER['REQUEST_URI']));
?>
<?php if ( strtolower($_REQUEST['type']) == 'asx' ) {
header("Content-Type: video/x-ms-asf"); ?>
<ASX version="3.0" >
  <Repeat count="8">
  <Entry>
    <Ref href="<?php echo 'http://'.$_SERVER['SERVER_NAME'].'/'.$_REQUEST['file'] ?>" />
  </Entry>
</ASX>

<?php } elseif ( strtolower($_REQUEST['type']) == 'm3u' ) {
header("Content-Type: audio/x-mpegurl"); ?>
<?php echo 'http://'.$_SERVER['SERVER_NAME'].'/'.$_REQUEST['file'] ?>

<?php } elseif ( strtolower($_REQUEST['type']) == 'pls' ) {
header("Content-Type: audio/x-scpls"); ?>
<?php echo 'http://'.$_SERVER['SERVER_NAME'].'/'.$_REQUEST['file'] ?>


<?}
?>

Then, add the following rewrite directives into an .htaccess file in the same location:

RewriteEngine on
RewriteRule ^(.*)\.(asx|pls|m3u)$ playlist.php?file=$1&type=$2

Now simply upload some media files (.wma, .wmv, .mp3, ect).

For example, you have set this up on somedomain.com and uploaded test.mp3. To access it on-demand using a standard player, use the following URL:

http://somedomain.com/test.m3u

To access it using Windows Media Player, use the following instead:

http://somedomain.com/test.asx

Make sense? I hope so!

mod_security rule for e107 ‘plugindir’ and ‘ifile’ remote include vulnerability

Here are modsecurity2 rules for the latest string of vulnerabilities affecting the E107 CMS system described in the following links:

http://www.exploit-db.com/exploits/12818/
http://www.exploit-db.com/exploits/12715/

SecRule ARGS:THEMES_DIRECTORY "^http" "t:htmlEntityDecode,t:urlDecode,t:lowercase,deny,log,auditlog,msg:'Denied e107 vulnerability'"
SecRule ARGS:ifile "^http" "t:htmlEntityDecode,t:urlDecode,t:lowercase,deny,log,auditlog,msg:'Denied e107 vulnerability'"
SecRule ARGS:plugindir "^http" "t:htmlEntityDecode,t:urlDecode,t:lowercase,deny,log,auditlog,msg:'Denied e107 vulnerability'"
SecRule ARGS:author_name "\[php\]" "t:htmlEntityDecode,t:urlDecode,t:lowercase,deny,log,auditlog,msg:'Denied e107 vulnerability'"

Remotely installing PFSense to hard drive with VGA and without CD-ROM

FreeBSD is great for certain tasks (such as firewalls and other embedded devices), but has some real shortcomings when it comes to booting from attached or remote storage. This severely complicates the installation process in some cases.

In my case, I have a remote server in a rack with no CD-ROM. Pulling the server from the rack and plugging in an IDE/SATA CD-ROM is not an option, as there is no physical access at the current moment (I’m about 90 miles away on travel). So far the following methods of getting FreeBSD / PFSense installed have failed miserably:

1. Boot CD-ROM ISO over PXE (current memdisk). Negative.

2. Boot CD-ROM ISO from USB CD-ROM. Negative.

3. Boot CD-ROM ISO from virtual storage (IPMI CD-ROM). Negative.

OK, so it’s 2010 and FreeBSD can’t boot from anything other than a plain old IDE/PATA CD-ROM (which isn’t an option). Seriously, WTF?

So back to the drawing board. Let’s try some other more or less obvious options:

4. Burn PFSense embedded image directly to hard drive using the same instructions for CF memory. Negative. I even watched the serial console through IPMI – nothing.

5. Boot embedded image from network (memdisk). Negative.

All five of these methods have failed.


Time to use brute force, and if this doesn’t work I am banning FreeBSD from my life.

Yank HDD from laptop, throw in a spare, and boot the CD-ROM on the laptop.

Proceed to install PFSense.

Boot into Linux (or FreeBSD, doesn’t matter) rescue mode CD with network connectivity.

Copy the HDD image off to a remote site:

Remote machine (intermediary storage server, in this case my mirror box):

nc -l -p 2222 | dd of=pfsense.img.gz

Local machine (laptop):

dd if=/dev/sda conv=sync,noerror bs=64K | gzip -c -9  | nc mirror.ash.fastserv.com -p 2222

Once you’ve got the image, it’s time to boot the target PFSense machine into the same Linux/FreeBSD rescue mode and copy the image to the HDD. In the previous step, my working directory was the /pub folder on a public HTTP mirror. This allows me to burn the image directly over HTTP in the following step:

wget -O- http://mirror.ash.fastserv.com/pub/pfsense.img.gz | gzip -cd | dd of=/dev/sda

Much to my surprise, this actually worked! The saving grace for FreeBSD/PFSense, is that it’s able to be installed on one machine, then booted on another. Now on to learning how to use PFSense and configuring firewall rules.

If you want a copy of the HDD image to save you over half of this hassle, you can find it HERE. It was created with an 80Gb HDD so you’ll need at least an 80Gb disk for this to work. Good luck, you’ll probably need it.

Arbitrary MIME support for aacplus streaming with libshout and perl bindings

If you want to be able to support alternative stream types (such as AACplus) using libshout-2.2.2, you’ll need a patch to add a ‘mime’ method. You can then manually set the mime-type for arbitrary stream types. I also included a minor change which sends ‘content-type’ headers to Shoutcast/ICY stream servers, which is required for Shoutcast server support of AACplus streams:

libshout-2.2.2 patch

Additionally, if you’re streaming through Perl, the Perl bindings (Shout-2.1) will also have to be patched similarly both for mime support and for support of the ‘send-raw’ method which is required for non-mp3/ogg streaming. For some reason, send-raw was left out:

Shout-2.1 patch

Decrypting a SSL Server Key for importing into Cpanel

In case someone accidentally encrypts a server key (e.g. not following directions) then expects it to be accepted in to Cpanel, you’ll need to decrypt it first.

Most web hosting platforms (like Cpanel) need the server key to be in clear text. The private key can be decrypted with:

 openssl rsa -in encrypted.pem -out plaintext.key 

Make sure whomever encrypted the key provides you with the pass phrase.

Categories: Linux and Technical. Comments Off on Decrypting a SSL Server Key for importing into Cpanel

Automatically purge old voicemail on Asterisk/FreePBX/Trixbox

Run this nifty Perl script daily or weekly via cron. This will keep your voicemail from overflowing and unknowingly rejecting new voicemail.

#!/usr/bin/perl
#
# Script to expire voicemail after a specified number of days
# by Steve Creel
#

# Directory housing the voicemail spool for asterisk
$dir = "/var/spool/asterisk/voicemail";

# Context for which the script should be running
$context = "default";

# Age (Delete files older than $age days old)
$age = 31;

# Age for unheard messages (Defaults to same age for all messages)
# Set to 0 to not delete unheard messages
$unheardage = $age;

# Delete all files older than $age and $unheardage
# (named msg????.??? to get the audio and txt files,
# but we don't delete greetings or the user's name)

if($age==$unheardage) {

# Save time by doing one find if we're treating everything the same
system('find '.$dir.'/'.$context.' -name msg????.??? -mtime +'.$age.' -exec rm {} \; -exec echo Deleted {} \;');

} else {

# Find everything not in a folder called 'INBOX' and delete it after $age days
system('find '.$dir.'/'.$context.' -path \'*INBOX*\' -prune -o -name msg????.??? -mtime +'.$age.' -exec rm {} \; -exec echo Deleted {} \;');

# If unheardage is set to 0, we won't delete any unheard messages
if($unheardage > 0) {

# Delete things that are in a folder called INBOX after $unheardage days
system('find '.$dir.'/'.$context.' -path \'*INBOX*\' -name msg????.??? -mtime +'.$unheardage.' -exec rm {} \; -exec echo Deleted {} \;');

}
}

# For testing - what number to we start when we renumber?
$start = "0";

# Rename to msg and a 4 digit number, 0 padded.
$fnbase = sprintf "msg%04d", $start;

# Make $dir include the context too
$dir.="/".$context;