├── .gitignore ├── .vimrc ├── Dockerfile ├── README.md ├── build_docker.sh ├── rm_docker.sh ├── run_docker.sh ├── task1 ├── README.md ├── echo_client.py └── echo_server.py ├── task2 ├── Dockerfile ├── README.md ├── entrypoint.sh ├── port_scanner.py ├── run_test_server.sh └── stop_test_server.sh ├── task3 ├── README.md ├── docker-compose.yml ├── run_tester.sh └── subnet_scanner.py └── task4 ├── README.md ├── nids.py ├── test.rules └── tester.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | -------------------------------------------------------------------------------- /.vimrc: -------------------------------------------------------------------------------- 1 | set ts=4 2 | set sw=4 3 | set et 4 | set nu 5 | set bg=dark 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:jammy 2 | RUN apt update -y 3 | ARG DEBIAN_FRONTEND=noninteractive 4 | ENV TZ=Asia/Seoul 5 | RUN apt install python3 vim tmux tcpdump iputils-ping traceroute nmap python3-pip net-tools tzdata -y 6 | RUN mkdir -p /root/homework1 7 | RUN pip3 install scapy 8 | COPY .vimrc /root/.vimrc 9 | WORKDIR /homework1 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Network Security Lab #1 2 | 3 | ## Due Date: 2024 April 07 4 | 5 | In this lab, we will implement simple network scanners and NIDS to understand how they work more intuitively. As previously mentioned, we will use Python 3 in order to quickly build and run network applications. 6 | 7 | This repository has four subdirectories, each of which corresponds to an individual task. Please read the following descriptions carefully before you get started. 8 | 9 | ## Installing and Running Docker 10 | 11 | To run the same environment across different OSes (i.e., Windows, macOS), we'll implement and test our programs on a Docker container, a lightweight virtualization platform. The easiest way of using Docker at any OS is to install Docker Desktop, which can be downloaded from [here](https://www.docker.com/products/docker-desktop/). You will need to choose a suitable binary according to your OS and hardware architecture. For example, I installed the Docker Desktop built for Apple Chip because my laptop is m1 MacBook. 12 | 13 | **!!Important**: if you use Windows, you must install **Git Bash** to make the provided bash script work well. Please click [this link](https://git-scm.com/download/win) and install the Git Desktop. 14 | 15 | Once you install Docker Desktop and run it, you will be able to use Docker commands. You can check this by opening terminal (i.e., Git Bash if you use Windows) and type `docker -v`. For example, in Windows, you should use Git Bash (I strongly recommend it because PowerShell didn't work well from my experience.). In macOS, you can use the terminal app or iTerm2. If you see that the terminal prints a Docker version, you're now prepared to run our Dockerfile. 16 | 17 | This directory contains some useful shell script files, such as `build_docker.sh` and `run_docker.sh`. Open the terminal and type `bash build_docker.sh`. It builds a Docker container image by reading `Dockerfile`. 18 | 19 | Now type `bash run_docker.sh`. You should see the following output: 20 | 21 | ``` 22 | jinwoo@MacBook-Pro homework1 % bash run_docker.sh 23 | root@9f963f7291fd:/homework1# 24 | ``` 25 | 26 | What just happened? Your Docker engine built a Docker image through reading Dockerfile, ran a container, and finally entered it. You can check this by seeing that the username (i.e., root) and hostname (9f963f7291fd) are different from that of your host's. 27 | 28 | It's important to note that a container is an isolated environment similar to a virtual machine (VM), so writing/removing a certain file won't affect your host directory. However, we often want to write a program from a host-side using IDEs, such as [Pycharm](https://www.jetbrains.com/ko-kr/pycharm/) and [VSCode](https://code.visualstudio.com/). For this, I made this directory transparent to the container, so if you make a change from the host side, it will be reflected from the container-side immediately. You can check this by typing the `ls` command as follows: 29 | 30 | ``` 31 | root@a95a45a58379:/homework1# ls 32 | Dockerfile README.md run_docker.sh init_docker.sh task1 task2 task3 33 | ``` 34 | 35 | You can see the same files of this directory at the container-side as well. 36 | 37 | **Note**: I strongly recommend that you implement and test your programs in the given Docker container. If you do so in your OS (e.g., Windows), some unexpected problems may occur when running your program in the container. 38 | 39 | ## Some Useful Tools 40 | 41 | In this lab, I assume that you're familiar with Linux systems. But, those who're NOT familiar with Linux systems can refer to [here](https://www.guru99.com/linux-commands-cheat-sheet.html) to look at some important commands. 42 | 43 | Also, you may need to utilize following tools for this lab: 44 | 45 | ### tmux 46 | 47 | When you do network programming, you may want to split a terminal into several windows to run programs there individually. For this purpose, you can simply type `tmux` after running a Docker container, and then you will see a new tmux terminal. There are many useful tmux commands, but it's enough to use the followings: 48 | 49 | ``` 50 | ctrl+b, " Vertical split 51 | ctrl+b, % Horizontal Split 52 | ctrl+b, arrow Move to the pane 53 | ctrl+b, x Kill pane 54 | ``` 55 | 56 | For more information, please refer to [here](https://gist.github.com/MohamedAlaa/2961058). 57 | 58 | ### tcpdump 59 | 60 | When testing network programs, you may want to see how actual packets look like and how they are exchanged. You can use `tcpdump`, a popular command line utility for capturing packets in Linux. You can simply type: 61 | 62 | ``` 63 | $ tcpdump -i lo -n 64 | ``` 65 | 66 | The above command will capture and display all packets involved in the interface `lo` whose IP address is "127.0.0.1". 67 | 68 | ### vi 69 | 70 | `vi` is a popular editor included by default on Linux operating systems. For example, you can simply type: 71 | 72 | ``` 73 | $ vi server.py 74 | ``` 75 | 76 | to create a python file named `server.py` and start editing. 77 | 78 | If you feel that `vi` is inconvenient, you can use other editors and IDEs as well, such as [Sublime Text](https://www.sublimetext.com/), VSCode, and pyCharm. 79 | 80 | ## Writing-up 81 | 82 | You need to write a report to describe how you implement and resolve challenges. Each task has some required questions you need to answer in your report. 83 | You can use any tool for writing, such as Word and 아래아한글, but you must submit a `.pdf` file, whose name should be `lab1_your_student_number.pdf`. 84 | 85 | ## Submission 86 | 87 | After finishing this lab, you need to compress the **contents** of this directory with your **report** before submission. 88 | The compressed file should be `lab1_your_student_number.zip`. 89 | You must submit your file to KLAS. 90 | -------------------------------------------------------------------------------- /build_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker image build -t cs3864 . 3 | docker network create cs3864_net --subnet 10.0.100.0/24 4 | -------------------------------------------------------------------------------- /rm_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker stop cs3864 4 | docker rm cs3864 5 | -------------------------------------------------------------------------------- /run_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | name="cs3864" 4 | output=`docker ps -aq --filter name=$name` 5 | 6 | if [ -z "$output" ] 7 | then 8 | if [[ "$OSTYPE" == "msys" ]]; then 9 | docker run --name $name -itd --network cs3864_net -v /$(pwd):/homework1/ cs3864 10 | else 11 | docker run --name $name -itd --network cs3864_net -v $(pwd):/homework1/ cs3864 12 | fi 13 | fi 14 | 15 | if [[ "$OSTYPE" == "msys" ]]; then 16 | winpty docker exec -it $name bash 17 | else 18 | docker exec -it $name /bin/bash 19 | fi 20 | -------------------------------------------------------------------------------- /task1/README.md: -------------------------------------------------------------------------------- 1 | # Task 1: Implementing Echo Client-Server 2 | 3 | In our class, we have learned that a basic TCP client-server model can be easily implemented with Python. This is the good starting point for learning how to implement a socket program. 4 | 5 | ## Requirement 6 | 7 | The `task1` directory has `echo_server.py` and `echo_client.py` where some code snippets are already written. Fill the rest of the code where the `TODO:` is commented in order to make them work properly. 8 | 9 | If you write code properly, the programs will be working as follows: 10 | 11 | 1. Run `python3 echo_server.py` in one terminal. 12 | 3. Run `python3 echo_client.py` in another terminal. 13 | 4. Type any string you want to send to server from the client. 14 | 5. Check that the string is printed out from the server correctly. 15 | 16 | ## Writing-up 17 | 18 | When writing a report, make sure that it contains answers for the following questions: 19 | 20 | 1. Briefly describe how your program works. 21 | 2. What's the purpose of `line 8` code in the `echo_server.py`? 22 | 3. If you want to use UDP instead of TCP in this program, which code snippets should be modified? 23 | -------------------------------------------------------------------------------- /task1/echo_client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | IP = '127.0.0.1' 4 | PORT = 9999 5 | BUF_SIZE = 4096 6 | 7 | socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 8 | #TODO: connect to the server 9 | 10 | try: 11 | while True: 12 | your_input = input().encode() 13 | #TODO: send your input string to the server 14 | response = socket.recv(BUF_SIZE) 15 | if not response: break 16 | print(response.decode()) 17 | except: 18 | socket.close() 19 | 20 | -------------------------------------------------------------------------------- /task1/echo_server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | IP = '0.0.0.0' 4 | PORT = 9999 5 | BUF_SIZE = 4096 6 | 7 | #TODO: make a server-socket 8 | server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 9 | #TODO: bind the IP, port number to the server-socket 10 | server.bind((IP, PORT)) 11 | #TODO: make the socket a listening state 12 | 13 | client, addr = server.accept() 14 | print(f"Connected from {addr}") 15 | 16 | try: 17 | while True: 18 | response = client.recv(BUF_SIZE) 19 | if not response: break 20 | #TODO: send the response back to the client 21 | print(buf.decode()) 22 | except: 23 | client.close() 24 | server.close() 25 | 26 | -------------------------------------------------------------------------------- /task2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:jammy 2 | RUN apt update -y 3 | RUN apt install openssh-server vsftpd apache2 net-tools -y 4 | COPY entrypoint.sh /entrypoint.sh 5 | 6 | ENTRYPOINT service ssh start && service vsftpd start && service apache2 start && bash 7 | -------------------------------------------------------------------------------- /task2/README.md: -------------------------------------------------------------------------------- 1 | # Task 2: Implementing Port Scanner 2 | 3 | We have learned that port scanning is one of effective methods to identify which services are currently running on a target host. In this task, you will implement a toy port scanner that scans a certain range of TCP ports for a given host. 4 | 5 | ## Requirement 6 | 7 | In this directory, `port_scanner.py` is given. This script takes `target_ip`, `start_portno`, `end_portno` as inputs. Thus, if you give the script a target IP address and TCP port range, it performs TCP scanning and prints out open ports. In order to make the script work, replace the `TODO:` comment with your code. 8 | 9 | To test your scanner, you can give your home router IP address or use the given dummy Docker container. If you execute `run_test_server.sh`, it creates a dummy Ubuntu server and prints out its IP address. Give this IP address to the scanner so that it scans open ports of the dummy server. If your scanner can find 21, 22, 80 open ports, it works well. 10 | 11 | Hint: We will simply invoke the `connect()` method for sending a TCP SYN packet. However, it often blocks until a SYN-ACK or RST packet comes from the target, so scanning time will be too long. How can we resolve this situation? Find a hint from [here](https://docs.python.org/ko/3/library/socket.html). 12 | 13 | ## Writing-up 14 | 15 | When writing a report, please include the answers for the following questions: 16 | 17 | 1. Describe how your scanner works. Concretely, describe how your logic scans open/closed ports of a target host. 18 | 2. If you want to perform UDP scanning instead of TCP, which part of this script should be modified? 19 | 3. If you run tcpdump while performing this task, you will see that if a port is closed, an opposing host will reply with a TCP RST packet. Suppose you send a UDP packet to a closed port on a specific host. Do you think the host will reply with a UDP packet? If not, which packet are you likely to receive? 20 | 21 | ## Ethical Issue 22 | 23 | Do only test your scanner against your home router, your local host, VMs, and containers. Please do **NOT** test this program against public networks, such as university and cafe. Performing TCP scans indiscriminately could be detected by security devices (e.g., IDS/IPS/firewall), and then your host can be blocked by them. 24 | -------------------------------------------------------------------------------- /task2/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash: 2 | service ssh start 3 | service vsftpd start 4 | service apache2 start 5 | bash 6 | -------------------------------------------------------------------------------- /task2/port_scanner.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import sys 3 | 4 | def port_scanner(target_ip, start_portno, end_portno): 5 | 6 | for port_number in range(start_portno, end_portno): 7 | #TODO: your code here 8 | 9 | try: 10 | #TODO: your code here 11 | except ConnectionRefusedError: 12 | continue 13 | except TimeoutError: 14 | continue 15 | 16 | if __name__ == '__main__': 17 | 18 | target_ip = sys.argv[1] 19 | start_portno = int(sys.argv[2]) 20 | end_portno = int(sys.argv[3]) 21 | 22 | port_scanner(target_ip, start_portno, end_portno) 23 | -------------------------------------------------------------------------------- /task2/run_test_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker build -t task2-tester . 3 | docker run -itd --name task2 --network cs3864_net task2-tester 4 | docker inspect task2 | grep IPAddress 5 | -------------------------------------------------------------------------------- /task2/stop_test_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker stop task2 3 | docker rm task2 4 | -------------------------------------------------------------------------------- /task3/README.md: -------------------------------------------------------------------------------- 1 | # Task 3: Implementing Subnet Scanner 2 | 3 | In our class, we've learned that an attacker can perform subnet scanning to discover alive hosts in a given subnet range. This task requires you to implement the subnet scanner. The `subnet_scanner.py` script takes a network prefix (e.g., 10.0.100.0/24) as input and performs UDP scanning to discover all possible hosts in the subnet (i.e., 254 hosts). 4 | 5 | ## Requirement 6 | 7 | In the script `subnet_scanner.py`, you need to implement two methods: `sniffer()` and `udp_sender()`. 8 | 9 | `sniffer()` captures all packets come to your host by using a raw socket. The raw socket is a special socket that allows us to modify TCP/IP headers, which are originally controlled by Linux kernel. By doing so, we can parse the header information of any incoming packet. When you send an UDP packet to an alive host, it replies with an **ICMP Port Unreachable** message, letting us know that the host is currently up. This means that when `sniffer()` captures a packet via raw socket, it needs to analyze its ICMP header information. You may need to refer [this document](https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol#Datagram_structure) to know what ICMP packet looks like. Note that you can utilize `IP` and `ICMP` classes in `subnet_scanner.py`. 10 | 11 | `udp_sender()` sends UDP packets (whose destination port is 19999) to all the hosts of a given subnet, which is taken from the parameter `subnet` from the command line. What you need to do is to get all the valid IP addresses belong to the subnet. For example, there are 254 valid hosts starting from 10.0.100.1 to 10.10.100.254 in 10.0.100.0/24. 12 | 13 | In order to execute the script, we type the following command: 14 | 15 | ``` 16 | $ python3 subnet_scanner.py [target_subnet] 17 | ``` 18 | 19 | When we type `Ctrl+C`, the program stops and prints out all the hosts discovered so far. 20 | 21 | You can test your subnet scanner in your home network (i.e., wifi subnet) or the given script `run_tester.sh`. 22 | When you type the following command (in your host), it prints out subnet information (which will be 10.0.100.0/24) and executes 5 containers: 23 | 24 | ``` 25 | jinwoo@MacBook-Pro task3 % ./run_tester.sh 26 | "Subnet": "10.0.100.0/24" 27 | [+] Running 5/0 28 | ⠿ Container task3-ubuntu1-1 Created 0.0s 29 | ⠿ Container task3-ubuntu2-1 Created 0.0s 30 | ⠿ Container task3-ubuntu3-1 Created 0.0s 31 | ⠿ Container task3-ubuntu5-1 Created 0.0s 32 | ⠿ Container task3-ubuntu4-1 Created 0.0s 33 | Attaching to task3-ubuntu1-1, task3-ubuntu2-1, task3-ubuntu3-1, task3-ubuntu4-1, task3-ubuntu5-1 34 | ``` 35 | 36 | Note that those 5 containers will run on the same network that you have previously run with `run_docker.sh` in the parent directory. 37 | If you correctly implement your scanner, it will find 7 IP addresses there (five for the above containers, one for the scanner where your scanner is running, and one for the Docker bridge interface.). 38 | 39 | ## Writing-up 40 | 41 | When writing a report, you need to include the answers for the following questions: 42 | 43 | 1. Describe how your subnet scanner works. You need to mention a *thread*. 44 | 2. In the case of subnet scanning, which protocol would be best between UDP and TCP? Explain why you think so. 45 | 46 | ## Ethical Issue 47 | 48 | Similar to the port scanning case, performing subnet scanning for public networks is NOT recommended. 49 | -------------------------------------------------------------------------------- /task3/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | services: 3 | ubuntu1: 4 | image: "ubuntu:jammy" 5 | tty: true 6 | networks: 7 | - network1 8 | ubuntu2: 9 | image: "ubuntu:jammy" 10 | tty: true 11 | networks: 12 | - network1 13 | ubuntu3: 14 | image: "ubuntu:jammy" 15 | tty: true 16 | networks: 17 | - network1 18 | ubuntu4: 19 | image: "ubuntu:jammy" 20 | tty: true 21 | networks: 22 | - network1 23 | ubuntu5: 24 | image: "ubuntu:jammy" 25 | tty: true 26 | networks: 27 | - network1 28 | 29 | networks: 30 | network1: 31 | name: cs3864_net 32 | external: true 33 | -------------------------------------------------------------------------------- /task3/run_tester.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker network inspect cs3864_net | grep Subnet 3 | docker compose up 4 | -------------------------------------------------------------------------------- /task3/subnet_scanner.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import ipaddress 3 | import struct 4 | import sys 5 | import time 6 | import threading 7 | 8 | # used for parsing an IP header 9 | class IP: 10 | def __init__(self, buf=None): 11 | header = struct.unpack('> 4 13 | self.ihl = header[0] & 0xF 14 | 15 | self.tos = header[1] 16 | self.len = header[2] 17 | self.id = header[3] 18 | self.offset = header[4] 19 | self.ttl = header[5] 20 | self.protocol_num = header[6] 21 | self.sum = header[7] 22 | self.src = header[8] 23 | self.dst = header[9] 24 | 25 | # human readable IP addresses 26 | self.src_address = ipaddress.ip_address(self.src) 27 | self.dst_address = ipaddress.ip_address(self.dst) 28 | 29 | # map protocol constants to their names 30 | self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"} 31 | try: 32 | self.protocol = self.protocol_map[self.protocol_num] 33 | except Exception as e: 34 | print('%s No protocol for %s' % (e, self.protocol_num)) 35 | self.protocol = str(self.protocol_num) 36 | 37 | # used for parsing an ICMP header 38 | class ICMP: 39 | def __init__(self, buff): 40 | header = struct.unpack(' 192.168.1.100 9999 25 | ``` 26 | ## Parsing Snort Rule 27 | 28 | Your first job is to parse a Snort rule file to build a rule set. 29 | The rule file contains several Snort rules like following: 30 | 31 | ``` 32 | alert tcp any any -> 192.168.1.0/24 any (msg:"r1 packet for subnet 192.168.1.0";) 33 | alert tcp any any -> any 23,25,21 (msg:"r2 packet for Telnet, FTP, and SSH";) 34 | alert udp any any -> any 10000:20000 (msg:"r3 udp ports from 10000 to 20000";) 35 | ... 36 | ``` 37 | 38 | Each row is a single Snort rule and it consists of two parts: **rule header** and **rule body**. 39 | 40 | The **rule header** includes action, protocol, IP addresses, port numbers, and direction of packets. The action indicates how Snort should work when an incoming packet is matched with a certain rule. For example, if the action is `alert`, your NIDS should print out a message (specified in `msg` field in the rule body) to a console. 41 | 42 | 43 | The **rule body** defines various options, such as the message being printed and packet details being analyzed. 44 | It's important to note that except for the `msg` field, others indicate specific packet fields (e.g., flags, itype) and payload (i.e., content). Also, your NIDS should take an action only if both the rule header and rule body are matched. For example, the following rule should be taken when a TCP packet whose destination port is 80 and its payload contains the string "GET". 45 | 46 | ``` 47 | alert tcp any any -> any 80 (content:"GET"; msg:"r5 HTTP GET message";) 48 | ``` 49 | 50 | For more information about the Snort rule syntax, please refer to the [Snort official document](https://docs.snort.org/rules/). 51 | 52 | ## Parsing Packets 53 | 54 | Your NIDS should captures an incoming a packet and analyzes its headers and payload. Packet capturing and parsing are not a easy task if we only use pure Python code. So, we utilize [Scapy](https://scapy.net/), a powerful packet manipulation tool that allows us to develop those code very easily. 55 | 56 | In order to use Scapy, we need to import Scapy library: 57 | 58 | ``` 59 | from scapy.all import * 60 | ``` 61 | 62 | If we want to capture a packet, we can use `sniff()`: 63 | 64 | ``` 65 | sniff(iface='eth0', prn=lambda p: parse_packet(p), filter='ip') 66 | ``` 67 | 68 | The above code captures all packets detected at the interface `eth0` (inside the container). Note that we use the `prn` option, so whenever a packet `p` is captured, it invokes `parse_packet()`, which is the method we'll implement. `filter='ip'` indicates that we'll only see IP packets. 69 | 70 | When a packet is captured, we need to parse it to extract its information. We can utilize Scapy again to do so very easily. 71 | For example, see the following code: 72 | 73 | ``` 74 | # Assume that the packet variable is `packet` and the captured packet is TCP packet. 75 | ip = packet[IP] 76 | print(f"{ip.src}, {ip.dst}) 77 | tcp = packet[TCP] 78 | print(f"{tcp.sport}, {tcp.dport}) 79 | ``` 80 | 81 | The above code extracts IP and TCP headers from the capture packet and accesses its fields. 82 | For more information, see [this document](https://scapy.readthedocs.io/en/latest/usage.html#starting-scapy). 83 | 84 | ## Printing Messages 85 | 86 | If an incoming packet is matched with one of Snort rules, your NIDS must print out a message. The message should follow the following format. 87 | 88 | ``` 89 | [detection_date_and_time] [msg] [protocol] [src_ip] [src_port] [direction] [dst_ip] [dst_port] 90 | ``` 91 | 92 | # Writing-up 93 | 94 | When writing a report, include the answers for the following questions: 95 | 96 | 1. Describe how your NIDS work and how to implement it. 97 | 2. What method did you use for implementing NIDS efficiently? 98 | 3. Do you think that your NIDS can be used for a large network? 99 | -------------------------------------------------------------------------------- /task4/nids.py: -------------------------------------------------------------------------------- 1 | # Skeleton code for NIDS 2 | import socket 3 | import sys 4 | import ipaddress 5 | from scapy.all import * 6 | from datetime import datetime 7 | 8 | protocol_dict = {1:'icmp', 6:'tcp', 17: 'udp'} 9 | option_dict = {'tcp': ['seq', 'ack', 'window', 'flags'], 10 | 'ip': ['id', 'tos', 'ttl'], 11 | 'icmp': ['itype', 'icode']} 12 | 13 | # You can utilize this class to parse the Snort rule and build a rule set. 14 | class Rule: 15 | def __init__(self, action, protocol, src_ip, src_port, direction, dst_ip, dst_port, options, msg, original_rule): 16 | self.action = action 17 | self.protocol = protocol 18 | self.src_ip = src_ip 19 | self.src_port = src_port 20 | self.direction = direction 21 | self.dst_ip = dst_ip 22 | self.dst_port = dst_port 23 | self.options = options 24 | self.msg = msg 25 | 26 | self.original_rule = original_rule 27 | 28 | def __str__(self): 29 | return (f"action: {self.action}\n" 30 | f"protocol: {self.protocol}\n" 31 | f"src_ip: {self.src_ip}\n" 32 | f"src_port: {self.src_port}\n" 33 | f"direction: {self.direction}\n" 34 | f"dst_ip: {self.dst_ip}\n" 35 | f"dst_port: {self.dst_port}\n" 36 | f"options: {self.options}") 37 | 38 | def parse_rule(line): 39 | #TODO: your code here 40 | pass 41 | 42 | def parse_packet(packet, rule_set): 43 | #TODO: your code here 44 | pass 45 | 46 | # example codes: 47 | # ether = packet[Ether] 48 | # ip = packet[IP] 49 | # tcp = packet[TCP] 50 | # ip.show() 51 | # tcp.summary() 52 | 53 | if __name__ == '__main__': 54 | rule_file = sys.argv[1] 55 | 56 | f = open(rule_file, 'r') 57 | 58 | rule_set = [] 59 | lines = f.readlines() 60 | for line in lines: 61 | rule = parse_rule(line) 62 | rule_set.append(rule) 63 | 64 | print("Start sniffing") 65 | sniff(iface='eth0', prn=lambda p: parse_packet(p, rule_set), filter='ip') 66 | 67 | f.close() 68 | 69 | -------------------------------------------------------------------------------- /task4/test.rules: -------------------------------------------------------------------------------- 1 | alert tcp any any -> 192.168.1.0/24 any (msg:"r1 packet for subnet 192.168.1.0";) 2 | alert tcp any any -> any 23,25,21 (msg:"r2 packet for Telnet, FTP, and SSH";) 3 | alert udp any any -> any 10000:20000 (msg:"r3 udp ports from 10000 to 20000";) 4 | alert tcp any any -> any any (flags:S; msg:"r4 tcp SYN packet";) 5 | alert tcp any any -> any 80 (content:"GET"; msg:"r5 HTTP GET message";) 6 | alert tcp any any -> any 22 (content:"/bin/sh"; msg:"r6 remote shell execution";) 7 | alert udp any any -> 8.8.8.8 53 (msg:"r7 DNS query for Google open resolver";) 8 | alert icmp any any -> 223.194.1.180 any (itype:8; icode:0; msg:"r8 ping to KWU";) 9 | alert icmp any any -> any any (itype:8; icode:0; msg:"r9 ping";) 10 | -------------------------------------------------------------------------------- /task4/tester.py: -------------------------------------------------------------------------------- 1 | # Tester example. Build your own tester to verify your NIDS! 2 | from scapy.all import * 3 | import random 4 | 5 | if __name__ == '__main__': 6 | packet = (Ether() / IP(dst = "192.168.1.100") / TCP(dport = random.randrange(1, 1024))) 7 | sendp(packet, iface='eth0') 8 | --------------------------------------------------------------------------------