Playing with sockets (port scanning)

EDB-ID:

13025

CVE:

N/A

Author:

Pepelux

Type:

papers

Platform:

Multiple

Published:

2008-09-14

=-|================================================-{ www.enye-sec.org }-====|
=-[ Playing with sockets (port scanning) ]-==================================|
=-|==========================================================================|
=-[ by Pepelux <pepelux@enye-sec.org> ]-======================-[13/11/2007]-=|



------[ 0.- Index ]


0.- Index

1.- Quickly introduction
  1.1.- Open Scan
  1.2.- Half Open Scan
  1.3.- Stealth Scan

2.- Playing with sockets
  2.1.- TCP Sockets
  2.2.- UDP / ICMP Sockets

3.- Operating system detection
  3.1.- Some tests

4.- Files

5.- References

6.- End



------[ 1.- Introduction ]

Many time ago we scanned the different ports making telnet manually. Today
people use more soffisticated programs with massive methods to scan IP
ranges searching a lot of ports.

I'm going to write very quickly the different methods of scanning because you
can found on Internet a lot of information about this. Furthermore, the
finally of this paper is to have fun showing and studing how the sockets
works.

------[ 1.1.- Open Scan ]

Known as TCP Scan and normally used to program sockets, this technique is
the oldest and works making a full connection with the server. Is similar to
make a telnet to each port but automatically.

For that it makes an autentication with 3 packets. Is known as
three-way-handshake:

For the ports opened:

      Client ---->   SYN   ---->
             <---- SYN/ACK <---- Server
      Client ---->   ACK   ---->

For the ports closed:

      Client ---->   SYN   ---->
             <----   RST   <---- Server

Advantages   : very easy to program.
Disadvantages: is very easy to detect and make logs on each connection.

------[ 1.2.- Half Open Scan ]

This technique works making only the start of the connection. Sending a SYN
but without send the ACK in the case that the port is opened.

Using RAW sockets we can program manually the socket headers and send only
the start of the connection. Lately we'll see better with some examples.

------[ 1.3.- Stealth Scan ]

Is similar to the previous method but sending another flag, or a combination
of flags.

The most known and used by nmap are:
      SYN
      SYN+ACK
      FIN
      ACK
      NULL (all flags with 0)
      XMAS (FIN=URG=PSH=1)

In the examples we'll see clearly.


------[ 2.- Playing with sockets ]

Is time to have fun. We are going to send different packets changing flags
and studing results.

For the tests I have used two computers:

Client: 192.168.2.5 (Linux Debian - kernel 2.6.18.1)
Server: 192.168.2.7 (Linux Debian - kernel 2.6.21.2)
Opened ports in the server: 22(TCP), 80(TCP), 111(UDP)


------[ 2.1.- TCP Sockets ]

To probe TCP ports we are going to use a program to send and receive RAW
sockets, allowing us to choose the flag values:
- sendsock.c -> 
Usage: ./sendsocket [s|a|r|p|u|f] [-x host_source] -d host_destination -c port
            -s SYN flag enabled
            -a ACK flag enabled
            -r RST flag enabled
            -p PSH flag enabled
            -u URG flag enabled
            -f FIN flag enabled


Send SYN (half open connection)
-------------------------------
Consists in make a half connection, sending a SYN, and when we obtain any
response (ACK+SYN or RST) sending a RST to end the comunication (using RAW
sockets, the RST packet is sent automatically by the kernel).

Original idea consists in send a SYN and if the resver response with an
ACK+SYN, we send a RST packet saying that we don't want to make a connection
... all is a wrong and the server doesn't save any log.

Today many computers can detect it and save a log or block this kind of
attacks with a firewall.

Advantages   : workss in all operating systems and ports, if don't use a
firewall.
Disadvantages: is very easy to detect and log it.

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -s
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  0  |  0  |  0  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=39553 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  1  |  0  |  0  |  0  |  0  | 64  | 53270
     seq=-1378930670 - ack_seq=863708102 - doff=6 - check=8049 - ID=0

