1#!/usr/bin/env python3 2# 3# SPDX-License-Identifier: BSD-2-Clause-FreeBSD 4# 5# Copyright (c) 2019 Kristof Provost <kp@FreeBSD.org> 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions 9# are met: 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26# SUCH DAMAGE. 27 28import argparse 29import logging 30logging.getLogger("scapy").setLevel(logging.CRITICAL) 31import scapy.all as sp 32import sys 33from sniffer import Sniffer 34 35def check_icmp_error(args, packet): 36 ip = packet.getlayer(sp.IP) 37 if not ip: 38 return False 39 if ip.dst != args.to[0]: 40 return False 41 42 icmp = packet.getlayer(sp.ICMP) 43 if not icmp: 44 return False 45 if icmp.type != 3 or icmp.code != 3: 46 return False 47 48 return True 49 50def main(): 51 parser = argparse.ArgumentParser("CVE-2019-icmp.py", 52 description="CVE-2019-icmp test tool") 53 parser.add_argument('--sendif', nargs=1, 54 required=True, 55 help='The interface through which the packet will be sent') 56 parser.add_argument('--recvif', nargs=1, 57 required=True, 58 help='The interface on which to check for the packet') 59 parser.add_argument('--src', nargs=1, 60 required=True, 61 help='The source IP address') 62 parser.add_argument('--to', nargs=1, 63 required=True, 64 help='The destination IP address') 65 66 args = parser.parse_args() 67 68 # Send the allowed packet to establish state 69 udp = sp.Ether() / \ 70 sp.IP(src=args.src[0], dst=args.to[0]) / \ 71 sp.UDP(dport=53, sport=1234) 72 sp.sendp(udp, iface=args.sendif[0], verbose=False) 73 74 # Start sniffing on recvif 75 sniffer = Sniffer(args, check_icmp_error, args.recvif[0]) 76 77 # Send the bad error packet 78 icmp_reachable = sp.Ether() / \ 79 sp.IP(src=args.src[0], dst=args.to[0]) / \ 80 sp.ICMP(type=3, code=3) / \ 81 sp.IP(src="4.3.2.1", dst="1.2.3.4") / \ 82 sp.UDP(dport=53, sport=1234) 83 sp.sendp(icmp_reachable, iface=args.sendif[0], verbose=False) 84 85 sniffer.join() 86 if sniffer.correctPackets: 87 sys.exit(1) 88 89 sys.exit(0) 90 91if __name__ == '__main__': 92 main() 93