diff --git a/katnip/legos/http.py b/katnip/legos/http.py new file mode 100644 index 000000000..b6d61aeab --- /dev/null +++ b/katnip/legos/http.py @@ -0,0 +1,74 @@ +from katnip.legos.url import DecimalNumber, Search, Path, urlparse +from kitty.model import Container +from kitty.model import String, Static, Group, Delimiter, Float +from kitty.model import ENC_BITS_BASE64 + + +def _valuename(txt): + return '%s_value' % txt + + +def _keyname(txt): + return '%s_key' % txt + + +class CustomHeaderField(Container): + def __init__(self, key, value, end=False, fuzzable_key=False, fuzzable_value=True): + fields = [ + String(name=_keyname(key), value=key, fuzzable=fuzzable_key), + Static(': '), + Container(name=_valuename(key), fields=value, fuzable=fuzzable_value), + Static('\r\n') + ] + if end: + fields.append(Static('\r\n')) + super(CustomHeaderField, self).__init__(name=key, fields=fields, fuzzable=fuzzable_value) + + +class TextField(CustomHeaderField): + def __init__(self, key, value, end=False, fuzzable_key=False, fuzzable_value=True): + value_field = [String(name="value", value=value)] + super(TextField, self).__init__(key, value_field, end, fuzzable_key, fuzzable_value) + + +class IntField(CustomHeaderField): + def __init__(self, key, value, end=False, fuzzable_key=False, fuzzable_value=True): + value_field = [DecimalNumber( + name="value", + value=value, + num_bits=32, + signed=True + )] + super(IntField, self).__init__(key, value_field, end, fuzzable_key, fuzzable_value) + + +class AuthorizationField(CustomHeaderField): + def __init__(self, key, username, password, end=False, delim=':', fuzz_username=True, fuzz_password=True, fuzzable_key=False, fuzzable=True): + value_field = [ + Static('Basic '), + Container(name='base64_auth', fields=[ + String(name='username', value=username, fuzzable=fuzz_username), + Delimiter(delim, fuzzable=False), + String(name='password', value=password, fuzzable=fuzz_password), + ], encoder=ENC_BITS_BASE64) + ] + super(AuthorizationField, self).__init__(key, value_field, end, fuzzable_key, fuzzable) + + +class HttpRequestLine(Container): + def __init__(self, method='GET', uri='/', protocol='HTTP', version=1.0, fuzzable_method=False, fuzzable_uri=False, fuzzable=True): + method_value = [method] if isinstance(method, str) else method + parsed = urlparse(uri) + uri_value = [Path(parsed.path, name='path', fuzz_delims=False)] + if parsed.query: + uri_value.append(Search(parsed.query, name='search', fuzz_value=True)) + fields = [ + Group(name='method', values=method_value, fuzzable=fuzzable_method), + Static(' '), + Container(name='uri', fields=uri_value, fuzzable=fuzzable_uri), + Static(' '), + String(name='protocol', value=protocol, fuzzable=False), + Float(name='version', value=version), + Static('\r\n'), + ] + super(HttpRequestLine, self).__init__(name='http url', fields=fields, fuzzable=fuzzable) diff --git a/katnip/legos/url.py b/katnip/legos/url.py index a19f94d25..085a22a14 100644 --- a/katnip/legos/url.py +++ b/katnip/legos/url.py @@ -49,6 +49,7 @@ class Url(Container): genericurl = scheme ":" schemepart ''' + def __init__(self, scheme, parts, fuzz_scheme=True, fuzz_parts=True, fuzz_delim=True, fuzzable=True, name=None): ''' :type scheme: str or instance of :class:`~kitty.model.low_level.field.BaseField` @@ -81,6 +82,7 @@ class IpUrl(Url): password = *[ uchar | ";" | "?" | "&" | "=" ] urlpath = *xchar ; depends on protocol see section 3.1 ''' + def __init__(self, scheme, login, url_path=None, fuzz_scheme=True, fuzz_login=True, fuzz_delims=True, fuzzable=True, name=None): ''' :type scheme: str or instance of :class:`~kitty.model.low_level.field.BaseField` @@ -178,6 +180,7 @@ class HostPort(Container): hostport = host [ ":" port ] port = digits ''' + def __init__(self, host, port=None, fuzz_host=True, fuzz_port=True, fuzz_delim=True, fuzzable=True, name=None): ''' :type host: @@ -210,6 +213,7 @@ class HostName(Container): host = hostname | hostnumber hostname = *[ domainlabel "." ] toplabel ''' + def __init__(self, host='', fuzz_delims=False, fuzzable=True, name=None): ''' :type host: str @@ -234,7 +238,8 @@ class Search(Container): .. todo:: real implementation (parse search string etc.) ''' - def __init__(self, search='', fuzz_delims=False, fuzzable=True, name=None): + + def __init__(self, search='', fuzz_delims=False, fuzz_param=False, fuzz_value=True, fuzzable=True, name=None): ''' :param search: search string (default: '') :param fuzz_delims: should fuzz the delimiters (default: False) @@ -243,8 +248,14 @@ def __init__(self, search='', fuzz_delims=False, fuzzable=True, name=None): ''' fields = [ Delimiter(name='search main delim', value='?', fuzzable=fuzz_delims), - String(name='search data', value=search), ] + for i, part in enumerate(search.split('&')): + part = part.split('=') + fields.append(Container(name='param_%s' % part[0], fields=[ + String(name='search_%d_key' % i, value=part[0], fuzzable=fuzz_param), + Delimiter(value='=', fuzzable=fuzz_delims), + String(name='search_%d_value' % i, value=part[1], fuzzable=fuzz_value) + ])) super(Search, self).__init__(name=name, fields=fields, fuzzable=fuzzable) @@ -252,6 +263,7 @@ class Path(Container): ''' Container to fuzz the path of the URL ''' + def __init__(self, path=None, path_delim='/', fuzz_delims=True, fuzzable=True, name=None): ''' :type path: str @@ -265,6 +277,8 @@ def __init__(self, path=None, path_delim='/', fuzz_delims=True, fuzzable=True, n if path is not None: fields.append(Delimiter(name='main delim', value='/', fuzzable=fuzz_delims)) path_parts = path.split(path_delim) + if not path_parts[0]: + path_parts = path_parts[1:] for i in range(len(path_parts) - 1): fields.append(String(name='path part %d' % i, value=path_parts[i])) fields.append(Delimiter(name='path delim %d' % i, value=path_delim, fuzzable=fuzz_delims)) @@ -285,6 +299,7 @@ class HttpUrl(Url): hsegment = *[ uchar | ";" | ":" | "@" | "&" | "=" ] search = *[ uchar | ";" | ":" | "@" | "&" | "=" ] ''' + def __init__(self, scheme='http', login=None, hostport=None, path=None, search=None, fuzz_scheme=True, fuzz_delims=True, fuzzable=True, name=None): ''' diff --git a/katnip/targets/ssl.py b/katnip/targets/ssl.py index 7d28de605..997d01572 100644 --- a/katnip/targets/ssl.py +++ b/katnip/targets/ssl.py @@ -34,7 +34,7 @@ def __init__(self, name, host, port, timeout=None, logger=None): :param timeout: socket timeout (default: None) :param logger: logger for the object (default: None) ''' - super(SslTarget, self).__init__(name, host, port, timeout, logger=None) + super(SslTarget, self).__init__(name, host, port, timeout, logger=logger) def _get_socket(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)