Port opened. We obtain SYN+ACK and Window<>0

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -s
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  0  |  0  |  0  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=54145 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  1  |  1  |  0  |  0  |  0  | 64  | 0
     seq=0 - ack_seq=863708102 - doff=5 - check=49281 - ID=0

Port closed. We obtain ACK+RST and Window=0


Send ACK
--------
This method only affect to some old BSD and Unix operating systems and
consists in check Window field. If we obtain a Window=0 the port is opened
and if we obtain a Window<>0 the port is closed.

Advantages   : is more difficult to detect.
Disadvantages: doesn't work in all operating systems.

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -a
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  1  |  0  |  0  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=35969 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  1  |  0  |  0  |  0  | 64  | 0
     seq=0 - ack_seq=0 - doff=5 - check=61122 - ID=0

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -a
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  1  |  0  |  0  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=50561 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  1  |  0  |  0  |  0  | 64  | 0
     seq=0 - ack_seq=0 - doff=5 - check=10179 - ID=0

In this case my linux is not vulnerable and we have obtained the same result
with opened and cloed ports.


Send RST
--------
This attack affect only some systems and is not effective always. In the
case we obtain only an ACK or if we don't obtain response, the port is
opened and if we obtain a RST, the port is closed.

Advantages   : is more diffcult to detect.
Disadvantages: doesn't work in all systems neither ports.

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -r
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  1  |  0  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=39041 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  1  |  0  |  1  |  0  |  0  | 53  | 37920
     seq=1193401395 - ack_seq=908914171 - doff=5 - check=43532 - ID=4979

Port opened. We obtain ACK+PSH, TTL<64 and Window<>0

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -r
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  1  |  0  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=53633 - ID=27032

Without response ... Port closed

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -r
      SENT DATA
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  1  |  0  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=53889 - ID=27032

Without response ... Response is not trustworthy because the port is opened

As we can see, if we obtain any response the port is opened and if we don't
obtain response, we can't know if the port is opened or closed.


Send PSH
--------
This attack affect only some systems and is not effective always. In the
case we obtain a ACK+PSH and a Windows<>0 or don't obtain response, the port
is opened and iif we obtain a RST, the port is closed.

Advantages   : is more diffcult to detect.
Disadvantages: doesn't work in all systems.

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -p
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  1  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=38017 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  1  |  0  |  1  |  0  |  0  | 53  | 17447
     seq=1710733151 - ack_seq=1317887646 - doff=5 - check=32634 - ID=4439

Port opened. We obtain ACK+PSH, TTL<64 and Window<>0

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -p
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  1  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=52609 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  1  |  1  |  0  |  0  |  0  | 64  | 0
     seq=0 - ack_seq=846930886 - doff=5 - check=49537 - ID=0

Port closed. We obtain RST, TTL=64 and Window=0

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -p
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  1  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=52865 - ID=27032

Without response ... port closed


Send URG
--------
This attack affect only some systems and is not effective always. In the
case we obtain an ACK without a RST or if we don't obtain response, the port
is opened and if we obtain a RST, the port is closed.

Advantages   : is more difficult to detect.
Disadvantages: works in my Linux but I don't know if works in other systems.

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -u
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  0  |  1  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=31873 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  1  |  0  |  1  |  0  |  0  | 53  | 37920
     seq=-716127216 - ack_seq=1741636632 - doff=5 - check=41524 - ID=59663

Port opened. We obtain ACK+PSH, TTL<64 and Window<>0

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -u
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  0  |  1  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=46465 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  1  |  1  |  0  |  0  |  0  | 64  | 0
     seq=0 - ack_seq=846930886 - doff=5 - check=49537 - ID=0

Port closed. We obtain ACK+RST, TTL=64 and Window=0

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -u
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  0  |  1  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=3968 - ID=27032

Without response ... port opened


Send FIN
--------
This attack affect only some systems and is not effective always. In the
case we obtain an ACK or if we don't obtain response, the port is opened and
if we obtain a RST, the port is closed.

If other systems you can see the difference in the RST flag. If is activated
the port is closed and if RST value is zero the port is opened.

