简体   繁体   中英

How to create a proxy that can decode SSL traffic?

I was writing a proxy that can capture the requests made in my selenium tests. In selenium I used this

host = '10.203.9.156'
profile  = webdriver.FirefoxProfile()
myProxy = "localhost:8899"

proxy = Proxy({
'proxyType': ProxyType.MANUAL,
'httpProxy': myProxy,
'ftpProxy': myProxy,
'sslProxy': myProxy,
'noProxy': '' # set this value as desired
})
driver = webdriver.Firefox(proxy=proxy)

The proxy part that accepts client requests

self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
###
ssl.wrap_socket(self.socket, ssl_version=ssl.PROTOCOL_TLSv1, keyfile = ??, certfile = ???, server_side=True)
###
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind((self.hostname, self.port))
self.socket.listen(self.backlog)
while True:
    conn, addr = self.socket.accept()
    logger.debug('Accepted connection %r at address %r' % (conn, addr))
    self.handle(conn,addr)

And this is the part where the connection is made twith the server

self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
###
ssl.wrap_socket(self.socket, ssl_version=ssl.PROTOCOL_TLSv1, keyfile = ??, certfile = ???, server_side=True)
###
self.conn.connect((self.addr[0], self.addr[1]))

I have access to the server. My question is what should be the part for both the client request acceptance part and also forwarding it to the server , in between ###, that would allow me to capture the traffic in a human readable format? I am not very good with certificates. Any help would be welcome.

BoilerPlate

SSL is a protocol providing an end-to-end encrypted communication between two parties each having one of the keys in private/public key pair. Typically a browser and a web server.

In normal circumstances any device between the two endpoints cannot decrypt the communication.

It is however possible using a proxy server that decrypts and re-encrypts communication thus allowing interception and decryption which is your case. It does however require adding an additional certificate to a trusted certificate store on a client machine (either automatically through a software management system or manually by users).

Solving your Problem

Overall you are creating 'Man in the Middle' type proxy, meaning every request passed to proxy server should be decrypted and encrypted again while client should have matching SSL private key. Try using mitmproxy/libmproxy libraries.

Check out possible proxy.py solution:

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
from libmproxy import controller, proxy
import os, sys, re, datetime, json

class RequestHacks:
  @staticmethod
  def example_com (msg):
    # tamper outgoing requests for https://example.com/api/v2
    if ('example.org' in msg.host) and ('action=login' in msg.content):
      fake_lat, fake_lng = 25.0333, 121.5333
      tampered = re.sub('lat=([\d.]+)&lng=([\d.]+)', 'lat=%s&lng=%s' % (fake_lat, fake_lng), msg.content)
      msg.content = tampered
      print '[RequestHacks][Example.com] Fake location (%s, %s) sent when logging in' % (fake_lat, fake_lng)


class ResponseHacks:
  @staticmethod
  def example_org (msg):
    # simple substitution for https://example.org/api/users/:id.json
    if 'example.org' in msg.request.host:
      regex = re.compile('/api/users/(\d+).json')
      match = regex.search(msg.request.path)
      if match and msg.content:
        c = msg.replace(''private_data_accessible':false', ''private_data_accessible':true')
        if c > 0:
          user_id = match.groups()[0]
          print '[ResponseHacks][Example.org] Private info of user #%s revealed' % user_id

  @staticmethod
  def example_com (msg):
    # JSON manipulation for https://example.com/api/v2
    if ('example.com' in msg.request.host) and ('action=user_profile' in msg.request.content):
      msg.decode() # need to decode the message first
      data = json.loads(msg.content) # parse JSON with decompressed content
      data['access_granted'] = true
      msg.content = json.dumps(data) # write back our changes
      print '[ResponseHacks][Example.com] Access granted of user profile #%s' % data['id']

  @staticmethod
  def example_net (msg):
    # Response inspection for https://example.net
    if 'example.net' in msg.request.host:
      data = msg.get_decoded_content() # read decompressed content without modifying msg
      print '[ResponseHacks][Example.net] Respones: %s' % data


class InterceptingMaster (controller.Master):
  def __init__ (self, server):
    controller.Master.__init__(self, server)

  def run (self):
    while True:
      try:
        controller.Master.run(self)
      except KeyboardInterrupt:
        print 'KeyboardInterrupt received. Shutting down'
        self.shutdown()
        sys.exit(0)
      except Exception:
        print 'Exception catched. Intercepting proxy restarted'
        pass

  def handle_request (self, msg):
    timestamp = datetime.datetime.today().strftime('%Y/%m/%d %H:%M:%S')
    client_ip = msg.client_conn.address[0]
    request_url = '%s://%s%s' % (msg.scheme, .msg.host, msg.path)
    print '[%s %s] %s %s' % (timestamp, client_ip, msg.method, request_url)

    RequestHacks.example_com(msg)
    msg.reply()

  def handle_response (self, msg):
    ResponseHacks.example_org(msg)
    ResponseHacks.example_com(msg)
    ResponseHacks.example_net(msg)
    msg.reply()


def main (argv):
  config = proxy.ProxyConfig(
    cacert = os.path.expanduser('./mitmproxy.pem'),
  )
  server = proxy.ProxyServer(config, 8080)
  print 'Intercepting Proxy listening on 8080'
  m = InterceptingMaster(server)
  m.run()

if __name__ == '__main__':
  main(sys.argv)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM