=-|================================================-{ www.enye-sec.org }-====| =-[ Playing with sockets (port scanning) ]-==================================| =-|==========================================================================| =-[ by Pepelux ]-======================-[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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 #include #include #include #include #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 #include #include #include #include #include #include #include #include #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 http://www.enye-sec.org =-|================================================================ EOF =====|