Advantages   : is more difficult to detect.
Disadvantages: doesn't work in all operating systems because is based on a
               bug.

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -f
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  0  |  0  |  1  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=39809 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  1  |  0  |  1  |  0  |  1  | 53  | 37920
     seq=-1722694640 - ack_seq=1741636632 - doff=5 - check=39751 - ID=56001

Port opened. We obtain ACK+PSH+FIN, TTL<64 and Window<>0

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -f
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  0  |  0  |  1  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=54401 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  1  |  1  |  0  |  0  |  0  | 64  | 0
     seq=0 - ack_seq=863708102 - doff=5 - check=49281 - ID=0

Port closed. We obtain ACK+RST, TTL=64 and Window=0

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -f
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  0  |  0  |  1  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=54657 - ID=27032

Without response ... port opened


Send SYN+ACK
------------
We are going to use some combinations of flags. This attack only work in
some systems. When it works, if you don't obtain any response, the port is
opened and if you obtain a RST, the port is closed.

Advantages   : is more difficult to detect.
Disadvantages: doesn't work in all systems.

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -s -a
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  1  |  0  |  0  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=35457 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  1  |  0  |  0  |  0  | 64  | 0
     seq=0 - ack_seq=0 - doff=5 - check=61122 - ID=0

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -s -a
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  1  |  0  |  0  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=50049 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  1  |  0  |  0  |  0  | 64  | 0
     seq=0 - ack_seq=0 - doff=5 - check=10179 - ID=0

In this case we obtain the same result and we don't know if the port is
opened or closed. As I saw before, only affect some systems :)


Send SYN+PSH
------------
When we don't obtain a RST and Windows<>0, the port is opened and with a RST
and Window=0, the port is clised.

Advantages   : is more difficult to detect.
Disadvantages: works in my Linux but I don't know if works in other systems.

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -s -p
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  0  |  0  |  1  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=37505 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  1  |  0  |  0  |  0  |  0  | 64  | 53270
     seq=1874969709 - ack_seq=863708102 - doff=6 - check=51491 - ID=0

Port opened. We obtain SYN+ACK and Window<>0

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -s -p
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  0  |  0  |  1  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=52353 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  1  |  0  |  0  |  0  |  0  | 64  | 53270
     seq=1445991790 - ack_seq=863708102 - doff=6 - check=52148 - ID=0

Port opened. We obtain SYN+ACK and Window<>0

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -s -p
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  0  |  0  |  1  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=52097 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  1  |  1  |  0  |  0  |  0  | 64  | 0
     seq=0 - ack_seq=863708102 - doff=5 - check=49281 - ID=0

Port closed. We obtain ACK+RST and Window=0


Send URG+FIN
------------
In this case, if we don't obtain response, the port is opened and with a
RST, the port is closed.

Advantages   : is more difficult to detect.
Disadvantages: works in my Linux but I don't know if works in other systems.

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -u -f
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  0  |  1  |  1  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=31617 - ID=27032

Without response ... port opened

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -u -f
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  0  |  1  |  1  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=46209 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  1  |  1  |  0  |  0  |  0  | 64  | 0
     seq=0 - ack_seq=863708102 - doff=5 - check=49281 - ID=0

Port closed. We obtain ACK+RST

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -u -f
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  0  |  1  |  1  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=46465 - ID=27032

Without response ... port opened


Send URG+FIN+PSH
----------------
More known as XMAS scan, when we don't obtain response or don't obtain a RST
the port is open. Is closed when we obtain a RST.

Advantages   : is more difficult to detect.
Disadvantages: only for some systems.

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -p -u -f
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  1  |  1  |  1  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=23938 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  1  |  0  |  0  |  0  |  0  | 47  | 10262
     seq=128745367 - ack_seq=338264191 - doff=10 - check=49414 - ID=0

Port opened. We obtain SYN+ACK

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -p -u -f
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  1  |  1  |  1  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=38786 - ID=27032

Without response ... port opened

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -u -f
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  1  |  1  |  1  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=38530 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  1  |  1  |  0  |  0  |  0  | 56  | 0
     seq=0 - ack_seq=863708102 - doff=5 - check=43650 - ID=56815

