Esempio n. 1
0
/*
UninstallFilter removes the firewall rule associated with the specified filter
request.

If forward is true, the rule blocks forwarded traffic.  This option is true
for routers.

This function returns immediately, and the rule is removed asynchronously.
*/
func UninstallFilter(req Request, forward bool) {
	var target string
	if forward {
		target = "FORWARD"
	} else {
		target = "OUTPUT"
	}

	/*Run the iptables command to remove the filter.*/
	cmd := exec.Command("iptables",
		"-D", target,
		"-s", fmt.Sprintf("%s/32", req.SrcIP),
		"-d", fmt.Sprintf("%s/32", req.DstIP),
		"-j", "DROP")

	go func() {
		/*If the command fails, it's because the filter has already been removed.
		This happens all the time, since all temporary filters are automatically
		removed after a timeout, weather or not they have already been legitimately
		removed.*/
		if cmd.Run() == nil {
			log.Printf("Removing filter: [%s to %s]", aitf.Hostname(req.SrcIP), aitf.Hostname(req.DstIP))
			return
		}
	}()
}
/*listenForRouteRecords intercepts all incoming packets to this host and
removes their route records before letting the operating system process them.

If listenForRouteRecords is true, it also sends filter requests whenever an
ICMP packet from 10.4.32.4 arrives.
*/
func listenForRouteRecords(sendFilterRequests bool) {
	routerecord.Init()

	nfq, err := netfilter.NewNFQueue(0, 100000, 0xffff)
	if err != nil {
		log.Fatal(err)
	}

	defer nfq.Close()

	for packet := range nfq.GetPackets() {
		var ipLayer *layers.IPv4

		if layer := packet.Packet.Layer(layers.LayerTypeIPv4); layer != nil {
			ipLayer = layer.(*layers.IPv4)
		} else {
			packet.SetVerdict(netfilter.NF_ACCEPT)
			continue
		}

		if routerecord.Shimmed(ipLayer) {
			/*If the IP layer has a shim, remove it.*/
			log.Println("Got AITF shimmed packet from", aitf.Hostname(ipLayer.SrcIP))
			rr := routerecord.Unshim(ipLayer)
			log.Println(rr)

			if sendFilterRequests {
				/*If this is a malicious packet, construct a filter request to stop any
				future undesired traffic from this flow. The policy module simply
				considers any ICMP traffic from the attacker to be "malicous".*/
				if ipLayer.Protocol == layers.IPProtocolICMPv4 && ipLayer.SrcIP.Equal(net.ParseIP("10.4.32.4")) {
					log.Println("Malicious packet detected from", aitf.Hostname(ipLayer.SrcIP))

					req := filter.Request{
						Type:  filter.FilterReq,
						SrcIP: ipLayer.SrcIP,
						DstIP: ipLayer.DstIP,
						Flow:  *rr,
					}
					req.Send(rr.Path[len(rr.Path)-1].IP)
				}
			}

			/*Serialize the IP packet. Assuming this is successful, accept it.*/
			b, err := routerecord.Serialize(ipLayer)
			if err != nil {
				log.Println(err)
				packet.SetVerdict(netfilter.NF_DROP)
			} else {
				packet.SetResult(netfilter.NF_ACCEPT, b)
			}
		} else {
			/*Any packets without a shim can be accepted as-is.*/
			log.Println("Got", ipLayer.Protocol, "packet from", aitf.Hostname(ipLayer.SrcIP))
			packet.SetVerdict(netfilter.NF_ACCEPT)
		}
	}
}
/*addRouteRecords intercepts any packets being sent through this router
and adds route records to a shim layer before forwarding them.*/
func addRouteRecords() {
	localIP := aitf.LocalIP()
	log.Println("My IP address is", aitf.Hostname(localIP))

	nfq, err := netfilter.NewNFQueue(0, 100000, 0xffff)
	if err != nil {
		log.Fatal(err)
	}

	defer nfq.Close()

	/*Listen for any packets being forwarded by this router and create/update the
	route record shim layer in each of them.*/
	for packet := range nfq.GetPackets() {
		var ipLayer *layers.IPv4

		/*Get the IPv4 layer, or ignore it if it doesn't exist. */
		if layer := packet.Packet.Layer(layers.LayerTypeIPv4); layer != nil {
			ipLayer = layer.(*layers.IPv4)
		} else {
			packet.SetVerdict(netfilter.NF_ACCEPT)
			continue
		}

		/*Any local loopback packets can be accepted with modification, as they
		do not actually go through the network. This is most likely to happen
		while testing using an iptables rule that may include loopback traffic.*/
		if ipLayer.SrcIP.IsLoopback() {
			packet.SetVerdict(netfilter.NF_ACCEPT)
			continue
		}

		if routerecord.Shimmed(ipLayer) {
			log.Println("Got AITF shimmed packet from", aitf.Hostname(ipLayer.SrcIP), "for", aitf.Hostname(ipLayer.DstIP))
		} else {
			log.Println("Got", ipLayer.Protocol, "packet from", aitf.Hostname(ipLayer.SrcIP), "for", aitf.Hostname(ipLayer.DstIP))
		}

		/*Shim up the packet. One of the assumptions made is that each route knows
		which hosts support AITF. All hosts in the test scenerios do, so there's
		never a need for a router to remove the shim layer.*/
		routerecord.Shim(ipLayer, routerecord.NewRouter(localIP, ipLayer.DstIP))

		/*Serialize the IP packet. Assuming this is successful, accept it.*/
		b, err := routerecord.Serialize(ipLayer)
		if err != nil {
			log.Println(err)
			packet.SetVerdict(netfilter.NF_DROP)
		} else {
			packet.SetResult(netfilter.NF_ACCEPT, b)
		}
	}
}
Esempio n. 4
0
/*
InstallFilter adds a firewall rule to implement the requested filter. The
filter will be removed after the specified duration has passed.

If forward is true, the rule will block forwarded traffic.  This option is true
for routers.

This function returns immediately, and the rule is applied and removed
asynchronously.
*/
func InstallFilter(req Request, d time.Duration, forward bool) {
	log.Printf("Adding filter: [%s to %s] for %s", aitf.Hostname(req.SrcIP), aitf.Hostname(req.DstIP), d)

	/*Run the iptables command to add the filter*/
	var target string
	if forward {
		target = "FORWARD"
	} else {
		target = "OUTPUT"
	}

	add := exec.Command("iptables",
		"-I", target,
		"-s", fmt.Sprintf("%s/32", req.SrcIP),
		"-d", fmt.Sprintf("%s/32", req.DstIP),
		"-j", "DROP")

	remove := exec.Command("iptables",
		"-D", target,
		"-s", fmt.Sprintf("%s/32", req.SrcIP),
		"-d", fmt.Sprintf("%s/32", req.DstIP),
		"-j", "DROP")

	err := add.Run()
	if err != nil {
		log.Println(err)
		return
	}

	/*Uninstall the filter after sleeping for d*/
	go func() {
		time.Sleep(d)
		if remove.Run() == nil {
			log.Println("Filter timed out.")
			log.Printf("Removing filter: [%s to %s]", aitf.Hostname(req.SrcIP), aitf.Hostname(req.DstIP))
			return
		}
	}()
}
/*
Send sends the given filter.Request over UDP port 54321 to the given
IP address.
*/
func (req Request) Send(to net.IP) error {
	log.Println("Sending", req.Type, "to", aitf.Hostname(to))

	udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:54321", to))
	if err != nil {
		return err
	}

	udpConn, err := net.DialUDP("udp", nil, udpAddr)
	if err != nil {
		return err
	}

	var b bytes.Buffer
	req.WriteTo(&b)
	b.WriteTo(udpConn)
	return nil
}
/*
listenForFilterRequest waits for a filter request from a client to come.  Then,
it verifies the authenticity of the request and takes the appropriate action
based on the AITF filter request protocol.
*/
func listenForFilterRequest(mode complianceMode) {
	if handshakes == nil {
		handshakes = make(map[uint64](*filter.Request))
	}

	/*Open up a UDP server on the filter request port and handle any messages
	that arrive.*/
	serverAddr, err := net.ResolveUDPAddr("udp", ":54321")
	if err != nil {
		log.Fatal(err)
	}

	serverConn, err := net.ListenUDP("udp", serverAddr)
	if err != nil {
		log.Fatal(err)
	}

	defer serverConn.Close()

	buf := make([]byte, 5000)
	for {
		n, addr, _ := serverConn.ReadFromUDP(buf)

		/*Read a request from the UDP connection*/
		var req filter.Request
		_, err := req.ReadFrom(bytes.NewBuffer(buf[:n]))
		if err != nil {
			log.Println(err)
			continue
		}

		/*Throw the request away if it is not authentic.*/
		if !req.Authentic() {
			log.Println("Received a forged filter request!")
			continue
		}

		log.Println("Got", req.Type, "from", aitf.Hostname(addr.IP))

		switch req.Type {
		case filter.FilterReq:
			/*When we receive a filter request, install a temporary filter and begin
			a counter-connection with the attacker's router. Also let the victim know
			that the attack should have stopped with a filter ACK. The filter is
			installed for the full filter time instead of the temporary time, but is
			automatically removed when we get a filter ACK.*/
			filter.InstallFilter(req, filter.LongFilterTime, true)
			req.Type = filter.FilterAck
			req.Send(addr.IP)

			/*If we've blocked this filter before and it's still happening, escalate
			the filter and just block it here.*/
			for _, req2 := range shadowFilters {
				if req2.SrcIP.Equal(req.SrcIP) && req2.DstIP.Equal(req.DstIP) {
					log.Println("Escalating request.")
					return
				}
			}

			req.Type = filter.CounterConnectionSyn
			req.Send(req.Flow.Path[0].IP)

		case filter.CounterConnectionSyn:
			if mode == comply || mode == lie {
				/*When we get a counter-connection SYN, continue the three-way handshake
				with a SYN-ACK. We don't install a filter until we get an ACK back with
				the right nonce.*/
				req.Type = filter.CounterConnectionSynAck
				req.Nonce = uint64(rand.Int63())
				handshakes[req.Nonce] = &req
				req.Send(addr.IP)
			}

		case filter.CounterConnectionSynAck:
			if mode == comply || mode == lie {
				/*When we receive a response to a counter-connection, complete the
				three-way handshake.*/
				req.Type = filter.CounterConnectionAck
				req.Send(addr.IP)
			}

		case filter.CounterConnectionAck:
			if mode == comply || mode == lie {
				/*When we receive a counter-connection ACK, make sure we're actually
				waiting for a response to that handshake, then install a temporary
				filter.*/
				originalReq := handshakes[req.Nonce]
				if originalReq == nil {
					log.Println("Received a forged three-way handshake:", req)
					continue
				} else {
					delete(handshakes, req.Nonce)
				}
			}

			if mode == comply {
				/*The filter is installed for the full filter time instead of the
				temporary time, but is automatically removed when we get a filter ACK.*/
				filter.InstallFilter(req, filter.LongFilterTime, true)

				/*The attacker should be informed of its wrongdoing, and the victim's
				router should be informed that this router is complying with the
				request.*/
				req.Type = filter.FilterReq
				req.Send(req.SrcIP)
				req.Type = filter.FilterAck
				req.Send(addr.IP)
			}

		case filter.FilterAck:
			if mode == comply {
				/*When we get acknowledgement of compliance with a filter, we can remove
				our temporary filter. Nobody lies on the internet.*/
				filter.UninstallFilter(req, true)

				shadowFilters = append(shadowFilters, req)
			}
		}
	}
}
/*
listenForFilterRequest waits for a filter request from a router to come.  Then,
it verifies the authenticity of the request and takes some action.

If action is comply, it filters the attack.

If action is ignore, it logs the request but does nothing about it.comply

If action is lie, it complies with the request but doesn't actually add a filter.
*/
func listenForFilterRequest(mode complianceMode) {
	/*Open up a UDP server on the filter request port and handle any messages
	that arrive.*/
	serverAddr, err := net.ResolveUDPAddr("udp", ":54321")
	if err != nil {
		log.Fatal(err)
	}

	serverConn, err := net.ListenUDP("udp", serverAddr)
	if err != nil {
		log.Fatal(err)
	}

	defer serverConn.Close()

	buf := make([]byte, 5000)
	for {
		n, addr, _ := serverConn.ReadFromUDP(buf)

		/*Read a request from the UDP connection*/
		var req filter.Request
		_, err := req.ReadFrom(bytes.NewBuffer(buf[:n]))
		if err != nil {
			log.Println(err)
			continue
		}

		log.Println("Got", req.Type, "from", aitf.Hostname(addr.IP))
		switch req.Type {
		case filter.FilterReq:
			switch mode {
			case comply:
				log.Println("Complying with filter request...")

				/*If this host is okay with filter requests, add a firewall rule to
				block the requested flow and respond with an acknowledgement.*/
				filter.InstallFilter(req, filter.LongFilterTime, false)
				req.Type = filter.FilterAck
				req.Send(addr.IP)

			case ignore:
				log.Println("Ignoring filter request...")

			case lie:
				log.Println("Pretending to comply with filter request...")

				/*If this host is a lier, send an acknowledgement without actually
				installing a filter rule.*/
				req.Type = filter.FilterAck
				req.Send(addr.IP)
			}

		case filter.FilterAck:
			/*Do nothing.  The routers should take care of mitigating the attack from
			now on.*/

		default:
			/*Hosts shouldn't get any of the other message types.*/
			log.Println("Unexpected filter request:", req)
		}
	}
}