보안 / AI / 프로그래밍

[0x432-0x433] [보충] TCP HEADER Urgent Pointer 본문

Hacking :: The Art Of Exploitation/0x400 :: 네트워킹

[0x432-0x433] [보충] TCP HEADER Urgent Pointer

DevKTW 2021. 2. 7. 15:55

[0x433] [보충] TCP HEADER Urgent Pointer

- Urgent Pointer 는 TCP HEADER URG Flag 가 1일때, 데이터 필드에서 어디까지가 긴급 데이터 인지 알려주는 포인터  입니다. URG가 설정된 경우 Urgent Pointer는 마지막 긴급 데이터 <바이트>를 가리킵니다.

 

해당 사진은 16,120 ( =0x3ef8 ) 번째 바이트가 Urgent Data의 끝임을 나타내고 있다. ( 출처 : TCP Window Size, Checksum & Urgent Pointer - Section 5 (firewall.cx) )

 

Given the above issues and potential interoperability issues with respect to the currently common default mode operation, it is strongly recommended that applications do not employ urgent indications.

RFC 6093에서 명시됐듯이, 긴급 지시자 ( URG 플래그와 Urgent Pointer ) 를 사용하지 않을 것을 추천하고 있습니다. 그렇기 때문에 현재 패킷을 캡쳐해보면, Urgent Pointer 가 0이거나 URG 가 설정되지 않은 패킷이 대부분입니다.

 

- 그렇다면 패킷을 직접 만들어보겠습니다. 아래 소스의 출처는 이 사이트 입니다. TCP HEADER 값들을 적절히 바꿔 주시기 바랍니다. TCP 패킷을 만들고 싶으시다면 계속 사용하기 좋은 소스인 것 같습니다.

 

/*
	Raw TCP packets //https://www.binarytides.com/raw-sockets-c-code-linux/
*/
#include <stdio.h>	//for printf
#include <string.h> //memset
#include <sys/socket.h>	//for socket ofcourse
#include <stdlib.h> //for exit(0);
#include <errno.h> //For errno - the error number
#include <netinet/tcp.h>	//Provides declarations for tcp header
#include <netinet/ip.h>	//Provides declarations for ip header
#include <arpa/inet.h> // inet_addr
#include <unistd.h> // sleep()

/* 
	96 bit (12 bytes) pseudo header needed for tcp header checksum calculation 
*/
struct pseudo_header
{
	u_int32_t source_address;
	u_int32_t dest_address;
	u_int8_t placeholder;
	u_int8_t protocol;
	u_int16_t tcp_length;
};

/*
	Generic checksum calculation function
*/
unsigned short csum(unsigned short *ptr,int nbytes) 
{
	register long sum;
	unsigned short oddbyte;
	register short answer;

	sum=0;
	while(nbytes>1) {
		sum+=*ptr++;
		nbytes-=2;
	}
	if(nbytes==1) {
		oddbyte=0;
		*((u_char*)&oddbyte)=*(u_char*)ptr;
		sum+=oddbyte;
	}

	sum = (sum>>16)+(sum & 0xffff);
	sum = sum + (sum>>16);
	answer=(short)~sum;
	
	return(answer);
}