Port closed. We obtain ACK+RST


Send null
---------
Known as null scan, it consist in send all flags with zero. In this case,
when response is an ACK or if we don't obtain response, the port is opened
and with a RST, the port is closed.

Advantages   : is more difficult to detect.
Disadvantages: only for some systems.

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  0  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=40065 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  1  |  0  |  0  |  0  |  0  | 53  | 22560
     seq=-515721121 - ack_seq=1706581918 - doff=5 - check=55050 - ID=61453

Port opened. We obtain ACK

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  0  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=54657 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  1  |  1  |  0  |  0  |  0  | 64  | 0
     seq=0 - ack_seq=846930886 - doff=5 - check=49537 - ID=0

Port closed. We obtain ACK+RST

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  0  |  0  |  0  |  0  |  0  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=54913 - ID=27032

Without response ... port opened


Send SYN+ACK+RST+PSH+URG+FIN
----------------------------
Consist in send all flags activated (with 1). In this case, when the
response is an ACK or if we don't obtain response, the port is opened. 
With a RST the port is closed.

Advantages   : is more difficult to detect.
Disadvantages: only for Unix.

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -s -a -r 
                  -p -u -f
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  1  |  1  |  1  |  1  |  1  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=23937 - ID=27032

      RECEIVED DATA
      -------------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  1  |  0  |  1  |  0  |  1  | 53  | 37920
     seq=2033962504 - ack_seq=-1093275500 - doff=5 - check=8061 - ID=35468

Port opened. We obtain ACK+PSH

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -s -a -r 
                  -p -u -f
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  1  |  1  |  1  |  1  |  1  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=38529 - ID=27032

Without response ... result not valid because the port is closed really

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -s -a -r 
                  -p -u -f
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  1  |  1  |  1  |  1  |  1  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=38785 - ID=27032

Without response ... result not valid because the port is opened really

If this case the technique is not valid for us because the responses ar not
fiables. As I have saw befote, only works on Unix.



------[ 2.2.- UDP / ICMP Sockets ]

To check UDP ports we are going to play a little more sending packets. UDP
protocol is not oriented into a connection, not as TCP, and we can't wait for
a response after send a packet. For that, using only UDP we can't known if
the port is opened.

To do the scan we are going to aid us with ICMP packets. Why? because when
you try to make a connection with an UDP port the server will response with
an ICP packet in case of error; justly a type 3 / code 3 packet , that if
you show RFC papers you can know: type 3 = destination unreachable message /
code 3 = port unreachable.

As we love to make this things manually, we are going to use two programs:
- checkicmp.c -> listen for ICMP connection with our computer
- sendudp.c   -> send an UDP packet into a host / port

     flashgordon# ./checkicmp
     Waiting data ...

While program is waiting, we open another terminal and send into a closed
port:

     flashgordon# ./sendudp 192.168.2.7 100
     Sending UDP packet to 192.168.2.7:100

If we show the other terminal we can see:

     flashgordon# ./checkicmp
     Waiting data ...
     Received:       type 3  code 3

This tell us that the port is closed ... check for an opened port to see
what happend:

     flashgordon# ./sendudp 192.168.2.7 111
     Sending UDP packet to 192.168.2.7:111

And in the other terminal:

     flashgordon# ./checkicmp
     Waiting data ...
     Received:       type 3  code 3

Not changes (the message is of the last packet :P) ... as we don't recive
any packet we can say that the port is opened.

Test another time with a closed port:

     flashgordon# ./sendudp 192.168.2.7 101
     Sending UDP packet to 192.168.2.7:101

And in the other terminal:

     flashgordon# ./checkicmp
     Waiting data ...
     Received:       type 3  code 3
     Received:       type 3  code 3

We have obtain another ICMP packet saying us that 101 port is closed too.


------[ 3.- Operationg system detection ]

We can known the operating system running into a server without make a TCP
connection. As occurs with scan methods showed before, different servers can
response different packets (we saw that the TCP connection send a SYN and
wait for a SYN+ACK or a RST) because each system can response differently.
Occurs the same sending UDP or ICMP packets that not are tipical to do an
standard connection.

