Diamantopoulos on Press

unzip;strip;touch;grep;finger;mount;fsck;more;yes;umount;sleep; Linux is so sexy…

Coding a Syn Scanner

Coding a Syn Scanner

From pcgrp @ ceid , by ithilgore ** ithilgore.ryu.L@gmail.com

Στο παρών άρθρο θα αναλύσουμε τη διαδικασία προγραμματισμού ενός Port Scanner που χρησιμοποιεί πιο stealthy μεθόδους για να
scannarei τον host που επιθυμούμε. Kοινώς θα δούμε πως φτιάχνεται ένας Syn Scanner. Το θέμα δεν είναι τόσο το αποτέλεσμα, καθώς στην κοινότητα του open source υπάρχουν ήδη αρκετά ποιοτικά tools που έχουν τη δυνατότητα τέτοιας λειτουργίας ( βλ. Nmap ). Το θέμα είναι η διαδικασία κατασκευής του εργαλείου , η οποία όπως θα δούμε παρακάτω περιλαμβάνει πολλά ενδιαφέροντα
κομμάτια του τομέα του security.
Ποιά είναι αυτά με λίγα λόγια?

–Raw Sockets
–Libpcap / Sniffing
–Tcp/ip header analysis
–Το ίδιο το SYN Scanning

To άρθρο αυτό όπως καταλαβαίνετε δεν έχει ως σκοπό απλά να δώσει τον κώδικα του SYN Scanner ( κάτι τέτοιο ,μόνο του,άλλωστε δεν έχει νόημα καθώς όπως αναφέραμε υπάρχουν ήδη ποιοτικότερα αντίστοιχα tools με ανοιχτό κώδικα ) αλλά να δώσει κάποιες κατευθυντήριες γραμμές για κάποιον που θέλει να μπεί στα βαθειά του Network Programming (σε Unix περιβάλλον) μέσα από τον προγραμματισμό και την ανάλυση δημιουργίας του συγκεκριμένου χρήσιμου εργαλείου. Αυτό σημαίνει ότι μαζί με το guide αυτό θα πρέπει συχνά να ανατρέξετε σε man pages,RFCs (ω ναι!) κλπ καθώς και να εκτελέσετε παράλληλα άλλα εργαλεία όπως το tcpdump.

Για να μπορέσει κάποιος να παρακολουθήσει τη ροή του παρόντος guide θα πρέπει να:
a) κατέχει αρκετά καλά την C
b) έχει κάνει μια εισαγωγή σε network programming (πχ http://beej.us/guide/bgnet/ )
c) διαθέτει κάποιες βασικές γνώσεις δικτύων
d) διαθέτει κάποιο box με εγκατεστημένο unix-flavored OS (root priviledges required)ώστε να μπορεί να πειραματιστεί με τον κώδικα
e) έχει κατά προτίμηση και κάποιο δεύτερο box διαθέσιμο (είτε ως virtual machine είτε ως real)
f) έχει αρκετό χρόνο καθώς αρκετές έννοιες είναι δυσκολο-χώνευτες σε πρώτη φαση

Να σημειωθεί ότι ο κώδικας έχει δοκιμαστεί επιτυχώς σε περιβάλλον Slackware 11 με kernel 2.4.33.3

…γιατί τα εύσυμα πρέπει να πηγαίνουν εκεί που αξίζουν η συνέχεια του άρθρου στην πηγή του!

… διαφορετικά εδώ :

------------------
0x2. SYN Scanning
------------------
Το SYN Scanning βασίζεται σε μια απλή μέθοδο που λέγεται half-open connection. Κατά τα καθιερωμένα, όταν 2 υπολογιστές
επικοινωνούν μέσω του TCP, ακολουθεί η παρακάτω διαδικασία:

a) client ———-SYN J————–> server
b) client <——–SYN K / ACK J+1—— server
c) client ——–ACK K+1 ————-> server

To παραπάνω λέγεται ως γνωστόν 3-way handshake καθώς περνά από 3 στάδια χειραψίας:
a)Αρχικά ο client στέλνει ένα TCP πακέτο στο port του server με μία αρχική ακολουθία (συνήθως random).
b)Σε 2η φάση αν ο server δέχεται connections στο συγκεκριμένο port ,θα στείλει ως απάντηση στον client ένα TCP πακέτο με μια
ακολουθία ACK ίση με το SYN+1 του client , καθώς και μια δική του καινούγια ακολουθία SYN.
c)Τέλος ο client θα απαντήσει στον server με ACK αυξημένη κατά 1 σε σχέση με το SYN που έστειλε ο server στο b) μέρος.Σε περίπτωση που το port του server είναι κλειστό , τότε ο server στο 2ο βήμα στέλνει ένα πακέτο RST και η χειραψία τελειώνει
εκεί.

Ο παραπάνω τρόπος είναι ο πλέον κλασσικός για ένα κοινό port-scanner , το οποίο scannarei τα ports που ενδιαφέρουν τον
attacker δοκιμάζοντας να ανοίξει στο καθένα από αυτά ένα connection μέσω του απλού 3-way handshake. Τι πιό ωραίος τρόπος για
να καταγραφεί από το firewall ή το IDS που τρέχει το victim ?

Μια καλύτερη λύση έρχεται να δώσει το half-open connection :

a) client ———-SYN J————–> server
b) client <——–SYN K / ACK J+1—— server
c) client ———-RST —————> server

Ποιά είναι η μόνη διαφορά ? Όπως καλά παρατηρήσατε είναι το τελευταίο βήμα στο οποίο αφού πλέον ο client γνωρίζει ότι ο
server του έχει στείλει θετική απάντηση , αντί να ανοίξει πλήρως το connection (το γνωστό state: Connection Established),
στέλνει ένα πακέτο RST τερματίζοντας πρόωρα τη σύνδεση.

Ποιό το όφελος ? Μια μικρότερη πιθανότητα να γίνει alarm οποιοσδήποτε μηχανισμός ασφαλείας στο victim καθώς ποτέ δεν
ανοίγεται πλήρως το connection. Αυτή είναι ουσιαστικά και η Stealth δυνατότητα που έρχεται να χρησιμοποιήσει ο SYN scanner.
Λεπτομέρειες για το πως γίνεται παρακάτω.

—————————-
0x3. TCP/IP header analysis
—————————-

Καιρός να δούμε πιο αναλυτικά τι ακριβώς γίνεται πίσω από τα παρασκήνια. Σε αυτό το σημείο ίσως θα ήταν μια καλή ευκαιρία
να ανατρέξουμε παράλληλα και στα RFCs 791 (Internet Protocol) και 793(Τransmission Control Protocol) καθώς οι γνώσεις τους
θα μας φανούν χρήσιμες.


Το format του ΤCP header φαίνεται παρακάτω. Ουσιαστικά μέχρι το σημείο του Urgent Pointer είναι
η απαραίτητη πληροφορία που πρέπει να έχει οποιδήποτε TCP πακέτο , δηλαδή σύνολο 20 bytes σε μέγεθος.0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Ας δούμε πιο αναλυτικά τι είναι το κάθε επιμέρους τμήμα του header:

—— —– ————
FIELD SIZE DESCRIPTION
—— —– ————

Source Port: (16bits) Ο αριθμός του Port του client
Destination Port: (16bits) O αριθμός του Port του server(destination)
Sequence Number: (32bits) O αριθμός ακολουθίας που χρησιμοποιείται για το enumeration των ΤCP πακέτων
(βλ 3-way handshake)
Acknowledgment Number: (32bits) O αριθμός απάντησης στο προηγούμενο SYN. Ισχυεί ότι ACK = previous_SYN + 1
Data Offset: (4 bits) O αριθμός που προσδιορίζει το μέγεθος του header.ΠΡΟΣΟΧΗ!-> μετριέται σε πολλαπλάσια
των 32bits/4bytes. Αυτό σημαίνει πως όταν δεν έχει το TCP πακέτο options, ο
data_offset = 5 .
Reserved: (6 bits) Απλά reserved , τα bits αυτά είναι όλα 0.

