The main Bubble source repository. Contains the Bubble API server, the web UI, documentation and utilities. https://getbubblenow.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

dns_spoofing.py 6.3 KiB

4 年之前
4 年之前
4 年之前
4 年之前
4 年之前
4 年之前
4 年之前
4 年之前
4 年之前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. #
  2. # Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://bubblev.com/bubble-license/
  3. #
  4. import re
  5. import time
  6. import uuid
  7. from bubble_api import bubble_matchers, bubble_log, CTX_BUBBLE_MATCHERS, BUBBLE_URI_PREFIX, CTX_BUBBLE_ABORT, CTX_BUBBLE_PASSTHRU, CTX_BUBBLE_REQUEST_ID, add_flow_ctx
  8. from bubble_config import bubble_host, bubble_host_alias
  9. # This regex extracts splits the host header into host and port.
  10. # Handles the edge case of IPv6 addresses containing colons.
  11. # https://bugzilla.mozilla.org/show_bug.cgi?id=45891
  12. parse_host_header = re.compile(r"^(?P<host>[^:]+|\[.+\])(?::(?P<port>\d+))?$")
  13. class Rerouter:
  14. @staticmethod
  15. def get_matchers(flow, host):
  16. if host is None:
  17. return None
  18. if flow.request.path and flow.request.path.startswith(BUBBLE_URI_PREFIX):
  19. bubble_log("get_matchers: not filtering special bubble path: "+flow.request.path)
  20. return None
  21. remote_addr = str(flow.client_conn.address[0])
  22. try:
  23. host = host.decode()
  24. except (UnicodeDecodeError, AttributeError):
  25. try:
  26. host = str(host)
  27. except Exception as e:
  28. bubble_log('get_matchers: host '+repr(host)+' could not be decoded, type='+str(type(host))+' e='+repr(e))
  29. return None
  30. if host == bubble_host or host == bubble_host_alias:
  31. bubble_log('get_matchers: request is for bubble itself ('+host+'), not matching')
  32. return None
  33. req_id = str(host) + '.' + str(uuid.uuid4()) + '.' + str(time.time())
  34. bubble_log("get_matchers: requesting match decision for req_id="+req_id)
  35. resp = bubble_matchers(req_id, remote_addr, flow, host)
  36. if not resp:
  37. bubble_log('get_matchers: no response for remote_addr/host: '+remote_addr+'/'+str(host))
  38. return None
  39. matchers = []
  40. if 'matchers' in resp and resp['matchers'] is not None:
  41. for m in resp['matchers']:
  42. if 'urlRegex' in m:
  43. bubble_log('get_matchers: checking for match of path='+flow.request.path+' against regex: '+m['urlRegex'])
  44. else:
  45. bubble_log('get_matchers: checking for match of path='+flow.request.path+' -- NO regex, skipping')
  46. continue
  47. if re.match(m['urlRegex'], flow.request.path):
  48. bubble_log('get_matchers: rule matched, adding rule: '+m['rule'])
  49. matchers.append(m)
  50. else:
  51. bubble_log('get_matchers: rule (regex='+m['urlRegex']+') did NOT match, skipping rule: '+m['rule'])
  52. else:
  53. bubble_log('get_matchers: no matchers. response='+repr(resp))
  54. decision = None
  55. if 'decision' in resp:
  56. decision = resp['decision']
  57. matcher_response = { 'decision': decision, 'matchers': matchers, 'request_id': req_id }
  58. bubble_log("get_matchers: returning "+repr(matcher_response))
  59. return matcher_response
  60. def request(self, flow):
  61. if flow.client_conn.tls_established:
  62. flow.request.scheme = "https"
  63. sni = flow.client_conn.connection.get_servername()
  64. port = 443
  65. else:
  66. flow.request.scheme = "http"
  67. sni = None
  68. port = 80
  69. host_header = flow.request.host_header
  70. # bubble_log("dns_spoofing.request: host_header is "+repr(host_header))
  71. if host_header:
  72. m = parse_host_header.match(host_header)
  73. if m:
  74. host_header = m.group("host").strip("[]")
  75. if m.group("port"):
  76. port = int(m.group("port"))
  77. # Determine if this request should be filtered
  78. if sni or host_header:
  79. matcher_response = self.get_matchers(flow, sni or host_header)
  80. if matcher_response:
  81. if 'decision' in matcher_response and matcher_response['decision'] is not None and matcher_response['decision'] == 'passthru':
  82. bubble_log('dns_spoofing.request: passthru response returned, passing thru and NOT performing TLS interception...')
  83. add_flow_ctx(flow, CTX_BUBBLE_PASSTHRU, True)
  84. return
  85. elif 'decision' in matcher_response and matcher_response['decision'] is not None and matcher_response['decision'].startswith('abort_'):
  86. bubble_log('dns_spoofing.request: found abort code: ' + str(matcher_response['decision']) + ', aborting')
  87. if matcher_response['decision'] == 'abort_ok':
  88. abort_code = 200
  89. elif matcher_response['decision'] == 'abort_not_found':
  90. abort_code = 404
  91. else:
  92. bubble_log('dns_spoofing.request: unknown abort code: ' + str(matcher_response['decision']) + ', aborting with 404 Not Found')
  93. abort_code = 404
  94. add_flow_ctx(flow, CTX_BUBBLE_ABORT, abort_code)
  95. return
  96. elif 'decision' in matcher_response and matcher_response['decision'] is not None and matcher_response['decision'] == 'no_match':
  97. bubble_log('dns_spoofing.request: decision was no_match, passing thru...')
  98. return
  99. elif ('matchers' in matcher_response
  100. and 'request_id' in matcher_response
  101. and len(matcher_response['matchers']) > 0):
  102. req_id = matcher_response['request_id']
  103. bubble_log("dns_spoofing.request: found request_id: " + req_id + ' with matchers: ' + repr(matcher_response['matchers']))
  104. add_flow_ctx(flow, CTX_BUBBLE_MATCHERS, matcher_response['matchers'])
  105. add_flow_ctx(flow, CTX_BUBBLE_REQUEST_ID, req_id)
  106. else:
  107. bubble_log('dns_spoofing.request: no rules returned, passing thru...')
  108. else:
  109. bubble_log('dns_spoofing.request: no matcher_response returned, passing thru...')
  110. else:
  111. bubble_log('dns_spoofing.request: no sni/host found, not applying rules to path: ' + flow.request.path)
  112. flow.request.host_header = host_header
  113. flow.request.host = sni or host_header
  114. flow.request.port = port
  115. addons = [Rerouter()]