sdaubert / packetgen Goto Github PK
View Code? Open in Web Editor NEWRuby library to easily generate and capture network packets
License: MIT License
Ruby library to easily generate and capture network packets
License: MIT License
Hello @sdaubert
As PacketGet is getting more awesome, the wiki is not synced with the lib.
You can create a repository like "packetgen_docs" and connect Gitbook to it so people either can PR to the wiki on github (it will be synced with gitbook once merged) or, you can add people you trust as writers to the gitbook space or both
What do you think?
Add Support for UDP over IPv6:
Rename all fields from header classes which do not use RFC name.
Sometimes I want to use PacketGen to work with packet captures that may not be in .pcapng
format. For example, if I dumped stuff with tcpdump and then wanted to work with that capture with packetgen later, it's not exactly straight forward.
Under the hood, I think we could pretty easily have the .read()
method work with both .pcap and .pcapng files by using PCAPRUB's .open_offline()
method.
pcap = PacketGen.read('example.pcap')
packets = PCAPRUB::Pcap.open_offline('example.pcap')
Here's basically how I've been getting around not being able to just use .read()
, because I need to:
parsed_packets = []
PCAPRUB::Pcap.open_offline('example.pcap').each_packet do |packet|
parsed_packets << PacketGen.parse(packet.to_s)
end
parsed_packets
# Read packets from +filename+.
#
# @see {PcapNG::File} for more on reading pcapng files.
# @param [String] filename PcapNG or Pcap file
# @return [Array<Packet>]
def self.read(filename)
extension = File.extname(filename).downcase
if extension == ".pcap"
# read pcap via PCAPRUB::Pcap.open_offline in some fashion
# honestly I haven't figured out the best way yet
# but, ya' know
elsif extension == ".pcapng"
PcapNG::File.new.read_packets filename
else
# error, extension be a lie OR:
# check for them, automagically read both even if the filename was kittens
end
end
Thoughts on this?
A candidate may be Packet#reply
to invert all fields that may be inverted in headers to send a response from current packet. Inverted fields may be:
Hello guys
I see you have done a tremendous work in packetgen
however I believe that more examples are needed for each header to use.
I was looking for sending Dot11 DeAuth packet and I couldn't make it using the available docs.
My suggestion is to make one or more practical example for each header as you've done with manipulate packets part.
Thanks again for the great effort
The DIAL (Discovery and Launch) protocol would be an interesting protocol to support in the future. ๐
Not sure how easy or hard it would be. According to Wikipedia is utilizes UPnP, SSDP and HTTP protocols.
when I set the dst to "255.255.255.255",
have the error:
irb(main):012:0> PacketGen.gen('IP', src: '192.168.0.6', dst: '255.255.255.255', ttl: 1).to_w
Errno::EACCES: Permission denied - send(2)
from /var/lib/gems/2.3.0/gems/packetgen-1.4.3/lib/packetgen/header/ip.rb:203:in `send'
from /var/lib/gems/2.3.0/gems/packetgen-1.4.3/lib/packetgen/header/ip.rb:203:in `to_w'
from /var/lib/gems/2.3.0/gems/packetgen-1.4.3/lib/packetgen/packet.rb:210:in `to_w'
from (irb):12
from /usr/bin/irb:11:in `<main>'
Current beahviour is merely broken: :op
keyword change how to handle multiple fields. But adding others bindings through others calls to .bind_header
breaks that as these calls use first defined :op
, and not operator define by these calls.
Hello there, the title says it all
Why do we need to call recalc
manually?
we can make it called by default or add it as an option to to_w
in case someone want to send it without calculation(no idea when someone needs that) to be something like
pkt.to_w(iface, recalc: false)
Also, I hope we can add number of packets to be sent
pkt.to_w(iface, interval: 1000)
Hello there, if you have seen this issue. I tried to use packetgen to do so but I could do it using the current documentation
Scapy version
pkt = IP(src="192.168.30.100", dst="192.168.30.199")/UDP(sport=161)/SNMP(community="private",PDU=SNMPset(varbindlist=SNMPvarbind(oid=ASN1_OID("1.3.6.1.4.1.9.2.1.55.192.168.30.10"),value="pwnd-router.config")]))
send(pkt)
tried
pkt1 = PacketGen.gen('IP', src: '192.168.102.205', dst: '127.0.0.1').
add('UDP', dport: 161, sport: 161).
add('SNMP', community: 'private', pdu: "DID KNOW HOW TO ADD PDU DETAILS THIS WAY" )
and
snmp = PacketGen::Header::SNMP.new
snmp.community = 'private'
snmp.data.chosen = 3 # SetRequest
snmp.pdu
I believe my issue that I couldn't understand how to assign the PDU details
SNMPset == SetRequest
from scapyPDU=SNMPset(varbindlist=SNMPvarbind(oid=ASN1_OID("1.3.6.1.4.1.9.2.1.55.192.168.30.10"),value="pwnd-router.config")])
$ pgconsole
/usr/local/lib/ruby/gems/2.4.0/gems/packetgen-1.4.1/lib/packetgen/config.rb:34:in `initialize': uninitialized constant Socket::AF_PACKET (NameError)
Did you mean? Socket::AF_CNT
from /usr/local/lib/ruby/gems/2.4.0/gems/packetgen-1.4.1/bin/pgconsole:26:in `new'
from /usr/local/lib/ruby/gems/2.4.0/gems/packetgen-1.4.1/bin/pgconsole:26:in `<top (required)>'
from /usr/local/bin/pgconsole:22:in `load'
from /usr/local/bin/pgconsole:22:in `<main>'
[ โฏ 'โก']โฏ๏ธตโปโโป)
The config for pgconsole doesn't seem to be compatible with osx/macos.
You're the best. There's no serious need for me to have this right now, but, I figured I'd let ya' know.
Maybe I can figure it out and make a PR.
Hello there!
It would be more convenient to add an alias to method Header.all
to be Header.list_headers
, or Header.list
Also, the comment/document would be more descriptive/obvious if it was
# List all available headers
# Get known header classes
# @return [Array<Class>]
def self.all
return @header_classes if @header_classes
@header_classes = @added_header_classes.values
end
Raises LocalJumpError: no block given (yield)
When working with PacketFu, I'm able to start capturing packets without needing to specify the interface to use because that is optionally figured out for me if I don't pass in that argument.
For PacketGen, I'm forced to specify the interface to listen on as the first argument. I think this should be moved to the second, optional hash that can be passed in optionally. I think we should put that interface specification as an option and make capturing that much simpler.
require 'packetgen'
PacketGen.capture('en0') do |packet|
#do_stuffs_with_packet
end
require 'packetgen'
PacketGen.capture(iface: 'en0') do |packet|
#do_stuffs_with_packet
end
And this:
require 'packetgen'
PacketGen.capture do |packet|
#do_stuffs_with_packet
end
And this:
require 'packetgen'
PacketGen.capture(iface: 'en0', max: 2) do |packet|
#do_stuffs_with_packet
end
We ( basically ) just need to use Pcaprub's interface lookup to achieve this by changing the set_options method for PacketGen::Capture:
def set_options(options)
@max = options[:max] if options[:max]
@filter = options[:filter] if options[:filter]
@timeout = options[:timeout] if options[:timeout]
if options[:promisc]
@promisc = options[:promisc]
else
@promisc ||= false
end
if options[:snaplen]
@snaplen = options[:snaplen]
else
@snaplen ||= DEFAULT_SNAPLEN
end
if options[:parse].nil?
@parse = true if @parse.nil?
else
@parse = options[:parse]
end
if options[:iface]
@iface = options[:iface]
else
@iface = Pcap.lookupdev
end
end
I've implemented this to work; but I'm having problems with some of the specs where I'm getting errors like these ( which probably has nothing to do with interface specification, I'd think, I dunno ):
I can make the pull request, otherwise if you want to implement it yourself, it's not too bad -- but requires some changes to the tests obviously to reflect the interface specification as a part of the main hash.
The Dropbox LAN Sync Discovery Protocol (DB-LSP-DISC) would be nice to have in the future.
While I haven't done this yet, I think it would be pretty trivial to implement since it just seems to just be a typical UDP communication on port 17500
with a JSON payload (plaintext).
For the .start
method when using the Capture
class, a @pcap
instance variable is created using PCAPRUB's open_live
method. The very last argument for the .open_live
method when using the .start
method is 1
. However, I'd assume this should be the @timeout
instance variable that is set in the set_options
method. Within the Capture
class's initialize
method a default for the @timeout
variable could be set to 1 when users don't provide anything, while also allowing them to actually change it for their capture needs.
Hope that makes sense. I'm going to make a PR to fix this. ๐
Adding support for LLMNR could be nice to have in the future. ๐
For whatever reason, I ended up writing my own HTTP parser. So, of course, there would be bugs. ๐
I need to submit a fix to handle these kinds of cases. It could look something like this:
tcp_pkt_body = "GET /wiggle HTTP/1.1\r\nHost: ocsp.comodoca.com\r\nConnection: close\r\nUser-Agent: wiggle\r\n\r\n\r\xD1"
# if I do this
/(CONNECT|DELETE|GET|HEAD|OPTIONS|PATCH|POST|PUT)/ =~ tcp_pkt_body
# raises error: ArgumentError: invalid byte sequence in UTF-8
# so I need to cleanse it
tcp_pkt_body = tcp_pkt_body.chars.select(&:valid_encoding?).join
# => "GET /wiggle HTTP/1.1\r\nHost: ocsp.comodoca.com\r\nConnection: close\r\nUser-Agent: wiggle\r\n\r\n\r"
# and now I can regex it to get a match
/(CONNECT|DELETE|GET|HEAD|OPTIONS|PATCH|POST|PUT)/ =~ tcp_pkt_body
# => 0
Unless @sdaubert has a better way of handling this invalid encoding problem.
PacketGen.parse crashes with message
ArgumentError: wrong number of arguments (given 2, expected 1)
Hey Sylvain, hope you're doing well!
I felt the need to mess around with packetgen some more this weekend. I really like this project and I use it quite often!
As a side note: I'm sort of curious to try and translate the programming logic from this gem to the Crystal programming language. If you haven't heard of that project, I think you should check it out!
Anyway, I decided to go through the README examples for capturing packets and noticed this:
This example is found in the README:
packets = PacketGen.capture('eth0', max: 10)
# => ArgumentError: wrong number of arguments (given 2, expected 0..1)
# from /Users/kentgruber/.rvm/gems/ruby-2.4.1/gems/packetgen-1.4.3/lib/packetgen.rb:45:in `capture'
To get it to work, need to use the new capture syntax:
packets = PacketGen.capture(iface: 'eth0', max: 10)
Let me know if you can re-produce this problem / let me know that I'm using the API wrong. It should be a pretty easy fix to update the README to include the newer syntax if that is the main problem. I do suspect some sort of error checking could be improved, just not entirely sure where yet.
This great gem messes SMB protocol
It would be great to add a binary file that works as an interactive console, something like Scapy console with the elegance of msfconsole way.
What do you think?
user$ gem install packetgen
Building native extensions. This could take a while...
ERROR: Error installing packetgen:
ERROR: Failed to build gem native extension.
current directory: /Users/user/.rvm/gems/ruby-2.4.2/gems/pcaprub-0.12.4/ext/pcaprub_c
/Users/yser/.rvm/rubies/ruby-2.4.2/bin/ruby -r ./siteconf20171014-32877-1wibpka.rb extconf.rb
xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun
checked program was:
/* begin */
1: #include "ruby.h"
2:
3: int main(int argc, char **argv)
4: {
5: return 0;
6: }
/* end */
My gem & ruby is up to date and I can't find any solution to this error.
๐ Hello @sdaubert, I really hope you're doing well!
This morning I decided to run rspec
on this repository and came across an error on macOS:
../packetgen/lib/packetgen/capture.rb:65:in `open_live': lo: No such device exists (BIOCSETIF failed: Device not configured) (RuntimeError)
I believe this to be attributed to the default capture interface lo
. On macOS, perhaps at the very least on my laptop, the loopback interface is named lo0
.
I wrote a gem a little while back called interfacez
. This gem uses the Ruby standard library under the hood and provides a clean semantics for this behavior I think we're looking for. Moreover, I really think it could be a replacement for NetworkInterface
( written by Rapid7, with lots of slow C bindings ) and event Pcap.lookupdev
.
require 'interfacez'
# check if loopback exists
Interfacez.loopback?
# => true
# get loopback
Interfacez.loopback
# => "lo0"
# get first network interface to capture on ( lookupdev )
Interfacez.default
# => "en0"
Define Header::IPv6#to_w to send packets without defining Eth header
Adding support for mDNS could be nice to have in the future. ๐
Idea:
By example: Header::IP has 2 fields of 4 bits each (version
and ihl
)
Header::IP will de declared as:
class IP < Struct.new(:u8, :tos, :length, :id, :frag, :ttl, :proto,:sum, :src, :dst, :body)
define_bit_fields_on :u8, :version, 4, :ihl, 4
...
end
This will create getter and setter methods to access version
and ihl
in u8
attribute.
This mechanism will be used to declare:
Define Header::IP#to_w to send packets without defining Eth header
From #55:
pkt = PacketGen.gen('Dot11::Management', mac1: client, mac2: bssid, mac3: bssid)
pkt.add('Dot11::DeAuth', reason: 7)
pkt.calc
pkt.to_w('wlan0')
Nothing is sent
Add protocols:
While running this script:
require 'packetgen'
ipv4 = 0
ipv6 = 0
other = 0
PacketGen.capture('en0', promisc: true) do |packet|
if packet.is? 'IP'
ipv4 += 1
elsif packet.is? 'IPv6'
ipv6 += 1
else
other += 1
end
print "IPv4: #{ipv4} , IPv6: #{ipv6} , OTHER: #{other} \r"
end
If I run nmap in another shell:
$ nmap 192.168.0.0/24
I will get ( after a couple of seconds ):
/usr/local/lib/ruby/gems/2.4.0/gems/packetgen-1.4.0/lib/packetgen/packet.rb:244:in `parse': cannot identify first header in string (PacketGen::ParseError)
from /usr/local/lib/ruby/gems/2.4.0/gems/packetgen-1.4.0/lib/packetgen/packet.rb:68:in `parse'
from /usr/local/lib/ruby/gems/2.4.0/gems/packetgen-1.4.0/lib/packetgen/capture.rb:54:in `block (2 levels) in start'
from /usr/local/lib/ruby/gems/2.4.0/gems/packetgen-1.4.0/lib/packetgen/capture.rb:51:in `each'
from /usr/local/lib/ruby/gems/2.4.0/gems/packetgen-1.4.0/lib/packetgen/capture.rb:51:in `block in start'
So, this seems to be tied to the fact that there are invalid packets being sent. Though, I think for the capture, should there be a graceful failure for these things, or an option to fail a little more gracefully and allow capturing to continue even if there's a failure? Perhaps classify the packet as an invalid packet type?
There is no real easy way to contribute to wiki
here is my initial proposal
https://github.com/KINGSABRI/packetgen-wiki/tree/master
Header::IPv6#calc_length
works well when its body is a String. But when its body is a Struct class, length is not correct.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.