Flags: (6 bits) Το κάθε flag δηλώνει μια ξεχωριστή κατάσταση.(1 bit each -on/off)
URG: Urgent: για γρήγορη δρομολόγηση
ACK: Αcknowledgment: για τη 2η και 3η φάση του 3way TCP handshake
PSH: Push: το σύστημα δεν κάνει buffer το segment στο ΙP stack
RST: Reset: για τον άμεσο τερματισμό μιας σύνδεσης
SYN: Synchronization: για καινούργια σύνδεση και TCP handshake
FIN: Final: για το κανονικό κλείσιμο μιας σύνδεσης ( βλ TCP termination)

Window: (16bits) H μέγιστη ποσότητα δεδομένων που θα λάβει ως απάντηση ο client
Checksum: (16bits) To checksum του πακέτου ,το οποίο θα αναλύσουμε παρακάτω.
Urgent Pointer: (16bits) Xρησιμοποιείται μαζί με το urgent flag.


Αυτά είναι με λίγα λόγια τα fields του TCP header που μας ενδιαφέρουν και θα μας απασχολήσουν παρακάτω όταν θα
κατασκευάσουμε το δικό μας datagram. Δεν αναλύουμε το καθένα από αυτά σε βάθος καθώς δεν είναι αυτός ο σκοπός μας.
Αντί αυτού θα εστιάσουμε σε ορισμένα από αυτά στη συνέχεια, τα οποία είναι ζωτικής σημασίας για το SYN scanner μας.
Για περισσότερες λεπτομέρειες καλείστε να δείτε τα αντίστοιχα RFCs.Ας δούμε όμως και σε επίπεδο κώδικα πως θα ήταν ένα τέτοιο TCP header:

/* TCP header */
typedef u_int tcp_seq;struct sniff_tcp {
u_short th_sport; /* source port */
u_short th_dport; /* destination port */
tcp_seq th_seq; /* sequence number */
tcp_seq th_ack; /* acknowledgement number */
u_char th_offx2; /* data offset, rsvd */
#define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4)
u_char th_flags;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
#define TH_ECE 0x40
#define TH_CWR 0x80
#define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
u_short th_win; /* window */
u_short th_sum; /* checksum */
u_short th_urp; /* urgent pointer */
};

Όπως παρατηρούμε υπάρχει 1 προς 1 αντιστοιχία των fields του header με τα fields του struct μας.
Να σημειωθεί πως το struct αυτό δεν είναι το κλασσικό BSD-flavored αλλά το προτεινόμενο στυλ από
τον Tim Carstens -> http://www.tcpdump.org/pcap.htm ( στην pcap θα αναφερθούμε αργότερα αν σας κίνησε
την περιέργεια το link )

Λίγη ακόμη υπομονή να δούμε και τον IP header και στην συνέχεια θα δούμε live παράδειγμα με το tcpdump!

O IP header λοιπόν:

0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

—— —– ————
FIELD SIZE DESCRIPTION
—— —– ————

Version: (4 bits) Η version του header. Μιλάμε για IPv4 άρα 4.
IHL: (4 bits) Header length: σε 32bit words!! Συνεπώς το min_value = 5 (πριν τα Options)
Type of Service: (8 bits) Xρησιμοποιείται για priorities σε συγκεκριμένα services κάποιων δικτύων
Total Length: (8 bits) Total datagram μήκος (σε bytes), που συμπεριλαμβάνει και τον ΤCP header.
Μας δείχνει που ξεκινά το payload.
Identification: (8 bits) Ένα unique value του sender για την περίπτωση reassembly ενός fragmented packet.
Flags: (3 bits) bit 0: reserved 0 , bit 1:DF (don’t fragment) , bit 2:MF(more fragments) σειρά:
b0,b1,b2
Fragment Offset: (13bits) Χρήση για reassembly fragmented packet.
Time to Live: (8 bits) Πόσα hops(routers) μπορεί να περάσει το πακέτο πριν γίνει discarded. max_value = 255
Protocol: (8 bits) /etc/protocols για info , tcp = 6 , udp = 17, icmp =1
Header Checksum: (16bits) To checksum όλου του datagram. Θα μας απασχολήσει αργότερα
Source Address: (32bits) To IP του sender
Destination Address: (32bits) To IP του receiver


Με ίδια λογική όπως στο tpc header , γράφουμε και την struct του ip header:/* IP header */
struct sniff_ip {
u_char ip_vhl; /* version << 4 | header length >> 2 */
u_char ip_tos; /* type of service */
u_short ip_len; /* total length */
u_short ip_id; /* identification */
u_short ip_off; /* fragment offset field */
#define IP_RF 0x8000 /* reserved fragment flag */
#define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
u_char ip_ttl; /* time to live */
u_char ip_p; /* protocol */
u_short ip_sum; /* checksum */
struct in_addr ip_src,ip_dst; /* source and dest address */
};
#define IP_HL(ip) (((ip)->ip_vhl) & 0x0f)
#define IP_V(ip) (((ip)->ip_vhl) >> 4)

Οι 2 παραπάνω headers αφορούν στο TCP/IP , όμως δεν πρέπει να ξεχνάμε ότι στο data link layer έχουμε και κάποια NICs που
μπαίνουν στη μέση. ΝICs ε ? Μα φυσικά δεν θα μπορούσαμε να παραλείψουμε να αναφέρουμε το ethernet και τις unique MAC
addresses που έχει η κάθε κάρτα δικτύου. Γι’αυτό θα εξετάσουμε άλλο ένα (μικρό) header που θα μπει στο πακέτο μετά. Το
συγκεκριμένο δεν θα μας απασχολήσει άμεσα στη συνέχεια , καλό ομως είναι να το γνωρίζουμε. Έτσι έχουμε:

/* ethernet headers are always exactly 14 bytes */
#define SIZE_ETHERNET 14

/* Ethernet addresses are 6 bytes */
#define ETHER_ADDR_LEN 6

/* Ethernet header */
struct sniff_ethernet {
u_char ether_dhost[ETHER_ADDR_LEN]; /* destination host address */
u_char ether_shost[ETHER_ADDR_LEN]; /* source host address */
u_short ether_type; /* IP? ARP? RARP? etc */
};

Τα πράγματα εδώ είναι απλά:

—— —– ————
FIELD SIZE DESCRIPTION
—— —– ————

ether_dhost (6bytes) Η MAC address του destination
ether_shost (6bytes) H ΜΑC address του sender
ether_type (2bytes) To πρωτόκολλο που υπάρχει “πάνω” από το ethernet (εδώ θα έχουμε IP)

O καλύτερος τρόπος για να καταλάβει και να εμπεδώσει κανείς τις παραπάνω έννοιες είναι με ένα πραγματικό παράδειγμα.

Ανοίγουμε λοιπόν 2 terminals και στο πρώτο γράφουμε:

root@hyena:/home/# tcpdump -i eth0 -l -n -x -vv ( όπου eth0 το name του δικού σας NIC )

ενώ στο άλλο κάνουμε telnet σε έναν host που γνωρίζουμε πως έχει ανοιχτό port 80 (ή κάποιο άλλο)
To web interface του router μας είναι ένα καλό παράδειγμα.

ithilgore@hyena:~$ telnet 10.0.0.2 80
Trying 10.0.0.2…
Connected to 10.0.0.2.


