First attempt at impersonating Chrome

* Headers and ciphers are aligned to Chrome 98 (Windows, non-incognito)
* GREASE enabled because chrome uses it as well
* TLS extensions 27, 5, 18 enabled.
This commit is contained in:
lwthiker
2022-02-18 18:07:28 +02:00
parent b00ad551b6
commit be4da0e70a
3 changed files with 178 additions and 0 deletions

78
Dockerfile.chrome Normal file
View File

@@ -0,0 +1,78 @@
# Use same base as the existing Dockerfile
FROM python:3.10.1-slim-buster
WORKDIR /build
# Dependencies for downloading and building BoringSSL
RUN apt-get update && \
apt-get install -y git g++ cmake golang-go ninja-build curl unzip zlib1g-dev libbrotli-dev
# BoringSSL doesn't have versions. Choose a commit that is used in a stable
# Chromium version.
ARG BORING_SSL_COMMIT=3a667d10e94186fd503966f5638e134fe9fb4080
RUN curl -L https://github.com/google/boringssl/archive/${BORING_SSL_COMMIT}.zip -o boringssl.zip && \
unzip boringssl && \
mv boringssl-${BORING_SSL_COMMIT} boringssl
# Compile BoringSSL.
# See https://boringssl.googlesource.com/boringssl/+/HEAD/BUILDING.md
RUN cd boringssl && \
mkdir build && cd build && \
cmake -DCMAKE_POSITION_INDEPENDENT_CODE=on -GNinja .. && \
ninja
# Fix the directory structure so that curl can compile against it.
# See https://everything.curl.dev/source/build/tls/boringssl
RUN mkdir boringssl/build/lib && \
ln -s ../crypto/libcrypto.a boringssl/build/lib/libcrypto.a && \
ln -s ../ssl/libssl.a boringssl/build/lib/libssl.a && \
cp -R boringssl/include boringssl/build
ARG NGHTTP2_VERSION=nghttp2-1.46.0
ARG NGHTTP2_URL=https://github.com/nghttp2/nghttp2/releases/download/v1.46.0/nghttp2-1.46.0.tar.bz2
# The following are needed because we are going to change some autoconf scripts,
# both for libnghttp2 and curl.
RUN apt-get install -y autoconf automake autotools-dev pkg-config libtool
# Download nghttp2 for HTTP/2.0 support.
RUN curl -o ${NGHTTP2_VERSION}.tar.bz2 -L ${NGHTTP2_URL}
RUN tar xf ${NGHTTP2_VERSION}.tar.bz2
# Patch nghttp2 pkg config file to support static builds.
COPY libnghttp2-*.patch ${NGHTTP2_VERSION}/
RUN cd ${NGHTTP2_VERSION} && \
for p in $(ls libnghttp2-*.patch); do patch -p1 < $p; done && \
autoreconf -i && automake && autoconf
# Compile nghttp2
RUN cd ${NGHTTP2_VERSION} && \
./configure && \
make && make install
# Download curl.
ARG CURL_VERSION=curl-7.81.0
RUN curl -o ${CURL_VERSION}.tar.xz https://curl.se/download/${CURL_VERSION}.tar.xz
RUN tar xf ${CURL_VERSION}.tar.xz
# Patch curl and re-generate the configure script
COPY curl-*.patch ${CURL_VERSION}/
RUN cd ${CURL_VERSION} && \
for p in $(ls curl-*.patch); do patch -p1 < $p; done && \
autoreconf -fi
# Compile curl with BoringSSL
RUN cd ${CURL_VERSION} && \
./configure --with-openssl=/build/boringssl/build --enable-static --disable-shared --with-nghttp2=/usr/local LIBS="-pthread" CFLAGS="-I/build/boringssl/build" && \
make
# 'xxd' is needed for the wrapper script
RUN apt-get install -y xxd
RUN mkdir out && \
cp ${CURL_VERSION}/src/curl out/curl-impersonate-ch
# Wrapper script
COPY curl_chrome* out/
RUN chmod +x out/*

60
curl-openssl.patch Normal file
View File

@@ -0,0 +1,60 @@
--- curl-7.81.0-original/lib/vtls/openssl.c 2022-01-03 18:36:46.000000000 +0200
+++ curl-7.81.0/lib/vtls/openssl.c 2022-02-18 17:05:57.253198793 +0200
@@ -78,2 +78,4 @@
+#include <brotli/decode.h>
+
#ifdef USE_AMISSL
@@ -2631,2 +2633,27 @@
+/* Taken from Chromium and adapted to C,
+ * see net/ssl/cert_compression.cc
+ */
+int DecompressBrotliCert(SSL* ssl,
+ CRYPTO_BUFFER** out,
+ size_t uncompressed_len,
+ const uint8_t* in,
+ size_t in_len) {
+ uint8_t* data;
+ CRYPTO_BUFFER* decompressed = CRYPTO_BUFFER_alloc(&data, uncompressed_len);
+ if (!decompressed) {
+ return 0;
+ }
+
+ size_t output_size = uncompressed_len;
+ if (BrotliDecoderDecompress(in_len, in, &output_size, data) !=
+ BROTLI_DECODER_RESULT_SUCCESS ||
+ output_size != uncompressed_len) {
+ return 0;
+ }
+
+ *out = decompressed;
+ return 1;
+}
+
static CURLcode ossl_connect_step1(struct Curl_easy *data,
@@ -2769,2 +2796,5 @@
#ifdef SSL_OP_NO_TICKET
+ /* curl-impersonate patch.
+ * Don't turn on SSL_OP_NO_TICKET, we want TLS extension 35 (session_ticket)
+ * to be sent. */
ctx_options |= SSL_OP_NO_TICKET;
@@ -2939,2 +2969,18 @@
+ /* curl-impersonate
+ * Configure BoringSSL to behave like Chrome.
+ * See Constructor of SSLContext at net/socket/ssl_client_socket_impl.cc
+ * and SSLClientSocketImpl::Init()
+ * in the Chromium's source code. */
+
+ /* Enable TLS GREASE. */
+ SSL_CTX_set_grease_enabled(backend->ctx, 1);
+
+ /* Add support for TLS extension 27 - compress_certificate.
+ * Add Brotli decompression. See Chromium net/ssl/cert_compression.cc */
+ SSL_CTX_add_cert_compression_alg(backend->ctx, TLSEXT_cert_compression_brotli, NULL, DecompressBrotliCert);
+
+ /* Enable TLS extensions 5 - status_request and 18 - signed_certificate_timestamp. */
+ SSL_CTX_enable_ocsp_stapling(backend->ctx);
+ SSL_CTX_enable_signed_cert_timestamps(backend->ctx);

40
curl_chrome98 Executable file
View File

@@ -0,0 +1,40 @@
#!/bin/bash
# Find the directory of this script
dir=`echo "$0" | sed 's%/[^/]*$%%'`
PIPE=/tmp/curl-pipe
rm -f "$PIPE" && mkfifo "$PIPE"
exec 5<>"$PIPE" 3>"$PIPE" 4<"$PIPE" 5>&-
# The list of ciphers can be obtained by looking at the Client Hello message in
# Wireshark, then converting it using this reference
# https://wiki.mozilla.org/Security/Cipher_Suites
"$dir/curl-impersonate-ch" \
--ciphers 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 \
-H 'Upgrade-Insecure-Requests: 1' \
-H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36' \
-H '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' \
-H 'Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"' \
-H 'Sec-Ch-Ua-Mobile: ?0' \
-H 'Sec-Ch-Ua-Platform: Windows' \
-H 'Sec-Fetch-Site: none' \
-H 'Sec-Fetch-Mode: navigate' \
-H 'Sec-Fetch-User: ?1' \
-H 'Sec-Fetch-Dest: document' \
-H 'Accept-Encoding: gzip, deflate, br' \
-H 'Accept-Language: en-US,en;q=0.9' \
--http2 --false-start --tlsv1.2 \
$@ >&3
exec 3>&-
IFS= read -d '' -r -n 2 -u 4 header
# Due to the "Accept-Encoding: gzip" header, we may receive a gzipped file.
if [ "$(echo -n $header | xxd -l 2 -p)" == "1f8b" ]; then
(printf "%s" "$header"; cat <&4) | gzip -cd;
else
printf "%s" "$header"; cat <&4;
fi