Agile Hacking - A Homegrown Telnet-based Portscanner

Sun, 11 May 2008 06:21:29 GMT
by pagvac

So here is the scenario: the attacker has limited access to a box and he/she needs to perform a portscan from it. However, he/she does not want to download any tools to the target system. There might be various reasons for not wanting to upload a portscanner to the box. Perhaps, the attacker wants to minimize the footprint.

In my case, the reason why I had to come up with a solution to this problem is because I had to simulate an attack in which the attacker had gained access to a Internet-visible web server. In this case, I needed to perform a portscan of the backend database server and make sure that only required ports are visible (a customized mssql port in this case). For reasons that are irrelevant to this post, the customer could only give me restricted access (NOT root) to the web server via SSH.

I really didn't want to download a tool such as nmap and then compile it. In theory, I wouldn't be able to cause serious damage to the system since I was using a restricted user account. Even then, I always try to be as polite as possible with customers' environments during security assessments, especially when it's a production system.

Anyway, my solution to this problem was to write a simple TCP portscanner in bash which glues around the telnet command which is present on most Unix/Linux distributions. Literally, all I'm doing is looking for Connected to responses generated by telnet which tells us that a successful TCP connection was established (open port). Very vanilla and trivial stuff as you can see! Nevertheless, I accomplished what I wanted, which is to perform a portscan without having to download any tools and without requiring root privileges.

The following is the short version of our agile hacking TCP portscanner which you can literally copy and paste on your shell (just change the value of the HOST variable to the IP address of the system you want to scan):

HOST=127.0.0.1;for((port=1;port<=65535;++port));do echo -en "$port ";if echo -en "open $HOST $port\nlogout\quit" | telnet 2>/dev/null | grep 'Connected to' > /dev/null;then echo -en "\n\nport $port/tcp is open\n\n";fi;done

The following is a more elaborate version of our portscanner which supports scanning for either common or all ports. The list of common ports is read from the /etc/services file which is present on most Unix/Linux systems:

#!/bin/bash

# telnet-based TCP portscanner
# By Adrian 'pagvac' Pastor | www.gnucitizen.org

# delay in seconds

DELAY=0.001