Η έξοδος του tcpdump θα είναι κάπως έτσι:tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes

19:46:00.774299 IP (tos 0x10, ttl 64, id 17418, offset 0, flags [DF], proto: TCP (6), length: 60)
10.0.0.4.39507 > 10.0.0.2.80: S, cksum 0x8691 (correct), 1121958480:1121958480(0) win 5840 <mss 1460,
sackOK,timestamp 7536218 0,nop,wscale 0>
0x0000: 4510 003c 440a 4000 4006 e29c 0a00 0004
0x0010: 0a00 0002 9a53 0050 42df ba50 0000 0000
0x0020: a002 16d0 8691 0000 0204 05b4 0402 080a
0x0030: 0072 fe5a 0000 0000 0103 0300

19:46:00.775223 IP (tos 0x0, ttl 64, id 14712, offset 0, flags [none], proto: TCP (6), length: 60)
10.0.0.2.80 > 10.0.0.4.39507: S, cksum 0xb576 (correct), 448057277:448057277(0) ack 1121958481 win 8192
<mss 1460,nop,wscale 0,nop,nop,timestamp 188518 7536218>
0x0000: 4500 003c 3978 0000 4006 2d3f 0a00 0002
0x0010: 0a00 0004 0050 9a53 1ab4 cfbd 42df ba51
0x0020: a012 2000 b576 0000 0204 05b4 0103 0300
0x0030: 0101 080a 0002 e066 0072 fe5a

19:46:00.775264 IP (tos 0x10, ttl 64, id 17419, offset 0, flags [DF], proto: TCP (6), length: 52)
10.0.0.4.39507 > 10.0.0.2.80: ., cksum 0xea69 (correct), 1:1(0) ack 1 win 5840 <nop,nop,timestamp 7536219 188518>
0x0000: 4510 0034 440b 4000 4006 e2a3 0a00 0004
0x0010: 0a00 0002 9a53 0050 42df ba51 1ab4 cfbe
0x0020: 8010 16d0 ea69 0000 0101 080a 0072 fe5b
0x0030: 0002 e066

3 πακέτα … τι μας θυμίζει? Όπως ήδη μαντέψατε είναι το 3way handshake του TCP.
Ας πάρουμε μία μία τις τιμές των πακέτων για να βρούμε ποιά είναι η αντιστοιχία με τα
headers που είδαμε πιο πάνω.

1o πακέτο: ( ξεκινά με το IP header )

0x0000: 4510 003c 440a 4000 4006 e29c 0a00 0004

4510: 4 = version , 5 = header length , 10 = type of service ,
003c: 003c = total length ( 0x3c = 60d ) άρα ξέρουμε που αρχίζει το payload
440a: 440a = identification
4000: 4006 = 010 | 0 0000 0000 0110 όπου τα 3 MSB είναι το flag field ( άρα έχουμε DF flag που το βλέπουμε
και στο tcpdump ) – τα υπόλοιπα 13 bit είναι το fragment offset field
4006: 40 = time to live ( 64d ) , 06 = protocol number ( το TCP είπαμε είναι 6d )
e29c: το header checksum
0a00: Το πρώτο σκέλος της Source IP address ( 10.0 )
0004: To δεύτερο και τελευταίο σκέλος της Source IP address ( 0.4 ) άρα 10.0.0.4

0x0010: 0a00 0002 9a53 0050 42df ba50 0000 0000

0a00: Πρώτο μέρος Destination IP address ( 10.0 )
0002: Δεύτερο μέρος Destination IP address ( 0.2 ) άρα 10.0.0.2
—————–τέλος του IP header/ αρχή του TCP header————————–
9a53: Source Port ( 0x9a53 = 39507d )
0050: Destination Port ( 0x0050 = 80d )
42df: To πρώτο μέρος του sequence number ( στέλνουμε SYN )
ba50: To δέυτερο μέρος του seq number , τελικά: seq = 0x42df ba50 = 1121958480d
0000: Πρώτο μέρος ACK
0000: Δεύετερο μέρος ACK: δεν έχουμε ACK αφού στο πρώτο πακέτο κάνουμε μόνο initiate το connection

0x0020: a002 16d0 8691 0000 0204 05b4 0402 080a

a002: a = data offset (εδώ λόγω telnet έχουμε κάποια options συνεπώς 0xa = 10d != 5d = min_value(no options/data)),
002 = 0000 00 | 00 0010 τα 6 MSB είναι τα reserved 0 , τα υπόλοιπα τα flags όπου έχουμε στο 2o LSB ON
(SYN flag)
16d0: window size
8691: datagram checksum ( κρατήστε το για μετά )
0000: urgent pointer , (δεν υφίσταται στην προκειμένη περίπτωση)
—————ακολουθούν options και data τα οποία δεν θα μας απασχολήσουν——–
0204: <>
05b4: <>
0402: <>
080a: <>

Αναλύοντας τα παραπάνω βλέπουμε την αντιστοιχία με τα headers που μελετήσαμε προηγουμένως.

Aς δούμε και κάποια πράγματα από τα 2 επόμενα πακέτα για να επαληθεύσουμε το 3way handshake :

2o πακέτο:

0x0000: 4500 003c 3978 0000 4006 2d3f 0a00 0002
0x0010: 0a00 0004 0050 9a53 1ab4 cfbd 42df ba51
0x0020: a012 2000 b576 0000 0204 05b4 0103 0300
0x0030: 0101 080a 0002 e066 0072 fe5a


Εδώ οι τιμές είναι λίγο πολύ παρόμοιες. Εκεί που θα πρέπει να εστιάσει κανείς είναι στο 42df ba51
Μήπως μοιάζει με κάτι που είδαμε πιο πριν ? Είναι το προηγούμενο Syn Sequence + 1 και βρίσκεται
στη θέση που αντιστοιχεί στο Acknowledgment Number του TCP header. Επαληθεύτηκε το ACK = previousSYN + 1 .
Πριν από αυτό ας κρατήσουμε στη μνήμη μας ( ή στο clipboard για όσους δεν μπορούν :P ) τον αριθμό 1ab4 cfbd
ο οποίος αντιστοιχεί στο Sequence Number του παρόντος πακέτου.Πάμε να δούμε και το 3ο πακέτο και συγκεκριμένα τα αντίστοιχα values ( seq και ack )

3o πακέτο:

0x0000: 4510 0034 440b 4000 4006 e2a3 0a00 0004
0x0010: 0a00 0002 9a53 0050 42df ba51 1ab4 cfbe
0x0020: 8010 16d0 ea69 0000 0101 080a 0072 fe5b
0x0030: 0002 e066

Εδώ έχουμε Sequence Number = 42df ba51 = previous_ACK (όχι η ουσία) και επίσης
ACK = 1ab4 cfbe = previoysSYN + 1

Μήπως να θυμίσουμε πάλι το σχέδιο ?

a) client ———-SYN J————–> server
b) client <——–SYN K / ACK J+1—— server
c) client ——–ACK K+1 ————-> server

Αυτά από την ανάλυση των TCP/IP πακέτων. Μην διστάσετε να ξαναδιαβάσετε όσα σημεία δεν καταλάβατε και να πειραματιστείτε
και με άλλα παραδείγματα με το tcpdump. (διαβάζοντας παράλληλα και τα man pages του)

—————–
0x4. Raw Sockets
—————–

Για να κατασκευαστεί ένας SYN Scanner θα χρειαστεί να στείλουμε explicitly ένα δικό μας πακέτο με SYN flag on και στη
συνέχεια να εξετάσουμε κατά πόσο η απάντηση είναι ACK ή RST.
Πώς θα κατασκευάσουμε δικό μας datagram ? Enter Raw Sockets.

Tα Raw Sockets δεν είναι τίποτα άλλο από την δυνατότητα κατασκευής ενός δικού μας Network Datagram ( ICMP ,TCP,IP,IGMP )
Γνωρίζουμε πως το πρωτόκολο TCP αντιστοιχεί στο Transport Layer του μοντέλου ΟSI ενώ το IP στο Νetwork Layer τα οποία
διαχειρίζεται , κανονικά , ο ίδιος ο πυρήνας του λειτουργικού συστήματος.
Όταν το application θέλει να ανοίξει ένα connection με έναν server , τότε ο πλέον κλασσικός τρόπος
για να το κάνει είναι το sockets interface τα οποία μας επιτρέπουν σε κάποιο περιορισμένο βαθμό να θέσουμε
κάποιες από τις συνθήκες και παραμέτρους υπό τις οποίες θα πραγματοποιηθεί το connection αυτό.

Σχηματικά έχουμε:

OSI TCP

Layer 7: Application layer -application- |
Layer 6: Presentation layer -application- <-| user process
Layer 5: Session layer -application- __________________________ sockets interface / raw sockets–|
Layer 4: Transport layer <———> TCP/UDP | <——-|
Layer 3: Network layer <———> IPv4/IPv6 <-| kernel
Layer 2: Data Link layer <———> BPF/DLPI/drivers |
Layer 1: Physical layer hardware

Τα raw sockets αντίθετα όμως με το απλό interface μας δίνουν τη δυνατότητα να προσδιορίσουμε εμείς όπως θέλουμε το
datagram που θα στείλουμε.

Ορίζουμε ένα raw socket ως εξής:

int sockfd;
sockfd = socket(AF_INET, SOCK_RAW, protocol);

όπου το protocol είναι μία από τις σταθερές του <netinet/in.h> header

Επίσης είναι απαραίτητο να θέσουμε ως socket option το IP_HDRINCL το οποίο μας επιτρέπει να ορίσουμε όπως θέλουμε το
IP header.

const int on;
if ( setsockopt ( sockfd , IPPROTO_IP , IP_HDRINCL , &on , sizeof(on)) < 0 )
error

Να σημειωθεί πως για τη δημιουργία raw socket χρειαζόμαστε root priviledges.


Από τη στιγμή που ξέρουμε τα format των headers που είδαμε στο section 0x3 θα είναι σχετικά απλό να δημιουργήσουμε το
datagram που θέλουμε:int sockfd,i;
struct sockaddr_in sin;
char datagram[4096]; // buffer for datagrams
struct sniff_ip *iph = (struct sniff_ip *) datagram;
struct sniff_tcp *tcph = (struct sniff_tcp *) (datagram + sizeof (struct sniff_ip));

sockfd = socket (AF_INET, SOCK_RAW, IPPROTO_TCP) ;

sin.sin_family = AF_INET;
sin.sin_port = htons (i);

memset (datagram, 0, 4096); /* zero out the buffer */
iph->ip_vhl = 0x45; /* version=4,header_length=5 (no data) */
iph->ip_tos = 0; /* type of service not needed */
iph->ip_len = sizeof (struct sniff_ip) + sizeof (struct tcphdr); /* no payload */
iph->ip_id = htonl (54321); /*simple id*/
iph->ip_off = 0; /*no fragmentation*/
iph->ip_ttl = 255; /*Time to Live -> set maximum value*/
iph->ip_p = IPPROTO_TCP; /* 6 as a value – see “/etc/protocols” */
iph->ip_src.s_addr = ipP->sin_addr.s_addr; /*local device IP */
iph->ip_dst.s_addr = sin.sin_addr.s_addr; /*destination address*/
iph->ip_sum = 0; /*no need to fill ip checksum- kernel does that*/
tcph->th_sport = htons (1234); /* arbitrary port */
tcph->th_dport = htons (i); /* scanned destination port */
tcph->th_seq = random (); /* the random SYN sequence */
tcph->th_ack = 0; /* No ACK needed */
tcph->th_offx2 = 0x50; /* 50h (5 offset) ( 8 0s reserverd )*/
tcph->th_flags = TH_SYN; /* initial connection request FLAG*/
tcph->th_win = (65535); /* maximum allowed window size*/
tcph->th_sum = 0; /* will compute later */
tcph->th_urp = 0; /* no urgent pointer */

{
int one = 1;
const int *val = &one;
if (setsockopt (sockfd, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0)
printf (“Warning: Cannot set HDRINCL for port %d\n”,i);
}

Αφού το datagram μας είναι έτοιμο , το μόνο που μένει είναι να το στείλουμε :

if (sendto (sockfd ,datagram, iph->ip_len , 0, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
printf (“Error sending datagram for port %d\n”,i);
}

Εδώ χρησιμοποιήσαμε απλά μια τιμή i για το port του victim. Βάζοντας τα παραπάνω σε ένα loop μπορούμε να στείλουμε SYN
πακέτα σε όσα ports ενδιαφερόμαστε.

Κάτι λείπει όμως ακόμα… γιατι κάποια σημαντική τιμή την αφήσαμε ξεκρέμαστη και χωρίς αυτή το datagram μας θα γίνει drop
όποιος και αν είναι ο receiver !! Για όσους δεν καταλάβαν ακόμα, μιλάμε για το TCP checksum field.

Ο αλγόριθμος περιγράφεται αναλυτικά από το RFC 1071 αλλά με λίγα λόγια είναι ο εξής:

Σχηματίζουμε 16bit λέξεις από τα bytes που θα γίνουν checksumed και υπολογίζουμε το άθροισμα τους σε συμπλήρωμα ως
προς 1 ( 1’s complement ).
Αυτό ακριβώς υλοποιεί η παρακάτω συνάρτηση:

uint16_t checksum_comp ( uint16_t *addr , int len ) { /* compute TCP header checksum */
/* with the usual algorithm a bit changed */
/* for byte ordering problem resolving */
/* RFC 1071 for more info */
/* Compute Internet Checksum for “count” bytes
* beginning at location “addr”.
*/
register long sum = 0;
int count = len;
uint16_t temp;


while( count > 1 ) {temp = htons(*addr++); // in this line:added -> htons
sum += temp;
count -= 2;
}

/* Add left-over byte, if any */
if( count > 0 )
sum += * (unsigned char *) addr;

/* Fold 32-bit sum to 16 bits */
while (sum>>16)
sum = (sum & 0xffff) + (sum >> 16);

uint16_t checksum = ~sum;
return checksum;
}

Αυτό που δεν αναφέρεται γενικά τόσο ξεκάθαρα είναι ότι το TCP checksum δεν πρέπει να υπολογιστεί μόνο πάνω στον tcp header
αλλά ΠΡΕΠΕΙ να υπολογιστεί μαζί με ένα pseudo-header που περιέχει τις εξής πληροφορίες:

struct pseudo_hdr {
u_int32_t src; /* 32bit source ip address*/
u_int32_t dst; /* 32bit destination ip address */
u_char mbz; /* 8 reserved bits (all 0) */
u_char proto; /* protocol field of ip header */
u_int16_t len; /* tcp length (both header and data */
};

Συνεπώς θα πρέπει να γράψουμε:

struct pseudo_hdr *phdr = (struct pseudo_hdr *) ( datagram +
sizeof(struct sniff_ip) + sizeof(struct sniff_tcp) ) ;

phdr->src = iph->ip_src.s_addr;
phdr->dst = iph->ip_dst.s_addr;
phdr->mbz = 0;
phdr->proto = IPPROTO_TCP;
phdr->len = ntohs (0x14);

tcph->th_sum = htons ( checksum_comp ( (unsigned short *) tcph ,
sizeof(struct pseudo_hdr)+sizeof(struct sniff_tcp)));

Προσοχή θα πρέπει να δώσουμε στα fields που χρειάζεται να μετατραπούν σε network byte order (big endian) από host byte
order ( little endian or big endian ανάλογα το box ) καλώντας τις αντίστοιχες συναρτήσεις htonl ή htons καθώς και τις
ανάποδες ntohs ή ntohl. Μερικές φορές μπορεί να χρειαστεί να πειραματιστούμε, όπως ο γράφων όταν υπολόγιζε το checksum
με το χέρι.

Εδώ είναι ένα καλό σημείο να χρησιμοποιήσετε τις τιμές που μας είχε δώσει το tcpdump στο προηγούμενο section για το tcp
checksum field και να το επαληθεύσετε υπολογίζοντας manually τα 1’s sums των bytes των tcp + pseudo headers.

———————————
0x5. Η libpcap /sniffing session
———————————

Αφού στείλαμε και τα SYN πακέτα θα πρέπει να εξετάσουμε την απάντηση του victim για να καταγράψουμε ποια ports έχει ανοιχτά.
Το ερώτημα που τίθεται είναι αν μπορούμε να το κάνουμε με παρόμοια λογική που χρησιμοποιούμε στις απλές sockets. H απάντηση
έιναι όχι.

Ο kernel δεν περνάει ποτέ TCP ( και UDP ) πακέτα σε raw socket. Αυτό σημαίνει πως δεν θα μπορέσουμε να διαβάσουμε την
απάντηση του victim με κάποιον τόσο παραδοσιακό τρόπο όπως το sockets API.
Θα χρειαστούμε access στο ίδιο το datalink layer!

Oι 3 κοινές μέθοδοι για να γίνει κάτι τέτοιο σε Unix περιβάλλον είναι οι:
a) BPF (berkley packet filter)
b) DLPI (SVR4 Datalink Provider Interface)
c) SOCK_PACKET interface στο Linux

