Core dump

〜戦わなければ生き残れない〜

SYNスキャンをするプログラム

HACKING:美しき策謀を読んでいるのですが、この本のネットワークの章で取り扱われてるlibnetライブラリは古いバージョンの1.0で、現在入手できるバージョンのものとは互換性がないようです。

構造体の名前や関数名とその仕様が一新されている部分が多く、この本の通りにプログラムを実装することはできません。

なので新しいバージョンのlibnetの使い方を調べた上で、今回は本の中でも紹介されているSYNスキャンを行うプログラムを作成しようと思います。SYNスキャンはターゲット上の特定のポートにSYNパケットを送り、SYN/ACKが帰ってきたらそのポートが空いていると判断するというものです。
通常通りの3wayハンドシェイクの流れですね。
今回のプログラムでは一応最後にRSTパケットを送信していますが、私の勉強不足もあり、この処理が有効に作用するものであるか正直自身がありません。

そのような者のプログラムですので、拙いものになりますが、あしからず。

#include<stdio.h>
#include<libnet.h>
#include<pcap.h>

void usage(char *name){
	fprintf(stderr, "usage: %s <target IP> <target port> <device>\n", name);
}

//libnetコンテキストを破棄
void quit(libnet_t *l){
	libnet_destroy(l);
	printf("quit\n");
	exit(0);
}

//受信したパケットにACKフラグが立っているか確認
int check_ctrflag(const u_char *header_start){
	u_int header_size;
	const struct libnet_tcp_hdr *tcp_header;
	uint8_t ctrflag;

	tcp_header = (const struct libnet_tcp_hdr *)header_start;
	if(tcp_header->th_flags & TH_ACK)
		return 1;
	else
		return 0;
}

void pcap_fatal(const char *failed_in, const char *errbuf){
	printf("fatal error: %s: %s\n", failed_in, errbuf);
	exit(1);
}

//キャプチャしている間に表示するスピナー(全く必要ない処理ですが何かカッコイイのでつけました)
void efect(int second){
	int cnt = 0;
	char mark[5] = {'|', '/', '-', '\\'};
	printf("capturing packet....");
	for(cnt=0; cnt<second; cnt++){
		//\033[?25l はカーソルを非表示にする
		printf("\033[?25l");
		//\e[1D でカーソルの左一文字を削除し、スピナーを上書きする 
		fprintf(stdout, "%c\e[1D", mark[cnt%4]);
		fflush(stdout);
		usleep(80000);
	}
	// \e[2K\r で一行を削除し、行頭までカーソルを移動
	fprintf(stdout, "\e[2K\r");
	fflush(stdout);
	// \033[?25h でカーソルの表示を復活
	printf("\n\033[?25h");
}