int main (void)
{
	//Create a raw socket
	int s = socket (PF_INET, SOCK_RAW, IPPROTO_TCP);
	
	if(s == -1)
	{
		//socket creation failed, may be because of non-root privileges
		perror("Failed to create socket");
		exit(1);
	}
	
	//Datagram to represent the packet
	char datagram[4096] , source_ip[32] , *data , *pseudogram;
	
	//zero out the packet buffer
	memset (datagram, 0, 4096);
	
	//IP header
	struct iphdr *iph = (struct iphdr *) datagram;
	
	//TCP header
	struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof (struct ip));
	struct sockaddr_in sin;
	struct pseudo_header psh;
	
	//Data part
	data = datagram + sizeof(struct iphdr) + sizeof(struct tcphdr);
	strcpy(data , "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
	
	//some address resolution
	strcpy(source_ip , "192.168.1.2");
	sin.sin_family = AF_INET;
	sin.sin_port = htons(80);
    
    //****패킷을 캡쳐하는 컴퓨터 주소를 적절히 바꿔주세요.****
	sin.sin_addr.s_addr = inet_addr ("121.168.125.37");
	
	//Fill in the IP Header
	iph->ihl = 5;
	iph->version = 4;
	iph->tos = 0;
	iph->tot_len = sizeof (struct iphdr) + sizeof (struct tcphdr) + strlen(data);
	iph->id = htonl (54321);	//Id of this packet
	iph->frag_off = 0;
	iph->ttl = 255;
	iph->protocol = IPPROTO_TCP;
	iph->check = 0;		//Set to 0 before calculating checksum
	iph->saddr = inet_addr ( source_ip );	//Spoof the source ip address
	iph->daddr = sin.sin_addr.s_addr;
	
	//Ip checksum
	iph->check = csum ((unsigned short *) datagram, iph->tot_len);
	
	//TCP Header
	tcph->source = htons (1234);
	tcph->dest = htons (80);
	tcph->seq = 0;
	tcph->ack_seq = 0;
	tcph->doff = 5;	//tcp header size
	tcph->fin=0;
	tcph->syn=1;
	tcph->rst=0;
	tcph->psh=0;
	tcph->ack=0;
    
    //**** URG FLAG 설정했습니다****
	tcph->urg=1;
	tcph->window = htons (5840);	/* maximum allowed window size */
	tcph->check = 0;	//leave checksum 0 now, filled later by pseudo header
    
    //**** URG Pointer 값을 2 Bytes로 4로 설정해줍니다.
    //htons 함수는 제 글을 참고해주세요 : 소켓 관련 글
	tcph->urg_ptr = htons(4);
	
	//Now the TCP checksum
	psh.source_address = inet_addr( source_ip );
	psh.dest_address = sin.sin_addr.s_addr;
	psh.placeholder = 0;
	psh.protocol = IPPROTO_TCP;
	psh.tcp_length = htons(sizeof(struct tcphdr) + strlen(data) );
	
	int psize = sizeof(struct pseudo_header) + sizeof(struct tcphdr) + strlen(data);
	pseudogram = malloc(psize);
	
	memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header));
	memcpy(pseudogram + sizeof(struct pseudo_header) , tcph , sizeof(struct tcphdr) + strlen(data));
	
	tcph->check = csum( (unsigned short*) pseudogram , psize);
	
	//IP_HDRINCL to tell the kernel that headers are included in the packet
	int one = 1;
	const int *val = &one;
	
	if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0)
	{
		perror("Error setting IP_HDRINCL");
		exit(0);
	}
	
	//loop if you want to flood :)
    //**** 총 5번만 보내도록 하겠습니다. ****
	for (int k = 0; k < 5; k++)
	{
		//Send the packet
		if (sendto (s, datagram, iph->tot_len ,	0, (struct sockaddr *) &sin, sizeof (sin)) < 0)
		{
			perror("sendto failed");
		}
		//Data send successfully
		else
		{
			printf ("Packet Send. Length : %d \n" , iph->tot_len);
		}
        //**** 5초 Sleep 걸어줬습니다. ****
        sleep(5);
	}
	
	return 0;
}

- 이를 컴파일한 후, 패킷을 보내면 아래와 같은 결과가 나옵니다.

- Data 필드 앞에 보이는 2 Bytes 크기의 "00 04"가 Urgent Pointer 값입니다. 이는 "41 42 43 44" 값이 긴급 데이터임을 의미합니다. 아까 말씀드렸듯이, Urgent Pointer 는 긴급 데이터의 끝을 나타냅니다.

 

- 정상적인 과정의 통신이 아니므로 TCP Out-Of-Order 같은 오류가 발생한 것을 볼 수 있습니다.

  (SYN + URG 조합의 패킷은 본 적도 없는걸요..)

 

- WireShark 로 정상적인 패킷을 보고 싶으시다면 이 사이트를 참조하세요.

 


참조자료

techlog.gurucat.net/314#footnote_link_314_1

tools.ietf.org/html/rfc6093#section-4.2.2.4

ko.wikipedia.org/wiki/%EC%A0%84%EC%86%A1_%EC%A0%9C%EC%96%B4_%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C

www.binarytides.com/raw-sockets-c-code-linux/

TCP Window Size, Checksum & Urgent Pointer - Section 5 (firewall.cx)

Comments