Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 30 additions & 9 deletions scapy/layers/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -823,9 +823,24 @@ def sr1(self, req, **kwargs):
)
return resp

def request(self, url, data=b"", timeout=5, follow_redirects=True, **headers):
def request(self,
url,
data=b"",
timeout=5,
follow_redirects=True,
http_headers={},
**headers):
"""
Perform a HTTP(s) request.

:param url: the full URL to connect to.
e.g. https://google.com/test
:param data: the data to send as payload
:param follow_redirects: if True, request() will follow 302 return codes
:param http_headers: if specified, overwrites the HTTP headers
(except Host and Path).
:param headers: any additional HTTPRequest parameter to add.
e.g. Method="POST"
"""
# Parse request url
m = re.match(r"(https?)://([^/:]+)(?:\:(\d+))?(/.*)?", url)
Expand All @@ -844,14 +859,20 @@ def request(self, url, data=b"", timeout=5, follow_redirects=True, **headers):
self._connect_or_reuse(host, port=port, tls=tls, timeout=timeout)

# Build request
http_headers = {
"Accept_Encoding": b'gzip, deflate',
"Cache_Control": b'no-cache',
"Pragma": b'no-cache',
"Connection": b'keep-alive',
"Host": host,
"Path": path,
}
headers.setdefault("Host", host)
headers.setdefault("Path", path)

if not http_headers:
http_headers = {
"Accept_Encoding": b'gzip, deflate',
"Cache_Control": b'no-cache',
"Pragma": b'no-cache',
"Connection": b'keep-alive',
}
else:
http_headers = {
k.replace("-", "_"): v for k, v in http_headers.items()
}
http_headers.update(headers)
req = HTTP() / HTTPRequest(**http_headers)
if data:
Expand Down
93 changes: 84 additions & 9 deletions scapy/layers/kerberos.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
>>> enc.decrypt(k)
"""

from collections import namedtuple
from collections import namedtuple, deque
from datetime import datetime, timedelta, timezone
from enum import IntEnum

Expand Down Expand Up @@ -117,7 +117,7 @@
XStrField,
)
from scapy.packet import Packet, bind_bottom_up, bind_top_down, bind_layers
from scapy.supersocket import StreamSocket
from scapy.supersocket import StreamSocket, SuperSocket
from scapy.utils import strrot, strxor
from scapy.volatile import GeneralizedTime, RandNum, RandBin

Expand Down Expand Up @@ -2523,14 +2523,77 @@ class KDC_PROXY_MESSAGE(ASN1_Packet):
ASN1F_optional(
ASN1F_FLAGS(
"dclocatorHint",
"",
None,
FlagsField("", 0, -32, _NV_VERSION).names,
explicit_tag=0xA2,
)
),
)


class KdcProxySocket(SuperSocket):
"""
This is a wrapper of a HTTP_Client that does KKDCP proxying,
disguised as a SuperSocket to be compatible with the rest of the KerberosClient.
"""

def __init__(
self,
url,
targetDomain,
dclocatorHint=None,
no_check_certificate=False,
**kwargs,
):
self.url = url
self.targetDomain = targetDomain
self.dclocatorHint = dclocatorHint
self.no_check_certificate = no_check_certificate
self.queue = deque()
super(KdcProxySocket, self).__init__(**kwargs)

def recv(self, x=None):
return self.queue.popleft()

def send(self, x, **kwargs):
from scapy.layers.http import HTTP_Client

cli = HTTP_Client(no_check_certificate=self.no_check_certificate)
try:
# sr it via the web client
resp = cli.request(
self.url,
Method="POST",
data=bytes(
# Wrap request in KDC_PROXY_MESSAGE
KDC_PROXY_MESSAGE(
kerbMessage=bytes(x),
targetDomain=ASN1_GENERAL_STRING(self.targetDomain.encode()),
# dclocatorHint is optional
dclocatorHint=self.dclocatorHint,
)
),
http_headers={
"Cache-Control": "no-cache",
"Pragma": "no-cache",
"User-Agent": "kerberos/1.0",
},
)
if resp and conf.raw_layer in resp:
# Parse the payload
resp = KDC_PROXY_MESSAGE(resp.load).kerbMessage
# We have an answer, queue it.
self.queue.append(resp)
else:
raise EOFError
finally:
cli.close()

@staticmethod
def select(sockets, remain=None):
return [x for x in sockets if isinstance(x, KdcProxySocket) and x.queue]


# Util functions


Expand Down Expand Up @@ -2558,6 +2621,8 @@ def __init__(
u2u=False,
for_user=None,
s4u2proxy=False,
kdc_proxy=None,
kdc_proxy_no_check_certificate=False,
etypes=None,
key=None,
port=88,
Expand Down Expand Up @@ -2590,7 +2655,7 @@ def __init__(
if not ticket:
raise ValueError("Invalid ticket")

if not ip:
if not ip and not kdc_proxy:
# No KDC IP provided. Find it by querying the DNS
ip = dclocator(
realm,
Expand Down Expand Up @@ -2630,7 +2695,8 @@ def __init__(
self._timeout = timeout
self._ip = ip
self._port = port
sock = self._connect()
self.kdc_proxy = kdc_proxy
self.kdc_proxy_no_check_certificate = kdc_proxy_no_check_certificate

if self.mode in [self.MODE.AS_REQ, self.MODE.GET_SALT]:
self.host = host.upper()
Expand All @@ -2651,16 +2717,25 @@ def __init__(
# Negotiated parameters
self.pre_auth = False
self.fxcookie = None

sock = self._connect()
super(KerberosClient, self).__init__(
sock=sock,
**kwargs,
)

def _connect(self):
sock = socket.socket()
sock.settimeout(self._timeout)
sock.connect((self._ip, self._port))
sock = StreamSocket(sock, KerberosTCPHeader)
if self.kdc_proxy:
sock = KdcProxySocket(
url=self.kdc_proxy,
targetDomain=self.realm,
no_check_certificate=self.kdc_proxy_no_check_certificate,
)
else:
sock = socket.socket()
sock.settimeout(self._timeout)
sock.connect((self._ip, self._port))
sock = StreamSocket(sock, KerberosTCPHeader)
return sock

def send(self, pkt):
Expand Down
Loading