Intro
Hi,
In today's
lab we will learn how to make a tiny scanning tool that will help us understand
a little bit more about how the network works and how an attacker can utilize
in-the-wild resources in order to gain useful information.
Before we
start coding, let's remember that this usage is malicious and it is strictly forbidden
outside the lab (not including homework testing).
What are we going to do?
Ok, so a
hacker can gain information from the network by simply querying it. But what
does querying means?
So as we can
see, opening a socket is just like opening a phone call with a friend. One difference
is that we're not noticing the amount of data that is being transferred while
being concentrated on talking to each other.
For use humans
its only 'chit-chat'. For the wire-hacker it is a chunk of useful information J
What is
being streamed over the line?
We'll divide
it into 2 main categories:
1.
Header
2.
Body or Message
The Header
is every piece of information that is not being transferred as message, while
message is… well… message is the content that both humans were interested in,
before they picked up the phone.
Header
information: Phone number for caller, Phone number on receiver,
Service name (Service Provider), Language, Is the conversation still on,
Hang-up method etc.
Message
information: John: "Hello Gil, How are you?", Gil: "Hi John!
I'm great. Thank you for asking."
By the
way: when calling a friend, we as entities are also in charge of some
Header values.
Example: Gil can end the call whenever she wants, by pressing
the END button.
Now that we
know we would like to start a conversation in order to get information, think
of a situation where John doesn’t want to talk to Gil, but still knowing she's
at home.
Trying to
call, hearing her voice and immediately hang-up is one way. Can you think of
other ways?
What if John
wants to know if there are other phones in Gil's house?
What if
through the phone, John could somehow call Gil's fax machine, or even her
computer?
In order to
get that information we have to query, remember?
We want to
call and get the Header. Or we can look in the white-pages for more phone
numbers related to Gil and start calling them, just to check if its ringing or
not (just like a Ping).
ToolKit
First we
will need to find a language that we can write our code in.
I know that .NET and Java are very comfortable and can autocomplete stuff, so we're going to use Python.
Not just that. We're going to make it a little more interesting by using another open source tool.
Why? So we can write our Python code in it. Yes, we can use the Python Idle, but let's start with this one.
This tool is a Text Editor with abilities to read/write and execute scripts. Compiler is not an option for this lab.
Please
download a very close friend of mine: SublimeText
That’s it. You're
ready for action. J
Instructions
1.
Open Sublime Text
2.
Open a new label by going
to File >> New File or simply hit Ctrl+N
3.
Write import socket (grey color)
4.
Save it as .py file
(Python extension). It supposed to change the string to import socket
***if it doesn't work - download SublimeREPLit is an addon to get a Python Shell. (and a lot more!)
Goals
We are
building a scanning tool, so we would like it to have the following abilities:
1.
Ping Sweep –
scanning multiple remote machines using the "Ping" software.
2.
Port Scanner –
Scanning a target for open ports.
3.
Banner Grabbing –
Grabbing a banner (Header) from the conversation and investigating its content.
Let's see an example shall we?
Ping Sweep:
Scanning 1.1.1.1 should return:
1.1.1.0
Is UP!
1.1.1.1
Returned time out (means it
is not answering, but could be open)
1.1.1.2
Is UP!
1.1.1.255 is UP!
Port Scanner:
Scanning 1.1.1.1 for ports [21, 22,
25, 80, 443, 3306] should return:
Port 21 (FTP) is open
Port 22 (SSH) is open
Port 25 (SMTP) is closed
Port 80 (HTTP) is open
Port 443 (HTTPS) is closed (no SSL
connection, what an attacker will think about it?)
Port 3306 (MySQL) is open (juicy
information)
Banner Grabbing:
Scanning 1.1.1.1 on port 80 with (GET
/ HTTP/1.1) should return:
HTTP/1.1 200 OK
Host: 1.1.1.1
Server: Apache/2.2.3
X-Powered-By: PHP/5.3.27
….
Now that we know what we want to do.
Let's start working.
Coding
Import the
Socket and Subprocess modules:
import socket
import
subprocess
|
Socket is a
built-in module for easy creation and connection to TCP/UDP and other
sockets.
This module provides access to the BSD socket interface. It is available on all modern Unix systems, Windows, Mac OS X, BeOS, OS/2, and probably additional platforms.
Subprocess
module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes.example - CMD
Now let's
write the first function, follow my lead:
Ping
Sweep
Blue for definition of public function,
Green for function name, Orange
for stored variable and function variables and Yellow
for strings.
To get the
ping communication we have to use the Subprocess module. Subprocess has a
common function called Popen() which receives 3 arguments: 1st
is an array with command and host and the other two is for piping the
information from the process to 2 other arguments stdout, stderr
One takes
the message and the other takes the error.
You can see
the payload from subprocess.Popen is being imported into ping
variable. ping will than receive an "extensions power-up" (i.e. inherit functions from subprocess) and will be able
to use a function called communicate(). This function returns 2 values in correlation to the 2 arguments we set in Popen().
The out
variable will return from the function, but not before we will parse some
information from this big payload. We don't need all the information, so we
will look for a pattern like IP:
[+] m =
re.search | re stands for Regular Expressions
and search is simply a search in content.
What is the
content you ask? search receives 2 arguments: 1 is what to look for and
2 is the content. As for this example: 2 is out which is our ping
payload, for example–
C:\>ping 1.1.1.1
Pinging 1.1.1.1 with 32 bytes
of data:
Request timed out.
Request timed out.
|
So if we
want the function to work, we need to add:
import re
|
Right under
the other imports.
But what is
this:
(\d+\.\d+\.\d+\.\d+)
Well [\d] is
a digit placeholder in Regular Expression language. [\.] Is simply [.], because
[.] means "every character".
Adding a [+]
sign means we are looking for more than 1 digit. 1 or more to be exact.
So we have [\d+]
(digits) [\.] (dot) repeated 3 times and ends with [\d+] (digits) – IPv4
representation!
*** 1234.1234.1234.1234 is also a valid output... Can you fix the Regex to receive only IPv4?
That’s it!
We have a Ping command. Later we will create a loop to create the Ping Sweep
Moving
forward:
Port
Scanner
So what do we have here?
First we
have the function and name, but that you already know. The function takes 3
arguments.
We can't be
sure that the protocol value will be in the same case (lower, upper) so
we use the upper() function which will turn this "tcp" into
this "TCP", because Python is case sensitive.
Then we will
use the Socket module (import socket) as followed:
We've
created a variable called "tcp" that receives socket with AF_INET
(which means – I'm using IPv4) and the SOCK_STREAM (which means – I'm
using TCP).
How can we
represent IPv6? And UDP? Find out!
The socket.connect()
function will create a TCP handshake using the tuple of 2 arguments (host, port) and internal variables which are being automatically loaded by the socket module.
Then, if the
port is not 80, we will use a general "Hello" string and if it is 80, we will generate an HTTP request (GET/POST/HEAD…)
***Yes, other socket methods such as SSH, FTP over TCP has conventions and syntax, but I'll leave it to you to find out.
***Yes, other socket methods such as SSH, FTP over TCP has conventions and syntax, but I'll leave it to you to find out.
Finally we
need to somehow capture the response from the communication we've created with the remote server. Using recv() with size of payload (200) (i.e. approximately the size of a header) we can capture the right information we need to return the header data.
If you want more data simply increase the value of recv().
That’s it!
Port Scanner is done. Easy right J
Banner
Grabbing
Hold on.
Banner grabber is our longest function.
Ok, so we have a bit more code here.
Let's dig it
up.
First of all
we have the same function declaration. This time the function name is
bannerGrabbing.
Than we have
a variable called header which will receive data coming from the portScanner()
function.
Why, you
ask? Because we need to open a TCP socket anyways, so instead of implementing
the same code twice, we will just use our function.
Header will
receive payload such as:
SSH-2.0-OpenSSH_5.3p1
Debian-3ubuntu7
HTTP/1.0 200
OK
Content-type: text/html
Date: Wed, 12 Mar 2014 20:29:52 GMT
Content-type: text/html
Date: Wed, 12 Mar 2014 20:29:52 GMT
Server: Apache/2.2.3
Connection: close
Connection: close
X-Powered-By: PHP/5.3.27
Cache-Control: no-store
Cache-Control: no-store
220 FTP version 1.0 ready at Wed Mar
12 19:52:27 2014
530 User anonymous cannot log in.
Login failed.
530 User anonymous cannot log in.
Login failed.
And more.
What we need
is to parse those banners and identify if we can extract valuable information.
Continuing with
the code, if header has data in it, we will try to look for information.
If the
port is 80, means HTTP, we're looking for the Server: banner using the
Regular Expression re.search. This will give us information about the
operating system. For instance – IIS is installed only on Microsoft Windows machines. Nginx and Apache could be a
Linux web servers, but can also be installed on a Windows machine (not so common).
Plus, SSH
utility is only supported on Linux, So we can gather information using multiple ports.
If port 80 has an Nginx server and port 22 is open (SSH), that means it's a Linux server, right?
We can also see it in the example above: Debian-3ubuntu7
We are also
searching for X-Powered-By header which maybe result in disclosing the back-end language and its version.
Then we're
simply looking for SSH, FTP and MySQL. We can extend it to
look for more banners, but its enough for now.
service_name = re.search(re.compile('SSH',
re.I|re.M), header)
As for lines
like the above: when using re.compile we need to supply another argument
along with the string we're looking for.
This argument
re.I and re.M stands for Ignore case (case sensitivity)
and Multi-line for multi-lined payload parse.
Service_name
will not return as string so we are using group(0) to get the string
representation.
Moving forward
to our next block of code:
**notice that the if __name__=='__main__': line indicates the interpreter that it starts the script execution from that line down.
It means that the above lines are class/func/... and other pieces of code which are callable.
**notice that the if __name__=='__main__': line indicates the interpreter that it starts the script execution from that line down.
It means that the above lines are class/func/... and other pieces of code which are callable.
Help
manual
This is very
self-explanatory once you look at the menu:
C:\proj>python
portScanner.py
Usage: portScanner.py
[options]
Options:
-h, --help show this help message and exit
-t www.exemple.com Enter target name or ip
-i TIME_INTERVAL Time interval between each scan in
milliseconds
-p [TCP/ICMP] Returns the type of scan.
-b
BANNER Set to 1 for Banner
Grabbing
C:\proj>
|
The only
thing that is not in the menu is dest, which stores the value
of the argument.
If dest="banner"
and the user supplied –b something
Then now options.banner
= "something". The type is optional, but it is very
good for input validation.
Ok so we
have the manual. Now we need to write an else statement to that last if.
The last if
states that if the user did not supply a targethost and/or a protocol
(-t or -p), than the menu will pop again and the program
won't start.
From here on out, you're on your own!
Coding yourself
1.
For c:\>python Scanner.py
–t example.com –p ICMP
a.
Call pingSweep
with loop.
[Hint
– if example.com translated to 12.15.75.53 you need to grab 12.15.75. and start
appending range(1,254)]
b.
Use Regular Expression
module to slice the IP string.
[Hint
– parsed_ip = re.search(r'HERE_COMES_YOUR_RE',
ip)
To switch from host to IP use: ip=socket.gethostbyname(host)]
c.
Figure out how to
return if the host is UP or not. If UP return – [IP number] is UP!
Else:
return the ICMP replay (time out, destination unreachable).
2.
For c:\>python Scanner.py
–t example.com –p TCP
a.
Return portScanner
for the list of ports in the above code snippet
b.
If port is open return
"port %s is open" % (port)
Else:
"port %s is closed" % (port)
**the
% after the string is a reference for the %s place holder.
3.
For c:\>python Scanner.py
–t example.com –p TCP –b 1
a.
Return bannerGrabbing
+ portScanner
[Hint
– create a loop of port numbers from ports array that returns the banner
payload]
b.
Use try: and except:
to capture errors. Return "Error" on except
4.
For c:\>python Scanner.py
–t localhost –p UDP
a.
Create a client in our
code, where the if UDP comment is, that simulates a UDP client (sends
data through UDP to a fixed port (say 5454)).
b.
Create another python
file called server.py that will listen to the same port (on localhost).
[Hint
– UDP does not use connect(), it uses bind() from socket module.]
*To
send and receive data through UDP, both files should run simultaneously.
Good Luck!
No comments:
Post a Comment