目录
前文
前几天第一次接触到ja3,十分的好奇。经过今天几天的研究和学习,发现了点东西,记录下。另外下篇会分享自己用php撸的ja3获取工具。
另外本文将https://ja3er.com/form简称为ja3er。
ja3_Hash获取规则
- 场景
tls clint hello,准确的说是:
- 字节 0:值 22,表示根据 TLS 规范的“握手”。
- 字节 5:根据 TLS 规范,值 1,在握手包内指示客户端问候。
- 字节 9:值 3,两个字节中的第一个字节,与任何版本的 TLS 对齐的 TLS 版本。如果匹配 SSL,该值也可能为 0。
- 字节 1:与字节 9 相同,但与记录 TLS 版本有关”
- 字段顺序
TLSVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats
- 拼接规则
按顺序连接在一起,使用“,”分隔每个字段,使用“-”分隔每个字段中的每个值。
- MD5 哈希处理
拼接后的ja3_str经过MD5处理得到ja3_hash
- 忽略的值,该值在Ciphers,Extensions,EllipticCurves会出现
GREASE
具体为:
$GREASE_TABELE = [
2570,
6682,
10794,
14906,
19018,
23130,
27242,
31354,
35466,
39578,
43690,
47802,
51914,
56026,
60138,
64250,
];
发现
忽略的扩展
ja3er 中的ja3_str会把类型值为17513的扩展忽略掉
Type: application_settings (17513)
例子:
// from ja3er
……27-21,29-23-24,0
// from mycode
……27-17513,29-23-24,0
变化的ja3
与curl 不同,ja3_str 在chrome中是2个值
//第一次访问时是一个值
……27-17513-21,29-23-24,0
// 之后的访问是另外一个值
……27-17513,29-23-24,0
主要是取决于场景,在chrome中
-
首次访问session_ticket为空
此时会增加一个拓展,名为padding,类型值为21
Type: padding (21)
-
之后访问session_ticket有值
此时没有padding扩展
这个情况在ja3er和我的代码中都会出现,应该是tls session 机制导致。ja3er中比较难复现,可以尝试用无痕。
关于Wireshark的JA3
示例数据:
[JA3 Fullstring: 771,10794-4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,60138-0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513-31354,23130-29-23-24,0]
[JA3: 432d80490caf1b032a7e091754f2f597]
如果你也有测试,你会发现在Wireshark中,JA3不是固定的。原因很简单,它并没有忽略GREASE。如实例中,771,10794-4865 中的 10794,正是10794,//(0x2A2A)。
- 2022年6月10日 更新
今天发现 linux tshark 3.6.5 和 win Wireshark 3.6.5 都的ja3_full和ja3 hash已经更新,已经是正确值。
- 2022年6月13日 更新
按照过滤器参考,3.0版本之后就支持了。
tls.handshake.ja3 JA3 Character string 3.6.0 to 3.6.5
tls.handshake.ja3_full JA3 Fullstring Character string 3.6.0 to 3.6.5
tls.handshake.ja3s JA3S Character string 3.6.0 to 3.6.5
tls.handshake.ja3s_full JA3S Fullstring Character string 3.6.0 to 3.6.5
然而我使用这个版本却提示错误
版本:
TShark (Wireshark) 3.4.2 (Git v3.4.2 packaged as 3.4.2-1~ubuntu16.04.0+wiresharkdevstable1)
错误:
tshark: Some fields aren't valid:
tls.handshake.ja3_full
nginx几个与ja3相关的变量
只测试了本地和我的服务器,仅供参考
nginx 配置
location ~ \.php(.*)$ {
fastcgi_param ssl_protocol $ssl_protocol;
fastcgi_param ssl_curves $ssl_curves;
fastcgi_param ssl_cipher $ssl_cipher;
fastcgi_param ssl_ciphers $ssl_ciphers;
fastcgi_param ssl_server_name $ssl_server_name;
fastcgi_param ssl_client_fingerprint $ssl_client_fingerprint;
fastcgi_param ssl_session_id $ssl_session_id;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
include fastcgi_params;
}
php代码
var_dump(__FILE__.' line:'.__LINE__,$_SERVER);exit;
结果
["ssl_session_id"]=>
string(64) "8ba7742fd5a32ddb4c842aa34ce97325cd19015720389ff8c04650e049be4b75"
["ssl_client_fingerprint"]=>
string(0) ""
["ssl_server_name"]=>
string(5) "xxx.com"
["ssl_ciphers"]=>
string(355) "0xcaca:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA:AES256-SHA"
["ssl_cipher"]=>
string(27) "ECDHE-RSA-AES256-GCM-SHA384"
["ssl_curves"]=>
string(0) "0x1a1a:X25519:prime256v1:secp384r1"
["ssl_protocol"]=>
string(7) "TLSv1.2"
变量 |
ssl_protocol |
ssl_curves |
ssl_cipher |
ssl_server_name |
ssl_client_fingerprint |
ssl_session_id |
首次访问 |
Y |
Y |
Y |
Y |
empty |
empty |
后续访问 |
Y |
empty |
Y |
Y |
empty |
Y |
- ssl_session_id 并不是固定值,每次请求都会变化。
- ssl_session_id存在时会与Wireshark抓到的一致,也就是时传递过来的,不太懂为啥第一次就没有。
Session ID: 8ba7742fd5a32ddb4c842aa34ce97325cd19015720389ff8c04650e049be4b75
- 首次访问时,与jas_str字段差距只是缺少Extensions。或许可以作为一个简化版的ja3。
TLSVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats
SNI 真的很好取
备案检测是怎么做到的?,另外了解到,ie部分版本是不支持SNI,也就是你可以用它上未备案的网站,然而这没卵用。具体支持情况:
参考
tls-fingerprinting