PDA

View Full Version : Reading TCP Control Bits


Encrypted
05-09-2003, 02:23 PM
Hey,

when i send out a raw SYN packet, the program exits after sending and my kernel sends out an RST to the computer responding with SYN-ACK.

But i want to read the Control bit that is set in the response packet,
Now, we dont have socket (yet) because it is under construction with the handshake, so where do i read from or listen for reply?

I havent really looked into this, so if im asking a dumb question, tell me ;)

thanks,
encrypted

[edit]
Ok, i found some code that looks like it is reading tcp control bits (in nmap source)

portlist syn_scan(struct in_addr target, unsigned short *portarray,
struct in_addr *source, int fragment, portlist *ports) {
int i=0, j=0, received, bytes, starttime;
struct sockaddr_in from;
int fromsize = sizeof(struct sockaddr_in);
int sockets[max_parallel_sockets];
struct timeval tv;
char packet[65535];
struct iphdr *ip = (struct iphdr *) packet;
struct tcphdr *tcp = (struct tcphdr *) (packet + sizeof(struct iphdr));
fd_set fd_read, fd_write;
int res;
struct hostent *myhostent;
char myname[MAXHOSTNAMELEN + 1];
int source_malloc = 0;

FD_ZERO(&fd_read);
FD_ZERO(&fd_write);

tv.tv_sec = 7;
tv.tv_usec = 0;

if ((received = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0 )
perror("socket trobles in syn_scan");
unblock_socket(received);
FD_SET(received, &fd_read);

/* First we take what is given to us as source. If that isn't valid, we take
what should have swiped from the echo reply in our ping function. If THAT
doesn't work either, we try to determine our address with gethostname and
gethostbyname. Whew! */
if (!source) {
if (ouraddr.s_addr) {
source = &ouraddr;
}
else {
source = safe_malloc(sizeof(struct in_addr));
source_malloc = 1;
if (gethostname(myname, MAXHOSTNAMELEN) ||
!(myhostent = gethostbyname(myname)))
fatal("Your system is fucked up.\n");
memcpy(source, myhostent->h_addr_list[0], sizeof(struct in_addr));
}
if (debugging)
printf("We skillfully deduced that your address is %s\n",
inet_ntoa(*source));
}

starttime = time(NULL);

do {
for(i=0; i < max_parallel_sockets && portarray[j]; i++) {
if ((sockets[i] = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0 )
perror("socket trobles in syn_scan");
else {
if (fragment)
send_small_fragz(sockets[i], source, &target, MAGIC_PORT,
portarray[j++], TH_SYN);
else send_tcp_raw(sockets[i], source , &target, MAGIC_PORT,
portarray[j++],0,0,TH_SYN,0,0,0);
usleep(10000);
}
}
if ((res = select(received + 1, &fd_read, NULL, NULL, &tv)) < 0)
perror("select problems in syn_scan");
else if (res > 0) {
while ((bytes = recvfrom(received, packet, 65535, 0,
(struct sockaddr *)&from, &fromsize)) > 0 ) {
if (ip->saddr == target.s_addr) {
if (tcp->th_flags & TH_RST) {
if (debugging > 1) printf("Nothing open on port %d\n",
ntohs(tcp->th_sport));
}
else /*if (tcp->th_flags & TH_SYN && tcp->th_flags & TH_ACK)*/ {
if (debugging || verbose) {
printf("Possible catch on port %d! Here it is:\n",
ntohs(tcp->th_sport));
readtcppacket(packet,1);
}
addport(ports, ntohs(tcp->th_sport), IPPROTO_TCP, NULL);
}
}
}
}
for(i=0; i < max_parallel_sockets && portarray[j]; i++) close(sockets[i]);

} while (portarray[j]);
if (debugging || verbose)
printf("The TCP SYN scan took %ld seconds to scan %d ports.\n",
time(NULL) - starttime, number_of_ports);
if (source_malloc) free(source); /* Gotta save those 4 bytes! ;) */
close(received);
return *ports;
}
though i still dont get it......

Loco
05-10-2003, 02:20 AM
I'm new to this whole thing, but for me it seems that the code does this:

1) Creates a RAW SOCKET for recv'ing the responses (received), it uses the socket() call with AF_INET, SOCK_RAW and (interestingly beacuse I have no clue about RAW sockets) with IPPROTO_TCP!!! - This in fact is very different to the way it creates the sockets for sending packets. I guess this part in fact allows it to recv any TCP packet...
2) Calls an unknown function unblock_socket() which I assume does the hacking of allowing it to receive any TCP packet, unless the previous step already did that... I suggest you take a look at this one, maybe you can find the magic words here
3) Creates the sockets for sending probing packets, and starts sending packets to the ports (either complete or fragmented packets, why fragmented? who knows, I guess it is trying to avoid detection by perimeter devices or IDS - have you fragmented the TCP header for a logical reason???)
4) Waits for packets to arrive (using the all-mighty select()), recv's using recvfrom(), analyzes the flags of the response (SYN/ACK or RST) and updates the port status based on that.