Το κακό με αυτές είναι ότι είναι platform-dependent και συνεπώς μειώνουν το portability.
Εδώ έρχεται να δώσει τη λύση η libpcap – the packet capture library.
Τώρα είναι η καταλληλη στιγμή να επισκεπτείτε το link που είχαμε δώσει πριν ως credit στον Tim Carstens:

http://www.tcpdump.org/pcap.htm

To ίδιο το tcpdump έχει γραφτεί με την βιβλιοθήκη αυτή , απόδειξη προς τις δυνατότητες που μας προσφέρει.

Για να συνεχίσει κανείς ομάλα θα πρέπει να ρίξει μια καλή ματία και στα man pages της βιβλιοθήκης
(online version εδώ-> http://www.tcpdump.org/pcap3_man.html) και κυρίως για τις συναρτήσεις:

int pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf)
char *pcap_lookupdev(char *errbuf)
int pcap_lookupnet(const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf)
int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask)
int pcap_setfilter(pcap_t *p, struct bpf_program *fp)
pcap_t *pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *errbuf)
int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
void pcap_breakloop(pcap_t *)

Tο πρώτο πράγμα που πρέπει να κάνουμε είναι να βρούμε ποια devices είναι διαθέσιμα στο δικό μας box και να χρησιμοποιήσουμε
ένα από αυτά για sniffing στο datalink layer.Ίσως καταλαβάτε ήδη ποια είναι η κατάλληλη function για αυτή τη δουλειά:

pcap_if_t *alldev;

if ((pcap_findalldevs (&alldev, errbuf)) == -1) {
printf ( “%s\n” , errbuf );
exit(-1);
}

Αφού βρούμε κάποιο device και μια διαθέσιμη IP που του αντιστοιχεί (το default case συνήθως είναιάλλωστε ένα IP ανά device),
μπορούμε να ξεκινήσουμε το sniffing session καλώντας την

pcap_t *pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *errbuf)

η οποία επιστρέφει δείκτη σε pcap session id που ορίζεται ως pcap_t.
Τα ορίσματα της function μπορεί να τα μαντέψει κανέις μόνο από τα ονόματα:

char * device: το device που βρήκαμε πριν με την pcap_findalldevs() και θα χρησιμοποιήσουμε για το session
int snaplen: το maximum μέγεθος bytes του κάθε πακέτου που sniff-άρουμε – 65535 είναι υπέραρκετό
int promisc: 1 ή 0 , promiscuous ή όχι , hate mail από τον net admin ή όχι κλπ
int to_ms: read timeout σε ms για όσες πλατφόρμες το υποστηρίζουν -δεν μας απασχολεί
char *errbuf: o buffer για τα errors της libpcap

Αφού ανοίξαμε και το session πάμε να εφαρμόσουμε φιλτράρισμα.
Η μεγαλύτερη ίσως διευκόλυνση που μας προσφέρει η βιβλιοθήκη είναι τα φίλτρα ,που περιγράφονται αναλυτικά στα man pages
του tcpdump (online version εδώ-> http://www.tcpdump.org/tcpdump_man.html )
Φιλτράρουμε τo network traffic σε αυτό μας ενδιαφέρει και αναλύουμε τα εκάστοτε πακέτα για τα fields που μας ενδιαφέρουν.
Στην δική μας περίπτωση θέλουμε να δούμε την απάντηση του victim μόνο , συνεπώς το φίλτρο θα πρέπει να είναι της μορφής:

src host ip

όπου ip η διεύθυνση του victim.Προχωράμε στο compilation του φίλτρου:


int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask)pcap_t *p: το session μας
struct bpf_program *fp: η struct στην οποία θα αποθηκευτεί το compiled φίλτρο
char *str: το filter expression , κοινώς το src host ip
Tα υπόλοιπα 2 options δεν θα μας χρειαστούν ( int optimize , bpf_u_int32 netmask ) – 0 both

Το τελευταίο βήμα για την sniff engine είναι η εφαρμογή του φίλτρου:

int pcap_setfilter(pcap_t *p, struct bpf_program *fp)

τα 2 options μπορείτε να καταλάβετε και μόνοι σας τι είναι αν διαβάσατε καλά τα παραπάνω.

Τώρα που η engine είναι έτοιμη το μόνο που μένει να κάνουμε είναι να την βάλουμε να λειτουργήσει.
Ουσιαστικά αυτό γίνεται με μια από τις

int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user) ή
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)

Μπορείτε να ανατρέξετε στις man pages για να δείτε την λεπτή διαφορά τους που όμως δεν είναι
σημαντική για τον scanner.
O pcap_handler είναι η συνάρτηση που έχουμε ορίσει να επεξεργάζεται το εκάστοτε πακέτο που κάνει
capture η engine. Μήπως ξεχάσαμε τον σκοπό μας? Αυτό που θέλουμε να εξετάσουμε είναι κατά πόσο
το victim έχει απαντήσει με SYN και ACK FLAGS ON:

if ( ( (tcp->th_flags & 0x02) == TH_SYN) && ( tcp->th_flags & 0x10 ) == TH_ACK )) {
//PORT OPEN !!!
}

Σε αντίθετη περίπτωση το victim απαντά με RST οπότε μπορούμε να πούμε με σχεδόν απόλυτη σιγουριά ότι το port είναι κλειστό.
Υπάρχει βέβαια πάντα η πιθανότητα τα πακέτα να γίνουν DROP για 0xE2A λόγους (1002d) στην πορεία ή από τον ίδιο τον host
ή από firewall ή άλλο gateway , οπότε σε αυτήν την περίπτωση ορίζουμε την κατάσταση του port state ως unknown/filtered.
Έχουμε φροντίσει ωστόσο με ένα εσωτερικό timeout μέσω ενός SIGALRM που πυροδοτείται από μια alarm() και ενός signal handler
να ξεφεύγουμε από την ακινησία που αναγκαστικά έχει η κεντρική μας pcap_dispatch ( πρέπει να λειτουργεί σε blocking mode )
και να συνεχίζουμε το scan-άρισμα στα επόμενα ports.