if [[ $# -ne 2 ]]
then
    echo "usage: $0 <mode> <host>"
    echo -e "modes:\t1 - common TCP ports only"
    echo -e "\t2 - all TCP ports"
    exit
fi

if [[ $1 -eq 1 ]]
then
    echo "scanning for the following common TCP ports on $2 ..."
    for port in `grep '/tcp' /etc/services | cut -d '/' -f 1 | cut -d ' ' -f 2 | grep -v '#' | awk '{print $2}' | sort | uniq`
    do
        echo -en "$port "
        if echo -en "open $2 $port\nlogout\quit" | telnet 2>/dev/null | grep 'Connected to' > /dev/null
        then    
            echo -en "\n\nport $port/tcp is open\n\n"
        fi
        sleep $DELAY
    done
    echo -en "\n"
elif [[ $1 -eq 2 ]]
then
    echo "scanning for all TCP ports on $2 ..."
    for((port=1;port<=65535;++port))
    do
        echo -en "$port "
        if echo -en "open $2 $port\nlogout\quit" | telnet 2>/dev/null | grep 'Connected to' > /dev/null
        then    
            echo -en "\n\nport $port/tcp is open\n\n"
        fi
        sleep $DELAY
    done
    echo -en "\n"
fi

Syntax follows:

gnucitizen $ ./telnetps.sh
usage: ./telnetps.sh  <mode> <host>
modes:
        1 - common TCP ports only
        2 - all TCP ports
![Homegrown Telnet Portscanner](/files/2008/05/homegrown_telnet_portscanner.png "Homegrown Telnet Portscanner")

I realize this is not a very elegant tool, but I hope you can see how it can be useful in certain scenarios!

Archived Comments

mindcorrosivemindcorrosive
hmm.. Isn't telnet one of the first services that every sysadmin turns off - for reasons all too known?
defcondefcon
doesnt netcat do this? nc -z host.example.com 20-30
UdiUdi
we use to do that 5 years ago. nice to see it still rocks . yihaaaaa
Sandro GauciSandro Gauci
Such a tool can be useful ;) Most UNIX and linux systems come with perl builtin - even the older ones. So I'd make use of that instead if its available. Has the advantage of not relying on screen scraping telnet and not relying on having telnet on the system. the idea:
use strict;
use IO::Socket;
my ($target,$remote,$results,$port,@ports);
unless (@ARGV > 0) { die "usage: $0 [ip]" }
$target = shift(@ARGV);
for ($port = 0; $port<65536; $port++)
{
    $remote = IO::Socket::INET->new(
                        Proto    => "tcp",
                        PeerAddr => $target,
                        PeerPort => $port,
                    );
    if ($remote) {print  "$port is open\n" };
}
Adrian 'pagvac' PastorAdrian 'pagvac' Pastor
@mindcorrosive: I see your point, however I usually find the telnet command available on many systems I audit. @defcon: that's completely correct. This solution I proposed would be useful in cases in which netcat is not already installed on the system but telnet is. Also remember that in my case installing/downloading an additional tool was NOT an option. @Udi: some solutions will always rock! I'm with you on that one :-D @Sandro: Good to see you at GNUCITIZEN dude :) . Thanks a lot for another very useful solution. Another trick to use when needed. To me, it's all about knowing different tricks which come in handy in different scenarios! We'll definitely consider your solution for the Agile Hacking book! (you'll be credited of course).
Sandro GauciSandro Gauci
@pagvac: the more tricks the merrier :) re the book - that sounds great. looking forward to that @mindcorrosive: the post refers to telnet the client rather than the daemon/server/service
JohannJohann
@mindcorrosive: Yes, telnet as a service, but this is using the telnet client. Not all sysadmins remove the telnet client.
Shoaib YousufShoaib Yousuf
Its more then 5 years old....Good to see refresh version of it by Adrian. This is really worth using it if you are performing audit in restrictive mode and you see telnet option is available...Bingo!!
Adrian 'pagvac' PastorAdrian 'pagvac' Pastor
I'll repeat it again in case it wasn't clear: my proposed homegrown port-scanner relies on the 'telnet' *CLIENT* (NOT server), which again is present on most Unix/Linux systems. @Shoaib: I've never seen this specific implementation (telnet parser) of a portscanner in the public, but of course I'm not so naive to think this hasn't been done before! ;) All in all, this is just another trick of the trade which fits the Agile Hacking book project quite nicely IMHO.
Venom23Venom23
Ok, again. Try this code. Does the same without telnet. It is still buggy but works.
HOST=127.0.0.1;for((port=1;port<=65535;++port));do echo -en "$port ";if exec 5<>/dev/tcp/$HOST/$port 2>/dev/null;then echo -en "\n\nport $port/tcp is open\n\n";fi;done
Adrian 'pagvac' PastorAdrian 'pagvac' Pastor
@Venom23: I'm on a Debian-based system now (Ubuntu) which does not support /dev/tcp. However, it looks like your script should work on any systems that support /dev/tcp. Thanks for your solution to this problem! Any other ideas guys? Any default clients with TCP capabilities (i.e. ftp) is a good candidate for a homegrown port-scanner which doesn't require root privileges to be run. Also, as Sandro mentioned, using any commonly-supported scripting environments such as Perl is another good candidate.
Shoaib YousufShoaib Yousuf
Adrian, I totally agree. Another great piece of work from you guys. Keep it up!
macubergeekmacubergeek
This is cool. I believe Ed Skoudis has done something similar to this on Windows.... Here is an alternative, though not as polished, using curl... Open ports return this response to our stimulous sorry if line is wrapped:
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  5242  100  5242    0     0  15489      0 --:--:-- --:--:-- --:--:--     0
close ports look like this:
scanning port 122...

curl: (7) couldn't connect to host
----------script-----------
## portscanner implimented with curl
#!/bin/bash

