Linux/Linux_Source

Linux :: 간단한 Port scanner 구현하기

sosal 2010. 1. 27. 10:01
반응형

/*
 http://sosal.tistory.com/
 * made by so_Sal
 */


c언어와 소켓 프로그래밍에 대한 지식, OSI 7계층 프로토콜중
TCP, IP, UDP, ICMP와 전송계층, 네트워크 계층의 프로토콜 지식이 있으면
충분히 이해할 수 있는 간단한 내용입니다.

예제 소스도 아주 간단하게 "열려있는지"만 파악하는 내용이므로
어렵지 않게 이해하실 수 있을것이라 생각합니다.

포트스캔 하는 방법도 상당히 여러가지고, 무척 어려운 부분이지만
아주 간단하게 준비하였습니다.
이 글을 보시고 난 후 옵션이나 기능을 추가하시면서
더 멋있는 포트스캐너를 만들어 보세요.


1. port?
2. portscan?
3. 예제 소스



CTF 해킹방어대회나 서버 모의 해킹을 할 때
가장 기본적으로 하는것중에 하나가 포트스캔입니다.
국내에서 포트스캔 하는 행위 자체는 불법 행위이므로
포트스캐너를 구현 한다음 타 ip에 스캔하는 행위는 해선 안됩니다.


1. port?
아마 소켓프로그래밍 하면서 충분히 공부하셨을거라 생각합니다.
포트에도 상황에 따라서 다양한 의미가 있겠지만,
여기서는 "프로그램과 외부와의 통로" 로 이해하시면 되겠습니다.
워크, 아프리카tv, 네이트온과 같은 네트워크 프로그램은 로컬이 아닌 외부와의 컴퓨터와
데이터를 교환합니다. 만약 포트가 없다면 네이트온에 전달되야 할 데이터인지,
아프리카 tv로 전달되어야 할 데이터인지 컴퓨터가 구분하지 못하게 됩니다.

때문에 컴퓨터는 외부에서 들어오는 패킷들을 구별하기 위해
통로를 두어 0번통로에 들어오는 데이터들은 네이트온으로 향한다.
1번통로에 들어오는 데이터는 아프리카tv,2번, 3번, 4번~~ 65535번 통로~~
를 만들게 되었습니다.

이 통로가 포트를 말하고, 0번부터 1023번까지 총 1024 포트는 well-known 포트라고
web, ftp, ssh 등 세계에 공용으로 누구든지 쓰는 포트에 대해서는 미리 정해놓고 사용합니다.
포트는 0~65535번까지 총 65536개가 존재합니다. (2의.. 몇승이였더라? 16승..이네요)


2.portscan?
포트스캔은 말 그대로 "포트를 스캔한다" 가 되겠습니다.
네이트온이 7777 포트로 패킷을 공유하고 있다면,
외부에서 로컬 컴퓨터에 7777번 포트로 connect가 가능합니다.
하지만 6666 포트를 사용하는 프로그램은 없습니다.
따라서 외부에서 로컬로 6666 포트로 connect는 불가능합니다.

이때 7777번 포트는 open 되어있다 라고 하고,
6666번 포트는 close 되어있다 라고 표현합니다.

이처럼 리눅스에서는 네트워크를 하는 데몬 (윈도우에서는 적절한 프로그램이 되겠네요)들은
필수적으로 어떤 포트를 열것이고, 외부에서 그 포트로 접근이 가능하겠죠?
어떤 포트에 대해서 열려있는지, 닫혀있는지 확인하는것이 포트스캔입니다.

어떤 서버를 공략하고 싶을때 모든 포트에 대해서
검사를 한다음, 열려있는 포트에 대해 취약점을 조사하면 되겠죠?


예제를 보겠습니다.

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/stat.h>
#include<arpa/inet.h>

int main( int argc, char *argv[] )
{
    int Sock;                       // 소켓
    int clen;
    struct sockaddr_in ServAddr;    // 구조체
    int port[2];                    // 포트 변수( port[0] 부터 ~ port[1] 까지 검색 )
    int stat = 1;                   // 1 : Close, Open 모두 출력, 0 : Open 만 출력
    int cnt = 0;                    // 검색된 Open port 수

    if( argc < 4 )          // 필요 매개변수가 없다면 사용방법을 알려줌
    {
        printf( " Usage :: <ip> <port> <port>  <0 or 1> \n");
        printf( " <  ip  > xxx.xxx.xxx.xxx ");
        printf( " < port > 0 ~ <port> ~ 65535 \n");
        printf( " <0 or 1>  0 : open + close, 1 :: only open \n");
        exit(0);
    }

    if( argc == 5 )         // 만약 뒤에 option 이 추가되었다면
    {
        if( atoi(argv[4]) == 0 ) stat = 1;      // 0 : stat = 1 : Open, Close
        else stat = 0;                               // else : stat = 0 ; Open
    }

    port[0] = atoi( argv[2] );
    port[1] = atoi( argv[3] );

    if( !( 0 < port[0] && port[0] < 65536 && 0 < port[1] && port[1] < 65536 ) ) 
    {   // Port 숫자가 유효하지 않다면

        printf( "%d %d\n\n 0 < port number < 65536 \n", port[0], port[1] );
        exit(0);
    }

    int i = port[0] - 1;        // i 번 반복( port[0] 부터 port[1] 까지 )
    while( ++i <= port[1] )
    {
        if( (Sock = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP) ) < 0 )  // 소켓 생성
        {
            printf("socket() error!\n");
            exit(0);
        }

        clen = sizeof( ServAddr );                                      // 구조체 설정
        memset( &ServAddr, 0, sizeof( ServAddr ) );
        ServAddr.sin_family = AF_INET;
        ServAddr.sin_addr.s_addr = inet_addr( argv[1] );
        ServAddr.sin_port = htons( i );

        int ret = connect( Sock, (struct sockaddr*)&ServAddr, clen );   // connect

        if( ret < 0 )   //connect() 성공 여부에 따라 open / close 나뉨
        {
            if( stat ) printf( "%d : Close\n", i );
        }else
        {
            cnt++;
            printf( "%d : Open\n", i );
        }

        close ( Sock );
    }
    printf("Open Port count : %d\n\n", cnt );
    return 0;
}

결과



소스는 제가 사랑하는 Limsy님께서 1분만에 구현하셨습니다.