To 3o βήμα του SYN scanning άραγε δεν χρειάζεται? Δεν θα’πρεπε να στείλουμε και ένα RST στον receiver αν μας απαντήσει
θετικά? Aπ’ότι φαίνεται εδώ μας βοηθάει ο ίδιος o kernel ο οποίος (όχι και τόσο παραδόξως) στέλνει αυτόματα RST πακέτο στο
victim στην περίπτωση αυτή. Προσοχή όμως γιατί αυτό μπορεί να διαφέρει από λειτουργικό σε λειτουργικό και να χρειαστεί
να το στείλουμε explicitly. In that case , δεν έχετε παρά να συμπληρώσετε με δικό σας κώδικα το explicit sending του TCP
datagram με RST on. Αν καταλαβάτε και μελετήσατε όλα τα παραπάνω , δεν θα πρέπει να σας φαίνεται πλέον ιδιαίτερα δύσκολο.

’λλο ένα κομμάτι του scanner τελείωσε. Καιρός να βάλουμε όλα τα κομμάτια μαζί.
Συνδυάζοντας λοιπόν όλα τα παραπάνω φτιάχνουμε τον SYN scanner μας.

———————————-
0x6. The SYN port scanner (source)
———————————-

/****************************************************************************/
/* SYN SCANNER -codename:creeper */
/* version 1.0 */
/* by ithilgore */
/* ithilgore.ryu.L@gmail.com */
/* */
/* compile with: gcc creeper.c -lpcap -o creeper */
/* some compilers may also need -fpack-struct */
/* */
/* Use of this code is for educational purposes only. I am not responsible */
/* for any illegal or criminal activities performed with this tool or */
/* any modifications of it. */
/* This tool is free and open software. This means you can do anything you */
/* like with it with your own responsibility and with no warranty from me. */
/* However this tag must not be removed even if any modification is made to */
/* to the source. In general I would really value: */
/* 1) any code improvement you make and send me */
/* 2) any information on bugs and flaws found */
/* 3) testing in other platforms */
/* 4) any kind of feedback and comments */
/* And finally,do not expect me to reply to lamers’/flame/idiots’ mails */
/* */
/* This tool has been tested so far and works sucessfully in: */
/* Slackware 11 with kernel 2.2.4.33 */
/* end_of_tag */
/****************************************************************************/

//#define __USE_BSD
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <netinet/ip.h>
//#include <netinet/in.h>
//#define __FAVOR_BSD
//#include <netinet/tcp.h>
#include <unistd.h>
#include <pcap.h>
#include <signal.h>

#define BUFSIZE 65535 //maximum size of any datagram(16 bits the size of identifier)
#define TRUE 1
#define FALSE 0
#define default_low 1
#define default_high 1024

/* change the timeout at will
* it defines how long the scanner will wait for
* an answer from the scanned host
* careful though – testing shows < 4 is bad
* it is just another factor between speed and accuracy
*/
#define DEFAULT_S_TIMEOUT 5

/* default snap length (maximum bytes per packet to capture) */
#define SNAP_LEN 1518

/* ethernet headers are always exactly 14 bytes [1] */
#define SIZE_ETHERNET 14

/* Ethernet addresses are 6 bytes */
#define ETHER_ADDR_LEN 6

/* USING TCPDUMP-like header structs */

/* Ethernet header */
struct sniff_ethernet {
u_char ether_dhost[ETHER_ADDR_LEN]; /* destination host address */
u_char ether_shost[ETHER_ADDR_LEN]; /* source host address */
u_short ether_type; /* IP? ARP? RARP? etc */
};

/* IP header */
struct sniff_ip {
u_char ip_vhl; /* version << 4 | header length >> 2 */
u_char ip_tos; /* type of service */
u_short ip_len; /* total length */
u_short ip_id; /* identification */
u_short ip_off; /* fragment offset field */
#define IP_RF 0x8000 /* reserved fragment flag */
#define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
u_char ip_ttl; /* time to live */
u_char ip_p; /* protocol */
u_short ip_sum; /* checksum */
struct in_addr ip_src,ip_dst; /* source and dest address */
};
#define IP_HL(ip) (((ip)->ip_vhl) & 0x0f)
#define IP_V(ip) (((ip)->ip_vhl) >> 4)

/* TCP header */
typedef u_int tcp_seq;

struct sniff_tcp {
u_short th_sport; /* source port */
u_short th_dport; /* destination port */
tcp_seq th_seq; /* sequence number */
tcp_seq th_ack; /* acknowledgement number */
u_char th_offx2; /* data offset, rsvd */
#define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4)
u_char th_flags;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
#define TH_ECE 0x40
#define TH_CWR 0x80
#define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
u_short th_win; /* window */
u_short th_sum; /* checksum */
u_short th_urp; /* urgent pointer */
};

/* pseudo header used for tcp checksuming
* a not so well documented fact … in public
*/

struct pseudo_hdr {
u_int32_t src;
u_int32_t dst;
u_char mbz;
u_char proto;
u_int16_t len;
};

/* Global Variables */
int verbose_mode;
int stealth_mode; /* Syn Scanning */
int s_timeout; /* timeout seconds for Syn Scanning */

/* Function Prototypes */

void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);
void print_usage( const char *argv );
int check_Port ( long * lport ,long * hport, char *optar );
uint16_t checksum_comp ( uint16_t *addr , int len );
pcap_t* EnginePreparing ( char * vicIP, struct sockaddr_in ** ipP, pcap_t *session );
struct hostent* host_resolve();
void Syn_Scanning();
void Connect_Scanning();
void sigfunc();

/* Function Prototypes end*/

/********************************MAIN PROGRAM********************************/

long int low_port = default_low;
long int high_port = default_high;
char *ipArg = NULL;

int main(int argc, char *argv[] ) {

if ( argc == 1 ) {
print_usage( argv[0] );
exit(0);
}

int opt;
while ( (opt = getopt(argc , argv , “h:vp:S”) ) != -1 ) {
switch (opt)
{
case ‘h':
ipArg = optarg;
break;
case ‘v':
verbose_mode = TRUE;
break;
case ‘p':
check_Port ( &low_port , &high_port, optarg );
break;
case ‘S':
stealth_mode = TRUE;
break;
case ‘?':
fprintf ( stdout, “option inconsistency : -%c \n”
“see usage(no arguments)\n”, optopt );
exit(-1);
}
}

if ( ipArg == NULL ){
fprintf (stdout , “No host given-see usage(no arguments)\n” );
exit(-1);
}

if ( !stealth_mode ) {
Connect_Scanning();
}
else {
if ( getuid() && geteuid() ) {
fprintf ( stdout, “Need to be root to initiate Syn Scanning\n”);
exit(-1);
}
Syn_Scanning ();
}
exit(0);

}

void print_usage( const char *argv ) {
fprintf ( stdout,
“Port Scanner by ithilgore\n”
“usage: %s -h Host [OPTIONS]\n”
“Host -> IP or Name\n”
“OPTIONS include:\n”
“-v : verbose mode\n”
“-p : port range (eg. -p23 , -p0-1024)\n”
“-S : stealth mode on ( syn scanning )\n”
“more options to be included\n\n”
, argv );
}

