Author: sskaje

  • EE_KEY_TOO_SMALL

    Windows 11 + Python 3.10, ssl 加载了 1024-bit 的 证书和私钥。然后遇到了错误 EE_KEY_TOO_SMALL 。

    > python3.10 proxy.py
    Traceback (most recent call last):
      File "\proxy\proxy.py", line 11, in <module>
        server_context.load_cert_chain('../../docs/cert.pem', '../../docs/cert.key')
    ssl.SSLError: [SSL: EE_KEY_TOO_SMALL] ee key too small (_ssl.c:3874)

    尝试手动修改并加载 openssl.cnf ,无效。

    由于是临时服务,只对内,所以简单粗暴

    server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    server_context.set_ciphers('ALL:@SECLEVEL=0')
    server_context.load_cert_chain('../../docs/cert.pem', '../../docs/cert.key')

    Python 3.10 的 OpenSSL 版本

    >>> import ssl
    >>> ssl.OPENSSL_VERSION
    'OpenSSL 1.1.1s  1 Nov 2022'
  • Windows 使用 mitmproxy 劫持 TLS 连接

    本来是一个直连 127.0.0.1:xxx 的连接,发现服务器可以手动指定端口,就xxx + 1 手动起服务器连接,不改端口的方案先不管,只做技术验证。

    环境:Windows 11 + mitmproxy 9.0.1

    原有软件的连接,使用 openssl s_client -connect 127.0.0.1:xxx 看到是 TLS 1.2,经过某些手段提取了私钥,配合证书,如果还能还原协议就可以自建server或者篡改请求了。

    私钥的环节其他另文。

    假设已经拿到了 PEM 格式的私钥,加上 openssl s_client 看到的证书 PEM,在验证之后,可以编辑一个满足 mitmproxy 要求的证书文件。格式说明在 https://docs.mitmproxy.org/stable/concepts-certificates/,结构如下

    -----BEGIN PRIVATE KEY-----
    <private key>
    -----END PRIVATE KEY-----
    -----BEGIN CERTIFICATE-----
    <cert>
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
    <intermediary cert (optional)>
    -----END CERTIFICATE-----

    这个场景下只是针对明确目标的 MITM 攻击,所以选用 mitmproxy 的反向代理模式 “reverse”,文档在 https://docs.mitmproxy.org/stable/concepts-modes/。

    基本结构是

    Client ---> [port xxx] mitmproxy(reverse mode) [port xxx+1] ---> [port xxx+1] Server

    假设xxx = 12300,则对应的命令是

    mitmproxy --mode reverse:tls://127.0.0.1:12301@12300 

    由于上游证书是自签的,经验证服务端也没有要求客户端使用证书,所以需要使用 -k 关闭证书校验 。

    然后,还需要让下游客户端继续维持上游的证书,防止内部有额外的校验,引入之前的证书文件假设为 mitm.pem 则完整命令为

    mitmproxy --mode reverse:tls://127.0.0.1:12301@12300 -k --certs *=mitm.pem

    脚本看文档 https://docs.mitmproxy.org/

  • Protected: NanoPi R6s Ubuntu Build Kernel Module Error

    This content is password-protected. To view it, please enter the password below.

  • Protected: 某家App签名算法还原过程

    This content is password-protected. To view it, please enter the password below.

  • apc.use_request_time

    发现 Ubuntu 下的 PHP 里的 apcu 的 cache 始终不过期,结果查了半天,cli 模式下, apc.use_request_time 是被开启的状态。

  • WordPress Extra Authentication

    Nginx snippets adding extra basic auth to wordpress.

        location ~ ^/(xmlrpc|wp-.+)/?.*\.php$ {
            auth_basic "hahaha";
            auth_basic_user_file "/etc/nginx/sskaje.auth";
    
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass unix:/run/php/php-fpm.sock;
            fastcgi_index index.php;
            include fastcgi.conf;
    
            fastcgi_intercept_errors on;
        }
    

    Create Password File

    htpasswd -c /etc/nginx/sskaje.auth sskaje

    Add User

    htpasswd /etc/nginx/sskaje.auth sskaje
  • 长 Cookies 导致的 cloudflare Error 520

    Cloudflare 520 是 CF 自定义的错误,根据官方文档描述,可能的原因如下:

    • Origin web server application crashes
    • Cloudflare IPs not allowed at your origin
    • Headers exceeding 16 KB (typically due to too many cookies)
    • An empty response from the origin web server that lacks an HTTP status code or response body
    • Missing response headers or origin web server not returning proper HTTP error responses.
      • upstream prematurely closed connection while reading response header from upstream is a common error we may notice in our logs. This indicates the origin web server was having issues which caused Cloudflare to generate 520 errors.

    我4k多的cookies就引发了 520,而且出问题的是在 iOS Chrome里,没法直接调试,抓包看到 Cookies 的长度,试验了一下删减,不到4k的时候没问题。只能尝试用 cURL 去调用源服务器。结果发现,源服务器

    *   Trying 127.0.0.1:443...
    * TCP_NODELAY set
    * Connected to xxx.xx (127.0.0.1) port 443 (#0)
    * ALPN, offering h2
    * ALPN, offering http/1.1
    * successfully set certificate verify locations:
    *   CAfile: /etc/ssl/certs/ca-certificates.crt
      CApath: /etc/ssl/certs
    * TLSv1.3 (OUT), TLS handshake, Client hello (1):
    * TLSv1.3 (IN), TLS handshake, Server hello (2):
    * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
    * TLSv1.3 (IN), TLS handshake, Certificate (11):
    * TLSv1.3 (IN), TLS handshake, CERT verify (15):
    * TLSv1.3 (IN), TLS handshake, Finished (20):
    * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
    * TLSv1.3 (OUT), TLS handshake, Finished (20):
    * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
    * ALPN, server accepted to use h2
    * Server certificate:
    *  subject: O=CloudFlare, Inc.; OU=CloudFlare Origin CA; CN=CloudFlare Origin Certificate
    *  start date: Dec  9 15:19:00 2021 GMT
    *  expire date: Dec  5 15:19:00 2036 GMT
    *  issuer: C=US; O=CloudFlare, Inc.; OU=CloudFlare Origin SSL Certificate Authority; L=San Francisco; ST=California
    *  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
    * Using HTTP2, server supports multi-use
    * Connection state changed (HTTP/2 confirmed)
    * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
    * Using Stream ID: 1 (easy handle 0x559c87f16890)
    > GET / HTTP/2
    ...
    ...
    > 
    * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
    * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
    * old SSL session ID is stale, removing
    * Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
    * TLSv1.3 (IN), TLS alert, close notify (256):
    * Empty reply from server
    * Closing connection 0
    curl: (52) Empty reply from server

    没有响应。

    源服务器是 Ubuntu 20.04,nginx 版本

    # nginx -V
    nginx version: nginx/1.18.0 (Ubuntu)
    built with OpenSSL 1.1.1f  31 Mar 2020
    TLS SNI support enabled

    最开始按nginx 文档里的可能的选项,试验了下边的几项

        client_header_buffer_size 32k;
        large_client_header_buffers 8 32k;
        ssl_buffer_size 32k;

    都一样。

    观察了一下请求,再开了个虚拟机,做了个最小化的nginx 配置。

    server {
        #listen 443 ssl http2 backlog=20480;
        listen 443 ssl http2;
        #listen 443 ssl ;
        listen 80;
    
        ssl_certificate      /etc/nginx/cert/rst.im/server.crt;
        ssl_certificate_key  /etc/nginx/cert/rst.im/server.key;
    
    
        client_header_buffer_size 32k;
        large_client_header_buffers 8 32k;
        ssl_buffer_size 32k;
    
        root /tmp/web/;
        index index.html index.htm index.php;
        server_name rst.im *.rst.im;
    
        location ~ /\.(ht|git) {
            deny all;
        }
    }

    试验了如下方案:

    • http:成功
    • https + http 1.1:成功
    • https + http 2.0:复现

    翻过来去看 http2 模块的文档,发现了 http2_max_field_size,默认大小4k,但是写了 1.19.7 之后被废弃。然而 ubuntu 20.04 的 nginx 比这个老。加上配置,

    http2_max_field_size 16k;

    问题解决。

  • Protected: i百联 加密分析

    This content is password-protected. To view it, please enter the password below.

  • iOS HTTPS 抓包工具

    介绍一些用过的好用的 iOS 下 HTTPS 的抓包工具。

    这些工具的实现原理都是使用 Network Extension 实现VPN,设置路由规则到 VPN 的设备里,再进行流量筛选,例如筛 TCP Port 443 的,转发到内部实现的一个代理服务上,通过中间人攻击的方式,实现 HTTPS 的协议解密甚至劫持。

    这些工具的操作步骤大都如下:

    • 安装 App。
    • 安装并信任证书。最近的 iOS 安装自定义 CA 证书,会需要用户自己到设置里安装描述文件,再去关于本机证书信任设置里手动启用 CA 证书。
    • 创建 VPN。App 里有明确的引导,将用户跳转到 VPN 添加页面。但是这里的VPN 在 App 卸载的时候不会自动删除。
    • 配置规则或者默认全部TCP 443
    • 启用并解析

    这里有个风险,如果使用者不能确认 App 安装的证书完全是本机现生成的私钥及证书且都没有上传到服务器上,那请在使用完相关App 后,至少是取消掉对该 CA 的信任。

    以下对比仅限于被对比的对象,优点和缺点不是绝对的。

    1. Stream,一款免费的 iOS 程序,看简介应该是中国开发者开发的,可以在 IAP 里赞助开发者。

    • 优点:免费,功能直观,而且还算比较完整,可以构造请求
    • 缺点:用户引导做得不够好,如果不懂原理,可能配置不成功。

    2. Charles for iOS,Charles Proxy 的 iOS版,功能很简单,就是小贵。

    • 优点:功能简单,用户体验好,支持阻断连接(Block List)
    • 缺点:付费(不是坏事),而且功能还是稍微少了点,不支持构造请求。

    上述两款软件都支持的功能有:

    • DNS Spoofing 劫持 DNS 解析
    • 记录的黑白名单

    都缺的功能

    • 修改请求 (Charles 电脑版的 Rewrite 和 Breakpoint)
    • 回放请求。

    还有个对比项没有去验证,但是大多数情况应该用不到,就是直接的SSL/TLS 请求的记录和解析。

    其他软件就太夸张了,要不贵的要死,要不不可理喻。

  • MITM 与 HTTPS 流量劫持

    VPS 硬盘挂之前,blog 里留了一篇文章,如何使用 redsocks + linux policy based routing 实现对手机 HTTPS 流量的劫持。具体方案懒得再写了。简单描述一下原理,后边的文章会需要引用。

    An illustration of the man-in-the-middle attack

    上图是引用自 Wikipedia 的中间人攻击的页面

    最古老的年代,通信缺少加密,所以中间传信的人可能能看到并篡改内容。后来,从算法保密,到算法公开密钥保密,再到后来 PKI 的出现,逐渐实现了相对安全的加密通信。

    但是,从开发、调试和安全研究的角度看,在拿不到 SSL/TLS 通信密钥的情况下,中间人攻击是协议调试的一个重要手段。(当然,如果能拿到 SSL/TLS 通信密钥,例如可以修改 SSL/TLS 握手的库,记录下来,就可以直接 tcpdump/wireshark 抓包,再用 wireshark 填入密钥就可以解密了。)

    加密通讯的中间人攻击,第一个典型场景的必要手段是让客户端信任中间人的证书,常见的手段:

    • 例如客户端不校验证书(签发者,时间,Common Name等),
    • 或者当客户端使用系统的方法和系统的 CA 库去校验的时候,安装自签的 CA 证书再用这个 CA 去签发被劫持的证书,
    • 甚至 Patch 的方式替换原有程序里写死的证书或证书策略(例如 Android)。

    第二个典型的必要手段是劫持流量,或者信道。常见的做法:

    • 如果被劫持的目标对象使用系统的代理,或者有代理设置的选项,修改这个选项。
    • 如果该目标不使用代理,但是系统平台有方法劫持,例如 proxifier(一个值得买的 win/mac 代理工具),使用 proxifier 可以按通讯地址端口或者进程名称将应用的流量。
    • 如果是不认代理设置的运行在封闭系统(iOS / Android)下的程序,就可以考虑网络设备上 NAT + redsocks 的方案将流量转到 Charles Proxy 或者 Fiddler 的 socks 代理上。
    • 其他的,例如 iOS/mac 的 Network Extension。。。

    近些年有些麻烦,一个是 Certificate Pinning,一个是 TLS 1.3。遇到自己实现的 Certificate Pinning,时间成本会上去,这里在越狱的 iOS 设备上推荐 https://github.com/nabla-c0d3/ssl-kill-switch2,可能大部分程序的检查都能绕过,FB 家的不行。TLS 1.3,如果自己实现的客户端不接受降级到 TLS 1.2或者更低,目前我无解,不知道有没有best practice。