if [ $# -ne 1 ]; then
    echo 1>&2 "usage: $0  "
    echo 1>&2 "mode 1 = well known ports 1-1024"
    echo 1>&2 "mode 2 = all ports"
    exit 127
fi

case "$1" in
1)
LIMIT=1024
for ((a=1; a  /dev/null > out
  cat out | grep -v "curl: (7) couldn't connect to host"
done;                           
;;
2)
LIMIT=65535
for ((a=1; a  /dev/null > out
  cat out | grep -v "curl: (7) couldn't connect to host"
done;              
;;
esac
Venom23Venom23
Or let's use the wget command to perform the scan ;)
HOST=192.168.178.88;for((port=1;port<=65535;++port));do echo -en "$port ";if wget -F -S -t 1 -T 1 -v -O banner.txt $HOST:$port 2>&1 | grep connected;then echo -en "\n\nport $port/tcp is open\n\n";cat banner.txt;fi;done
wget should also be available on most of the systems. And - the coolest - it does a "banner grabbing" as well. Nice, isn't it?
macubergeekmacubergeek
Venom23: nicely done! I particularly like the banner grabbing ;-)
maehmaeh
Here's a one for windows using netsh that just prints out any open ports it finds.
@ECHO OFF & ECHO start & (FOR /L %p IN (1,1,65535) DO (FOR /F "tokens=*" %a IN ('netsh diag connect iphost 127.0.0.1 %p ^| find /C /I "[NONE]"') DO ( IF %a == 0 echo %p))) & ECHO stop & @ECHO ON
It's rather slow since netsh seems to take quite a while to load, so you might want to narrow down the port range a bit ;>. What I found interesting is the message the "netsh diag connect iphost" command outputs: "Server appears to be running on port(s) [NONE]" which seems to suggest you could enter more than one port to connect to, but I could'nt find out how to do so.
Adrian 'pagvac' PastorAdrian 'pagvac' Pastor
It's awesome to see so many solutions and implementations for on-the-fly portscanning. This is great guys, keep it coming!
Simon StrohSimon Stroh
Here's a perl solution I just threw together, thought the ones presented here might be a tad slow when scanning all the ports, so I made this one multithreaded: :-)
#!/usr/bin/perl
use IO::Socket;
@ARGV||die'usage: perl scanner.pl host [number of threads]';
($|,$h,$t)=(1,@ARGV,20);$p=65535/$t;
for$n(1..$t){
        pipe($r[$n],$w[$n]);next if fork;
        print IO::Socket::INET->new(PeerAddr=>$h,PeerPort=>$_)?"Port $_ open\n":''for($p*($n-1)...$p*$n-1);
        print{$w[$n]}'x';exit;
}
read($r[$_],$x,1)for(1..$t);
pdppdp
nice. this is quite neat actually.
BroeisiBroeisi
Simon Stroh... Your perl script isn't working.
NOVANOVA
This is a script i wrote to demonstrate a sort of bounce technique i use :) its written in python which i am currently in love with :)
import socket
import getpass
import sys
import telnetlib

#Edit these
HOSTA = "78.32.236.185"
PasswordA = "Sch5636$\n"

HOSTB = "82.111.251.241"
PasswordB = "LLUcpe99\n"

HOSTC = "82.108.105.177"
PasswordC = "LLUcpe99\n"

PORT = "23"



#RAS Commands

jmp1 = "ip telnet "+HOSTB+" "+PORT+"\n"
jmp2 = "ip telnet "+HOSTC+" "+PORT+"\n" 

#Connect to 1st router.
tn = telnetlib.Telnet(HOSTA)
print tn.read_until("Password: ") 
tn.write(PasswordA)
print tn.read_until("ras>")
tn.write(jmp1)

#Connect to second router.
print tn.read_until("Password: ")
tn.write(PasswordB)
print tn.read_until("ras>")
tn.write(jmp2)

#Connect to target system.
print tn.read_until("Password: ")
tn.write(PasswordC)
print tn.read_until("ras>")
tn.write(jmp3)

print tn.read_until("Password: ")
tn.write(PasswordC)
print tn.read_until("ras>")
Simon StrohSimon Stroh
Here's another one. This one is special, because it only uses bash builtins! No programs other than bash shells are called :-)
HOST=127.0.0.1;for p in {0..65535};do((bash -c "(>/dev/tcp/$HOST/$p)" 2> /dev/null && echo open: $p)&read -t0.1;kill $! 2>/dev/null)2>/dev/null;done
BobBob
Would this be a correct three-liner to test single port connectivity (say to test for an install prerequisite with same restrictions)?
echo -en "open $HOST $PORT\nlogout\quit" | telnet 2>/dev/null | grep 'Connected to' > /dev/null
CONNECT_ERROR=$?
if[$CONNECT_ERROR]; then echo"no good"