mirror of https://github.com/Kkevsterrr/geneva
test
This commit is contained in:
parent
337f916920
commit
586f6c8174
|
@ -0,0 +1,602 @@
|
||||||
|
|
||||||
|
# This file gutted from scapy as the module there doesn't quite work there.
|
||||||
|
|
||||||
|
# This file is part of Scapy
|
||||||
|
# See http://www.secdev.org/projects/scapy for more information
|
||||||
|
# Copyright (C) 2019 Gabriel Potter <gabriel@potter.fr>
|
||||||
|
# Copyright (C) 2012 Luca Invernizzi <invernizzi.l@gmail.com>
|
||||||
|
# Copyright (C) 2012 Steeve Barbeau <http://www.sbarbeau.fr>
|
||||||
|
# This file is a modified version of the former scapy_http plugin.
|
||||||
|
# It was reimplemented for scapy 2.4.3+ using sessions, stream handling.
|
||||||
|
# Original Authors : Steeve Barbeau, Luca Invernizzi
|
||||||
|
# Originally published under a GPLv2 license
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from scapy.compat import plain_str, bytes_encode, \
|
||||||
|
gzip_compress, gzip_decompress
|
||||||
|
from scapy.config import conf
|
||||||
|
from scapy.consts import WINDOWS
|
||||||
|
from scapy.error import warning
|
||||||
|
from scapy.fields import StrField
|
||||||
|
from scapy.packet import Packet, bind_layers, bind_bottom_up, Raw
|
||||||
|
from scapy.utils import get_temp_file, ContextManagerSubprocess
|
||||||
|
|
||||||
|
from scapy.layers.inet import TCP, TCP_client
|
||||||
|
|
||||||
|
from scapy.modules import six
|
||||||
|
|
||||||
|
if "http" not in conf.contribs:
|
||||||
|
conf.contribs["http"] = {}
|
||||||
|
conf.contribs["http"]["auto_compression"] = True
|
||||||
|
|
||||||
|
# https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
|
||||||
|
|
||||||
|
GENERAL_HEADERS = [
|
||||||
|
"Cache-Control",
|
||||||
|
"Connection",
|
||||||
|
"Permanent",
|
||||||
|
"Content-Length",
|
||||||
|
"Content-MD5",
|
||||||
|
"Content-Type",
|
||||||
|
"Date",
|
||||||
|
"Keep-Alive",
|
||||||
|
"Pragma",
|
||||||
|
"Upgrade",
|
||||||
|
"Via",
|
||||||
|
"Warning"
|
||||||
|
]
|
||||||
|
|
||||||
|
COMMON_UNSTANDARD_GENERAL_HEADERS = [
|
||||||
|
"X-Request-ID",
|
||||||
|
"X-Correlation-ID"
|
||||||
|
]
|
||||||
|
|
||||||
|
REQUEST_HEADERS = [
|
||||||
|
"A-IM",
|
||||||
|
"Accept",
|
||||||
|
"Accept-Charset",
|
||||||
|
"Accept-Encoding",
|
||||||
|
"Accept-Language",
|
||||||
|
"Accept-Datetime",
|
||||||
|
"Access-Control-Request-Method",
|
||||||
|
"Access-Control-Request-Headers",
|
||||||
|
"Authorization",
|
||||||
|
"Cookie",
|
||||||
|
"Expect",
|
||||||
|
"Forwarded",
|
||||||
|
"From",
|
||||||
|
"Host",
|
||||||
|
"HTTP2-Settings",
|
||||||
|
"If-Match",
|
||||||
|
"If-Modified-Since",
|
||||||
|
"If-None-Match",
|
||||||
|
"If-Range",
|
||||||
|
"If-Unmodified-Since",
|
||||||
|
"Max-Forwards",
|
||||||
|
"Origin",
|
||||||
|
"Proxy-Authorization",
|
||||||
|
"Range",
|
||||||
|
"Referer",
|
||||||
|
"TE",
|
||||||
|
"User-Agent"
|
||||||
|
]
|
||||||
|
|
||||||
|
COMMON_UNSTANDARD_REQUEST_HEADERS = [
|
||||||
|
"Upgrade-Insecure-Requests",
|
||||||
|
"Upgrade-Insecure-Requests",
|
||||||
|
"X-Requested-With",
|
||||||
|
"DNT",
|
||||||
|
"X-Forwarded-For",
|
||||||
|
"X-Forwarded-Host",
|
||||||
|
"X-Forwarded-Proto",
|
||||||
|
"Front-End-Https",
|
||||||
|
"X-Http-Method-Override",
|
||||||
|
"X-ATT-DeviceId",
|
||||||
|
"X-Wap-Profile",
|
||||||
|
"Proxy-Connection",
|
||||||
|
"X-UIDH",
|
||||||
|
"X-Csrf-Token",
|
||||||
|
"Save-Data",
|
||||||
|
]
|
||||||
|
|
||||||
|
RESPONSE_HEADERS = [
|
||||||
|
"Access-Control-Allow-Origin",
|
||||||
|
"Access-Control-Allow-Credentials",
|
||||||
|
"Access-Control-Expose-Headers",
|
||||||
|
"Access-Control-Max-Age",
|
||||||
|
"Access-Control-Allow-Methods",
|
||||||
|
"Access-Control-Allow-Headers",
|
||||||
|
"Accept-Patch",
|
||||||
|
"Accept-Ranges",
|
||||||
|
"Age",
|
||||||
|
"Allow",
|
||||||
|
"Alt-Svc",
|
||||||
|
"Content-Disposition",
|
||||||
|
"Content-Encoding",
|
||||||
|
"Content-Language",
|
||||||
|
"Content-Location",
|
||||||
|
"Content-Range",
|
||||||
|
"Delta-Base",
|
||||||
|
"ETag",
|
||||||
|
"Expires",
|
||||||
|
"IM",
|
||||||
|
"Last-Modified",
|
||||||
|
"Link",
|
||||||
|
"Location",
|
||||||
|
"Permanent",
|
||||||
|
"P3P",
|
||||||
|
"Proxy-Authenticate",
|
||||||
|
"Public-Key-Pins",
|
||||||
|
"Retry-After",
|
||||||
|
"Server",
|
||||||
|
"Set-Cookie",
|
||||||
|
"Strict-Transport-Security",
|
||||||
|
"Trailer",
|
||||||
|
"Transfer-Encoding",
|
||||||
|
"Tk",
|
||||||
|
"Vary",
|
||||||
|
"WWW-Authenticate",
|
||||||
|
"X-Frame-Options",
|
||||||
|
]
|
||||||
|
|
||||||
|
COMMON_UNSTANDARD_RESPONSE_HEADERS = [
|
||||||
|
"Content-Security-Policy",
|
||||||
|
"X-Content-Security-Policy",
|
||||||
|
"X-WebKit-CSP",
|
||||||
|
"Refresh",
|
||||||
|
"Status",
|
||||||
|
"Timing-Allow-Origin",
|
||||||
|
"X-Content-Duration",
|
||||||
|
"X-Content-Type-Options",
|
||||||
|
"X-Powered-By",
|
||||||
|
"X-UA-Compatible",
|
||||||
|
"X-XSS-Protection",
|
||||||
|
]
|
||||||
|
|
||||||
|
def _strip_header_name(name):
|
||||||
|
"""Takes a header key (i.e., "Host" in "Host: www.google.com",
|
||||||
|
and returns a stripped representation of it
|
||||||
|
"""
|
||||||
|
return plain_str(name.strip()).replace("-", "_")
|
||||||
|
|
||||||
|
def _header_line(name, val):
|
||||||
|
"""Creates a HTTP header line"""
|
||||||
|
# Python 3.4 doesn't support % on bytes
|
||||||
|
return bytes_encode(name) + b": " + bytes_encode(val)
|
||||||
|
|
||||||
|
def _parse_headers(s):
|
||||||
|
headers = s.split(b"\r\n")
|
||||||
|
headers_found = {}
|
||||||
|
for header_line in headers:
|
||||||
|
try:
|
||||||
|
key, value = header_line.split(b':', 1)
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
header_key = _strip_header_name(key).lower()
|
||||||
|
# headers_found[header_key] = (key, value.strip()) # The first big change occurs here, using the header_line instead of value.strip()
|
||||||
|
headers_found[header_key] = (key, header_line .strip())
|
||||||
|
return headers_found
|
||||||
|
|
||||||
|
def _parse_headers_and_body(s):
|
||||||
|
''' Takes a HTTP packet, and returns a tuple containing:
|
||||||
|
_ the first line (e.g., "GET ...")
|
||||||
|
_ the headers in a dictionary
|
||||||
|
_ the body
|
||||||
|
'''
|
||||||
|
crlfcrlf = b"\r\n\r\n"
|
||||||
|
crlfcrlfIndex = s.find(crlfcrlf)
|
||||||
|
if crlfcrlfIndex != -1:
|
||||||
|
headers = s[:crlfcrlfIndex + len(crlfcrlf)]
|
||||||
|
body = s[crlfcrlfIndex + len(crlfcrlf):]
|
||||||
|
else:
|
||||||
|
headers = s
|
||||||
|
body = b''
|
||||||
|
first_line, headers = headers.split(b"\r\n", 1)
|
||||||
|
return first_line.strip(), _parse_headers(headers), 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_and_body(s)
|
||||||
|
for f in obj.fields_desc:
|
||||||
|
# We want to still parse wrongly capitalized fields
|
||||||
|
stripped_name = _strip_header_name(f.name).lower()
|
||||||
|
try:
|
||||||
|
_, value = headers.pop(stripped_name)
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
obj.setfieldval(f.name, value)
|
||||||
|
if headers:
|
||||||
|
headers = {key: value for key, value in six.itervalues(headers)}
|
||||||
|
obj.setfieldval('Unknown_Headers', headers)
|
||||||
|
return first_line, body
|
||||||
|
|
||||||
|
class _HTTPContent(Packet):
|
||||||
|
# https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Transfer-Encoding
|
||||||
|
def _get_encodings(self):
|
||||||
|
encodings = []
|
||||||
|
if isinstance(self, HTTPResponse):
|
||||||
|
if self.Transfer_Encoding:
|
||||||
|
encodings += [plain_str(x).strip().lower() for x in
|
||||||
|
plain_str(self.Transfer_Encoding).split(",")]
|
||||||
|
if self.Content_Encoding:
|
||||||
|
encodings += [plain_str(x).strip().lower() for x in
|
||||||
|
plain_str(self.Content_Encoding).split(",")]
|
||||||
|
return encodings
|
||||||
|
|
||||||
|
def hashret(self):
|
||||||
|
# The only field both Answers and Responses have in common
|
||||||
|
return self.Http_Version
|
||||||
|
|
||||||
|
def post_dissect(self, s):
|
||||||
|
if not conf.contribs["http"]["auto_compression"]:
|
||||||
|
return s
|
||||||
|
encodings = self._get_encodings()
|
||||||
|
# Un-chunkify
|
||||||
|
if "chunked" in encodings:
|
||||||
|
data = b""
|
||||||
|
while s:
|
||||||
|
length, _, body = s.partition(b"\r\n")
|
||||||
|
try:
|
||||||
|
length = int(length, 16)
|
||||||
|
except ValueError:
|
||||||
|
# Not a valid chunk. Ignore
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
load = body[:length]
|
||||||
|
if body[length:length + 2] != b"\r\n":
|
||||||
|
# Invalid chunk. Ignore
|
||||||
|
break
|
||||||
|
s = body[length + 2:]
|
||||||
|
data += load
|
||||||
|
if not s:
|
||||||
|
s = data
|
||||||
|
# Decompress
|
||||||
|
try:
|
||||||
|
if "deflate" in encodings:
|
||||||
|
import zlib
|
||||||
|
s = zlib.decompress(s)
|
||||||
|
elif "gzip" in encodings:
|
||||||
|
s = gzip_decompress(s)
|
||||||
|
elif "compress" in encodings:
|
||||||
|
import lzw
|
||||||
|
s = lzw.decompress(s)
|
||||||
|
except Exception:
|
||||||
|
# Cannot decompress - probably incomplete data
|
||||||
|
pass
|
||||||
|
return s
|
||||||
|
|
||||||
|
def post_build(self, pkt, pay):
|
||||||
|
if not conf.contribs["http"]["auto_compression"]:
|
||||||
|
return pkt + pay
|
||||||
|
encodings = self._get_encodings()
|
||||||
|
# Compress
|
||||||
|
if "deflate" in encodings:
|
||||||
|
import zlib
|
||||||
|
pay = zlib.compress(pay)
|
||||||
|
elif "gzip" in encodings:
|
||||||
|
pay = gzip_compress(pay)
|
||||||
|
elif "compress" in encodings:
|
||||||
|
import lzw
|
||||||
|
pay = lzw.compress(pay)
|
||||||
|
return pkt + pay
|
||||||
|
|
||||||
|
def self_build(self, field_pos_list=None):
|
||||||
|
''' Takes an HTTPRequest or HTTPResponse object, and creates its
|
||||||
|
string representation.'''
|
||||||
|
if not isinstance(self.underlayer, HTTP):
|
||||||
|
warning(
|
||||||
|
"An HTTPResponse/HTTPRequest should always be below an HTTP"
|
||||||
|
)
|
||||||
|
p = b""
|
||||||
|
# Walk all the fields, in order
|
||||||
|
for f in self.fields_desc:
|
||||||
|
if f.name == "Unknown_Headers":
|
||||||
|
continue
|
||||||
|
# Get the field value
|
||||||
|
val = self.getfieldval(f.name)
|
||||||
|
if not val:
|
||||||
|
# Not specified. Skip
|
||||||
|
continue
|
||||||
|
# Fields used in the first line have a space as a separator,
|
||||||
|
# whereas headers are terminated by a new line
|
||||||
|
if isinstance(self, HTTPRequest):
|
||||||
|
if f.name in ['Method', 'Path']:
|
||||||
|
separator = b' '
|
||||||
|
else:
|
||||||
|
separator = b'\r\n'
|
||||||
|
elif isinstance(self, HTTPResponse):
|
||||||
|
if f.name in ['Http_Version', 'Status_Code']:
|
||||||
|
separator = b' '
|
||||||
|
else:
|
||||||
|
separator = b'\r\n'
|
||||||
|
# Add the field into the packet
|
||||||
|
p = f.addfield(self, p, val + separator)
|
||||||
|
# Handle Unknown_Headers
|
||||||
|
if self.Unknown_Headers:
|
||||||
|
headers_text = b""
|
||||||
|
for name, value in six.iteritems(self.Unknown_Headers):
|
||||||
|
headers_text += _header_line(name, value) + b"\r\n"
|
||||||
|
p = self.get_field("Unknown_Headers").addfield(
|
||||||
|
self, p, headers_text
|
||||||
|
)
|
||||||
|
# The packet might be empty, and in that case it should stay empty.
|
||||||
|
if p:
|
||||||
|
# Add an additional line after the last header
|
||||||
|
p = f.addfield(self, p, b'\r\n')
|
||||||
|
return p
|
||||||
|
|
||||||
|
def guess_payload_class(self, payload):
|
||||||
|
"""Detect potential payloads
|
||||||
|
"""
|
||||||
|
if self.Connection and b"Upgrade" in self.Connection:
|
||||||
|
from scapy.contrib.http2 import H2Frame
|
||||||
|
return H2Frame
|
||||||
|
return super(_HTTPContent, self).guess_payload_class(payload)
|
||||||
|
|
||||||
|
class _HTTPHeaderField(StrField):
|
||||||
|
"""Modified StrField to handle HTTP Header names"""
|
||||||
|
__slots__ = ["real_name"]
|
||||||
|
|
||||||
|
def __init__(self, name, default):
|
||||||
|
self.real_name = name
|
||||||
|
name = _strip_header_name(name)
|
||||||
|
StrField.__init__(self, name, default, fmt="H")
|
||||||
|
|
||||||
|
def _generate_headers(*args):
|
||||||
|
"""Generate the header fields based on their name"""
|
||||||
|
# Order headers
|
||||||
|
all_headers = []
|
||||||
|
for headers in args:
|
||||||
|
all_headers += headers
|
||||||
|
# Generate header fields
|
||||||
|
results = []
|
||||||
|
for h in sorted(all_headers):
|
||||||
|
results.append(_HTTPHeaderField(h, None))
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Create Request and Response packets
|
||||||
|
class HTTPRequest(_HTTPContent):
|
||||||
|
name = "HTTPRequest"
|
||||||
|
fields_desc = [
|
||||||
|
# First line
|
||||||
|
_HTTPHeaderField("Method", "GET"),
|
||||||
|
_HTTPHeaderField("Path", "/"),
|
||||||
|
_HTTPHeaderField("Http-Version", "HTTP/1.1"),
|
||||||
|
# Headers
|
||||||
|
] + (
|
||||||
|
_generate_headers(
|
||||||
|
GENERAL_HEADERS,
|
||||||
|
REQUEST_HEADERS,
|
||||||
|
COMMON_UNSTANDARD_GENERAL_HEADERS,
|
||||||
|
COMMON_UNSTANDARD_REQUEST_HEADERS
|
||||||
|
)
|
||||||
|
) + [
|
||||||
|
_HTTPHeaderField("Unknown-Headers", None),
|
||||||
|
]
|
||||||
|
|
||||||
|
def do_dissect(self, s):
|
||||||
|
"""From the HTTP packet string, populate the scapy object"""
|
||||||
|
first_line, body = _dissect_headers(self, s)
|
||||||
|
try:
|
||||||
|
Method, Path, HTTPVersion = re.split(br"\s+", first_line, 2)
|
||||||
|
self.setfieldval('Method', Method)
|
||||||
|
self.setfieldval('Path', Path)
|
||||||
|
self.setfieldval('Http_Version', HTTPVersion)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
if body:
|
||||||
|
self.raw_packet_cache = s[:-len(body)]
|
||||||
|
else:
|
||||||
|
self.raw_packet_cache = s
|
||||||
|
return body
|
||||||
|
|
||||||
|
def mysummary(self):
|
||||||
|
return self.sprintf(
|
||||||
|
"%HTTPRequest.Method% %HTTPRequest.Path% "
|
||||||
|
"%HTTPRequest.Http_Version%"
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: decide to keep or not
|
||||||
|
class HTTPResponse(_HTTPContent):
|
||||||
|
name = "HTTP Response"
|
||||||
|
fields_desc = [
|
||||||
|
# First line
|
||||||
|
_HTTPHeaderField("Http-Version", "HTTP/1.1"),
|
||||||
|
_HTTPHeaderField("Status-Code", "200"),
|
||||||
|
_HTTPHeaderField("Reason-Phrase", "OK"),
|
||||||
|
# Headers
|
||||||
|
] + (
|
||||||
|
_generate_headers(
|
||||||
|
GENERAL_HEADERS,
|
||||||
|
RESPONSE_HEADERS,
|
||||||
|
COMMON_UNSTANDARD_GENERAL_HEADERS,
|
||||||
|
COMMON_UNSTANDARD_RESPONSE_HEADERS
|
||||||
|
)
|
||||||
|
) + [
|
||||||
|
_HTTPHeaderField("Unknown-Headers", None),
|
||||||
|
]
|
||||||
|
|
||||||
|
def answers(self, other):
|
||||||
|
return HTTPRequest in other
|
||||||
|
|
||||||
|
def do_dissect(self, s):
|
||||||
|
''' From the HTTP packet string, populate the scapy object '''
|
||||||
|
first_line, body = _dissect_headers(self, s)
|
||||||
|
try:
|
||||||
|
HTTPVersion, Status, Reason = re.split(br"\s+", first_line, 2)
|
||||||
|
self.setfieldval('Http_Version', HTTPVersion)
|
||||||
|
self.setfieldval('Status_Code', Status)
|
||||||
|
self.setfieldval('Reason_Phrase', Reason)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
if body:
|
||||||
|
self.raw_packet_cache = s[:-len(body)]
|
||||||
|
else:
|
||||||
|
self.raw_packet_cache = s
|
||||||
|
return body
|
||||||
|
|
||||||
|
def mysummary(self):
|
||||||
|
return self.sprintf(
|
||||||
|
"%HTTPResponse.Http_Version% %HTTPResponse.Status_Code% "
|
||||||
|
"%HTTPResponse.Reason_Phrase%"
|
||||||
|
)
|
||||||
|
|
||||||
|
class HTTP(Packet):
|
||||||
|
name = "HTTP 1"
|
||||||
|
fields_desc = []
|
||||||
|
show_indent = 0
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def dispatch_hook(cls, _pkt=None, *args, **kargs):
|
||||||
|
if _pkt and len(_pkt) >= 9:
|
||||||
|
from scapy.contrib.http2 import _HTTP2_types, H2Frame
|
||||||
|
# To detect a valid HTTP2, we check that the type is correct
|
||||||
|
# that the Reserved bit is set and length makes sense.
|
||||||
|
while _pkt:
|
||||||
|
if len(_pkt) < 9:
|
||||||
|
# Invalid total length
|
||||||
|
return cls
|
||||||
|
if ord(_pkt[3:4]) not in _HTTP2_types:
|
||||||
|
# Invalid type
|
||||||
|
return cls
|
||||||
|
length = struct.unpack("!I", b"\0" + _pkt[:3])[0] + 9
|
||||||
|
if length > len(_pkt):
|
||||||
|
# Invalid length
|
||||||
|
return cls
|
||||||
|
sid = struct.unpack("!I", _pkt[5:9])[0]
|
||||||
|
if sid >> 31 != 0:
|
||||||
|
# Invalid Reserved bit
|
||||||
|
return cls
|
||||||
|
_pkt = _pkt[length:]
|
||||||
|
return H2Frame
|
||||||
|
return cls
|
||||||
|
|
||||||
|
# tcp_reassemble is used by TCPSession in session.py
|
||||||
|
@classmethod
|
||||||
|
def tcp_reassemble(cls, data, metadata):
|
||||||
|
detect_end = metadata.get("detect_end", None)
|
||||||
|
is_unknown = metadata.get("detect_unknown", True)
|
||||||
|
if not detect_end or is_unknown:
|
||||||
|
metadata["detect_unknown"] = False
|
||||||
|
http_packet = HTTP(data)
|
||||||
|
# Detect packing method
|
||||||
|
if not isinstance(http_packet.payload, _HTTPContent):
|
||||||
|
return http_packet
|
||||||
|
length = http_packet.Content_Length
|
||||||
|
if length is not None:
|
||||||
|
# The packet provides a Content-Length attribute: let's
|
||||||
|
# use it. When the total size of the frags is high enough,
|
||||||
|
# we have the packet
|
||||||
|
length = int(length)
|
||||||
|
# Subtract the length of the "HTTP*" layer
|
||||||
|
if http_packet.payload.payload or length == 0:
|
||||||
|
http_length = len(data) - len(http_packet.payload.payload)
|
||||||
|
detect_end = lambda dat: len(dat) - http_length >= length
|
||||||
|
else:
|
||||||
|
# The HTTP layer isn't fully received.
|
||||||
|
detect_end = lambda dat: False
|
||||||
|
metadata["detect_unknown"] = True
|
||||||
|
else:
|
||||||
|
# It's not Content-Length based. It could be chunked
|
||||||
|
encodings = http_packet[HTTP].payload._get_encodings()
|
||||||
|
chunked = ("chunked" in encodings)
|
||||||
|
if chunked:
|
||||||
|
detect_end = lambda dat: dat.endswith(b"\r\n\r\n")
|
||||||
|
else:
|
||||||
|
# If neither Content-Length nor chunked is specified,
|
||||||
|
# it means it's the TCP packet that contains the data,
|
||||||
|
# or that the information hasn't been given yet.
|
||||||
|
detect_end = lambda dat: metadata.get("tcp_end", False)
|
||||||
|
metadata["detect_unknown"] = True
|
||||||
|
metadata["detect_end"] = detect_end
|
||||||
|
if detect_end(data):
|
||||||
|
return http_packet
|
||||||
|
else:
|
||||||
|
if detect_end(data):
|
||||||
|
http_packet = HTTP(data)
|
||||||
|
return http_packet
|
||||||
|
|
||||||
|
def guess_payload_class(self, payload):
|
||||||
|
"""Decides if the payload is an HTTP Request or Response, or
|
||||||
|
something else.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
prog = re.compile(
|
||||||
|
br"^(?:OPTIONS|GET|HEAD|POST|PUT|DELETE|TRACE|CONNECT) "
|
||||||
|
br"(?:.+?) "
|
||||||
|
br"HTTP/\d\.\d$"
|
||||||
|
)
|
||||||
|
crlfIndex = payload.index(b"\r\n")
|
||||||
|
req = payload[:crlfIndex]
|
||||||
|
result = prog.match(req)
|
||||||
|
if result:
|
||||||
|
return HTTPRequest
|
||||||
|
else:
|
||||||
|
prog = re.compile(br"^HTTP/\d\.\d \d\d\d .*$")
|
||||||
|
result = prog.match(req)
|
||||||
|
if result:
|
||||||
|
return HTTPResponse
|
||||||
|
except ValueError:
|
||||||
|
# Anything that isn't HTTP but on port 80
|
||||||
|
pass
|
||||||
|
return Raw
|
||||||
|
|
||||||
|
|
||||||
|
class GenevaHTTPRequest():
|
||||||
|
"""
|
||||||
|
Defines a Geneva HTTP request, where we can replace, set, delete, or insert to existing fields.
|
||||||
|
"""
|
||||||
|
def __init__(self, content):
|
||||||
|
"""
|
||||||
|
content: Raw string of the request (Bytes)
|
||||||
|
Creates a Geneva HTTP request
|
||||||
|
"""
|
||||||
|
self.original_content = content # Save off the original just in case
|
||||||
|
self.parsed_content = HTTPRequest(content)
|
||||||
|
|
||||||
|
def replace(self, header, index, content):
|
||||||
|
# replace contents at index
|
||||||
|
if header not in self.parsed_content["HTTPRequest"].fields:
|
||||||
|
# TODO: throw some sort of error, this header doesn't exist
|
||||||
|
return None
|
||||||
|
|
||||||
|
if index+len(content) > len(self.parsed_content["HTTPRequest"].fields[header]):
|
||||||
|
# TODO: throw some sort of error, this index is too large
|
||||||
|
return None
|
||||||
|
|
||||||
|
self.parsed_content["HTTPRequest"].fields[header] = self.parsed_content["HTTPRequest"].fields[header][0:index] \
|
||||||
|
+ content + \
|
||||||
|
self.parsed_content["HTTPRequest"].fields[header][index+len(content):]
|
||||||
|
return str(self)
|
||||||
|
|
||||||
|
def delete(self, header, index, num):
|
||||||
|
# delete num characters from header beginning at index
|
||||||
|
if header not in self.parsed_content["HTTPRequest"].fields:
|
||||||
|
# TODO: throw some sort of error, this header doesn't exist
|
||||||
|
return None
|
||||||
|
|
||||||
|
if index+num > len(self.parsed_content["HTTPRequest"].fields[header]):
|
||||||
|
# TODO: throw some sort of error, this index is too large
|
||||||
|
return None
|
||||||
|
|
||||||
|
self.parsed_content["HTTPRequest"].fields[header] = self.parsed_content["HTTPRequest"].fields[header][0:index] \
|
||||||
|
+ self.parsed_content["HTTPRequest"].fields[header][index+num:]
|
||||||
|
return str(self)
|
||||||
|
|
||||||
|
def set_header(self, header, content):
|
||||||
|
# Completely replaces a header with content. We don't care if the
|
||||||
|
# field already exists.
|
||||||
|
|
||||||
|
self.parsed_content["HTTPRequest"].fields[header] = content
|
||||||
|
|
||||||
|
return str(self)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.parsed_content)
|
|
@ -6,6 +6,8 @@ import urllib.parse
|
||||||
|
|
||||||
from scapy.all import IP, RandIP, UDP, DNS, DNSQR, Raw, TCP, fuzz
|
from scapy.all import IP, RandIP, UDP, DNS, DNSQR, Raw, TCP, fuzz
|
||||||
|
|
||||||
|
from actions.http import HTTPRequest
|
||||||
|
|
||||||
|
|
||||||
class Layer():
|
class Layer():
|
||||||
"""
|
"""
|
||||||
|
@ -601,6 +603,81 @@ class UDPLayer(Layer):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class HTTPRequestLayer(Layer):
|
||||||
|
"""
|
||||||
|
Defines an interface to access parsed HTTP fields
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = "HTTPRequest"
|
||||||
|
protocol = HTTPRequest
|
||||||
|
|
||||||
|
_fields = [
|
||||||
|
"Method",
|
||||||
|
"Path",
|
||||||
|
"Http_Version",
|
||||||
|
"Host"
|
||||||
|
]
|
||||||
|
fields = _fields
|
||||||
|
|
||||||
|
def __init__(self, layer):
|
||||||
|
"""
|
||||||
|
Initializes the HTTP layer.
|
||||||
|
"""
|
||||||
|
Layer.__init__(self, layer)
|
||||||
|
self.request = HTTPRequest(bytes(layer)) # TODO: I dont like this
|
||||||
|
self.getters = {
|
||||||
|
'load' : self.get_load,
|
||||||
|
}
|
||||||
|
self.setters = {
|
||||||
|
'load' : self.set_load,
|
||||||
|
}
|
||||||
|
self.generators = {
|
||||||
|
'load' : self.gen_load,
|
||||||
|
}
|
||||||
|
|
||||||
|
def get(self, field):
|
||||||
|
"""
|
||||||
|
Override get, since the HTTPRequest doesn't immediately make its fields known
|
||||||
|
"""
|
||||||
|
assert field in self.fields
|
||||||
|
if field in self.getters:
|
||||||
|
return self.getters[field](field)
|
||||||
|
return getattr(self.request, field)
|
||||||
|
|
||||||
|
def set(self, packet, field, value):
|
||||||
|
"""
|
||||||
|
Override get, since the HTTPRequest doesn't immediately make its fields known
|
||||||
|
"""
|
||||||
|
print('entering set, but I dont like this')
|
||||||
|
print(packet)
|
||||||
|
print(field)
|
||||||
|
print(value)
|
||||||
|
assert field in self.fields
|
||||||
|
base = field.split("-")[0]
|
||||||
|
if field in self.setters:
|
||||||
|
self.setters[field](packet, field, value)
|
||||||
|
|
||||||
|
# Dual field accessors are fields that require two pieces of information
|
||||||
|
# to retrieve them (for example, "options-eol"). These are delimited by
|
||||||
|
# a dash "-".
|
||||||
|
elif "-" in field and base in self.setters:
|
||||||
|
self.setters[base](packet, field, value)
|
||||||
|
else:
|
||||||
|
setattr(self.layer, field, value)
|
||||||
|
|
||||||
|
# Request the packet be reparsed to confirm the value is stable
|
||||||
|
# XXX Temporarily disabling the reconstitution check due to scapy bug (#2034)
|
||||||
|
#assert bytes(self.protocol(bytes(self.layer))) == bytes(self.layer)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def name_matches(cls, name):
|
||||||
|
"""
|
||||||
|
Override the name parsing to check for HTTP REQUEST here.
|
||||||
|
"""
|
||||||
|
return name.upper() in ["HTTPREQUEST"]
|
||||||
|
|
||||||
|
|
||||||
class DNSLayer(Layer):
|
class DNSLayer(Layer):
|
||||||
"""
|
"""
|
||||||
Defines an interface to access DNS header fields.
|
Defines an interface to access DNS header fields.
|
||||||
|
|
|
@ -3,13 +3,18 @@ import random
|
||||||
|
|
||||||
import actions.layer
|
import actions.layer
|
||||||
|
|
||||||
|
from scapy.layers.http import HTTP as ScapyHTTP, HTTPRequest as ScapyHTTPRequest
|
||||||
|
from scapy.all import IP, TCP
|
||||||
|
from actions.http import HTTPRequest as GenevaHTTPRequest
|
||||||
|
|
||||||
_SUPPORTED_LAYERS = [
|
_SUPPORTED_LAYERS = [
|
||||||
actions.layer.IPLayer,
|
actions.layer.IPLayer,
|
||||||
actions.layer.TCPLayer,
|
actions.layer.TCPLayer,
|
||||||
actions.layer.UDPLayer,
|
actions.layer.UDPLayer,
|
||||||
actions.layer.DNSLayer,
|
actions.layer.DNSLayer,
|
||||||
actions.layer.DNSQRLayer
|
actions.layer.DNSQRLayer,
|
||||||
|
#actions.layer.HTTPLayer,
|
||||||
|
actions.layer.HTTPRequestLayer
|
||||||
]
|
]
|
||||||
SUPPORTED_LAYERS = _SUPPORTED_LAYERS
|
SUPPORTED_LAYERS = _SUPPORTED_LAYERS
|
||||||
|
|
||||||
|
@ -23,6 +28,12 @@ class Packet():
|
||||||
"""
|
"""
|
||||||
Initializes the packet object.
|
Initializes the packet object.
|
||||||
"""
|
"""
|
||||||
|
if(ScapyHTTPRequest in packet.layers()):
|
||||||
|
# We have a ScapyHTTPRequest. Instead, let's convert to our HttpRequest.
|
||||||
|
# Overwrite the current ScapyHTTPRequest with a GenevaHTTPRequest
|
||||||
|
self.packet = packet
|
||||||
|
self.packet[ScapyHTTPRequest] = GenevaHTTPRequest(bytes(packet[ScapyHTTPRequest]))
|
||||||
|
else:
|
||||||
self.packet = packet
|
self.packet = packet
|
||||||
self.layers = self.setup_layers()
|
self.layers = self.setup_layers()
|
||||||
self.sleep = 0
|
self.sleep = 0
|
||||||
|
@ -131,7 +142,7 @@ class Packet():
|
||||||
"""
|
"""
|
||||||
layers = {}
|
layers = {}
|
||||||
for layer in self.read_layers():
|
for layer in self.read_layers():
|
||||||
layers[layer.name.upper()] = layer
|
layers[layer.name] = layer
|
||||||
return layers
|
return layers
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
|
@ -179,7 +190,11 @@ class Packet():
|
||||||
if self.haslayer("TCP"):
|
if self.haslayer("TCP"):
|
||||||
del self.packet["TCP"].chksum
|
del self.packet["TCP"].chksum
|
||||||
|
|
||||||
return self.layers[str_protocol].set(self.packet, field, value)
|
lay = self.layers[str_protocol].set(self.packet, field, value)
|
||||||
|
|
||||||
|
print(lay)
|
||||||
|
|
||||||
|
return lay
|
||||||
|
|
||||||
def get(self, str_protocol, field):
|
def get(self, str_protocol, field):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -85,4 +85,5 @@ class Strategy(object):
|
||||||
# If no action tree was applicable, send the packet unimpeded
|
# If no action tree was applicable, send the packet unimpeded
|
||||||
if not ran:
|
if not ran:
|
||||||
packets_to_send = [packet]
|
packets_to_send = [packet]
|
||||||
|
|
||||||
return packets_to_send
|
return packets_to_send
|
||||||
|
|
|
@ -15,28 +15,37 @@ import actions.utils
|
||||||
from actions.layer import DNSLayer
|
from actions.layer import DNSLayer
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
from actions.http import HTTPRequest as HTTPRequest
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
import string
|
||||||
|
|
||||||
# All supported tamper primitives
|
# All supported tamper primitives
|
||||||
SUPPORTED_PRIMITIVES = ["corrupt", "replace", "add", "compress"]
|
SUPPORTED_PRIMITIVES = ["corrupt", "replace", "add", "compress", "insert", "delete"]
|
||||||
|
|
||||||
|
|
||||||
class TamperAction(Action):
|
class TamperAction(Action):
|
||||||
"""
|
"""
|
||||||
Defines the TamperAction for Geneva.
|
Defines the TamperAction for Geneva.
|
||||||
"""
|
"""
|
||||||
def __init__(self, environment_id=None, field=None, tamper_type=None, tamper_value=None, tamper_proto="TCP"):
|
def __init__(self, environment_id=None, field=None, tamper_type=None, tamper_value=None, tamper_proto="TCP", start_index=None, end_index=None, encoded_payload=None):
|
||||||
Action.__init__(self, "tamper", "both")
|
Action.__init__(self, "tamper", "both")
|
||||||
self.field = field
|
self.field = field
|
||||||
self.tamper_value = tamper_value
|
self.tamper_value = tamper_value
|
||||||
self.tamper_proto = actions.utils.string_to_protocol(tamper_proto)
|
self.tamper_proto = actions.utils.string_to_protocol(tamper_proto)
|
||||||
self.tamper_proto_str = tamper_proto
|
self.tamper_proto_str = tamper_proto
|
||||||
self.tamper_type = tamper_type
|
self.tamper_type = tamper_type
|
||||||
|
self.start_index = start_index
|
||||||
|
self.end_index = end_index
|
||||||
|
self.encoded_payload = encoded_payload
|
||||||
|
if encoded_payload:
|
||||||
|
self.decoded_payload = bytes(urllib.parse.unquote(encoded_payload), "UTF-8")
|
||||||
|
|
||||||
def tamper(self, packet, logger):
|
def tamper(self, packet, logger):
|
||||||
"""
|
"""
|
||||||
Edits a given packet according to the action settings.
|
Edits a given packet according to the action settings.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Return packet untouched if not applicable
|
# Return packet untouched if not applicable
|
||||||
if not packet.haslayer(self.tamper_proto_str):
|
if not packet.haslayer(self.tamper_proto_str):
|
||||||
return packet
|
return packet
|
||||||
|
@ -48,11 +57,40 @@ class TamperAction(Action):
|
||||||
# If corrupting the packet field, generate a value for it
|
# If corrupting the packet field, generate a value for it
|
||||||
try:
|
try:
|
||||||
if self.tamper_type == "corrupt":
|
if self.tamper_type == "corrupt":
|
||||||
|
if self.tamper_proto == HTTPRequest:
|
||||||
|
packet = corrupt(packet, self.field, self.start_index, self.end_index)
|
||||||
|
del packet["IP"].chksum
|
||||||
|
del packet["IP"].len
|
||||||
|
del packet["TCP"].chksum
|
||||||
|
del packet["TCP"].dataofs
|
||||||
|
return packet
|
||||||
|
else:
|
||||||
new_value = packet.gen(self.tamper_proto_str, self.field)
|
new_value = packet.gen(self.tamper_proto_str, self.field)
|
||||||
elif self.tamper_type == "add":
|
elif self.tamper_type == "add":
|
||||||
new_value = int(self.tamper_value) + int(old_value)
|
new_value = int(self.tamper_value) + int(old_value)
|
||||||
elif self.tamper_type == "compress":
|
elif self.tamper_type == "compress":
|
||||||
return packet.dns_decompress(logger)
|
return packet.dns_decompress(logger)
|
||||||
|
elif self.tamper_type == "insert":
|
||||||
|
packet = insert(packet, self.field, self.start_index, self.decoded_payload)
|
||||||
|
|
||||||
|
del packet["IP"].chksum
|
||||||
|
del packet["IP"].len
|
||||||
|
del packet["TCP"].chksum
|
||||||
|
del packet["TCP"].dataofs
|
||||||
|
return packet
|
||||||
|
elif self.tamper_type == "replace":
|
||||||
|
packet = replace(packet, self.field, self.start_index, self.decoded_payload)
|
||||||
|
|
||||||
|
del packet["IP"].chksum
|
||||||
|
del packet["IP"].len
|
||||||
|
del packet["TCP"].chksum
|
||||||
|
del packet["TCP"].dataofs
|
||||||
|
|
||||||
|
return packet
|
||||||
|
elif self.tamper_type == "delete":
|
||||||
|
packet = delete(packet, self.field, self.start_index, self.end_index)
|
||||||
|
return packet
|
||||||
|
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
# If a primitive does not support the type of packet given
|
# If a primitive does not support the type of packet given
|
||||||
return packet
|
return packet
|
||||||
|
@ -62,7 +100,7 @@ class TamperAction(Action):
|
||||||
|
|
||||||
logger.debug(" - Tampering %s field `%s` (%s) by %s (to %s)" %
|
logger.debug(" - Tampering %s field `%s` (%s) by %s (to %s)" %
|
||||||
(self.tamper_proto_str, self.field, str(old_value), self.tamper_type, str(new_value)))
|
(self.tamper_proto_str, self.field, str(old_value), self.tamper_type, str(new_value)))
|
||||||
|
print("about to call set")
|
||||||
packet.set(self.tamper_proto_str, self.field, new_value)
|
packet.set(self.tamper_proto_str, self.field, new_value)
|
||||||
|
|
||||||
return packet
|
return packet
|
||||||
|
@ -82,11 +120,19 @@ class TamperAction(Action):
|
||||||
"""
|
"""
|
||||||
s = Action.__str__(self)
|
s = Action.__str__(self)
|
||||||
if self.tamper_type == "corrupt":
|
if self.tamper_type == "corrupt":
|
||||||
|
if self.tamper_proto == HTTPRequest:
|
||||||
|
s += "{%s:%s:%s:%s}" % (self.tamper_proto_str, self.field, self.tamper_type, str(self.start_index) + "-" + str(self.end_index))
|
||||||
|
else:
|
||||||
s += "{%s:%s:%s}" % (self.tamper_proto_str, self.field, self.tamper_type)
|
s += "{%s:%s:%s}" % (self.tamper_proto_str, self.field, self.tamper_type)
|
||||||
elif self.tamper_type in ["replace", "add"]:
|
elif self.tamper_type in ["replace", "add", "insert"]:
|
||||||
|
if self.tamper_proto == HTTPRequest:
|
||||||
|
s += "{%s:%s:%s:%s:%s}" % (self.tamper_proto_str, self.field, self.tamper_type, str(self.start_index), self.encoded_payload)
|
||||||
|
else:
|
||||||
s += "{%s:%s:%s:%s}" % (self.tamper_proto_str, self.field, self.tamper_type, self.tamper_value)
|
s += "{%s:%s:%s:%s}" % (self.tamper_proto_str, self.field, self.tamper_type, self.tamper_value)
|
||||||
elif self.tamper_type == "compress":
|
elif self.tamper_type == "compress":
|
||||||
s += "{%s:%s:compress}" % ("DNS", "qd", )
|
s += "{%s:%s:compress}" % ("DNS", "qd", )
|
||||||
|
elif self.tamper_type == "delete":
|
||||||
|
s += "{%s:%s:%s:%s}" % (self.tamper_proto_str, self.field, self.tamper_type, str(self.start_index) + "-" + str(self.end_index))
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
@ -105,19 +151,34 @@ class TamperAction(Action):
|
||||||
# Count the number of params in this given string
|
# Count the number of params in this given string
|
||||||
num_parameters = string.count(":")
|
num_parameters = string.count(":")
|
||||||
|
|
||||||
# If num_parameters is greater than 3, it's not a valid tamper action
|
# If num_parameters is greater than 4, it's not a valid tamper action
|
||||||
if num_parameters > 3 or num_parameters < 2:
|
if num_parameters > 4 or num_parameters < 2:
|
||||||
msg = "Cannot parse tamper action %s" % string
|
msg = "Cannot parse tamper action %s" % string
|
||||||
logger.error(msg)
|
logger.error(msg)
|
||||||
raise Exception(msg)
|
raise Exception(msg)
|
||||||
params = string.split(":")
|
params = string.split(":")
|
||||||
if num_parameters == 3:
|
if num_parameters == 4:
|
||||||
self.tamper_proto_str, self.field, self.tamper_type, self.tamper_value = params
|
# HTTP replace or insert
|
||||||
|
self.tamper_proto_str, self.field, self.tamper_type, self.start_index, self.encoded_payload = params
|
||||||
|
self.start_index = int(self.start_index)
|
||||||
self.tamper_proto = actions.utils.string_to_protocol(self.tamper_proto_str)
|
self.tamper_proto = actions.utils.string_to_protocol(self.tamper_proto_str)
|
||||||
|
self.decoded_payload = bytes(urllib.parse.unquote(self.encoded_payload), "UTF-8")
|
||||||
|
|
||||||
|
elif num_parameters == 3:
|
||||||
|
# HTTP corrupt or delete could be here, check for those first
|
||||||
|
self.tamper_proto_str = params[0]
|
||||||
|
self.tamper_proto = actions.utils.string_to_protocol(self.tamper_proto_str)
|
||||||
|
if self.tamper_proto_str == "HTTPRequest":
|
||||||
|
self.field = params[1]
|
||||||
|
self.tamper_type = params[2]
|
||||||
|
indices = params[3].split('-')
|
||||||
|
self.start_index = int(indices[0])
|
||||||
|
self.end_index = int(indices[1])
|
||||||
|
else:
|
||||||
|
self.tamper_proto_str, self.field, self.tamper_type, self.tamper_value = params
|
||||||
if "options" in self.field:
|
if "options" in self.field:
|
||||||
if not self.tamper_value:
|
if not self.tamper_value:
|
||||||
self.tamper_value = '' # An empty string instead of an empty byte literal
|
self.tamper_value = '' # An empty string instead of an empty byte literal
|
||||||
|
|
||||||
# tamper_value might be parsed as a string despite being an integer in most cases.
|
# tamper_value might be parsed as a string despite being an integer in most cases.
|
||||||
# Try to parse it out here
|
# Try to parse it out here
|
||||||
try:
|
try:
|
||||||
|
@ -125,8 +186,94 @@ class TamperAction(Action):
|
||||||
self.tamper_value = int(self.tamper_value)
|
self.tamper_value = int(self.tamper_value)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
else:
|
|
||||||
|
elif num_parameters == 2:
|
||||||
self.tamper_proto_str, self.field, self.tamper_type = params
|
self.tamper_proto_str, self.field, self.tamper_type = params
|
||||||
self.tamper_proto = actions.utils.string_to_protocol(self.tamper_proto_str)
|
self.tamper_proto = actions.utils.string_to_protocol(self.tamper_proto_str)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def insert(packet, header, index, content):
|
||||||
|
"""
|
||||||
|
Helper method to insert content into packet[header][index]
|
||||||
|
"""
|
||||||
|
if header not in packet["HTTPRequest"].fields:
|
||||||
|
# TODO: throw some sort of error, this header doesn't exist
|
||||||
|
return None
|
||||||
|
|
||||||
|
if index > len(packet["HTTPRequest"].fields[header]):
|
||||||
|
# TODO: throw some sort of error, this index is too large
|
||||||
|
return None
|
||||||
|
packet["HTTPRequest"].fields[header] = packet["HTTPRequest"].fields[header][0:index] \
|
||||||
|
+ content + \
|
||||||
|
packet["HTTPRequest"].fields[header][index:]
|
||||||
|
|
||||||
|
return packet
|
||||||
|
|
||||||
|
def replace(packet, header, index, content):
|
||||||
|
"""
|
||||||
|
Helper method to replace packet[header][index] with content
|
||||||
|
"""
|
||||||
|
if header not in packet["HTTPRequest"].fields:
|
||||||
|
# TODO: throw some sort of error, this header doesn't exist
|
||||||
|
return None
|
||||||
|
|
||||||
|
if index+len(content) > len(packet["HTTPRequest"].fields[header]):
|
||||||
|
# TODO: throw some sort of error, this index is too large
|
||||||
|
return None
|
||||||
|
|
||||||
|
packet["HTTPRequest"].fields[header] = packet["HTTPRequest"].fields[header][0:index] \
|
||||||
|
+ content + \
|
||||||
|
packet["HTTPRequest"].fields[header][index+len(content):]
|
||||||
|
return packet
|
||||||
|
|
||||||
|
|
||||||
|
def delete(packet, header, start_index, end_index):
|
||||||
|
"""
|
||||||
|
Helper method to remove the characters at header[start_index] to header[end_index]
|
||||||
|
"""
|
||||||
|
if header not in packet["HTTPRequest"].fields:
|
||||||
|
# TODO: throw some sort of error, this header doesn't exist
|
||||||
|
return None
|
||||||
|
|
||||||
|
if end_index+1 > len(packet["HTTPRequest"].fields[header]):
|
||||||
|
# TODO: throw some sort of error, this index is too large
|
||||||
|
return None
|
||||||
|
|
||||||
|
packet["HTTPRequest"].fields[header] = packet["HTTPRequest"].fields[header][0:start_index] \
|
||||||
|
+ packet["HTTPRequest"].fields[header][end_index+1:]
|
||||||
|
return packet
|
||||||
|
|
||||||
|
def corrupt(packet, header, start_index, end_index):
|
||||||
|
"""
|
||||||
|
Helper method to remove the characters at header[start_index] to header[end_index]
|
||||||
|
"""
|
||||||
|
print("in cor")
|
||||||
|
if header not in packet["HTTPRequest"].fields:
|
||||||
|
# TODO: throw some sort of error, this header doesn't exist
|
||||||
|
return None
|
||||||
|
|
||||||
|
if end_index+1 > len(packet["HTTPRequest"].fields[header]):
|
||||||
|
# TODO: throw some sort of error, this index is too large
|
||||||
|
return None
|
||||||
|
print("in cor")
|
||||||
|
try:
|
||||||
|
old_field = packet["HTTPRequest"].fields[header]
|
||||||
|
tampered_field = packet["HTTPRequest"].fields[header][0:start_index]
|
||||||
|
|
||||||
|
for i in range(0, end_index - start_index + 1):
|
||||||
|
# Ensure we're getting a new character
|
||||||
|
new_character = bytes(random.choice(string.printable), "UTF-8")
|
||||||
|
while(new_character == old_field[i]):
|
||||||
|
new_character = bytes(random.choice(string.printable), "UTF-8")
|
||||||
|
tampered_field = tampered_field + new_character
|
||||||
|
|
||||||
|
tampered_field = tampered_field + packet["HTTPRequest"].fields[header][end_index+1:]
|
||||||
|
print("settings tampered field to:")
|
||||||
|
print(tampered_field)
|
||||||
|
packet["HTTPRequest"].fields[header] = bytes(tampered_field, "UTF-8")
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
return packet
|
||||||
|
|
|
@ -14,6 +14,7 @@ import actions.trigger
|
||||||
import actions.packet
|
import actions.packet
|
||||||
|
|
||||||
from scapy.all import TCP, IP, UDP, rdpcap
|
from scapy.all import TCP, IP, UDP, rdpcap
|
||||||
|
from actions.http import HTTPRequest
|
||||||
import netifaces
|
import netifaces
|
||||||
|
|
||||||
|
|
||||||
|
@ -153,6 +154,8 @@ def string_to_protocol(protocol):
|
||||||
return IP
|
return IP
|
||||||
elif protocol.upper() == "UDP":
|
elif protocol.upper() == "UDP":
|
||||||
return UDP
|
return UDP
|
||||||
|
elif protocol.upper() == "HTTPREQUEST":
|
||||||
|
return HTTPRequest
|
||||||
|
|
||||||
|
|
||||||
def get_id():
|
def get_id():
|
||||||
|
|
|
@ -177,10 +177,14 @@ class WindowsEngine(GenericEngine):
|
||||||
try:
|
try:
|
||||||
self.logger.debug("Sending packet %s", str(packet))
|
self.logger.debug("Sending packet %s", str(packet))
|
||||||
# Convert the packet to a bytearray so memoryview can edit the underlying memory
|
# Convert the packet to a bytearray so memoryview can edit the underlying memory
|
||||||
|
print("About to cnvert")
|
||||||
pack = bytearray(bytes(packet.packet))
|
pack = bytearray(bytes(packet.packet))
|
||||||
|
print("about to send")
|
||||||
|
print(pack)
|
||||||
# Don't recalculate checksum since sometimes we will have already changed it
|
# Don't recalculate checksum since sometimes we will have already changed it
|
||||||
self.divert.send(pydivert.Packet(memoryview(pack), self.interface, dir), recalculate_checksum=False)
|
self.divert.send(pydivert.Packet(memoryview(pack), self.interface, dir), recalculate_checksum=False)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
self.logger.exception("Error in engine mysend.")
|
self.logger.exception("Error in engine mysend.")
|
||||||
|
|
||||||
def handle_outbound_packet(self, divert_packet):
|
def handle_outbound_packet(self, divert_packet):
|
||||||
|
|
Loading…
Reference in New Issue