R E P O R T 제목 : 포트스캐닝 (Port Scanning) 과목명 : 네트워크보안프로그래밍 학 과 : 사이버경찰학과 학 번 : 10100245 이 름 : 김민관 제출일 : 2016년 6월 10일 ( 금 ) 담당교수 : 소길자교수님
목 차 1. 포트스캐닝 (Port Scanning) 이란? 2. 포트 (Port) 란? 3. 스캔 (Scan) 의종류 4. 포트스캐너 (Port Scanner) 구현 5. 포트스캔 (Port Scan) 구현 6. 참조
1. 포트스캐닝 (Port Scanning) 이란? 해당호스트에서어떤포트가열려있는지스캔해보는것이다. 2. 포트 (Port) 란? 모뎀과컴퓨터사이에데이터를주고받을수있는통로두가지의미를지닌다. 첫째, 컴퓨터의주변장치를접속하기위해사용되는연결부분을의미한다. 대개소켓이나플러그등의형태로되어있다. 둘째, 프로그래밍에서는논리적인접속장소를뜻한다. TCP/IP의상위프로토콜을사용하는응용프로그램에서는인터넷번호할당허가위원회 (IANA) 에의해미리지정된포트번호들을가지고있다. 잘알려진포트들 이라부르며, 다른응용프로그램프로세스들은접속할때마다포트번호가새로부여된다. 포트번호는 0부터 65535까지이며, 0부터 1023까지는어떤특권을가진서비스에의해사용될수있도록예약되어있다. 잘알려진포트 (Well known ports) : 0~1023, 등록된포트 (Registered ports) : 1024 ~ 49151, 다이나믹포트 (Dynamic or Private ports) : 49152~65535 3. 스캔 (Scan) 의종류 Sweeps 에는 ICMP ECHO, NON-ECHO, TCP sweep, UDP sweep 있다. Sweeps 는돌아본다는의미이며, 해당네트워크를돌면서살아있는 Host, Port 를찾는다. Open scan 에는 TCP connect, Reverse ident 있다. Open scan 는완전한연결을하는스캔하며, 스캔결과의신뢰성을매우높지만정상적인 3-way handshake 를모두수행하기때문에타켓호스트에로그가남으며탐지되기도쉽다. Half-open 에는 SYN flag, IP ID header dumb scan 있다. stealth 에는 FIN flag, NULL flag, XMAS flag, ACK flag, TCP flagment 있다. 스캔하는대상에단순히로그를남기지않는다. 공격대상을속이고자신의위치또한숨기는스캔이다. 기타에는 FTP bounce, Spoof 있다. 4. 포트스캐너 (Port Scanner) 구현 #include "stdafx.h" #include <stdio.h> #include <stdlib.h> #include <WinSock2.h> #pragma comment (lib, "ws2_32.lib") typedef struct OpenPort int port; OpenPort *next;
OpenPort; void error_proc(); int main(int argc, char* argv[]) WSADATA wsa; SOCKET clntsd; SOCKADDR_IN clntaddr; char address[20]; // 주소저장 int Start_port = 0; // 시작 int End_port = 65535; // 끝 OpenPort* openportlist = (OpenPort *)malloc(sizeof(openport)); OpenPort* openportnodetail = openportlist; FILE *file = fopen("openedportlist.txt","wt"); printf(" 포트스캔할서버주소를입력하세요. : "); scanf("%s",address); printf(" 시작할포트스캔번호를입력하세요. : "); scanf(" %d", &Start_port); printf(" 마지막포트스캔번호를입력하세요. : "); scanf(" %d", &End_port); int num=0; for(num=start_port;num<=end_port;num++) // 시작에서끝까지스캔μ if(wsastartup(makeword(2,2), &wsa)!= 0) error_proc(); clntsd = socket(af_inet, SOCK_STREAM, 0); if(clntsd == INVALID_SOCKET) error_proc(); ZeroMemory(&clntAddr,sizeof(clntAddr)); clntaddr.sin_family = AF_INET; clntaddr.sin_port = htons(num); clntaddr.sin_addr.s_addr = inet_addr(address); SOCKET_ERROR) if (connect(clntsd, (SOCKADDR *)&clntaddr, sizeof(clntaddr)) == else printf("%d 포트닫힘 \n", num); printf("%d 포트열림 \n", num);
fprintf(file,"%d 포트열림 \n", num); OpenPort* openedportnode = (OpenPort *)malloc(sizeof(openport)); openedportnode->port = num; openedportnode->next = NULL; openportnodetail->next = openedportnode; openportnodetail = openportnodetail->next; closesocket(clntsd); WSACleanup(); fclose(file); void error_proc() fprintf(stderr,"error: %d \n", WSAGetLastError()); exit(1); 5. 포트스캔 (Port Scan) 구현 #include "stdafx.h" #include "pcap.h" #include <WinSock2.h> #include <stdio.h> #include <string.h> #pragma warning (disable:4996) void packet_handler(u_char *param, const struct pcap_pkthdr *h, const u_char *data); typedef struct IPHeader
u_char HeaderLength : 4; // 헤더길이 * 4 u_char Version : 4; // IP v4 or IPv6 u_char TypeOfService; // 서비스종류 u_short TotalLength; // 헤더길이 + 데이터길이 / u_short ID; // 프래그먼트의 Identification u_short FlagOffset; // 플래그 + 프래그먼트오프셋 u_char TimeToLive; // TimeToL u_char Protocol; // 프로토콜종류 (1. ICMP 2. IGMP 6. TP 17:UDP; u_short checksum; u_char SenderIP[4]; u_char ReceiverIP[4]; u_int Option_Padding; IPHeader; typedef struct TCPHeader u_short SourcePort; u_short DestPort; u_int SeqNum; u_int AckNum; u_char TCPlength; u_char flags; u_short WinSize; u_short CheckSum; u_short UrgentPointer; TCPHeader; // Linked List 구조체 ( 수신주소, 포트번호와다음링크를저장한다.) typedef struct visitlist u_char SenderIP[4]; int port = -1; visitlist* next = NULL; visitlist* past = NULL; visitlist; visitlist *headlink=null, *visitlink = NULL, *taillink = NULL; int checkscan(visitlist *List); void main() // 패킷필터링설정구문 u_int netmask;
char packet_filter[] = "tcp"; // 프로토콜및포트번호설정예 )tcp 80 struct bpf_program fcode; pcap_if_t *alldevice; // 찾아낸디바이스를 LinkedList로묶고, 그중첫번째오브젝트를담을변수생성 pcap_if_t *device; // Linked List의다음오브젝트를담을공간 char errormsg[256]; // 에러메시지를담을변수생성 char counter = 0; pcap_t *pickeddev; // 사용할디바이스를저장하는변수 // 1. 장치검색 ( 찾아낸디바이스를 LinkedList로묶음 ) if ((pcap_findalldevs(&alldevice, errormsg)) == -1)// 변수생성시에는 1 포인터지만, pcap_findalldevice에쓰는건더블포인트이므로주소로주어야함. //pcap_if_t는 int형태를반환하며, -1이나올경우, 디바이스를찾지못했을경우이다. printf(" 장치검색오류 "); // 2. 장치출력 int count = 0; for (device = alldevice; device!= NULL; device = device->next) // dev에 alldevice의첫시작주소를넣으며, dev의값이 NULL( 끝 ) 일경우종료, dev는매 for마다다음주소값으로전환 printf(" %d 번네트워크카드 \n", count); printf(" 어댑터정보 : %s\n", device->name); printf(" 어댑터설명 : %s\n", device->description); printf(" \n"); count = count + 1; // 3. 네트워크카드를선택하고선택된디바이스로수집할패킷결정하기 printf(" 패킷을수집할네트워크카드를선택하세요 : "); device = alldevice;// 카드를선택하지않고그냥첫번째카드로설정했음. int choice; scanf_s("%d", &choice); for (count = 0; count < choice; count++) device = device->next; // 네트워크장치를열고, 수집할패킷양을설정한다. pickeddev = pcap_open_live(device->name, 65536, 0, 1000, errormsg); // 랜카드의이름, 수집할패킷크기 ( 최대 65536), 프로미스큐어스모드 ( 패킷수집모드 ) 설정, 패킷대기시간, 에러정보를저장할공간 )
// 패킷을필터링하는정보를추가한다. netmask = 0xffffff; if (pcap_compile(pickeddev, &fcode, packet_filter, 1, netmask) < 0) fprintf(stderr, "\nunable to compile the packet filter. Check the syntax.\n"); pcap_freealldevs(alldevice); return; if (pcap_setfilter(pickeddev, &fcode) < 0) fprintf(stderr, "\nerror setting the filter.\n"); pcap_freealldevs(alldevice); return; // 4. 랜카드리스트정보를저장한메모리를비워준다. pcap_freealldevs(alldevice); // 5. 설정한네트워크카드에서패킷을무한캡쳐할함수를만들고캡쳐를시작한다. pcap_loop(pickeddev, 0, packet_handler, NULL); void packet_handler(u_char *param, const struct pcap_pkthdr *h, const u_char *data) // 링크리스트를위해가장앞부분과끝부분을미리선언한다. // 실습을위해로컬호스트에서로컬호스트로만날리므로 // 복수의링크리스트는필요없다. // ( 애초에다중스캐닝의경우데이터베이스를사용했을것이다.) IPHeader *IH = (IPHeader *)(data + 14); if (IH->Protocol == 6) TCPHeader *TCP = (TCPHeader *)(((u_char*)ih) + (IH->HeaderLength * 4)); // IP헤더시작위치 + ip 헤더길이 // 포트가열려있을경우, 끝링크항목의 next에추가시키며 // 끝링크는다시해당항목으로이동시킨다. // 첫링크가없을경우, 첫링크에등록시킨다. if (headlink == NULL) visitlink = (visitlist *)malloc(sizeof(visitlist)); int count = 0; for (count = 0; count < 4; count++) visitlink->senderip[count] = IH->SenderIP[count]; visitlink->port = ntohs(tcp->destport)-1;
visitlink->next = NULL; headlink = visitlink; taillink = visitlink; // 물론꼬리링크도잊지않는다. // 출력형태는 [ 이전의포트 ] - [ 현재들어온포트 ] 이지만, // 첫링크가없을경우만 [ 이전의포트 ] 영역에표시된다. printf("[%d - X]\n", visitlink->port); // 헤드링크의다음항목이연결되어있을경우 else if (headlink->next!= NULL) // 만일이전에통신한포트와다른포트로통신이될경우 if (ntohs(tcp->destport)-1!= taillink->port) visitlink = (visitlist *)malloc(sizeof(visitlist)); int count = 0; for (count = 0; count < 4; count++) visitlink->senderip[count] = IH->SenderIP[count]; visitlink->port = ntohs(tcp->destport)-1; visitlink->next = NULL; visitlink->past = taillink; taillink->next = visitlink; taillink = visitlink; printf("[%d - %d]\n", visitlink->past->port, visitlink->port); // 이때부터 3회이상통신이므로통신을검사하기시작합니다. checkscan(headlink); // 두번째통신의경우 else if (ntohs(tcp->destport) - 1!= taillink->port) visitlink = (visitlist *)malloc(sizeof(visitlist)); int count = 0; for (count = 0; count < 4; count++) visitlink->senderip[count] = IH->SenderIP[count]; visitlink->port = ntohs(tcp->destport) - 1; visitlink->next = NULL; visitlink->past = taillink; taillink->next = visitlink; taillink = visitlink; printf("[%d - %d]\n", visitlink->past->port, visitlink->port);
// 3회이상서로다른포트에통신을했을경우실행되는함수 int checkscan(visitlist *List) List = List->next; int changedcounter = 0; int port1; int port2; // 가장최근통신한포트와, 그이전의포트, 그그이전의포트를비교하여 // 3번이상다른포트로통신하였을경우, 콘솔창에포트스캔의심을알립니다. while (1) port1 = List->port; if (List->next == NULL) break; else List = List->next; port2 = List->port; if (port1!= port2) changedcounter++; if (List->next == NULL) break; else List = List->next; port1 = List->port; if (port1!= port2) printf("%d.%d.%d.%d 측에서포트스캐닝을하고있습니다!\n", List->SenderIP[0], List->SenderIP[1], List->SenderIP[2], List->SenderIP[3]); return 0; 6. 참조 김주명 : 포트스캐닝 김태룡 : C++ 을이용한포트스캔및탐지