I'm not show all differences between operating systems because it require a
los of test with a lot of operating systems and a lot of free time :) ...
furthermore, nmap has a very good database of operating systems detectables,
based in the famouse QueSO of Savage.

But we are going to play a little more with the sockets to see any example
and understand all that.

For the tests I have used four computers:

Client : 192.168.2.5 (Linux Debian - kernel 2.6.18.1)
Server1: 192.168.2.7 (Linux Debian)
Server2: 192.168.2.6 (Windows 2000 Server)
Server3: 192.168.2.8 (Solaris)
Pport opened in the three servers: 80(TCP)

Before to do anything to know that for operating systems detection you can
play with more header fields of each packet, but for not change the programs
and as this is only a test, we are going to use the same flags as before.

------[ 3.1.- Some tests ]

To do the tests we are going to send a TCP packet to the port 80 (opened)
with flags SYN+URG+FIN activated.

Fist server, under Linux:

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -s -u -f
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  0  |  0  |  0  |  1  |  1  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=31617 - ID=27032

      RECEIVED DATA
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  1  |  0  |  0  |  0  |  0  | 64  | 6272
     seq=24764339 - ack_seq=863708102 - doff=6 - check=32130 - ID=0

Second server, under Windows:

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.6 -c 80 -s -u -f
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  0  |  0  |  0  |  1  |  1  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=17292 - ID=27032

      RECEIVED DATA
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  0  |  1  |  0  |  1  |  0  |  0  | 48  | 33820
     seq=2043592525 - ack_seq=772288166 - doff=5 - check=30285 - ID=15717

Third server, under Solaris:

     flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.8 -c 80 -s -u -f
      SENT DATA
      ---------
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  0  |  0  |  0  |  1  |  1  | 64  | 65535
     seq=846930886 - ack_seq=0 - doff=5 - check=50815 - ID=27032

      RECEIVED DATA
     | SYN | ACK | RST | PSH | URG | FIN | TTL | Window
     |  1  |  1  |  0  |  0  |  0  |  0  | 41  | 65535
     seq=1684159920 - ack_seq=1278904408 - doff=6 - check=34271 - ID=62243

If we analyze differences:
- Linux  : SYN+ACK - TTL=64 - Window= 6272
- Windows: ACK+PSH - TTL=48 - Window=33820
- Solaris: SYN+ACK - TTL=41 - Window=65535

As I said before, this is only a test to understand as operating systems
detection works. Really we have to change more fields and combine with UDP
and ICMP packets. The differences between Linux, Windows and Solaris is
evident, but using more tests we can see differences between differents
versionsof each operating system, for example between Windows 98 and Windows
200, or see differences between each Linux distributions or kernels.


------[ 4.- Files ]

Programs used to do the tests:

--- sendsocket.c --------------------------8<---------------------------------
// sendsocket.c
// By Pepelux

// Change DEFAULT_HOST writing your private IP if you want to use always the 
// same IP address

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netdb.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/wait.h>
#include<sys/select.h>
#include<sys/time.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<linux/if_ether.h>
#include<linux/if_packet.h>
#include<netinet/tcp.h>


#define Error(msg) { perror(msg); exit -1; }
#define DEFAULT_HOST	"PUT HERE YOUR PRIVATE IP"
#define PBUFFER		10000
#define BUFFER_LONG	65536
#define DEFAULT_LEN     (sizeof(struct tcphdr)+sizeof(struct iphdr))

void usage(char *nom);
unsigned short cksum(unsigned short *, int);
void SendPacket(struct sockaddr_in saddr, struct sockaddr_in daddr, int dport,
	int syn, int ack, int psh, int rst, int urg, int fin);