int check_Port ( long * lport ,long * hport, char *optar ) {

char * s1 = optar ; //point to the char after ‘p’
errno = 0;
*lport = strtol ( s1 , (char **)NULL , 10);

if ( errno != 0 ) {
perror ( “Port number problem \n”);
exit(0);
}

if ( !(s1 = index ( s1 , ‘-‘)) ) { //if no port range specified (no other ‘-‘ found)
*hport = *lport;
return 0;
} else {
*hport = strtol ( ++s1 , NULL , 10 ) ;
if ( errno != 0 ) {
perror ( “Port number problem \n”);
exit(0);
}

if ( low_port > high_port ) {
fprintf ( stdout, “low_port is higher than high_port: swapping…\n”);
*lport ^= *hport;
*hport ^= *lport;
*lport ^= *hport;
}
}

}

struct hostent* host_resolve() {
struct hostent *hostname;
extern char * ipArg;

if ( !(hostname = gethostbyname(ipArg) )) {
fprintf ( stdout, “Host name resolution failed for %s \n”
“Try using the nslookup prog to find the IP address\n” , ipArg );
exit(-1);
}

// TODO:use execv nslookup !!!here

if (verbose_mode) {
fprintf ( stdout, “Host Resolution results:\n”
“Name: %s\n”
“Aliases:” , hostname->h_name);
char **alias = hostname->h_aliases;
while(*alias) {
fprintf( stdout, “%s “, *alias);
alias++;
}
char **addrs = hostname->h_addr_list;
printf (“\nIP address/es:\n”);
while(*addrs) {
fprintf( stdout, ” %s “, inet_ntoa( *(struct in_addr *)*addrs ));
addrs++;
}
printf( “\n” );
}
return hostname ;

}

void Connect_Scanning() {

int sockfd;
char temp_addr[7];

extern long int low_port;
extern long int high_port;
extern char * ipArg;

struct sockaddr_in sockaddr;
struct servent *serv;
struct hostent * hostname;
hostname = (struct hostent *)host_resolve();

char **addr = hostname->h_addr_list;

/* transfer of dotted-decimal IP of first IP from resolving */

strcpy ( temp_addr , inet_ntoa ( *(struct in_addr *)*addr ) );

fprintf( stdout, “Initiating Connect() Scan against %s [%ld ports] \n”,
temp_addr,
(high_port-low_port+1));

int i=0;
for ( i=low_port ; i<= high_port ; i++) {

if ( (sockfd = socket ( AF_INET , SOCK_STREAM , 0 )) == -1) {
perror(“Socket error”);
exit(-1);
}
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(i);
inet_aton ( temp_addr , & sockaddr.sin_addr );

if ( !connect ( sockfd , (struct sockaddr*)& sockaddr, sizeof(sockaddr) ) ) {
serv = getservbyport ( htons(i), “tcp” );
fprintf ( stdout, “TCP port %d open , possible service: %s\n” , i , serv->s_name);
}

close ( sockfd );
}
fprintf ( stdout, “Connect Scanning completed\n” );
}

pcap_t *session;

void sigfunc() { /* signal handler */

pcap_breakloop(session);

}

