Permalink
Browse files

hello :)

  • Loading branch information...
1 parent d43f514 commit d716bdff62f0c46253319487ae9e31bb79b5f28b @invernizzi committed with Jun 27, 2012
Showing with 381 additions and 1 deletion.
  1. +202 −0 HTTP.py
  2. +167 −1 README.md
  3. +12 −0 example.py
  4. BIN example_network_traffic.pcap
View
202 HTTP.py
@@ -0,0 +1,202 @@
+#! /usr/bin/env python
+# -*- coding: UTF-8 -*-
+# Author : Steeve Barbeau, Luca Invernizzi
+
+import re
+from scapy.all import TCP, bind_layers, Packet, StrField
+
+
+def _canonicalize_header(name):
+ ''' Takes a header key (i.e., "Host" in "Host: www.google.com",
+ and returns a canonical representation of it '''
+ return name.strip().lower()
+
+
+def _parse_headers(s):
+ ''' Takes a HTTP packet, and returns a tuple containing:
+ - the first line (e.g., "GET ...")
+ - the headers in a dictionary
+ - the body '''
+ try:
+ headers, body = s.split("\r\n\r\n", 1)
+ except:
+ headers = s
+ body = ''
+ headers = headers.split("\r\n")
+ first_line, headers = headers[0].strip(), headers[1:]
+ headers_found = {}
+ for header_line in headers:
+ try:
+ key, value = header_line.split(':', 1)
+ except:
+ continue
+ headers_found[_canonicalize_header(key)] = header_line.strip()
+ return first_line, headers_found, body
+
+
+def _dissect_headers(obj, s):
+ ''' Takes a HTTP packet as the string s, and populates the scapy layer obj
+ (either HTTPResponse or HTTPRequest). Returns the first line of the
+ HTTP packet, and the body
+ '''
+ first_line, headers, body = _parse_headers(s)
+ for f in obj.fields_desc:
+ canonical_name = _canonicalize_header(f.name)
+ try:
+ header_line = headers[canonical_name]
+ except:
+ continue
+ key, value = header_line.split(':', 1)
+ obj.setfieldval(f.name, value.strip())
+ del headers[canonical_name]
+ if headers:
+ obj.setfieldval(
+ 'Additional-Headers', '\r\n'.join(headers.values()) + '\r\n')
+ return first_line, body
+
+
+def _self_build(obj, field_pos_list=None):
+ ''' Takse an HTTPRequest or HTTPResponse object, and creates its internal
+ scapy representation as a string. That is, generates the HTTP
+ packet as a string '''
+ p = ""
+ for f in obj.fields_desc:
+ val = obj.getfieldval(f.name)
+ if not val:
+ continue
+ val += '\r\n'
+ if f.name in ['Method', 'Additional-Headers', 'Status-Line']:
+ p = f.addfield(obj, p, val)
+ else:
+ p = f.addfield(obj, p, "%s: %s" % (f.name, val))
+ return p
+
+
+class HTTPRequest(Packet):
+
+ name = "HTTP Request"
+ http_methods = "^(OPTIONS|GET|HEAD|POST|PUT|DELETE|TRACE|CONNECT)"
+ fields_desc = [StrField("Method", None, fmt="H"),
+ StrField("Host", None, fmt="H"),
+ StrField("User-Agent", None, fmt="H"),
+ StrField("Accept", None, fmt="H"),
+ StrField("Accept-Language", None, fmt="H"),
+ StrField("Accept-Encoding", None, fmt="H"),
+ StrField("Accept-Charset", None, fmt="H"),
+ StrField("Referer", None, fmt="H"),
+ StrField("Authorization", None, fmt="H"),
+ StrField("Expect", None, fmt="H"),
+ StrField("From", None, fmt="H"),
+ StrField("If-Match", None, fmt="H"),
+ StrField("If-Modified-Since", None, fmt="H"),
+ StrField("If-None-Match", None, fmt="H"),
+ StrField("If-Range", None, fmt="H"),
+ StrField("If-Unmodified-Since", None, fmt="H"),
+ StrField("Max-Forwards", None, fmt="H"),
+ StrField("Proxy-Authorization", None, fmt="H"),
+ StrField("Range", None, fmt="H"),
+ StrField("TE", None, fmt="H"),
+ StrField("Cache-Control", None, fmt="H"),
+ StrField("Connection", None, fmt="H"),
+ StrField("Date", None, fmt="H"),
+ StrField("Pragma", None, fmt="H"),
+ StrField("Trailer", None, fmt="H"),
+ StrField("Transfer-Encoding", None, fmt="H"),
+ StrField("Upgrade", None, fmt="H"),
+ StrField("Via", None, fmt="H"),
+ StrField("Warning", None, fmt="H"),
+ StrField("Keep-Alive", None, fmt="H"),
+ StrField("Allow", None, fmt="H"),
+ StrField("Content-Encoding", None, fmt="H"),
+ StrField("Content-Language", None, fmt="H"),
+ StrField("Content-Length", None, fmt="H"),
+ StrField("Content-Location", None, fmt="H"),
+ StrField("Content-MD5", None, fmt="H"),
+ StrField("Content-Range", None, fmt="H"),
+ StrField("Content-Type", None, fmt="H"),
+ StrField("Expires", None, fmt="H"),
+ StrField("Last-Modified", None, fmt="H"),
+ StrField("Cookie", None, fmt="H"),
+ StrField("Additional-Headers", None, fmt="H")]
+
+ def do_dissect(self, s):
+ ''' From the HTTP packet string, populate the scapy object '''
+ first_line, body = _dissect_headers(self, s)
+ self.setfieldval('Method', first_line)
+ return body
+
+ def self_build(self, field_pos_list=None):
+ ''' Generate the HTTP packet string (the oppposite of do_dissect) '''
+ return _self_build(self, field_pos_list)
+
+
+class HTTPResponse(Packet):
+
+ name = "HTTP Response"
+ fields_desc = [StrField("Status-Line", None, fmt="H"),
+ StrField("Accept-Ranges", None, fmt="H"),
+ StrField("Age", None, fmt="H"),
+ StrField("E-Tag", None, fmt="H"),
+ StrField("Location", None, fmt="H"),
+ StrField("Proxy-Authenticate", None, fmt="H"),
+ StrField("Retry-After", None, fmt="H"),
+ StrField("Server", None, fmt="H"),
+ StrField("Vary", None, fmt="H"),
+ StrField("WWW-Authenticate", None, fmt="H"),
+ StrField("Cache-Control", None, fmt="H"),
+ StrField("Connection", None, fmt="H"),
+ StrField("Date", None, fmt="H"),
+ StrField("Pragma", None, fmt="H"),
+ StrField("Trailer", None, fmt="H"),
+ StrField("Transfer-Encoding", None, fmt="H"),
+ StrField("Upgrade", None, fmt="H"),
+ StrField("Via", None, fmt="H"),
+ StrField("Warning", None, fmt="H"),
+ StrField("Keep-Alive", None, fmt="H"),
+ StrField("Allow", None, fmt="H"),
+ StrField("Content-Encoding", None, fmt="H"),
+ StrField("Content-Language", None, fmt="H"),
+ StrField("Content-Length", None, fmt="H"),
+ StrField("Content-Location", None, fmt="H"),
+ StrField("Content-MD5", None, fmt="H"),
+ StrField("Content-Range", None, fmt="H"),
+ StrField("Content-Type", None, fmt="H"),
+ StrField("Expires", None, fmt="H"),
+ StrField("Last-Modified", None, fmt="H"),
+ StrField("Additional-Headers", None, fmt="H")]
+
+ def do_dissect(self, s):
+ ''' From the HTTP packet string, populate the scapy object '''
+ first_line, body = _dissect_headers(self, s)
+ self.setfieldval('Status-Line', first_line)
+ return body
+
+ def self_build(self, field_pos_list=None):
+ ''' From the HTTP packet string, populate the scapy object '''
+ return _self_build(self, field_pos_list)
+
+
+class HTTP(Packet):
+
+ name = "HTTP"
+
+ def do_dissect(self, s):
+ return s
+
+ def guess_payload_class(self, payload):
+ ''' Decides if the payload is an HTTP Request or Response, or
+ something else '''
+ prog = re.compile("^(OPTIONS|GET|HEAD|POST|PUT|DELETE|TRACE|CONNECT)")
+ result = prog.search(payload)
+ if result:
+ return HTTPRequest
+ else:
+ prog = re.compile("^HTTP/((0\.9)|(1\.0)|(1\.1))\ [0-9]{3}.*")
+ result = prog.search(payload)
+ if result:
+ return HTTPResponse
+ return Packet.guess_payload_class(self, payload)
+
+
+bind_layers(TCP, HTTP, sport=80)
+bind_layers(TCP, HTTP, dport=80)
View
168 README.md
@@ -1,4 +1,170 @@
scapy-http
==========
-Support for HTTP in Scapy
+Support for parsing HTTP in Scapy (http://www.secdev.org/projects/scapy/).
+
+Example
+--------
+
+## Code
+```python
+#!/usr/bin/env python
+try:
+ import scapy.all as scapy
+except ImportError:
+ import scapy
+
+import HTTP
+
+packets = scapy.rdpcap('example_network_traffic.pcap')
+for p in packets:
+ print '=' * 78
+ p.show()
+```
+
+## Output
+
+```python
+###[ Ethernet ]###
+ dst = 00:21:29:77:3d:d8
+ src = 64:80:99:63:29:94
+ type = 0x800
+###[ IP ]###
+ version = 4L
+ ihl = 5L
+ tos = 0x0
+ len = 154
+ id = 46316
+ flags = DF
+ frag = 0L
+ ttl = 64
+ proto = tcp
+ chksum = 0x100b
+ src = 192.168.1.105
+ dst = 207.97.227.243
+ \options \
+###[ TCP ]###
+ sport = 52157
+ dport = http
+ seq = 3687400232
+ ack = 2748912324
+ dataofs = 5L
+ reserved = 0L
+ flags = PA
+ window = 14600
+ chksum = 0xb333
+ urgptr = 0
+ options = []
+###[ HTTP ]###
+###[ HTTP Request ]###
+ Method = 'GET / HTTP/1.1'
+ Host = 'www.github.com'
+ User-Agent= 'Wget/1.13.4 (linux-gnu)'
+ Accept = '*/*'
+ Accept-Language= None
+ Accept-Encoding= None
+ Accept-Charset= None
+ Referer = None
+ Authorization= None
+ Expect = None
+ From = None
+ If-Match = None
+ If-Modified-Since= None
+ If-None-Match= None
+ If-Range = None
+ If-Unmodified-Since= None
+ Max-Forwards= None
+ Proxy-Authorization= None
+ Range = None
+ TE = None
+ Cache-Control= None
+ Connection= 'Keep-Alive'
+ Date = None
+ Pragma = None
+ Trailer = None
+ Transfer-Encoding= None
+ Upgrade = None
+ Via = None
+ Warning = None
+ Keep-Alive= None
+ Allow = None
+ Content-Encoding= None
+ Content-Language= None
+ Content-Length= None
+ Content-Location= None
+ Content-MD5= None
+ Content-Range= None
+ Content-Type= None
+ Expires = None
+ Last-Modified= None
+ Cookie = None
+ Additional-Headers= None
+
+==============================================================================
+
+###[ Ethernet ]###
+ dst = 64:80:99:63:29:94
+ src = 00:21:29:77:3d:d8
+ type = 0x800
+###[ IP ]###
+ version = 4L
+ ihl = 5L
+ tos = 0x0
+ len = 418
+ id = 29348
+ flags = DF
+ frag = 0L
+ ttl = 55
+ proto = tcp
+ chksum = 0x5a4b
+ src = 207.97.227.243
+ dst = 192.168.1.105
+ \options \
+###[ TCP ]###
+ sport = http
+ dport = 52157
+ seq = 2748912324
+ ack = 3687400346
+ dataofs = 5L
+ reserved = 0L
+ flags = PA
+ window = 5840
+ chksum = 0x78e7
+ urgptr = 0
+ options = []
+###[ HTTP ]###
+###[ HTTP Response ]###
+ Status-Line= 'HTTP/1.1 301 Moved Permanently'
+ Accept-Ranges= None
+ Age = None
+ E-Tag = None
+ Location = 'http://github.com/'
+ Proxy-Authenticate= None
+ Retry-After= None
+ Server = 'nginx/1.0.13'
+ Vary = None
+ WWW-Authenticate= None
+ Cache-Control= None
+ Connection= 'keep-alive'
+ Date = 'Wed, 27 Jun 2012 06:53:41 GMT'
+ Pragma = None
+ Trailer = None
+ Transfer-Encoding= None
+ Upgrade = None
+ Via = None
+ Warning = None
+ Keep-Alive= None
+ Allow = None
+ Content-Encoding= None
+ Content-Language= None
+ Content-Length= '185'
+ Content-Location= None
+ Content-MD5= None
+ Content-Range= None
+ Content-Type= 'text/html'
+ Expires = None
+ Last-Modified= None
+ Additional-Headers= None
+###[ Raw ]###
+ load = '<html>\r\n<head><title>301 Moved Permanently</title></head>\r\n<body bgcolor="white">\r\n<center><h1>301 Moved Permanently</h1></center>\r\n<hr><center>nginx/1.0.13</center>\r\n</body>\r\n</html>\r\n'
+```
View
12 example.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+try:
+ import scapy.all as scapy
+except ImportError:
+ import scapy
+
+import HTTP
+
+packets = scapy.rdpcap('example_network_traffic.pcap')
+for p in packets:
+ print '=' * 78
+ p.show()
View
BIN example_network_traffic.pcap
Binary file not shown.

0 comments on commit d716bdf

Please sign in to comment.