Why are there multiple IP addresses for a IP object? - scapy

In scapy, I see the following.
>>> a=IP(dst="www.slashdot.org/30")
>>> [p for p in a]
[<IP dst=216.105.38.12 |>,
<IP dst=216.105.38.13 |>,
<IP dst=216.105.38.14 |>,
<IP dst=216.105.38.15 |>]
But I don't see multiple targets with dig. Does anybody know why is it so? What is the "/30" in the above IP command mean?
$ dig www.slashdot.org a +noall +answer
www.slashdot.org. 384 IN A 216.105.38.15

Subnet Math
The /30 refers to CIDR notation. It is equivalent to a subnet mask of 255.255.255.252.
Essentially it's a mask to determine which bits of the IPv4 address are network bits and host bits.
/30 and 216.105.38.15 in binary are
11111111.11111111.11111111.11111100
11011000.01101001.00100110.00001111
To get the network address, you use a binary & to get 216.105.38.12. This subnet consists of all combinations of addresses where the host bits are variable. So these last two bits can be 00, 01, 10, or 11 (i.e. 0, 1, 2, 3). This translates to the .12, .13, .14, .15 we see scapy output.
Scapy classes
Per the scapy IP class (scapy.layers.inet.IP), when you input a subnet for the dest IP (scapy.layers.inet.DestIPField) with dst=, it's interpreted as a subnet (scapy.base_classes.Net), and all addresses in the subnet are returned.
So I will get the same result if I pass the subnet to the Net class.
>>> from scapy.base_classes import Net # if in Python and not Scapy
>>> a = Net("www.slashdot.net/30")
>>> [p for p in a]
['216.105.38.12', '216.105.38.13', '216.105.38.14', '216.105.38.15']

Related

Scapy - crafted ICMP replies being ignored by sender

I have wrote a small app to respond to ICMP, however it seems that the sender is ignoring the ICMP replies. Inspecting the packets in Wireshark they are missing the timestamp fields, and working ICMP replies from actual devices contain the timestamp fields, is it just a case I'm missing them in mine?
I've poisoned the ARP cache so it has a correct ARP mapping in the ARP table.
I've confirmed using TCPDump that the ICMP replies are at least hitting the interface, not sure if they are being processed beyond that - if anyone has any advice on where to check for that, it would be appreciated
def respond_to_icmp(self, icmp_packet):
if icmp_packet["IP"].dst == self.ip: # if this is my IP
eth = Ether(dst=icmp_packet["Ether"].src, src=self.mac)
ip = IP(src=icmp_packet["IP"].dst, dst=icmp_packet["IP"].src)
icmp = ICMP(type=0, id=icmp_packet['ICMP'].id, seq=icmp_packet['ICMP'].seq)
icmp_reply = eth/ip/icmp
sendp(icmp_reply, iface=self.interface) # eth0 (on which it is received)
ICMP Reply Packet
###[ Ethernet ]###
dst = de:ad:be:ef:ee:ff
src = aa:bb:cc:dd:ee:ff
type = IPv4
###[ IP ]###
version = 4
ihl = None
tos = 0x0
len = None
id = 1
flags =
frag = 0
ttl = 64
proto = icmp
chksum = None
src = 192.168.3.46
dst = 192.168.3.1
\options \
###[ ICMP ]###
type = echo-reply
code = 0
chksum = None
id = 0x6b86
seq = 0x19f
ICMP activity from sender
21:14:06.487711 IP 192.168.3.46 > 192.168.3.1: ICMP echo reply, id 0, seq 0, length 8
21:14:07.417939 IP 192.168.3.1 > 192.168.3.46: ICMP echo request, id 27526, seq 52, length 64
21:14:07.487494 IP 192.168.3.46 > 192.168.3.1: ICMP echo reply, id 0, seq 0, length 8
21:14:08.441948 IP 192.168.3.1 > 192.168.3.46: ICMP echo request, id 27526, seq 53, length 64
21:14:08.548995 IP 192.168.3.46 > 192.168.3.1: ICMP echo reply, id 0, seq 0, length 8
Actual ICMP Result
From 192.168.3.1 icmp_seq=368 Destination Host Unreachable
From 192.168.3.1 icmp_seq=369 Destination Host Unreachable
From 192.168.3.1 icmp_seq=370 Destination Host Unreachable
From 192.168.3.1 icmp_seq=458 Destination Host Unreachable
From 192.168.3.1 icmp_seq=459 Destination Host Unreachable
From 192.168.3.1 icmp_seq=460 Destination Host Unreachable
Turns out when Linux is responsible for ICMP, it includes a nice little data payload, without adding this payload to the reply, Linux must think that there was an error or malformation of packet.
def respond_to_icmp(self, icmp_packet):
if icmp_packet["IP"].dst == self.ip:
eth = Ether(dst=icmp_packet["Ether"].src, src=icmp_packet["Ether"].dst)
ip = IP(src=icmp_packet["IP"].dst, dst=icmp_packet["IP"].src)
icmp = ICMP(type=0, id=icmp_packet['ICMP'].id, seq=icmp_packet['ICMP'].seq)
raw = Raw(load=icmp_packet["Raw"].load)
icmp_reply = eth/ip/icmp/raw
sendp(icmp_reply, iface=self.interface)