I guess that the first and/or second point do the magic trick of allowing you to receive all incoming TCP packets (for sake of simplicity). But what is not clear for me is the following:

With the poor information I have about RAW sockets, i think the packets are also processed by the kernel. If this code does something about that you will have two options:
a) Don't let the kernel process the packets at all, your socket will be the only one receiving all TCP packets (i.e. they won't be delivered to other applications)
b) Let the kernel process packets and your socket receive them as well. So you will have a duplicate of all the packets the kernel receives

Either one is problematic:
a) If the kernel does not process the packets, then communications for this machine will stop until the socket is closed. It will be the only one receiving TCP packets, unless the application has a way to feed the kernel with the packets for further processing (which I don't see anywhere in the code)
b) The kernel will generate a RST packet for each SYN/ACK packet received (just like it is happening to you right now) on response to your packets

:idea: :idea: :idea:
Ohhhhh, I get it... Maybe I have misunderstood the whole raw sockets concept!!!

If the thing happens this way then it could work:

When you create a RAW socket of this sort (like in the code shown), the Kernel will forward all TCP packets (in this case) that have NO user-space socket owner or kernel space meaning (e.g. no listening socket in that port, or no established connection in that port!!!). That would mean that any TCP packet sent to any port that is not listening, and that has no connection will be delivered to your socket... That would be pretty neat, but what will happen then? Will you be responsible for making the replies (i.e. sending RST packets when necessary?), will you be able to send SYN/ACKs in response to SYN packets on ports that are not open???

These and many other questions come to my mind right now...

Now I am confused.

Encrypted
05-11-2003, 11:33 AM
yeah. im also confused :?

well, im gonna keep searching


encrypted

Encrypted
05-31-2003, 06:54 PM
Well, as you probably read, (in some topic above)
its really not that hard.


int size = 65535;
local.sin_addr.s_addr = htonl(INADDR_ANY); local.sin_family = AF_INET; local.sin_port = htons(8000);
if ( recvfrom( sockfd, recv, sizeof(struct tcphdr)+sizeof(struct ip), 0x0, (struct sockaddr *)&local, &size ) < 0 )
perror("recvfrom");

tcph = (struct tcphdr *)(recv+sizeof(struct ip)); if( tcph->th_flags & TH_RST )
{
printf("Got a RST :(\n");
exit(0);
}
else /* if tcph->th_flags & TH_SYN && tcph->th_flags & TH_ACK */
{
printf("Got a SYN/ACK :)\n"); exit(0);
}
}


thanks,
encrypted

mlampkin
06-01-2003, 08:56 AM
You're answering your own questions Encrypted?! :-P

Btw, a quick side note... one problem with the code posted... using a static size for the incoming ip packet can potentially fail... remember that the ip header is required to be at least 5 32 bit words... but the originator / creator of the packet can always add option bytes between the required header fields and the data (tcp in this case) which would result in you pointing into those options instead of the desired tcp header...

I guess all I am saying is always check the ip len field to calc the offset to the data / tcp hdr... you can't go wrong that way... and the same thing applies to the tcp header and its data offset pointer to get to it's contained data (yeah, I realize you are only interested in the control bits in the question)...

>> I'm editing this... just realized how old the original msg was... guess I missed it somehow... oops... :-/

Michael

Encrypted
06-01-2003, 01:23 PM
Btw, a quick side note... one problem with the code posted... using a static size for the incoming ip packet can potentially fail... remember that the ip header is required to be at least 5 32 bit words... but the originator / creator of the packet can always add option bytes between the required header fields and the data (tcp in this case) which would result in you pointing into those options instead of the desired tcp header...
Yeah thats true, he can add options....
But i have to allocate some memory for the packet, so i'd allocate some more memory, ill just stick to 65535 ;)
And for pointing to the wrong data, is this piece of code good for that?:

....
struct ip *iph = (struct ip *)recv;
struct tcphdr *tcph = (struct tcphdr *)(recv+iph->len);
....

You mean something like that?

thanks,
encrypted

mlampkin
06-01-2003, 07:59 PM
Though its 32 bit words so it would be 4 * iph->len (assuming ip->len is properly defined to bits 4-7 in the ip header) to get the byte count for the offset...

Off the top of my head, I believe the TCP data offset value is the same (no time to 2x check) i.e. a 4 bit number representing the number of 32 bit words in its header...

Michael

Encrypted
06-01-2003, 09:42 PM
Though its 32 bit words so it would be 4 * iph->len (assuming ip->len is properly defined to bits 4-7 in the ip header) to get the byte count for the offset...

ahh offcourse, i totally forgot about that :?

thanks