int main(int argc, char *argv[]) {
	int dport;
	char *host_dest, *host_source;
	struct sockaddr_in saddr, daddr;
	struct hostent *hostentry;
	int i, c;
	int h = 0;		// destination flag
	int x = 0;		// Source flag
	int syn = 0;		// SYN flag
	int ack = 0;		// ACK flag
	int rst = 0;		// RST flag
	int urg = 0;		// URG flag
	int fin = 0;		// FIN flag
	int psh = 0;		// PSH flag

        if (geteuid() != 0) {
                printf("You must be root to use RAW Sockets\n");
                exit(0);
        }

	// Check params
	while((c = getopt(argc, argv, "saprufd:x:c:")) != -1) {
		switch(c) {
			case 'd': // destination host
				if(strlen(optarg) == 0) usage(argv[0]);
				host_dest = optarg;
				h++;
				break;

			case 's': // SYN
				syn = 1;
				break;

			case 'a': // ACK
				ack = 1;
				break;

			case 'r': // RST
				rst = 1;
				break;

			case 'p': // PUSH
				psh = 1;
				break;

			case 'u': // URG
				urg = 1;
				break;

			case 'f': // FIN
				fin = 1;
				break;

			case 'x': // source host
				if(strlen(optarg) == 0) usage(argv[0]);
				host_source = optarg;
				x++;
				break;

			case 'c': // destination port
				if(strlen(optarg) == 0) usage(argv[0]);
				dport = atoi(optarg);
				break;

			default:
				usage(argv[0]);
				break;
		}
	}

	if (x == 0) host_source = DEFAULT_HOST;
	
	if (h == 0) usage(argv[0]);  // you must write destination host. Error

	// IP source
	if((hostentry = gethostbyname(host_source)) == NULL) 
		Error("Error solving source address");

	bzero(&saddr, sizeof(struct sockaddr));
	saddr.sin_family = AF_INET;
	saddr.sin_addr = *((struct in_addr *)hostentry->h_addr);

	// IP destination
	if((hostentry = gethostbyname(host_dest)) == NULL) 
		Error("Error solving destination address");

	bzero(&daddr, sizeof(struct sockaddr));
	daddr.sin_family = AF_INET;
	daddr.sin_addr = *((struct in_addr *)hostentry->h_addr);

	// Send data
	SendPacket(saddr, daddr, dport, syn, ack, psh, rst, urg, fin);
}


