<s> r'''<STR_LIT>''' <EOL> import logging <EOL> from nogotofail . mitm import util <EOL> from nogotofail . mitm . connection . handlers import preconditions <EOL> from nogotofail . mitm . connection . handlers . data import handlers <EOL> from nogotofail . mitm . connection . handlers . data import ClientReportDetection <EOL> from nogotofail . mitm . connection . handlers . data import DataHandler <EOL> from nogotofail . mitm . connection . handlers . store import handler <EOL> from nogotofail . mitm . event import connection <EOL> import re <EOL> @ handler ( handlers , internal = True ) <EOL> class BufferedHttpHandler ( DataHandler ) : <EOL> """<STR_LIT>""" <EOL> name = "<STR_LIT>" <EOL> description = ( <EOL> "<STR_LIT>" ) <EOL> class BufferState ( object ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self ) : <EOL> self . reset ( ) <EOL> def reset ( self ) : <EOL> self . buffer = "<STR_LIT>" <EOL> self . remaining = <NUM_LIT:0> <EOL> MAX_LENGTH = <NUM_LIT:2> ** <NUM_LIT> <EOL> def on_select ( self ) : <EOL> self . request_state = BufferedHttpHandler . BufferState ( ) <EOL> self . response_state = BufferedHttpHandler . BufferState ( ) <EOL> def on_request ( self , request ) : <EOL> return self . _handle_data ( request , self . request_state ) <EOL> def on_response ( self , response ) : <EOL> return self . _handle_data ( response , self . response_state ) <EOL> def _handle_data ( self , data , buffer_state ) : <EOL> if buffer_state . remaining > <NUM_LIT:0> : <EOL> return self . _handle_buffering ( data , buffer_state ) <EOL> length = self . _get_data_length ( data ) <EOL> if length <= <NUM_LIT:0> : <EOL> return data <EOL> if length > self . MAX_LENGTH : <EOL> return data <EOL> content_offset = data . find ( "<STR_LIT>" ) <EOL> if content_offset < <NUM_LIT:0> : <EOL> return data <EOL> buffer_state . remaining = content_offset + length + len ( "<STR_LIT>" ) <EOL> return self . _handle_buffering ( data , buffer_state ) <EOL> def _get_data_length ( self , data ) : <EOL> response = util . http . parse_response ( data ) <EOL> if response : <EOL> return int ( response . getheader ( "<STR_LIT>" , <NUM_LIT:0> ) ) <EOL> request = util . http . parse_request ( data ) <EOL> if request : <EOL> return int ( request . headers . get ( "<STR_LIT>" , <NUM_LIT:0> ) ) <EOL> def _handle_buffering ( self , data , buffer_state ) : <EOL> if len ( data ) >= buffer_state . remaining : <EOL> full_data = buffer_state . buffer + data <EOL> buffer_state . reset ( ) <EOL> return full_data <EOL> else : <EOL> buffer_state . buffer += data <EOL> buffer_state . remaining -= len ( data ) <EOL> return "<STR_LIT>" <EOL> @ handler . passive ( handlers ) <EOL> class HttpDetectionHandler ( DataHandler ) : <EOL> name = "<STR_LIT>" <EOL> description = "<STR_LIT>" <EOL> def on_request ( self , request ) : <EOL> http = util . http . parse_request ( request ) <EOL> if http and not http . error_code : <EOL> host = http . headers . get ( "<STR_LIT:host>" , self . connection . server_addr ) <EOL> if not self . connection . hostname : <EOL> self . connection . hostname = host <EOL> self . on_http ( http ) <EOL> return request <EOL> def on_http ( self , http ) : <EOL> host = http . headers . get ( "<STR_LIT:host>" , self . connection . server_addr ) <EOL> self . log ( logging . ERROR , "<STR_LIT>" <EOL> % ( http . command , host + http . path ) ) <EOL> self . log_event ( <EOL> logging . ERROR , <EOL> connection . AttackEvent ( <EOL> self . connection , self . name , True , <EOL> host + http . path ) ) <EOL> self . connection . vuln_notify ( util . vuln . VULN_CLEARTEXT_HTTP ) <EOL> @ handler . passive ( handlers ) <EOL> class HttpAuthHandler ( HttpDetectionHandler ) : <EOL> name = "<STR_LIT>" <EOL> description = "<STR_LIT>" <EOL> def on_http ( self , http ) : <EOL> auth = http . headers . get ( "<STR_LIT>" , None ) <EOL> host = http . headers . get ( "<STR_LIT:host>" , self . connection . server_addr ) <EOL> if auth : <EOL> self . log ( <EOL> logging . CRITICAL , <EOL> "<STR_LIT>" % <EOL> ( http . command , host + http . path ) ) <EOL> self . log_event ( <EOL> logging . ERROR , <EOL> connection . AttackEvent ( <EOL> self . connection , self . name , True , <EOL> host + http . path ) ) <EOL> self . connection . vuln_notify ( util . vuln . VULN_CLEARTEXT_AUTH ) <EOL> class _HttpReqReplacement ( DataHandler ) : <EOL> """<STR_LIT>""" <EOL> def filter ( self , http ) : <EOL> return False <EOL> def replace ( self , http ) : <EOL> return "<STR_LIT>" <EOL> def on_request ( self , request ) : <EOL> http = util . http . parse_request ( request ) <EOL> if http and not http . error_code : <EOL> host = http . headers . get ( "<STR_LIT:host>" , self . connection . server_addr ) <EOL> if not self . connection . hostname : <EOL> self . connection . hostname = host <EOL> if self . filter ( http ) : <EOL> return self . replace ( http ) <EOL> return request <EOL> class _ResponseReplacement ( DataHandler ) : <EOL> """<STR_LIT>""" <EOL> skip = <NUM_LIT:0> <EOL> def filter ( self , data ) : <EOL> return False <EOL> def replace ( self , data ) : <EOL> return "<STR_LIT>" <EOL> def on_response ( self , request ) : <EOL> if self . skip > <NUM_LIT:0> : <EOL> if self . skip >= len ( request ) : <EOL> self . skip -= len ( request ) <EOL> return "<STR_LIT>" <EOL> request = request [ self . skip : ] <EOL> self . skip = <NUM_LIT:0> <EOL> if self . filter ( request ) : <EOL> return self . replace ( request ) <EOL> return request <EOL> @ handler ( handlers , default = False ) <EOL> class AndroidWebviewJsRce ( _ResponseReplacement ) : <EOL> name = "<STR_LIT>" <EOL> description = "<STR_LIT>" <EOL> base_url = "<STR_LIT>" <EOL> base_payload = """<STR_LIT>""" <EOL> def filter ( self , data ) : <EOL> resp = util . http . parse_response ( data ) <EOL> return ( resp and resp . status == <NUM_LIT:200> and <EOL> resp . getheader ( "<STR_LIT>" , "<STR_LIT>" ) . startswith ( "<STR_LIT>" ) ) <EOL> def build_payload ( self ) : <EOL> url = ClientReportDetection . add_callback_url ( <EOL> self . on_report , self . base_url ) <EOL> return self . base_payload % ( url ) <EOL> def on_report ( self , data ) : <EOL> if data is None : <EOL> return <EOL> self . log ( <EOL> logging . CRITICAL , <EOL> "<STR_LIT>" ) <EOL> self . log_event ( <EOL> logging . ERROR , <EOL> connection . AttackEvent ( self . connection , self . name , True , None ) ) <EOL> self . connection . vuln_notify ( util . vuln . VULN_ANDROID_JAVASCRIPT_RCE ) <EOL> return False <EOL> def replace ( self , response ) : <EOL> resp = util . http . parse_response ( response ) <EOL> headers = dict ( resp . getheaders ( ) ) <EOL> old_length = int ( headers . get ( "<STR_LIT>" , <NUM_LIT:0> ) ) <EOL> contents = resp . read ( old_length ) <EOL> match = re . search ( "<STR_LIT>" , contents ) <EOL> if not match : <EOL> return response <EOL> payload = self . build_payload ( ) <EOL> contents = contents [ : match . end ( ) ] + payload + contents [ match . end ( ) : ] <EOL> message = ( "<STR_LIT>" + "<STR_LIT:\r\n>" . join ( <EOL> [ "<STR_LIT>" % ( k , v ) for k , v in headers . items ( ) ] ) + "<STR_LIT>" + "<STR_LIT>" ) <EOL> headers [ "<STR_LIT>" ] = old_length + len ( payload ) <EOL> version = "<STR_LIT>" if resp . version == <NUM_LIT:10> else "<STR_LIT>" <EOL> data = message . format ( version = version , data = contents ) <EOL> total_consumed = response . index ( "<STR_LIT>" ) + <NUM_LIT:4> + old_length <EOL> if total_consumed < len ( response ) : <EOL> data += response [ total_consumed : ] <EOL> return data <EOL> @ handler ( handlers , default = False ) <EOL> class SSLStrip ( _ResponseReplacement ) : <EOL> """<STR_LIT>""" <EOL> name = "<STR_LIT>" <EOL> description = ( <EOL> "<STR_LIT>" ) <EOL> content_types = [ <EOL> "<STR_LIT:application/json>" , <EOL> "<STR_LIT>" , <EOL> "<STR_LIT>" , <EOL> "<STR_LIT>" , <EOL> "<STR_LIT>" , <EOL> "<STR_LIT>" , <EOL> "<STR_LIT>" , <EOL> ] <EOL> def filter ( self , data ) : <EOL> resp = util . http . parse_response ( data ) <EOL> content_type = resp . getheader ( <EOL> "<STR_LIT>" , "<STR_LIT>" ) . strip ( ) if resp else "<STR_LIT>" <EOL> return resp and ( <EOL> ( resp . status == <NUM_LIT:200> and any ( <EOL> re . match ( type , content_type ) <EOL> for type in SSLStrip . content_types ) ) <EOL> or ( resp . status / <NUM_LIT:100> == <NUM_LIT:3> <EOL> and resp . getheader ( "<STR_LIT:location>" , "<STR_LIT>" ) . startswith ( "<STR_LIT>" ) ) ) <EOL> def replace ( self , response ) : <EOL> resp = util . http . parse_response ( response ) <EOL> if resp . status == <NUM_LIT:200> : <EOL> return self . replace_ok ( response ) <EOL> elif resp . status / <NUM_LIT:100> == <NUM_LIT:3> : <EOL> return self . replace_redirect ( response ) <EOL> else : <EOL> self . log ( <EOL> logging . FATAL , <EOL> "<STR_LIT>" % resp . status ) <EOL> return "<STR_LIT>" <EOL> def build_report_callback ( self , url ) : <EOL> def on_report ( data ) : <EOL> if data is None : <EOL> return <EOL> self . log ( logging . CRITICAL , "<STR_LIT>" % url ) <EOL> self . log_event ( <EOL> logging . CRITICAL , <EOL> connection . AttackEvent ( <EOL> self . connection , self . name , True , url ) ) <EOL> self . connection . vuln_notify ( util . vuln . VULN_SSL_STRIP ) <EOL> return False <EOL> return on_report <EOL> def replace_ok ( self , response ) : <EOL> """<STR_LIT>""" <EOL> resp = util . http . parse_response ( response ) <EOL> headers = dict ( resp . getheaders ( ) ) <EOL> old_length = int ( headers . get ( "<STR_LIT>" , <NUM_LIT:0> ) ) <EOL> contents = resp . read ( old_length ) <EOL> new_contents = "<STR_LIT>" <EOL> prev = <NUM_LIT:0> <EOL> urls = re . finditer ( <EOL> "<STR_LIT>" , <EOL> contents ) <EOL> for match in urls : <EOL> url = match . group ( <NUM_LIT:0> ) <EOL> callback = self . build_report_callback ( url ) <EOL> url = "<STR_LIT>" + url [ <NUM_LIT:8> : ] <EOL> new_url = ClientReportDetection . add_callback_url ( <EOL> callback , url , timeout = <NUM_LIT:20> ) <EOL> new_contents += contents [ prev : match . start ( ) ] + new_url <EOL> prev = match . end ( ) <EOL> self . log ( <EOL> logging . DEBUG , <EOL> "<STR_LIT>" % ( match . group ( <NUM_LIT:0> ) , new_url ) ) <EOL> new_contents += contents [ prev : ] <EOL> headers [ "<STR_LIT>" ] = len ( new_contents ) <EOL> version = "<STR_LIT>" if resp . version == <NUM_LIT:10> else "<STR_LIT>" <EOL> message = ( "<STR_LIT>" + "<STR_LIT:\r\n>" . join ( <EOL> [ "<STR_LIT>" % ( k , v ) for k , v in headers . items ( ) ] ) + "<STR_LIT>" + "<STR_LIT>" ) <EOL> data = message . format ( version = version , data = new_contents ) <EOL> total_consumed = response . index ( "<STR_LIT>" ) + <NUM_LIT:4> + old_length <EOL> if total_consumed < len ( response ) : <EOL> data += response [ total_consumed : ] <EOL> return data <EOL> def replace_redirect ( self , response ) : <EOL> """<STR_LIT>""" <EOL> resp = util . http . parse_response ( response ) <EOL> headers = dict ( resp . getheaders ( ) ) <EOL> location = headers [ "<STR_LIT:location>" ] <EOL> callback = self . build_report_callback ( location ) <EOL> new_location = "<STR_LIT>" + location [ <NUM_LIT:8> : ] <EOL> new_location = ClientReportDetection . add_callback_url ( <EOL> callback , new_location , timeout = <NUM_LIT:5> ) <EOL> headers [ "<STR_LIT:location>" ] = new_location <EOL> self . log ( logging . DEBUG , <EOL> "<STR_LIT>" % <EOL> ( location , new_location ) ) <EOL> version = "<STR_LIT>" if resp . version == <NUM_LIT:10> else "<STR_LIT>" <EOL> message = ( "<STR_LIT>" + "<STR_LIT:\r\n>" . join ( <EOL> [ "<STR_LIT>" % ( k , v ) for k , v in headers . items ( ) ] ) + "<STR_LIT>" ) <EOL> data = message . format ( version = version , status = resp . status ) <EOL> total_consumed = response . index ( <EOL> "<STR_LIT>" ) + <NUM_LIT:4> + int ( headers . get ( "<STR_LIT>" , <NUM_LIT:0> ) ) <EOL> if total_consumed < len ( response ) : <EOL> data += response [ total_consumed : ] <EOL> return data <EOL> @ handler ( handlers ) <EOL> @ preconditions . requires_files ( files = [ "<STR_LIT>" ] ) <EOL> class ImageReplacement ( _ResponseReplacement ) : <EOL> """<STR_LIT>""" <EOL> name = "<STR_LIT>" <EOL> description = ( <EOL> "<STR_LIT>" ) <EOL> file = "<STR_LIT>" <EOL> data = None <EOL> def filter ( self , response ) : <EOL> resp = util . http . parse_response ( response ) <EOL> return ( resp and resp . status == <NUM_LIT:200> <EOL> and resp . getheader ( "<STR_LIT>" , "<STR_LIT>" ) . startswith ( "<STR_LIT>" ) <EOL> and response . find ( "<STR_LIT>" ) != - <NUM_LIT:1> ) <EOL> def replace ( self , response ) : <EOL> resp = util . http . parse_response ( response ) <EOL> headers = dict ( resp . getheaders ( ) ) <EOL> if not ImageReplacement . data : <EOL> with open ( util . extras . get_extras_path ( self . file ) ) as f : <EOL> ImageReplacement . data = f . read ( ) <EOL> old_length = int ( headers . get ( "<STR_LIT>" , <NUM_LIT:0> ) ) <EOL> length = len ( self . data ) <EOL> headers [ "<STR_LIT>" ] = length <EOL> headers [ "<STR_LIT>" ] = "<STR_LIT>" <EOL> message = ( "<STR_LIT>" + "<STR_LIT:\r\n>" . join ( <EOL> [ "<STR_LIT>" % ( k , v ) for k , v in headers . items ( ) ] ) + "<STR_LIT>" + "<STR_LIT>" ) <EOL> version = "<STR_LIT>" if resp . version == <NUM_LIT:10> else "<STR_LIT>" <EOL> data = message . format ( version = version , data = self . data ) <EOL> if old_length > <NUM_LIT:0> : <EOL> content_offset = response . find ( "<STR_LIT>" ) <EOL> total_length = content_offset + old_length <EOL> if len ( response ) < total_length : <EOL> self . skip = total_length - len ( response ) <EOL> return data <EOL> @ handler ( handlers , default = False ) <EOL> class BlockHTTP ( HttpDetectionHandler ) : <EOL> """<STR_LIT>""" <EOL> name = "<STR_LIT>" <EOL> description = "<STR_LIT>" <EOL> def on_http ( self , http ) : <EOL> self . connection . close ( ) <EOL> @ handler ( handlers ) <EOL> class DisableCDCPEncryption ( HttpDetectionHandler ) : <EOL> """<STR_LIT>""" <EOL> name = "<STR_LIT>" <EOL> description = "<STR_LIT>" <EOL> def on_http ( self , http ) : <EOL> host = http . headers . get ( "<STR_LIT:host>" ) <EOL> path = http . path <EOL> if host == "<STR_LIT>" and path == "<STR_LIT>" : <EOL> self . connection . close ( ) </s>
