SOCKET - 实现任意 HTTPS 站点代理, 支持篡改内容

* 本文内容代码执行需要管理员权限

主要难点

  1. 将用户访问请求重定向到劫持服务器,
  2. 如何获取 https 传输内容并解密成明文.
  3. 修改 https 内容后重新打包并保证浏览器可以正常打开.

难点一

重定向用户请求相对来说比较简单, 这里只列举几个 windows 的方法:

  1. 通过配置系统 VPN 来过滤 443 端口请求
  2. hook mswsock.dll->connect 来重定向 443 端口请求
  3. 通过编写 Windows LSP 协议来重定向 443 端口请求

难点二

https 协议是公开协议, 其安全主要是靠证书来保证的, 如果你可以抓到 https 服务器和客户端的握手包即可拿到整次访问的加密秘钥.

你可以手动解析 https 协议, 或者寻找任意一个 https 服务器即可. 这里使用 golang 来作为劫持服务器.


func HTTPSGetCertificate(clientHello *tls.ClientHelloInfo) (cert *tls.Certificate, err error) {
    if cert, err = QueryTlsCertificate(clientHello.ServerName); nil == err {
        return cert, err
    }

    return CreateTlsCertificate(nil, clientHello.ServerName, -(365 * 24 * time.Hour), 200)
}

func StartHTTPSProxy(addr string, router socks.Dialer, tran *HTTPTransport) {
    serverHTTPS := &http.Server{
        ErrorLog: log.Warn,
        TLSConfig: &tls.Config{
            GetCertificate: HTTPSGetCertificate,
        },

        Addr: addr,
        Handler: &HTTPHandler{
            scheme:    "HTTPS",
            proxy:     socks.NewHTTPProxy("https", router, tran),
        },
    }

    if err := serverHTTPS.ListenAndServeTLS("", ""); nil != err {
        log.Error("Start HTTP proxy at ", addr, " failed, err:", err)
    }
}

难点三

怎样修改 https 内容后重新打包并保证浏览器可以正常打开, 这个可以明确的告诉你没辙. 除非你能拿到对方服务器的私钥.

我们既然拿不到对方的私钥, 那可不可以伪造一个?

这个是可以的, https 的证书是可以随意生成的, 我们可以通过生成自己的证书来重新打包 https 内容, 只是没办法通过浏览器验证而已,.

浏览器是通过什么验证证书是不是伪造证书?

其实系统中有个证书信任库, 里面保存了所有可以信任的证书列表, 浏览器是通过查询证书是否存在于这个库中来确定证书可不可以信任的.

了解了上面的内容后, 我们可以得到一个结论, 不管当前证书是否是真正服务器证书只要当前证书存在于系统的信任证书库中, 就可以通过浏览器的验证. 想通了这点我们可不可以手动将证书加到信任库里? 答案是可以的,下面给出 C++ 通过 Windows API 给系统添加证书的代码.

// pszSRCCert = 常规 X509 编码证书内容, 可以通过记事本打开证书文件获得
// pwszStoreName = 值可以为 "My" | "CA" | "Root", 分别对应 个人 | 颁发机构 | 根证书

bool WINAPI AddCertificateCryptContextToStore(const wchar_t * pwszStoreName, const wchar_t * pszSRCCert) {
    unsigned long dwCertData = 0;
    ULONG dwRootCert = (ULONG)wcslen(pszSRCCert);

    if (!CryptStringToBinaryW(pszSRCCert, dwRootCert, CRYPT_STRING_BASE64HEADER, NULL, &dwCertData, NULL, NULL)){
        return false;
    }

    unsigned char* pbCertData = (unsigned char*)calloc(1, dwCertData);
    if (NULL == pbCertData){
        return false;
    }
    defer_free _free1(pbCertData);

    if (!CryptStringToBinaryW(pszSRCCert, dwRootCert, CRYPT_STRING_BASE64HEADER, pbCertData, &dwCertData, NULL, NULL)) {
        return false;
    }

    return AddCertificateContextToStore(pwszStoreName, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, pbCertData, dwCertData);
}

bool WINAPI AddCertificateContextToStore(const wchar_t * pwszStoreName, DWORD dwCertEncodingType, BYTE * pbCertData, DWORD dwCertSize) {
    bool bIsSucc = false;
    HCERTSTORE hStore = NULL;
    PCCERT_CONTEXT m_pctx = NULL;

    do
    {
        m_pctx = CertCreateCertificateContext(dwCertEncodingType, pbCertData, dwCertSize);
        if (m_pctx == NULL) {
            break;
        }

        hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_STORE_OPEN_EXISTING_FLAG | CERT_SYSTEM_STORE_LOCAL_MACHINE, pwszStoreName);//my 个人  ca 中间证书颁发机构 root 受信任的根证书颁发机构,显然中国铁路的根证书要安装在root里面
        if (NULL == hStore) {
            break;
        }

        if (FALSE == CertAddCertificateContextToStore(hStore, m_pctx, CERT_STORE_ADD_NEW, 0) && CRYPT_E_EXISTS != GetLastError()) { // 如果安装失败,并且错误码不是以安装过
            break;
        }

        bIsSucc = true;
    } while (false);

    if (m_pctx)
        CertFreeCertificateContext(m_pctx);
    if (hStore)
        CertCloseStore(hStore, 0);

    return bIsSucc;
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实现支持Web Mail的邮件发送代理,可以使用Python的socket模块来实现。以下是一个简单的实现过程: 1. 建立一个监听代理端口的服务器socket,并等待客户端的连接。 2. 当客户端连接上来后,代理服务器会解析客户端发来的HTTP请求报文,获取到目标邮件服务器的地址和端口号,以及邮件的相关信息。 3. 代理服务器使用socket模块连接目标邮件服务器,并将用户发来的邮件信息转发给目标服务器。 4. 目标邮件服务器处理邮件信息后,将结果返回给代理服务器。 5. 代理服务器将目标服务器返回的结果转发给用户的浏览器,完成邮件发送操作。 下面是一个简单的示例代码: ```python import socket # 监听代理端口 proxy_host = 'localhost' proxy_port = 8888 proxy_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) proxy_socket.bind((proxy_host, proxy_port)) proxy_socket.listen(1) while True: # 等待客户端连接 client_socket, client_address = proxy_socket.accept() # 解析HTTP请求报文,获取目标邮件服务器地址和端口号 request_data = client_socket.recv(1024) request_lines = request_data.decode().split('\r\n') target_host = '' target_port = 0 for line in request_lines: if line.startswith('Host:'): target_host = line.split(': ')[1] if line.startswith('X-Forwarded-Port:'): target_port = int(line.split(': ')[1]) # 连接目标邮件服务器 target_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) target_socket.connect((target_host, target_port)) # 转发邮件信息 target_socket.sendall(request_data) while True: data = target_socket.recv(1024) if not data: break client_socket.sendall(data) # 关闭连接 target_socket.close() client_socket.close() ``` 需要注意的是,以上代码只是一个简单的示例,实际应用中还需要考虑安全性、错误处理等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值