void SendPacket(struct sockaddr_in saddr, struct sockaddr_in daddr, int dport,
	int syn, int ack, int psh, int rst, int urg, int fin) {
	int                 destination_port, source_port, on, s, rs, pid;
	int status, i;
	char                buffer[BUFFER_LONG], rbuffer[BUFFER_LONG];
	char string[BUFFER_LONG];
	struct iphdr        *iphdr, *riphdr;
	struct tcphdr       *tcphdr, *rtcphdr;
	struct sockaddr     from;
	int                 fromlen, ethlen;

	struct pseudohdr {
		struct in_addr saddr;
		struct in_addr daddr;
		unsigned char zero;
		unsigned char protocol;
		unsigned short length;
	} *pseudoheader;

	ethlen = sizeof(struct ethhdr);
	on = 1;
	source_port = htons(random());
	destination_port = htons(dport);
	
	setvbuf(stdout, NULL, _IONBF, 0);
	fflush(stdout);
	
	if((pid=fork()) == -1)
		Error("fork");

	if(pid) {
		if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
			Error("socket");

		if(setsockopt(s, IPPROTO_IP, IP_HDRINCL,(char *)&on, 
			sizeof(on)) < 0)
				Error("setsockopt");

		bzero(buffer, BUFFER_LONG);

		// TCP header
		tcphdr =                       (struct tcphdr *)(buffer+
						sizeof(struct iphdr));
		tcphdr->source =               htons(source_port);		// puerto origen
		tcphdr->dest =                 destination_port;			// puerto destino
		tcphdr->window =               htons(65535);			// ventana
		tcphdr->seq =                  random();			// numero de secuencia aleatorio
		tcphdr->syn =                  syn;				// flag SYN
		tcphdr->ack =                  ack;				// flag ACK
		tcphdr->rst =                  rst;				// flag RST
		tcphdr->psh =                  psh;				// flag PSH
		tcphdr->urg =                  urg;				// flag URG
		tcphdr->fin =                  fin;				// flag FIN
		tcphdr->doff =                 sizeof(struct tcphdr) / 4;

		// TCP pseudoheader
		pseudoheader =                 (struct pseudohdr *)
			((unsigned char *)tcphdr-sizeof(struct pseudohdr));
		pseudoheader->saddr =          saddr.sin_addr;			// direccion origen
		pseudoheader->daddr =          daddr.sin_addr;			// direccion destino
		pseudoheader->protocol =       IPPROTO_TCP;			// protocolo
		pseudoheader->length =         htons(sizeof(struct tcphdr));
		tcphdr->check =                cksum((unsigned short *)
		pseudoheader, sizeof(struct pseudohdr)+sizeof(struct tcphdr));
		
		// IP header
		bzero(buffer, sizeof(struct iphdr));
		iphdr =                        (struct iphdr *)buffer;
		iphdr->ihl =                   5;				// IHL (longitud de cabecera)
		iphdr->version =               4;				// version
		iphdr->tot_len =               htons(DEFAULT_LEN);		// longitud del datagrama
		iphdr->id =                    htons(random());			// numero de identifiacion (aleatorio)
		iphdr->ttl =                   IPDEFTTL;			// tiempo de vida
		iphdr->protocol =              IPPROTO_TCP;			// protocolo
		iphdr->daddr =                 daddr.sin_addr.s_addr;		// direccion origen
		iphdr->saddr =                 saddr.sin_addr.s_addr;		// direccion destino

		printf(" SENT DATA\n");
		printf(" ---------\n");
		printf("| SYN | ACK | RST | PSH | URG | FIN | TTL | 
			Window\n");
		printf("|  %d  |  %d  |  %d  |  %d  |  %d  |  %d  | %d  
			| %d\n", syn, ack, rst, psh, urg, fin, iphdr->ttl, 
			tcphdr->window);
		printf("seq=%d - ack_seq=%d - doff=%d - check=%d - ID=%d\n\n",
			tcphdr->seq, tcphdr->ack_seq, tcphdr->doff, 
			tcphdr->check, iphdr->id);
		
		if(sendto(s, buffer, DEFAULT_LEN, 0x0, (struct sockaddr *)
			&daddr, sizeof(struct sockaddr) ) != DEFAULT_LEN)
			Error("sendto");

		wait(&status);
		close(s);
		exit(0);
	} else {
		if((rs = socket(AF_INET, SOCK_PACKET, htons(ETH_P_IP))) < 0)
			Error("socket input");

		if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
			Error("socket");

		if(setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char *)&on,
			sizeof(on)) < 0)
			Error("setsockopt");

		while(1) {
			if(recvfrom(rs, rbuffer, BUFFER_LONG, 0x0, 
				(struct sockaddr *)&from, &fromlen) <= 0)
				Error("recvfrom");

			riphdr = (struct iphdr *)(rbuffer+ethlen);

			if(riphdr->protocol == IPPROTO_TCP) {
				rtcphdr = (struct tcphdr *)(rbuffer+ethlen+
				sizeof(struct iphdr));
			
				if(rtcphdr->source == destination_port) {
					bzero(buffer, BUFFER_LONG);

					printf(" RECEIVED DATA\n");
					printf(" -------------\n");
					printf("| SYN | ACK | RST | PSH | URG 
					| FIN | TTL | Window\n");
					printf("|  %d  |  %d  |  %d  |  %d  
					|  %d  |  %d  | %d  | %d\n", 
					rtcphdr->syn, rtcphdr->ack, 
					rtcphdr->rst, rtcphdr->psh, 
					rtcphdr->urg, rtcphdr->fin, 
					riphdr->ttl, rtcphdr->window);
					printf("seq=%d - ack_seq=%d - 
					doff=%d - check=%d - ID=%d\n\n", 
					rtcphdr->seq, rtcphdr->ack_seq, 
					rtcphdr->doff, rtcphdr->check, 
					riphdr->id);

					return;
				}
			}
		}

		close(rs);
		close(s);
	}

	return;
}