Python - parser over multiline text

my goal is to create a text parser for file containing multilines data:
Applying option loglevel (set logging level) with argument debug.
Successfully parsed a group of options.
Parsing a group of options: input url http://prod7.team.cn/test/tracks-v1a1/mono.
Successfully parsed a group of options.
Opening an input file: http://prod7.team.cn/test/tracks-v1a1/mono
[NULL # 000001e002039000] Opening 'http://prod7.team.cn/test/tracks-v1a1/mono' for reading
[http # 000001e00203a040] Setting default whitelist 'http,https,tls,rtp,tcp,udp,crypto,httpproxy'
[tcp # 000001e00203ba80] Original list of addresses:
[tcp # 000001e00203ba80] Address 92.223.97.22 port 80
[tcp # 000001e00203ba80] Interleaved list of addresses:
[tcp # 000001e00203ba80] Address 92.223.97.22 port 80
[tcp # 000001e00203ba80] Starting connection attempt to 92.223.97.22 port 80
[tcp # 000001e00203ba80] Successfully connected to 92.223.97.22 port 80
[http # 000001e00203a040] request: GET /test/tracks-v1a1/mono HTTP/1.1
User-Agent: Lavf/58.31.101
Accept: */*
Range: bytes=0-
Connection: close
Host: prod7.team.cn
Icy-MetaData: 1
each files contain multiple set of such information.
My target is to find every "Successfully conneted" IP address, followed by the HOST detail, till LF.
In the case mentioned a valid match should be
IP 92.223.97.22 HOST prod7.team.cn
I can easily find the IP using a regex, but I don't understand how to create a valid match, skipping further lines till "host".
UPDATE
If I use this Regex
(connected to).([0-9].(?:\.[0-9]+){3}.port.*.*)
I find:
Match 1
Full match connected to 92.223.97.22 port 80
Group 1. connected to
Group 2. 92.223.97.22 port 80
I'm receiving error if I add .* or .host.* at the end. I'm confused how to add another pattergn to detect 'Host:' and get match until end of row.
https://docs.python.org/3.7/library/re.html#re.MULTILINE
You want to run your regex in MULTILINE mode which should allow you to match over line breaks. Then you could use something like .* to capture the in-between.
A caveat to notice is that you should be sure to check to make a sure you don't run into a new matching start. Like CA.*B would match both CAB and CACB and CACAB. So most likely will want to explicitly check in your regex to not overrun the beginning of a valid match with the .*.
I was able to sort out using nested Regex:
ip_list = []
regex = r'connected(.*?)Host[^\n]+$'
text_as_string = open('C:\\temp\\log.txt', 'r').read()
matches = re.finditer(regex, text_as_string, re.DOTALL | re.MULTILINE)
for matchNum, match in enumerate(matches, start=1):
block = str(match.group())
#print connected IP
ip = re.compile('(connected to).[0-9]+(?:\.[0-9]+){3}.port.*')
for match in re.finditer(ip, block):
f_id=match.group()
#print connected host
host = re.compile('Host[^\n]+$')
for match in re.finditer(host, block):
f_host=match.group()
if f_id =='':
f_id='NA'
if f_host =='':
f_host='NA'
ip_list.append([f_id,f_host])
unique_ip = reduce(lambda l, x: l if x in l else l+[x], ip_list, [])

icmp response for ip options

How should I do it?
I have written a new IP Option field in the scapy 2.4 source code (scapy.layers.inet).
class IPOption_Ex(IPOption):
name = "IP Option Ex"
copy_flag = 1
option = 26
fields_desc = [_IPOption_HDR,
ByteField("op1", 16),
ShortField("op2", 0),
ShortField("op3", 0),
IPField("originator_ip", "0.0.0.0"),
LongField("op4", 0)]
It is just L3 level option so it does not involve TCP or UDP. Whenever the destination receives the new IP Option (say 26) I want a response ICMP packet (type 45). Say the response ICMP packet has four fields: 1.Type 2.Code 3.Checksum 4.source IP address. (scapy.layers.inet)
class ICMP(Packet):
name = "ICMP"
fields_desc = [ ByteEnumField("type",8, icmptypes),
MultiEnumField("code",0, icmpcodes, depends_on=lambda pkt:pkt.type,fmt="B"),
XShortField("chksum", None),
ConditionalField(IPField("originator_ip","0.0.0.0"), lambda pkt:pkt.type==45)]
Now what I don't understand how to write code in python/scapy to generate ICMP type-45 response packet from the destination and where to write in scapy source code?
You just need
IPOption_Ex.register_variant()
You'll then have
IP(options=[IPOption(option=26)])
working

Get IP address by subnet in Puppet

I have a couple machines, each with multiple network interfaces:
lead$ ip addr
2: enp0s2: ...
inet 10.1.1.11/24 brd 10.1.1.255 scope global enp0s2
3: enp0s3: ...
inet 10.2.2.11/24 brd 10.2.2.255 scope global enp0s3
iron$ ip addr
2: enp0s99: ...
inet 10.1.1.12/24 brd 10.1.1.255 scope global enp0s99
3: enp0s3: ...
inet 10.2.2.12/24 brd 10.2.2.255 scope global enp0s3
Note that on lead, 10.1.1.0/24 is on enp0s2, but on
iron, 10.1.1.0/24 is on enp0s99.
In Puppet, how would I get the IP address(es) (or interface name)
corresponding to the subnet 10.1.1.0/24? Using $::ipaddress_enp0s2
clearly won't work, as the interface name is different across machines.
What I want is something like $::ipaddress_10_1_1_0 (with the value
10.1.1.11 on lead and the value 10.1.1.12 on iron).
For reference: In Ansible, I would do something like:
- shell: "ip route get 10.1.1.0 | awk '{print $6}'"
register: ipaddr
- debug:
msg: "{{ ipaddr.stdout }} is my IP on 10.1.1.0/24"
You will need to use the same approach in Puppet, using custom facts to create your own.
Puppet Labs actually has an implementation of exactly this in their puppetlabs-openstack module:
require "ipaddr"
module Puppet::Parser::Functions
newfunction(:ip_for_network, :type => :rvalue, :doc => <<-EOS
Returns an ip address for the given network in cidr notation
ip_for_network("127.0.0.0/24") => 127.0.0.1
EOS
) do |args|
addresses_in_range = []
range = IPAddr.new(args[0])
facts = compiler.node.facts.values
ip_addresses = facts.select { |key, value| key.match /^ipaddress/ }
ip_addresses.each do |pair|
key = pair[0]
string_address = pair[1]
ip_address = IPAddr.new(string_address)
if range.include?(ip_address)
addresses_in_range.push(string_address)
end
end
return addresses_in_range.first
end
end
Rather than making a custom fact, this loops over the existing facts and finds ones that look like IP addresses.

Understanding the Scapy "Mac address to reach destination not found. Using broadcast." warning

If I generate an Ethernet frame without any upper layers payload and send it at layer two with sendp(), then I receive the "Mac address to reach destination not found. Using broadcast." warning and frame put to wire indeed uses ff:ff:ff:ff:ff:ff as a destination MAC address. Why is this so? Shouldn't the Scapy send exactly the frame I constructed?
My crafted package can be seen below:
>>> ls(x)
dst : DestMACField = '01:00:0c:cc:cc:cc' (None)
src : SourceMACField = '00:11:22:33:44:55' (None)
type : XShortEnumField = 0 (0)
>>> sendp(x, iface="eth0")
WARNING: Mac address to reach destination not found. Using broadcast.
.
Sent 1 packets.
>>>
Most people encountering this issue are incorrectly using send() (or sr(), sr1(), srloop()) instead of sendp() (or srp(), srp1(), srploop()). For the record, the "without-p" functions like send() are for sending layer 3 packets (send(IP())) while the "with-p" variants are for sending layer 2 packets (sendp(Ether() / IP())).
If you define x like I do below and use sendp() (and not send()) and you still have this issue, you should probably try with the latest version from the project's git repository (see https://github.com/secdev/scapy).
I've tried:
>>> x = Ether(src='01:00:0c:cc:cc:cc', dst='00:11:22:33:44:55')
>>> ls(x)
dst : DestMACField = '00:11:22:33:44:55' (None)
src : SourceMACField = '01:00:0c:cc:cc:cc' (None)
type : XShortEnumField = 0 (0)
>>> sendp(x, iface='eth0')
.
Sent 1 packets.
At the same time I was running tcpdump:
# tcpdump -eni eth0 ether host 00:11:22:33:44:55
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
12:33:47.774570 01:00:0c:cc:cc:cc > 00:11:22:33:44:55, 802.3, length 14: [|llc]

Resources