int main(int argc, char *argv[]){
	int flag = 0;
	libnet_t *l;
	char libnet_errbuf[LIBNET_ERRBUF_SIZE];
	uint32_t dstip;
	u_int16_t dport;
	libnet_ptag_t tcp = 0;
	libnet_ptag_t ip = 0;

	struct pcap_pkthdr header;
	const u_char *packet;
	char pcap_errbuf[PCAP_ERRBUF_SIZE];
	char *device = argv[3];
	pcap_t *pcap_handle;

	if(argc != 4){
		usage(argv[0]);	
		exit(0);
	}

	//syn packetを作る
	l = libnet_init(LIBNET_RAW4, "enp0s8", libnet_errbuf);
	if(l==NULL){
		fprintf(stderr, "Error opening context: %s", libnet_errbuf);
		exit(1);
	}

	dstip = libnet_name2addr4(l, argv[1], LIBNET_DONT_RESOLVE);
	dport = (u_int16_t)atoi(argv[2]);


	tcp = libnet_build_tcp(
			libnet_get_prand(LIBNET_PRu16),
			dport,
			libnet_get_prand(LIBNET_PRu32),
			libnet_get_prand(LIBNET_PRu32),
			TH_SYN,
			libnet_get_prand(LIBNET_PRu16),
			0,
			0,
			libnet_get_prand(LIBNET_PRu16),
			0,
			0,
			l,
			0);

	if(tcp == -1){
		fprintf(stderr, "Unable to build TCP header: %s\n", libnet_geterror(l));
		exit(1);
	}


	ip = libnet_autobuild_ipv4(
			libnet_get_prand(LIBNET_PRu16),
			IPPROTO_TCP,
			dstip,
			l);

	if(ip == -1){
		fprintf(stderr, "Unable to build IP header: %s\n", libnet_geterror(l));
		exit(1);
	}

	pcap_handle = pcap_open_live(device, 4096, 0, 0, pcap_errbuf);
	if(pcap_handle == NULL)
		pcap_fatal("pcap_open_live", pcap_errbuf);

	if(libnet_write(l) == -1){
		fprintf(stderr, "Unable to send packet: %s\n", libnet_geterror(l));
		exit(1);
	}

	//1個目のパケットは自分が送ったパケットなので、2個目のパケットを受信
	packet = pcap_next(pcap_handle, &header);
	efect(30);
	packet = pcap_next(pcap_handle, &header);
	int count =0;
	
	flag = check_ctrflag(packet+34);
	if(flag == 1){
		printf("this port is open!\n");

		//RSTパケットを送信
		//正直ここはいらないかもしれない
		tcp = libnet_build_tcp(
			libnet_get_prand(LIBNET_PRu16),
			dport,
			libnet_get_prand(LIBNET_PRu32),
			libnet_get_prand(LIBNET_PRu32),
			TH_RST,
			libnet_get_prand(LIBNET_PRu16),
			0,
			0,
			libnet_get_prand(LIBNET_PRu16),
			0,
			0,
			l,
			0);

		if(tcp == -1){
			fprintf(stderr, "Unable to build TCP header: %s\n", libnet_geterror(l));
			exit(1);
		}


		ip = libnet_autobuild_ipv4(
			libnet_get_prand(LIBNET_PRu16),
			IPPROTO_TCP,
			dstip,
			l);

		if(ip == -1){
			fprintf(stderr, "Unable to build IP header: %s\n", libnet_geterror(l));
			exit(1);
		}

		if(libnet_write(l) == -1){
			fprintf(stderr, "Unable to send packet: %s\n", libnet_geterror(l));
			exit(1);
		}
	}
	else
		printf("this port is closed :(\n");	
	
	

	pcap_close(pcap_handle);
	libnet_destroy(l);
	return 0;
}

libnetでパケットを送信するにはまず、libnet_init関数でlibnet_t型のlibnetコンテキストを作成しなければならないようです。
deviceにはインターフェース名を指定してください。(ifconfigでみれます)
その後、TCPヘッダ->IPヘッダの順にヘッダを作ります。
これにはlibnet_build_tcpやlibnet_build_ip関数を使用します。この関数にはそれぞれのヘッダ要素に値を設定していきますが、今回はほとんどの要素にlibnet_get_prand関数でランダムな値を設定しています。
ヘッダを作ったらlibnet_write関数で実際にパケットを送信します。エラーが発生すると-1を返すようです。

次はlibcapライブラリを使ってターゲットからの応答をキャプチャします。
libcapについては本で使われているバージョンと同じ記法で使えるようなので苦労しませんでした。なので省きます。
キャプチャしたパケットの先頭から14(etherヘッダの長さ) + 20(ipヘッダの長さ) = 34バイト分進んだ部分から最後までをTCPヘッダとして扱います。これは、libnetが用意するlibnet_tcp_hdr型に変換することで行います。
そしてlibnet_tcp_hdr構造体のth_flags要素とTH_ACKを&演算子で比較し、ACKフラグが立っているか確認します。

以上がSYNスキャンプログラムになります。このような処理はnmap -sSで簡単に行えますが、自分で作ってみると色々学べるものがあります。 今後も色々作って行こうと思います。

ここまで見ていただき、ありがとうございました。