unsigned short cksum(unsigned short *ptr,int nbytes)
{
	register long sum;
	unsigned short oddbyte;
	register unsigned short anwser;

	sum = 0;
	while(nbytes>1)
	{
		sum += *ptr++;
		nbytes -= 2;
	}
	if(nbytes==1)
	{
		oddbyte = 0;
		*((unsigned char *) & oddbyte) = *(unsigned char *)ptr;
		sum += oddbyte;
	}
	sum = (sum >> 16) + (sum & 0xffff);
	sum += (sum >> 16);
	anwser = ~sum;
	return(anwser);
}

void usage(char *nom) {
	printf("Usage: %s [s|a|r|p|u|f] [-x host_source] -d host_destination 
		-c port\n", nom);
	printf("\t-s SYN flag enabled\n");
	printf("\t-a ACK flag enabled\n");
	printf("\t-r RST flag enabled\n");
	printf("\t-p PSH flag enabled\n");
	printf("\t-u URG flag enabled\n");
	printf("\t-f FIN flag enabled\n");
	exit(-1);
}
-------------------------------------------8<---------------------------------


--- checkicmp.c ---------------------------8<---------------------------------
// checkicmp.c
// By Pepelux

#include <netinet/in.h>
#include <netinet/ip_icmp.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>

#define Error(msg) { perror(msg); exit -1; }


int main(void) {
	int s;
        struct sockaddr_in dir = {AF_INET, 0, 0 };
        char buff[1024];
        int len = sizeof(dir);
        struct icmphdr *rec = (struct icmphdr*) (buff + sizeof(struct iphdr));

        if (geteuid() != 0) {
                printf("You must be root\n");
                exit(0);
	}
	
	if ((s = socket(AF_INET, SOCK_RAW, 1)) < 0)
		Error("socket");

        printf("Waiting data ...\n");

	while (1) {
        	bzero(buff, 1024);

        	while (recvfrom(s, buff, 1024, 0, (struct sockaddr_in*) &dir, 
        		&len) > 0)
			printf("Received:\ttype %d\tcode %d\n", rec->type, 
			rec->code);
        }
}
-------------------------------------------8<---------------------------------


--- sendudp.c -----------------------------8<---------------------------------
// sendudp.c
// By Pepelux

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>

#define Error(msg) { perror(msg); exit -1; }


main (int argc, char *argv[])
{
	int s;
	int dport;
	struct sockaddr_in addr_dest;

	if (argc!=3) {
		printf ("Usage: %s ip port\n", argv[0]);
		exit(0);
	}

	dport = atoi(argv[2]);

	printf ("Sending UDP packet to %s:%d\n",argv[1], dport);

	if ((s=socket(AF_INET,SOCK_DGRAM,0)) < 0)
		Error("socket");

	addr_dest.sin_addr.s_addr=inet_addr(argv[1]);
	addr_dest.sin_family=AF_INET;
	addr_dest.sin_port=htons(dport);

	if (sendto(s,"\n",1,0,(struct sockaddr*)&addr_dest,sizeof(struct 
		sockaddr_in)) < 0)
		Error("sendto");

	close (s);
}
-------------------------------------------8<---------------------------------




------[ 5.- References ]

I've wrotten here links of interesting pages that I've used to write this
paper:

Standards (RFCs):
TCP Protocol : http://www.rfc-editor.org/rfc/rfc793.txt
IP Protocol  : http://www.rfc-editor.org/rfc/rfc791.txt
UDP Protocol : http://www.rfc-editor.org/rfc/rfc768.txt
ICMP Protocol: http://www.rfc-editor.org/rfc/rfc792.txt

Nmap documentation: http://insecure.org/nmap/man/


------[ 6.-End ]

Well, yes, all that is maked by nmap, more beutiful, more quickly and better
... but, this is funny, isn't it? :P

Nmap has a lot of scan methods but sometimes the response is unforseeable,
depending of the operating system running. For that is necessary to scan
manually, furthermore you'll leave less logs and you'll learn more.

Regards!!!


Pepelux <pepelux@enye-sec.org>
http://www.enye-sec.org


=-|================================================================ EOF =====|

# milw0rm.com [2008-09-14]