-- Leo's gemini proxy
-- Connecting to git.thebackupbox.net:1965...
-- Connected
-- Sending request
-- Meta line: 20 text/gemini
repo: tlswrap action: commit revision: path_from: revision_from: cbef2b1f80939434162179a559c0d52cdd35c218: path_to: revision_to:
commit cbef2b1f80939434162179a559c0d52cdd35c218 Author: epoch <epoch@thebackupbox.net> Date: Mon Apr 3 22:59:42 2023 +0000 hacked in some optional ja3 support diff --git a/Makefile b/Makefile
--- a/Makefile +++ b/Makefile @@ -2,11 +2,16 @@ PREFIX:=/usr/local .PHONY: install all -all: tlswrap +all: tlswrap-ja3 tlswrap tlswrap: CFLAGS=-pedantic -Wall tlswrap: LDLIBS=-lssl -lcrypto tlswrap: tlswrap.c +tlswrap-ja3: CFLAGS=-DJA3 -pedantic -Wall +tlswrap-ja3: LDLIBS=-lssl -lcrypto +tlswrap-ja3: tlswrap-ja3.c + install: all install -Dt $(PREFIX)/bin tlswrap + install -Dt $(PREFIX)/bin tlswrap-ja3 diff --git a/tlswrap-ja3.c b/tlswrap-ja3.c new file mode 120000 index 0000000000000000000000000000000000000000..f43f4a8e5fc1176b54d277a58bc193571590bd85 --- /dev/null +++ b/tlswrap-ja3.c @@ -0,0 +1 @@ +tlswrap.c \ No newline at end of file diff --git a/tlswrap.c b/tlswrap.c
--- a/tlswrap.c +++ b/tlswrap.c @@ -7,6 +7,7 @@ #include <netdb.h> #include <netinet/in.h> +#include <openssl/md5.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <errno.h> @@ -184,6 +185,217 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { return 1; } +//#define JA3 +#ifdef JA3 + +/* + hey, epoch, if you ever feel like finishing the JA3 shit, link below + https://github.com/fooinha/nginx-ssl-ja3/tree/master/src + */ + + +static const int nid_list[] = { + NID_sect163k1, /* sect163k1 (1) */ + NID_sect163r1, /* sect163r1 (2) */ + NID_sect163r2, /* sect163r2 (3) */ + NID_sect193r1, /* sect193r1 (4) */ + NID_sect193r2, /* sect193r2 (5) */ + NID_sect233k1, /* sect233k1 (6) */ + NID_sect233r1, /* sect233r1 (7) */ + NID_sect239k1, /* sect239k1 (8) */ + NID_sect283k1, /* sect283k1 (9) */ + NID_sect283r1, /* sect283r1 (10) */ + NID_sect409k1, /* sect409k1 (11) */ + NID_sect409r1, /* sect409r1 (12) */ + NID_sect571k1, /* sect571k1 (13) */ + NID_sect571r1, /* sect571r1 (14) */ + NID_secp160k1, /* secp160k1 (15) */ + NID_secp160r1, /* secp160r1 (16) */ + NID_secp160r2, /* secp160r2 (17) */ + NID_secp192k1, /* secp192k1 (18) */ + NID_X9_62_prime192v1, /* secp192r1 (19) */ + NID_secp224k1, /* secp224k1 (20) */ + NID_secp224r1, /* secp224r1 (21) */ + NID_secp256k1, /* secp256k1 (22) */ + NID_X9_62_prime256v1, /* secp256r1 (23) */ + NID_secp384r1, /* secp384r1 (24) */ + NID_secp521r1, /* secp521r1 (25) */ + NID_brainpoolP256r1, /* brainpoolP256r1 (26) */ + NID_brainpoolP384r1, /* brainpoolP384r1 (27) */ + NID_brainpoolP512r1, /* brainpool512r1 (28) */ + NID_X25519, /* X25519 (29) */ + NID_X448, /* X448 (30) */ +}; + + +static unsigned short +tlswrap_ssl_ja3_nid_to_cid(int nid) +{ + unsigned char i; + unsigned char sz = (sizeof(nid_list) / sizeof(nid_list[0])); + + for (i = 0; i < sz; i++) { + if (nid == nid_list[i]) { + return i+1; + } + } + + if (nid == NID_ffdhe2048) { + return 0x100; + } + if (nid == NID_ffdhe3072) { + return 0x101; + } + if (nid == NID_ffdhe4096) { + return 0x102; + } + if (nid == NID_ffdhe6144) { + return 0x103; + } + if (nid == NID_ffdhe8192) { + return 0x104; + } + + return nid; +} + + +struct ja3 { + int version; + + size_t ciphers_sz; + unsigned short *ciphers; + + size_t extensions_sz; + unsigned int *extensions; + + size_t curves_sz; + unsigned short *curves; + + size_t point_formats_sz; + unsigned char *point_formats; +}; + +// I probably could just export these values to env vars and do the actual concatenation and md5 externally +void ja3_shit(struct ja3 *j) { // how do we get ssl version? + int sz=4096; + char *s = malloc(sz); + int offset=snprintf(s,sz,"%u,",j->version); + int i; + + for(i=0;i < j->ciphers_sz;i++) offset += snprintf(s+offset,sz,"%u-",ntohs(j->ciphers[i])); + if(j->ciphers_sz) { offset-- ; } s[offset]=','; offset++; + + for(i=0;i < j->extensions_sz;i++) offset += snprintf(s+offset,sz,"%u-",j->extensions[i]); + if(j->extensions_sz) { offset-- ; } s[offset]=','; offset++; + + for(i=0;i < j->curves_sz;i++) offset += snprintf(s+offset,sz,"%u-",j->curves[i]); + if(j->curves_sz) { offset-- ; } s[offset]=','; offset++; + + for(i=0;i < j->point_formats_sz;i++) offset += snprintf(s+offset,sz,"%u-",j->point_formats[i]); + if(j->point_formats_sz) { offset-- ; } s[offset]='\0'; offset++; + // I tested the hashing code. it works compared to https://github.com/salesforce/ja3/tree/master/python + unsigned char *p=MD5((unsigned char *)s,strlen(s),NULL); + char q[33]; + for(i=0;i<16;i++) { + snprintf(q+(i*2),sizeof(q),"%02x",p[i]); + } + q[33]=0; + //printf("input: %s\n",s); + //printf("md5sum: %s\n",q); +// syslog(LOG_DAEMON|LOG_CRIT,"ja3:%s",s); +// syslog(LOG_DAEMON|LOG_CRIT,"ja3 hashed: %s",q); //let's put these into env vars. :D + setenv("SSL_JA3",s,1); + setenv("SSL_JA3_DIGEST",q,1); + // MD5 the string +} + +void tlswrap_SSL_client_features(struct ja3 *j, SSL *s) { + + unsigned short *ciphers_out = NULL; + int *curves_out = NULL; + int *point_formats_out = NULL; + size_t i = 0; + size_t len = 0, sz = 0; + + if (j == NULL) return; + j->version = SSL_version(s); + + /* Cipher suites */ + j->ciphers = NULL; + j->ciphers_sz = SSL_get0_raw_cipherlist(s, &ciphers_out); + j->ciphers_sz /= 2; + + if (j->ciphers_sz && ciphers_out) { + len = j->ciphers_sz * sizeof(unsigned short); + j->ciphers = malloc(len); + memcpy(j->ciphers, ciphers_out, len); + } + + /* Elliptic curve points */ + + j->curves_sz = 0; + sz = SSL_get1_curves(s, NULL); + if (sz) { + len = sz * sizeof(unsigned int); + curves_out = malloc(len); + if (curves_out != NULL) { + memset(curves_out, 0, len); + SSL_get1_curves(s, curves_out); +// for(i=0 ; i < sz; i++ ){ +// if( ! (curves_out[i] & 0x1000000)) { //unknown. skip. +// j->curves_sz++; +// } +// } + j->curves_sz = sz; + len = j->curves_sz * sizeof(unsigned short); + j->curves = malloc(len); + if (j->curves != NULL) { + for (i = 0; i < sz; i++) { + //if( ! (curves_out[i] & 0x1000000)) { //unknown. skip? + j->curves[i] = (unsigned short) tlswrap_ssl_ja3_nid_to_cid( curves_out[i]); + //syslog(LOG_DAEMON|LOG_CRIT,"%d",curves_out[i]); + //} + } + } + free(curves_out); + } + } + + /* Elliptic curve point formats */ + j->point_formats_sz = SSL_get0_ec_point_formats(s, &point_formats_out); + if (j->point_formats_sz && point_formats_out != NULL) { + len = j->point_formats_sz * sizeof(unsigned char); + j->point_formats = malloc(len); + if (j->point_formats != NULL) { + memcpy(j->point_formats, point_formats_out, len); + } + } +} + +int tlswrap_SSL_early_cb_fn(SSL *s, int *al, void *arg) { + int got_extensions; + int *ext_out; + size_t ext_len; + struct ja3 *j = arg; + //syslog(LOG_DAEMON|LOG_CRIT,"got into the early callback."); + got_extensions = SSL_client_hello_get1_extensions_present(s, + &ext_out, + &ext_len); + if (!got_extensions) return 1; + if (!ext_out) return 1; + if (!ext_len) return 1; + j->extensions = malloc(sizeof(int) * ext_len); + if (j->extensions != NULL) { + j->extensions_sz = ext_len; + memcpy(j->extensions, ext_out, sizeof(int) * ext_len); + } + OPENSSL_free(ext_out); + //syslog(LOG_DAEMON|LOG_CRIT,"got to the end of the early callback."); + return 1; +} +#endif + int main(int argc,char *argv[]) { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); @@ -204,6 +416,11 @@ int main(int argc,char *argv[]) { if(argc == 0 || !strcmp(argv[0],"--help") || !strcmp(argv[0],"-h")) { +#ifdef JA3 + fprintf(argc?stdout:stderr,"JA3 support was built into this version of tlswrap.\n"); +#else + fprintf(argc?stdout:stderr,"JA3 support was NOT built into this version of tlswrap.\n"); +#endif fprintf(argc?stdout:stderr,"usage: tlswrap [--help|-h][--verify-mode integer] <cert_chain_file> <priv_key_file> <absolute_path_to_exe> [<arg1>] [<arg2>] [...]\n"); fprintf(argc?stdout:stderr,"verify mode flags (can be or'd together):\n"); fprintf(argc?stdout:stderr,"SSL_VERIFY_NONE: %d (no other flags may be set)\n",SSL_VERIFY_NONE); @@ -278,7 +495,9 @@ int main(int argc,char *argv[]) { pipe(b); pipe(c); - SSL_CTX_set_verify(ctx, verify_mode, verify_callback); SSL_CTX_set_ecdh_auto(ctx, 1); + + SSL_CTX_set_verify(ctx, verify_mode, verify_callback); + SSL_CTX_set_ecdh_auto(ctx, 1); if(SSL_CTX_use_certificate_chain_file(ctx, cert_chain_file) <= 0) { syslog(LOG_DAEMON|LOG_ERR,"failed to load cert chain file: %s",cert_chain_file); @@ -292,6 +511,11 @@ int main(int argc,char *argv[]) { SSL_CTX_set_tlsext_servername_callback(ctx,sni_cb); ssl = SSL_new(ctx); +#ifdef JA3 + struct ja3 ja3; + SSL_CTX_set_client_hello_cb(ctx, tlswrap_SSL_early_cb_fn, &ja3); // JA3 +#endif + SSL_set_rfd(ssl, 0); SSL_set_wfd(ssl, 1); int err; @@ -330,6 +554,10 @@ int main(int argc,char *argv[]) { //fprintf(stderr,"SSL_accept() failed. %s\n",ERR_lib_error_string(SSL_get_error(ssl,err))); return 1; } +#ifdef JA3 + tlswrap_SSL_client_features(&ja3,ssl); + ja3_shit(&ja3); +#endif //fprintf(stderr,"made it here\n"); syslog(LOG_DAEMON|LOG_DEBUG,"accepted a connection!"); char buffer[65535];//fuck it. let's make it big. @@ -393,8 +621,8 @@ int main(int argc,char *argv[]) { close(a[0]); close(b[1]); close(c[1]); - unsigned int error_code; - unsigned int error_code_size = sizeof(error_code); + //unsigned int error_code; + //unsigned int error_code_size = sizeof(error_code); //syslog(LOG_DAEMON|LOG_DEBUG,"entering select loop"); //fprintf(stderr,"made it here\n"); for(;FD_ISSET(b[0],&master) || FD_ISSET(c[0],&master);) { //a select() brick that reads from ssl and writes to subprocess and reads from subprocess and writes to ssl @@ -455,3 +683,5 @@ int main(int argc,char *argv[]) { SSL_free(ssl); EVP_cleanup(); } + +
-----END OF PAGE-----
-- Response ended
-- Page fetched on Sun Jun 2 17:39:21 2024