void Syn_Scanning() {

s_timeout = DEFAULT_S_TIMEOUT; /* global var for timeout */
int sockfd;
int timeout = 0; /* check if timeout with return from dispatch */
char temp_addr[16];
extern long int low_port ;
extern long int high_port;
extern char * ipArg;

struct sockaddr_in sin;
struct servent *serv;
struct hostent * hostname;
struct sockaddr_in * ipP; /*local ip storage*/

hostname = (struct hostent *)host_resolve();

char **addr = hostname->h_addr_list;
strncpy ( temp_addr , inet_ntoa ( *(struct in_addr *)*addr ), 16 ) ;

char datagram[4096]; // buffer for datagrams
struct sniff_ip *iph = (struct sniff_ip *) datagram;
struct sniff_tcp *tcph = (struct sniff_tcp *) (datagram + sizeof (struct sniff_ip));

struct sigaction act;
act.sa_handler = sigfunc;
sigemptyset(&act.sa_mask);
act.sa_flags = 0; /* read man of libpcap -> SA_RESTART MUST BE OFF */

/* Prepare the Sniffing Engine */
session = (pcap_t *) EnginePreparing ( temp_addr , &ipP, session );

printf( “Initiating Syn Scanning against %s [%ld ports] \n”, temp_addr ,(high_port-low_port+1));
int i=0;
for ( i=low_port ; i<= high_port ; i++) {

sockfd = socket (AF_INET, SOCK_RAW, IPPROTO_TCP) ; // {
// fprintf ( stderr , “raw socket creation failed : probably not enough priviledges \n” );
// exit (-1) ;
//}

sin.sin_family = AF_INET;

inet_aton ( temp_addr , & sin.sin_addr );

memset (datagram, 0, 4096); /* zero out the buffer */
iph->ip_vhl = 0x45; /* version=4,header_length=5 (no data) */
iph->ip_tos = 0; /* type of service -not needed */
iph->ip_len = sizeof (struct sniff_ip) + sizeof (struct sniff_tcp); /* no payload */
iph->ip_id = htonl (54321); /*simple id */
iph->ip_off = 0; /* no fragmentation */
iph->ip_ttl = 255; /* time to live – set max value */
iph->ip_p = IPPROTO_TCP; /* 6 as a value – see /etc/protocols/ */
iph->ip_src.s_addr = ipP->sin_addr.s_addr; /*local device IP */
iph->ip_dst.s_addr = sin.sin_addr.s_addr; /* dest addr */
iph->ip_sum = 0; /* no need for ip sum */
tcph->th_sport = htons (1234); /* arbitrary port */
tcph->th_dport = htons (i); /* scanned dest port */
tcph->th_seq = random (); /* the random SYN sequence */
tcph->th_ack = 0; /* no ACK needed */
tcph->th_offx2 = 0x50; /* 50h (5 offset) ( 8 0s reserverd )*/
tcph->th_flags = TH_SYN; /* initial connection request */
tcph->th_win = (65535); /* maximum allowed window size */
tcph->th_sum = 0; /* will compute later */
tcph->th_urp = 0; /* no urgent pointer */

/* pseudo header for tcp checksum */
struct pseudo_hdr *phdr = (struct pseudo_hdr *) ( datagram +
sizeof(struct sniff_ip) + sizeof(struct sniff_tcp) ) ;
phdr->src = iph->ip_src.s_addr;
phdr->dst = iph->ip_dst.s_addr;
phdr->mbz = 0;
phdr->proto = IPPROTO_TCP;
phdr->len = ntohs (0x14); /* in bytes the tcp segment length */
/*- WhyTF is it network byte saved by default ????*/

tcph->th_sum = htons ( checksum_comp ( (unsigned short *) tcph ,
sizeof(struct pseudo_hdr)+sizeof(struct sniff_tcp)));

{
int one = 1;
const int *val = &one;
if (setsockopt (sockfd, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0)
fprintf ( stdout, “Warning: Cannot set HDRINCL for port %d\n”,i);
}

if (sendto (sockfd ,datagram, iph->ip_len , 0, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
fprintf ( stdout , “Error sending datagram for port %d\n”,i);
break;
}

sigaction ( SIGALRM , &act , 0 );
alarm(s_timeout);

// give port as argument to callback function
timeout = pcap_dispatch(session, -1, got_packet , (u_char *)i );
alarm(0); /* trigger off alarm for this loop */

if ( verbose_mode && timeout == -2 ) {
fprintf ( stdout, “timeout for port %d\n” ,i);
}

}

fprintf ( stdout, “SYN Scanning completed\n”);
}

uint16_t checksum_comp ( uint16_t *addr , int len ) { /* compute TCP header checksum */
/* with the usual algorithm a bit changed */
/* for byte ordering problem resolving */
/* see RFC 1071 for more info */
/* Compute Internet Checksum for “count” bytes
* beginning at location “addr”.
*/
register long sum = 0;
int count = len;
uint16_t temp;

while( count > 1 ) {

temp = htons(*addr++); // in this line:added -> htons
sum += temp;
count -= 2;
}

/* Add left-over byte, if any */
if( count > 0 )
sum += * (unsigned char *) addr;

/* Fold 32-bit sum to 16 bits */
while (sum>>16)
sum = (sum & 0xffff) + (sum >> 16);

uint16_t checksum = ~sum;
return checksum;
}

pcap_t* EnginePreparing ( char * vicIP , struct sockaddr_in ** ipP ,pcap_t *session ) {

char *dev;
char errbuf[PCAP_ERRBUF_SIZE];
bpf_u_int32 devip , netmask;
struct pcap_pkthdr header;
struct bpf_program filter; // compiled filter
char filter_exp[30] = “src host “; /*we filter the traffic to the victim */
pcap_if_t *alldev;

/* yes some numbers are HARDCODED as they should be */
/* guess why and then try exploiting it */

strncpy ( (char *)filter_exp+9 , vicIP , 16 );
fprintf ( stdout , “filter exp: %s \n ” , filter_exp );

if ((pcap_findalldevs (&alldev, errbuf)) == -1) {
fprintf ( stdout, “%s\n” , errbuf );
exit(-1);
}

struct pcap_addr *address = alldev->addresses;
address = address->next; /*first address is U(F)O*/
struct sockaddr_in * ip;

while (address) {
if (address->addr) {
ip = (struct sockaddr_in *) address->addr;
fprintf ( stdout , “Local IP: %s \n” , inet_ntoa(ip->sin_addr));
}
address = address->next;
}

*ipP = ip; /* local ip to be used in the raw datagram */
/* choose the last you find-no obvious reason */
dev = alldev->name;

/*if ( (dev = pcap_lookupdev(errbuf)) == NULL) {
printf ( “%s\n” , errbuf ) ;
printf ( “Using default eth0 \n”);
dev = “eth0″ ;
}*/

if (verbose_mode) {
fprintf ( stdout , “Using local IP: %s \n” , inet_ntoa((*ipP)->sin_addr));
fprintf( stdout , “Using local Device: %s\n”, dev);
}

if ( (session = pcap_open_live ( dev , BUFSIZE , 0 , 0, errbuf )) == NULL ) {
fprintf ( stdout, “Could not open device %s: error: %s \n ” , dev , errbuf );
exit (-1);
}

if ( pcap_compile( session , &filter , filter_exp , 0 , 0 ) == -1 ) {
fprintf ( stdout , “Couldn’t parse filter %s: %s \n ” , filter_exp , pcap_geterr(session) );
exit (-1);
}

if ( pcap_setfilter ( session , &filter ) == -1 ) {
fprintf ( stdout , “Couldn’t install filter %s: %s\n”, filter_exp, pcap_geterr(session) );
exit (-1);
}

return session;
}

void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) {

const struct sniff_tcp * tcp;
const struct sniff_ip * ip;
const struct sniff_ethernet * ether;
struct servent *serv;

int size_ip;
int size_tcp;

ether = (struct sniff_ethernet*) (packet);
ip = (struct sniff_ip *) (packet + SIZE_ETHERNET);

size_ip = IP_HL(ip)*4;
if ( size_ip < 20 ) {
fprintf ( stdout, “Invalid IP header length: %u bytes \n” , size_ip );
return;
}

if ( ip->ip_p != IPPROTO_TCP ) {
fprintf ( stdout, “Returned Packet is not TCP protocol \n”);
return;
}

tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
size_tcp = TH_OFF(tcp)*4;
if (size_tcp < 20) {
fprintf ( stdout, ” * Invalid TCP header length: %u bytes\n”, size_tcp);
return;
}

/* the actual SYN scanning (heh) : we examine if the SYN flag is on at the receiving packet(port open) */

if ( ( (tcp->th_flags & 0x02) == TH_SYN) && ( tcp->th_flags & 0x10 ) == TH_ACK ) {
serv = getservbyport ( htons((int)args), “tcp” );
fprintf ( stdout, “TCP port %d open , possible service: %s\n”, args, serv->s_name);
// TODO : send RST explicitly but kernel usually does this automatically
}
else if ( (tcp->th_flags & 0x04 ) == TH_RST && verbose_mode ) {
//fprintf ( stdout, “TCP port %d closed\n”, args ); too much info on screen

}
else if ( verbose_mode ) {
//fprintf ( stdout, “Port %d state unknown/filtered \n”, args);
}
}

————–
0x7. Επίλογος
————–

Αν αντέξατε ως εδώ και μελετήσατε και τον πηγαίο κώδικα , τότε χαρά στο κουράγιο σας. Ελπίζω να μάθατε κάτι
από όλο αυτό το guide και να σας κίνησε αρκετά το ενδιαφέρον για το κλάδο του network programming and security
applications/tools πάνω στον οποίο βασίζεται. Όπως αναφέρω και στο introduction του source θα εκτιμήσω οποιοδήποτε
feedback και σας προτρέπω να κάνετε τις δικές σας αλλαγές και improvements στον κώδικα καθώς πιστεύω οτί ως σύνολο
απλά θέτει έναν σκελετό για ένα custom security port scanner.
Πιθανές προεκτάσεις για το tool:
1) IP spoofing capabilities
2) Real time port randomization
3) Alternate probing and IP expiry tactics

Να σημειώσω ότι το source αυτό σε public version είναι ελαφρώς stripped. Μερικές από τις υπόλοιπες δυνατότητες είναι
σχετικά απλό να υλοποιηθούν, αλλά για προφανείς λόγους δεν δίνονται έτοιμες in public.
Για οποιοδήποτε (σοβαρό) είδος contact πάνω στο project στείλτε στο ithilgore.ryu.L@gmail.com

—————-
0x8. References
—————-

-UNIX Network Programming Volume 1, Third Edition: The Sockets Networking API

-http://mixter.void.ru/rawip.html (raw sockets tutorial)

-http://www.netfor2.com/checksum.html (IP checksum introduction)

-http://www.tcpipguide.com/free/t_TCPChecksumCalculationandtheTCPPseudoHeader.htm (TCP checksum and pseudo header)

RFCs
-http://www.ietf.org/rfc/rfc0791.txt (IP)
-http://www.ietf.org/rfc/rfc0793.txt (TCP)
-http://www.ietf.org/rfc/rfc1071.txt (Checksum)

-http://www.tcpdump.org (libpcap + tcpdump)

–EOF–

Ένα Σχόλιο to “Coding a Syn Scanner”

  1. ithilgore said

    Mporei na se endiaferei na deis kai kapoia alla papers pou exw grapsei kai briskodai hosted sto epishmo site:

    sock-raw.homeunix.org

    — ithilgore

Υποβολή απάντησης

Εισάγετε τα παρακάτω στοιχεία ή επιλέξτε ένα εικονίδιο για να συνδεθείτε:

WordPress.com Logo

Σχολιάζετε χρησιμοποιώντας τον λογαριασμό WordPress.com. Log Out / Αλλαγή )

Twitter picture

Σχολιάζετε χρησιμοποιώντας τον λογαριασμό Twitter. Log Out / Αλλαγή )

Facebook photo

Σχολιάζετε χρησιμοποιώντας τον λογαριασμό Facebook. Log Out / Αλλαγή )

Google+ photo

Σχολιάζετε χρησιμοποιώντας τον λογαριασμό Google+. Log Out / Αλλαγή )

Σύνδεση με %s

 
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: