3

HTTP2指纹识别(一种相对不为人知的网络指纹识别方法) - 俞正东

 1 year ago
source link: https://www.cnblogs.com/yudongdong/p/16654636.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
image

这是关于网络指纹识别的两部分系列的第二部分

上一部分我介绍了有关TLS 指纹识别方法(以及在不同客户端的指纹有何区别):

https://mp.weixin.qq.com/s/BvotXrFXwYvGWpqHKoj3uQ

HTTP/2 指纹识别

和Tls指纹类似也是一种 Web 服务器可以依赖指纹来识别哪个客户端。

例如,它可以识别浏览器类型和版本,或者是否使用了脚本(你是真实浏览器啊还是ScriptBoy?)。

该方法依赖于 HTTP/2 协议的内部结构,与其更简单的前身 HTTP/1.1 相比,这些内部结构鲜为人知。

在这篇文章中,我将首先简要介绍 HTTP/2协议,然后详细介绍我们可以协议的哪些参数来识别你究竟谁(what are you)!

与HTTP/1.1相比

使用HTTP/1.1协议,客户端向服务器发送文本请求(通常使用 TLS 加密)默认情况下,Chrome 的请求如下所示:


GET / HTTP/1.1
Host: www.wikipedia.org

sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9

User-Agent包含客户端的确切版本,虽然可用于识别客户端。但是很容易被任何 http 库或命令行工具伪造(地球人都知道)!

HTTP/2 简介

HTTP/2 是 HTTP 协议的主要修订版,从 2015 年左右开始出现。现在大约一半的网站使用 HTTP/2

image

基本上所有流行的网站都默认使用它!

如何看服务端使用的是否是http2协议呢?

在chrome上看是这样的

image

在Firefox上看是这样的

image
HTTP/2 的主要目标是提高性能
  • 多路复用(Multiplexing ) - 多个请求和响应可以同时共享同一个 TCP 连接,从而减少了获取具有大量资源(图像、脚本等)的站点的时间。
  • 优先级(PRIORITY) - HTTP/2 支持对某些请求和响应进行优先级排序。
  • 服务器推送(Server push) - 在 HTTP/2 中,服务器可以在客户端请求资源之前将资源发送给客户端。

然而,HTTP 协议的应用程序语义没有改变:它仍然由熟悉的请求/响应模型组成,包括 URI、HTTP 方法、HTTP 标头和状态码。

Frames and Streams

HTTP/2 是一种二进制协议,与文本 HTTP/1.1 不同。HTTP/2 中的消息由帧组成,有十种不同用途的帧。帧始终是流的一部分。

image

Stream都是有编号的,从0开始

如上图:编号为0的Stream包含如下

  • SETTINGS是客户端发送的第一帧,包含 HTTP/2 的特定配置,
  • WINDOW_UPDATE- 增加接收器的窗口大小,下面会讲到

然后是编号开始递增,代表了客户端给服务端发送的实际请求,如上图为1的Stream:

  • HEADERS 包含 URI、HTTP 方法和客户端的 HTTP 头
  • DATA 包含来请求的资源数据以及服务器的响应

使用 HTTP/2 进行客户端指纹识别

研究http2协议的工具

这里推荐使用nghttpd,它可以很方便的创建一个http2协议的webserver。
最关键的是,让客户端请求的时候它能够直观的把每一帧都给打印出来(下面会给大家演示)
我将它安装在wsl的ubuntu机器上,还得自建一个证书,这里我遇到了一点坑,
避坑指南请看我写的(wsl创建证书让chrome浏览器识别):

下面就是如何使用nghttpd跑h2协议server

image

我这里分别使用如下客户端来测试

  • Chrome浏览器
  • Firefox浏览器
  • Python脚本

1. SETTINGS

上面介绍到这是客户端发送的第一帧,里面有一些特殊配置

Chrome
image
recv SETTINGS frame <length=24, flags=0x00, stream_id=0>
      [SETTINGS_HEADER_TABLE_SIZE(0x01):65536]
      [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):1000]
      [SETTINGS_INITIAL_WINDOW_SIZE(0x04):6291456]
      [SETTINGS_MAX_HEADER_LIST_SIZE(0x06):262144]

Firefox
image
recv SETTINGS frame <length=18, flags=0x00, stream_id=0>
      [SETTINGS_HEADER_TABLE_SIZE(0x01):65536]
      [SETTINGS_INITIAL_WINDOW_SIZE(0x04):131072]
      [SETTINGS_MAX_FRAME_SIZE(0x05):16384]

image
recv SETTINGS frame <length=18, flags=0x00, stream_id=0>
      [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
      [SETTINGS_INITIAL_WINDOW_SIZE(0x04):1073741824]
      [SETTINGS_ENABLE_PUSH(0x02):0]

PYTHON
image
image
recv SETTINGS frame <length=36, flags=0x00, stream_id=0>
      [SETTINGS_HEADER_TABLE_SIZE(0x01):4096]
      [SETTINGS_ENABLE_PUSH(0x02):0]
      [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
      [SETTINGS_MAX_FRAME_SIZE(0x05):16384]
      [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
      [SETTINGS_MAX_HEADER_LIST_SIZE(0x06):65536]

很明显,根据测试,在SETTINGS Frame帧里面配置,
不同的客户端设置的种类和值都是不同的,这使得很容易区分是否是浏览器,
而且这个配置不容易控制,可以用于指纹识别!

WINDOW_UPDATE

HTTP/2 实现了一种流控制机制。
流量控制为接收方提供了在每个流的基础上调节流量的机制。
使用WINDOW_UPDATE大小来实现的

默认窗口大小由SETTINGS帧里面的 SETTINGS_INITIAL_WINDOW_SIZE中的值控制,
参考上方测试,可以看到 Chrome 使用 6MB (6291456) 而 Firefox 使用 128KB (131072)

当客户端接收数据时,它可以使用WINDOW_UPDATE框架来调整窗口大小,从而增加其窗口大小。

Chrome
recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=15663105)

Chrome 实际上将连接级窗口大小增加到 15MB (15663105+65535=15MB)

Firefox
recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=12517377)

Firefox 会将其增加到 12MB

recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=1073676289)

curl使用 32MB

参考:https://github.com/curl/curl/blob/10cd69623a544c83bae6d90acdf141981ae53174/lib/http2.c#L62

PYTHON
recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=16777216)

PYTHON 会将其增加到 16MB

所以我们也可以使用该参数用于指纹识别!

HEADERS

这个有点意思了

从广义上讲,HEADERS 包含了 HTTP/1.1 的所有功能,包含了
URI、方法(GET/POST/等)和客户端的头等!

下面的几个伪标头的顺序对于每个客户端是不同的。

  • :method
  • :authority
  • :scheme
  • :path

我们来测试一下

Chrome
image

顺序是:
m,a,s,p

Firefox
image

顺序是:
m,p,a,s

image

顺序是:
m,p,s,a

Python
image

顺序是:
m,a,s,p

这个看似很小的差异,也可以用于指纹识别

HTTP/2 指纹识别在哪里使用?

它用于与TLS 指纹识别类似的目的:比如反 DDOS 和反脚本等自动爬虫(提高门槛),只允许真实浏览器等。

如何让你的server具有提取客户端HTTP2指纹的能力

ja3是tls指纹的标准,wiresharp也默认带有

image

搞http2指纹的目前市面上还没有标准,
我开源了一款提取tls&http2指纹的中间件(面向aspnetcore的)

https://github.com/yuzd/ja3-csharp

image

在线测试:

https://kawayiyi.com/tls


{
    "tlsVersion": "Tls12",
    "tcpConnectionId": "0HMKCUARI97OU",
    "tlsHashOrigin": "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513-21,29-23-24,0",
    "tlsHashMd5": "cd08e31494f9531f560d64c695473da9",
    "cipherList": [
        "TLS_AES_128_GCM_SHA256",
        "TLS_AES_256_GCM_SHA384",
        "TLS_CHACHA20_POLY1305_SHA256",
        "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
        "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
        "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
        "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
        "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
        "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
        "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
        "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
        "TLS_RSA_WITH_AES_128_GCM_SHA256",
        "TLS_RSA_WITH_AES_256_GCM_SHA384",
        "TLS_RSA_WITH_AES_128_CBC_SHA",
        "TLS_RSA_WITH_AES_256_CBC_SHA"
    ],
    "extentions": [
        "server_name",
        "extended_master_secret",
        "renegotiation_info",
        "supported_groups",
        "ec_point_formats",
        "session_ticket",
        "application_layer_protocol_negotiation",
        "status_request",
        "signature_algorithms",
        "signed_certificate_timestamp",
        "key_share",
        "psk_key_exchange_modes",
        "supported_versions",
        "compress_certificate",
        "extensionApplicationSettings",
        "padding"
    ],
    "supportedgroups": [
        "X25519",
        "CurveP256",
        "CurveP384"
    ],
    "ecPointFormats": [
        "uncompressed"
    ],
    "proto": "HTTP/2",
    "h2": {
        "SETTINGS": {
            "1": "65536",
            "3": "1000",
            "4": "6291456",
            "6": "262144"
        },
        "WINDOW_UPDATE": "15663105"
    }
}

如何过http2指纹呢?

知道了原理,还不好过吗

指纹识别在整个网络中变得非常普遍,Http2的指纹相对来说不为人知,但是并不新鲜

比如这篇论文:

https://www.blackhat.com/docs/eu-17/materials/eu-17-Shuster-Passive-Fingerprinting-Of-HTTP2-Clients-wp.pdf

详细介绍了一项具有类似结论的研究

本文参考了@lwthiker大神的研究,加上了自己的实践(解析http2协议),感谢他的指点


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK