The main Bubble source repository. Contains the Bubble API server, the web UI, documentation and utilities. https://getbubblenow.com
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 

113 wiersze
5.3 KiB

  1. #
  2. # Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/
  3. #
  4. # Parts of this are borrowed from tls_passthrough.py in the mitmproxy project. The mitmproxy license is reprinted here:
  5. #
  6. # Copyright (c) 2013, Aldo Cortesi. All rights reserved.
  7. #
  8. # Permission is hereby granted, free of charge, to any person obtaining a copy
  9. # of this software and associated documentation files (the "Software"), to deal
  10. # in the Software without restriction, including without limitation the rights
  11. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. # copies of the Software, and to permit persons to whom the Software is
  13. # furnished to do so, subject to the following conditions:
  14. #
  15. # The above copyright notice and this permission notice shall be included in
  16. # all copies or substantial portions of the Software.
  17. #
  18. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  24. # SOFTWARE.
  25. #
  26. from mitmproxy.proxy.protocol import TlsLayer, RawTCPLayer
  27. from mitmproxy.exceptions import TlsProtocolException
  28. from bubble_api import bubble_log, bubble_passthru, bubble_activity_log, redis_set
  29. import redis
  30. import json
  31. REDIS_DNS_PREFIX = 'bubble_dns_'
  32. REDIS_PASSTHRU_PREFIX = 'bubble_passthru_'
  33. REDIS_PASSTHRU_DURATION = 60 * 60 # 1 hour timeout on passthru
  34. REDIS = redis.Redis(host='127.0.0.1', port=6379, db=0)
  35. def passthru_cache_prefix(client_addr, server_addr):
  36. return REDIS_PASSTHRU_PREFIX + client_addr + '_' + server_addr
  37. class TlsFeedback(TlsLayer):
  38. """
  39. Monkey-patch _establish_tls_with_client to get feedback if TLS could be established
  40. successfully on the client connection (which may fail due to cert pinning).
  41. """
  42. def _establish_tls_with_client(self):
  43. client_address = self.client_conn.address[0]
  44. server_address = self.server_conn.address[0]
  45. try:
  46. super(TlsFeedback, self)._establish_tls_with_client()
  47. except TlsProtocolException as e:
  48. bubble_log('_establish_tls_with_client: TLS error for '+repr(server_address)+', enabling passthru')
  49. cache_key = passthru_cache_prefix(client_address, server_address)
  50. fqdn = fqdn_for_addr(server_address)
  51. redis_set(cache_key, json.dumps({'fqdn': fqdn, 'addr': server_address, 'passthru': True}), ex=REDIS_PASSTHRU_DURATION)
  52. raise e
  53. def fqdn_for_addr(addr):
  54. fqdn = REDIS.get(REDIS_DNS_PREFIX + addr)
  55. if fqdn is None or len(fqdn) == 0:
  56. bubble_log('check_bubble_passthru: no FQDN found for addr '+repr(addr)+', checking raw addr')
  57. fqdn = b''
  58. return fqdn.decode()
  59. def check_bubble_passthru(remote_addr, addr, fqdn):
  60. if bubble_passthru(remote_addr, addr, fqdn):
  61. bubble_log('check_bubble_passthru: bubble_passthru returned True for FQDN/addr '+repr(fqdn)+'/'+repr(addr)+', returning True')
  62. return {'fqdn': fqdn, 'addr': addr, 'passthru': True}
  63. bubble_log('check_bubble_passthru: bubble_passthru returned False for FQDN/addr '+repr(fqdn)+'/'+repr(addr)+', returning False')
  64. return {'fqdn': fqdn, 'addr': addr, 'passthru': False}
  65. def should_passthru(remote_addr, addr):
  66. prefix = 'should_passthru: '+repr(addr)+' '
  67. bubble_log(prefix+'starting...')
  68. cache_key = passthru_cache_prefix(remote_addr, addr)
  69. passthru_json = REDIS.get(cache_key)
  70. if passthru_json is None or len(passthru_json) == 0:
  71. bubble_log(prefix+' not in redis or empty, calling check_bubble_passthru...')
  72. fqdn = fqdn_for_addr(addr)
  73. if fqdn is None or len(fqdn) == 0:
  74. bubble_log(prefix+' no fqdn found for addr '+addr+', returning (uncached) passthru = False')
  75. return {'fqdn': None, 'addr': addr, 'passthru': False}
  76. passthru = check_bubble_passthru(remote_addr, addr, fqdn)
  77. bubble_log(prefix+'check_bubble_passthru returned '+repr(passthru)+", storing in redis...")
  78. redis_set(cache_key, json.dumps(passthru), ex=REDIS_PASSTHRU_DURATION)
  79. else:
  80. bubble_log('found passthru_json='+str(passthru_json)+', touching key in redis')
  81. passthru = json.loads(passthru_json)
  82. REDIS.touch(cache_key)
  83. bubble_log(prefix+'returning '+repr(passthru))
  84. return passthru
  85. def next_layer(next_layer):
  86. if isinstance(next_layer, TlsLayer) and next_layer._client_tls:
  87. client_address = next_layer.client_conn.address[0]
  88. server_address = next_layer.server_conn.address[0]
  89. passthru = should_passthru(client_address, server_address)
  90. if passthru['passthru']:
  91. bubble_log('next_layer: TLS passthru for ' + repr(next_layer.server_conn.address))
  92. bubble_activity_log(client_address, server_address, 'tls_passthru', passthru['fqdn'])
  93. next_layer_replacement = RawTCPLayer(next_layer.ctx, ignore=True)
  94. next_layer.reply.send(next_layer_replacement)
  95. else:
  96. bubble_activity_log(client_address, server_address, 'tls_intercept', passthru['fqdn'])
  97. next_layer.__class__ = TlsFeedback