#include #include #include #include #include #include #include #include "utarray.h" #include "utstring.h" #include "SSL_Analyze.h" #include "ssl.h" #include "SSL_Message.h" #include "SSL_Proc.h" #include "SSL_Certificate.h" #include "fieldstat/fieldstat_easy.h" #define SUITE_VALUELEN 2 #define KEY_EXCHANGELEN_LEN 4 #define RECORD_DIGESTLEN_LEN 2 #define ESNILEN_LEN 2 #define SERVER_HELLO_HDRLEN 4 #define SESSION_TICKET_HDRLEN 4 #define MAX_EXT_DATA_LEN 256 #define ALPN_EXT_TYPE 0x0010 #define SERVER_NAME_EXT_TYPE 0x0000 #define SERVER_NAME_HOST_TYPE 0x0000 #define SERVER_NAME_OTHER_TYPE 0x0008 #define SESSION_TICKET_EXT_TYPE 0x0023 #define ENCRPTED_SERVER_NAME_EXT_TYPE 0xFFCE #define ENCRPTED_CLIENT_HELLO_EXT_TYPE 0xFE0D #define EC_POINT_FORMATS_EXT_TYPE 0x000B #define SUPPORTED_VERSIONS 0x002B #define SIGNATURE_ALGORITHMS 0x000D // https://datatracker.ietf.org/doc/html/rfc7919 // Supported Groups #define SUPPORTED_GROUPS_EXT_TYPE 0x000A #define CERTIFICATE_HDRLEN 7 #define SSL_CERTIFICATE_HDRLEN 3 extern struct ssl_serial_string g_astCipherSuit; UT_icd UT_ssl_hello_ciphersuites_icd={sizeof(unsigned short), NULL, NULL, NULL}; UT_icd UT_ssl_hello_extension_icd={sizeof(struct ssl_l2tv), NULL, NULL, NULL}; struct ssl_extenstions { UT_array *value; }; const struct ssl_serial_string g_astCompression[] = { {{0}, NULL}, }; const struct ssl_value2string ssl_version_list[] = { { TLCPV1_VERSION, "TLCP1.1" }, { DTLSV1_0_VERSION, "DTLS1.0" }, { DTLSV1_0_VERSION_NOT, "DTLS1.0(OpenSSL pre 0.9.8f)" }, { TLSV1_2_VERSION, "TLS1.2" }, { TLSV1_1_VERSION, "TLS1.1" }, { TLSV1_0_VERSION, "TLS1.0" }, { SSLV3_VERSION, "SSL3.0" }, { SSLV2_VERSION, "SSL2.0" }, { UNKNOWN_VERSION, NULL } }; static int ja4_sha256(const char *string, size_t string_sz, unsigned char *hash, size_t hash_sz) { if(string==NULL || string_sz==0 || hash_sz> 8) & 0xff)) { return SSL_FALSE; } return SSL_TRUE; } const char *ssl_get_suite(struct ssl_l2v *ciphersuites) { if (ciphersuites == NULL) return NULL; return fn_pcGetSuite((unsigned char *)ciphersuites->value, ciphersuites->len, (struct ssl_serial_string *)&g_astCipherSuit); } int ssl_parse_lv1(struct ssl_l1v *lv1, unsigned char *payload, int payload_len) { lv1->len=(unsigned char)BtoL1BytesNum((const char *)payload); int offset=(int)sizeof(lv1->len); if((payload_len-offset-lv1->len)<0) { return -1; } if(lv1->len>0) { lv1->value=payload+offset; offset+=lv1->len; } return offset; } int ssl_parse_lv2(struct ssl_l2v *lv2, unsigned char *payload, int payload_len) { lv2->len=(unsigned short)BtoL2BytesNum((const char *)payload); int offset=sizeof(lv2->len); if((payload_len-offset-(lv2->len))<0) { return -1; } if(lv2->len>0) { lv2->value=payload+offset; offset+=lv2->len; } return offset; } int ssl_parse_ltv2(struct ssl_l2tv *ltv2, unsigned char *payload, int payload_len) { ltv2->type=(unsigned short)BtoL2BytesNum((const char *)(payload)); int offset=(int)sizeof(ltv2->type); if((payload_len-offset)<0) { return -1; } ltv2->len=(unsigned short)BtoL2BytesNum((const char *)(payload+offset)); offset+=(int)sizeof(ltv2->len); if((payload_len-offset-ltv2->len)<0) { return -1; } if(ltv2->len>0) { ltv2->value=payload+offset; offset+=ltv2->len; } return offset; } void ssl_trunk_free(struct ssl_runtime_context *ssl_context, int thread_seq) { if(ssl_context!=NULL) { if(ssl_context->record.cache_buff!=NULL) { dictator_free(thread_seq, ssl_context->record.cache_buff); ssl_context->record.cache_buff=NULL; } memset(&(ssl_context->record), 0, sizeof(struct ssl_record_trunk)); } } int ssl_trunk_cache(struct ssl_runtime_context *ssl_context, char *payload, int payload_len, int thread_seq) { if(payload==NULL || payload_len<=0) { return 0; } if(ssl_context->record.cache_buff==NULL) { ssl_context->record.cache_buff=(char *)dictator_malloc(thread_seq, g_ssl_runtime_para.max_cache_len); } ssl_context->record.cache_len=0; int length=MIN(payload_len, g_ssl_runtime_para.max_cache_len); memmove(ssl_context->record.cache_buff+ssl_context->record.cache_len, payload, length); ssl_context->record.cache_len+=length; return 0; } int ssl_get_alpn_list(struct ssl_l2tv *extension_alpn, struct ssl_alpn_list* alpn_list, int alpn_size) { if(extension_alpn==NULL || (extension_alpn->value==NULL) || (extension_alpn->len==0) || (extension_alpn->type!=ALPN_EXT_TYPE)) { return 0; } int alpn_proto_num = 0; unsigned char* alpl=extension_alpn->value; int alpn_ext_len=BtoL2BytesNum((char *)alpl); if(alpn_ext_len<=0 || alpn_ext_len>extension_alpn->len) { return 0; } alpl+=2; while(alpn_ext_len>0 && alpn_proto_numlen; unsigned char *cur_data=extension->value; p_server_name.list_len-=sizeof(p_server_name.list_len); cur_data+=sizeof(p_server_name.list_len); /*3=sizeof(p_server_name.server_name_type)+sizeof(p_server_name.server_name_len)*/ while(p_server_name.list_len>3) { p_server_name.type = BtoL1BytesNum((const char *)cur_data); p_server_name.len = BtoL2BytesNum((const char *)(cur_data+1)); p_server_name.list_len -= 3; cur_data += 3; /*have data*/ if(((p_server_name.type == SERVER_NAME_HOST_TYPE)) && p_server_name.len>0&& p_server_name.list_len>=p_server_name.len) { p_server_name.data = cur_data; int length=MIN(sizeof(chello->server_name)-1, p_server_name.len); memcpy(chello->server_name, p_server_name.data, length); chello->server_name[length]='\0'; break; } p_server_name.list_len-=p_server_name.len; cur_data+=p_server_name.len; } return 0; } int ssl_parse_encrypt_server_name(struct ssl_client_hello *chello, struct ssl_l2tv *extension) { int offset=0,one_l2v=0; unsigned char* cur_data=extension->value; int extension_len=extension->len; if(extension_len-offset>SUITE_VALUELEN) { chello->esni.suite.value=cur_data+offset; chello->esni.suite.len=SUITE_VALUELEN; offset+=SUITE_VALUELEN; } if((extension_len-offset)>=(int)sizeof(chello->esni.key_exchange_group)) { chello->esni.key_exchange_group=(unsigned short)BtoL2BytesNum((const char *)(cur_data+offset)); offset+=sizeof(chello->esni.key_exchange_group); } else { return 0; } one_l2v=ssl_parse_lv2(&(chello->esni.key_exchange), cur_data+offset, extension_len-offset); if(one_l2v==-1) { return 0; } offset+=one_l2v; one_l2v=ssl_parse_lv2(&(chello->esni.record_digest), cur_data+offset, extension_len-offset); if(one_l2v==-1) { return 0; } offset+=one_l2v; one_l2v=ssl_parse_lv2(&(chello->esni.esni), cur_data+offset, extension_len-offset); if(one_l2v==-1) { return 0; } offset+=one_l2v; chello->esni.is_esni=1; return 1; } int ssl_parse_client_hello(struct ssl_client_hello *chello, unsigned char *payload, int payload_len) { int offset=0,one_ltv=0; chello->total_len=BtoL3BytesNum((const char *)(payload+1)); if(chello->total_len<0) /*CLIENT_HELLO_HDRLEN: 4 means client_type+len*/ { return SSL_FALSE; } if((chello->total_len+CLIENT_HELLO_HDRLEN > payload_len) || (chello->total_len-(int)sizeof(chello->version)<0)) { return SSL_CONTINUE; } chello->version=ssl_get_hello_version((unsigned char *)payload, payload_len); if(chello->version==0) { return SSL_FALSE; } offset+=(CLIENT_HELLO_HDRLEN+sizeof(chello->version)); /*get client hello random*/ chello->random.gmt_time=(unsigned int)BtoL4BytesNum((const char *)(payload+offset)); offset+=sizeof(chello->random.gmt_time); if(payload_len-offset-SSL_RANDOM_SIZE<=0) { return SSL_FALSE; } chello->random.bytes.len=SSL_RANDOM_SIZE; chello->random.bytes.value=payload+offset; offset+=SSL_RANDOM_SIZE; /*get client hello session*/ one_ltv=ssl_parse_lv1(&(chello->session), payload+offset, payload_len-offset); if(one_ltv==-1) { return SSL_FALSE; } offset+=one_ltv; /*get client hello suites*/ one_ltv=ssl_parse_lv2(&chello->ciphersuites, payload+offset, payload_len-offset); if(one_ltv==-1) { return SSL_FALSE; } offset+=one_ltv; /*get client hello compress*/ one_ltv=ssl_parse_lv1(&(chello->compress_method), payload+offset, payload_len-offset); if(one_ltv==-1) { return SSL_FALSE; } offset+=one_ltv; if(offset < payload_len) { /*get extension*/ unsigned short extensions_len=(unsigned short)BtoL2BytesNum((const char *)(payload+offset)); offset+=sizeof(extensions_len); unsigned short ex_offset=0; for(int i=0; (payload_len-offset >= 4) && ex_offsetextensions->value, &extension); switch(extension.type) { case SERVER_NAME_EXT_TYPE: ssl_parse_server_name(chello, &(extension)); break; case SESSION_TICKET_EXT_TYPE: chello->session_ticket=(struct ssl_l2tv *)utarray_eltptr(chello->extensions->value, utarray_len(chello->extensions->value)-1); break; case ENCRPTED_SERVER_NAME_EXT_TYPE: ssl_parse_encrypt_server_name(chello, &(extension)); break; case ENCRPTED_CLIENT_HELLO_EXT_TYPE: chello->encrypt_chello=(struct ssl_l2tv *)utarray_eltptr(chello->extensions->value, utarray_len(chello->extensions->value)-1); break; case ALPN_EXT_TYPE: chello->alpn=(struct ssl_l2tv *)utarray_eltptr(chello->extensions->value, utarray_len(chello->extensions->value)-1); break; default: break; } } } return SSL_TRUE; } // https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967/ void ssl_chello_ja3_generate(struct ssl_client_hello *chello) { if(chello==NULL) { return; } UT_string *ja3_string; utstring_new(ja3_string); utstring_printf(ja3_string, "%u,", chello->version); int32_t flag=SSL_FALSE; if(chello->ciphersuites.len>0 && chello->ciphersuites.value!=NULL) { for(unsigned short i=0; iciphersuites.len; i+=2) { unsigned short cipher_suite=BtoL2BytesNum((const char *)(chello->ciphersuites.value+i)); if(ssl_is_grease_value(cipher_suite)==SSL_TRUE) { continue; } utstring_printf(ja3_string, "%s%u", ((flag==SSL_FALSE) ? "" : "-"), cipher_suite); flag=SSL_TRUE; } } utstring_printf(ja3_string, "%s", ","); flag=SSL_FALSE; struct ssl_l2tv *ec=NULL; struct ssl_l2tv *ec_point_format=NULL; for(uint32_t i=0; iextensions->value); i++) { struct ssl_l2tv *ext=(struct ssl_l2tv *)utarray_eltptr(chello->extensions->value, i); if(ext==NULL || ssl_is_grease_value(ext->type)) { continue; } utstring_printf(ja3_string, "%s%u", ((flag==SSL_FALSE) ? "" : "-"), ext->type); flag=SSL_TRUE; switch(ext->type) { case EC_POINT_FORMATS_EXT_TYPE: ec_point_format=ext; break; case SUPPORTED_GROUPS_EXT_TYPE: ec=ext; break; default: break; } } utstring_printf(ja3_string, "%s", ","); if(ec!=NULL && ec->value!=NULL && ec->len>0) { flag=SSL_FALSE; unsigned short length=BtoL2BytesNum((const char*)(ec->value)); for(unsigned short j=0; (jlen); j+=2) { unsigned short group=BtoL2BytesNum((const char*)(ec->value+j+2)); if(ssl_is_grease_value(group)==SSL_TRUE) { continue; } utstring_printf(ja3_string, "%s%u", ((flag==SSL_FALSE) ? "" : "-"), group); flag=SSL_TRUE; } } utstring_printf(ja3_string, ","); if(ec_point_format!=NULL && ec_point_format->value!=NULL && ec_point_format->len>0) { size_t offset=0; char length=BtoL1BytesNum((const char*)(ec_point_format->value)); offset++; for(char j=0; jlen); j++) { utstring_printf(ja3_string, "%s%u", ((j==0) ? "" : "-"), ec_point_format->value[offset++]); } } chello->ja3.value_sz=ja3_md5sum(utstring_body(ja3_string), utstring_len(ja3_string), chello->ja3.value, sizeof(chello->ja3.value)); chello->ja3.value[chello->ja3.value_sz]='\0'; utstring_free(ja3_string); } int ssl_hello_extension_sort(const void *a, const void *b) { struct ssl_l2tv *ext_a=(struct ssl_l2tv *)a; struct ssl_l2tv *ext_b=(struct ssl_l2tv *)b; if(ext_a->type > ext_b->type) { return 1; } else if(ext_a->type < ext_b->type) { return -1; } else { return 0; } } int ssl_hello_short_sort(const void *a, const void *b) { unsigned short suite_a=*(unsigned short *)a; unsigned short suite_b=*(unsigned short *)b; if(suite_a > suite_b) { return 1; } else if(suite_a < suite_b) { return -1; } else { return 0; } } const char *ssl_hello_version_convert(unsigned short version) { switch(version) { case SSLV2_VERSION: return "s2"; case SSLV3_VERSION: return "s3"; case TLSV1_0_VERSION: return "10"; case TLSV1_1_VERSION: return "11"; case TLSV1_2_VERSION: return "12"; case TLSV1_3_VERSION: return "13"; case DTLSV1_0_VERSION: return "d1"; case DTLSV1_2_VERSION: return "d2"; case DTLSV1_3_VERSION: return "d3"; case DTLSV1_0_VERSION_NOT: return "00"; case TLCPV1_VERSION: return "00"; default: break; } return NULL; } const char *ssl_hello_supported_version_get(struct ssl_l2tv *supported_version) { if(supported_version==NULL || supported_version->value==NULL || supported_version->len==0) { return NULL; } // get length unsigned short len=BtoL1BytesNum((const char *)(supported_version->value)); if(len==0 || len>supported_version->len) { return NULL; } // get version for(int i=1; ivalue+i)); if(ssl_is_grease_value(version)==SSL_TRUE) { continue; } return ssl_hello_version_convert(version); } return NULL; } size_t ssl_hello_apln_first_protocol_get(struct ssl_l2tv *alpn, char *alpn_first_proto, size_t alpn_first_proto_sz) { if(alpn==NULL || alpn->value==NULL || alpn->len==0 || alpn_first_proto_sz<=2) { return 0; } // get length unsigned short len=BtoL2BytesNum((const char *)(alpn->value)); if(len==0 || len>alpn->len) { return 0; } size_t offset=2; // get first protocol length unsigned char proto_len=BtoL1BytesNum((const char *)(alpn->value+2)); if(proto_len<=1 || proto_len>len) { return 0; } offset+=1; // get first protocol alpn_first_proto[0]=BtoL1BytesNum((const char *)(alpn->value+offset)); alpn_first_proto[1]=BtoL1BytesNum((const char *)(alpn->value+offset+proto_len-1)); return 2; } void ssl_hello_cipher_suites_array_get(struct ssl_l2v *ciphersuites, UT_array *cs_array) { if(ciphersuites==NULL || ciphersuites->value==NULL || ciphersuites->len==0) { return ; } for(unsigned short i=0; ilen; i+=2) { unsigned short cipher_suite=BtoL2BytesNum((const char *)(ciphersuites->value+i)); if(ssl_is_grease_value(cipher_suite)==SSL_TRUE) { continue; } utarray_push_back(cs_array, &cipher_suite); } } void ssl_hello_signature_algorithms_array_get(struct ssl_l2tv *signature_algorithms, UT_array *sa_array) { if(signature_algorithms==NULL || signature_algorithms->value==NULL || signature_algorithms->len==0) { return ; } unsigned short len=BtoL2BytesNum((const char *)(signature_algorithms->value)); if(len==0 || len>signature_algorithms->len) { return ; } size_t offset=2; for(unsigned short i=0; ivalue+i+offset)); if(ssl_is_grease_value(signature_algorithm)==SSL_TRUE) { continue; } utarray_push_back(sa_array, &signature_algorithm); } } // https://github.com/FoxIO-LLC/ja4/blob/main/technical_details/JA4.md // https://blog.cloudflare.com/ja4-signals/ void ssl_chello_ja4_generate(struct ssl_client_hello *chello) { if(chello==NULL) { return; } UT_array *cs_array; utarray_new(cs_array, &UT_ssl_hello_ciphersuites_icd); ssl_hello_cipher_suites_array_get(&(chello->ciphersuites), cs_array); utarray_sort(cs_array, &ssl_hello_short_sort); UT_array *ext_dup; utarray_new(ext_dup, &UT_ssl_hello_extension_icd); utarray_concat(ext_dup, chello->extensions->value); utarray_sort(ext_dup, &ssl_hello_extension_sort); size_t alpn_first_proto_sz=0; char alpn_first_proto[4]={0}; char *supported_versions=NULL; struct ssl_l2tv *signature_algorithms=NULL; for(uint32_t i=0; itype)) { continue; } switch(ext->type) { case ALPN_EXT_TYPE: alpn_first_proto_sz=ssl_hello_apln_first_protocol_get(ext, alpn_first_proto, sizeof(alpn_first_proto)); break; case SUPPORTED_VERSIONS: supported_versions=(char *)ssl_hello_supported_version_get(ext); break; case SIGNATURE_ALGORITHMS: signature_algorithms=ext; break; default: break; } } if(supported_versions==NULL) { supported_versions=(char *)ssl_hello_version_convert(chello->version); } UT_string *ja4_a; utstring_new(ja4_a); utstring_printf(ja4_a, "t%s%s%02u%02u%s_", ((supported_versions==NULL) ? "00" : supported_versions), (strlen(chello->server_name)>0 ? "d" : "i"), ((utarray_len(cs_array) > 99) ? 99 : utarray_len(cs_array)), ((utarray_len(ext_dup) > 99) ? 99 : utarray_len(ext_dup)), ((alpn_first_proto_sz==0) ? "00" : alpn_first_proto) ); UT_string *ja4_b; utstring_new(ja4_b); for(uint32_t i=0; i0) { unsigned char ja4_b_sha256[SHA256_DIGEST_LENGTH]; ja4_sha256(utstring_body(ja4_b), utstring_len(ja4_b), ja4_b_sha256, sizeof(ja4_b_sha256)); utstring_printf(ja4_a, "%02x%02x%02x%02x%02x%02x_", ja4_b_sha256[0], ja4_b_sha256[1], ja4_b_sha256[2], ja4_b_sha256[3], ja4_b_sha256[4], ja4_b_sha256[5]); } else { utstring_printf(ja4_a, "000000000000_"); } UT_string *ja4_c; utstring_new(ja4_c); int32_t flag=SSL_FALSE; for(uint32_t i=0; itype)) { continue; } if(ext->type==ALPN_EXT_TYPE || ext->type==SERVER_NAME_EXT_TYPE) { continue; } utstring_printf(ja4_c, "%s%04x", ((flag==SSL_FALSE) ? "" : ","), ext->type); flag=SSL_TRUE; } UT_array *sa_array; utarray_new(sa_array, &UT_ssl_hello_ciphersuites_icd); ssl_hello_signature_algorithms_array_get(signature_algorithms, sa_array); if(utarray_len(sa_array)>0) { utstring_printf(ja4_c, "_"); } //utarray_sort(sa_array, &ssl_hello_short_sort); for(uint32_t i=0; i0) { unsigned char ja4_c_sha256[SHA256_DIGEST_LENGTH]; ja4_sha256(utstring_body(ja4_c), utstring_len(ja4_c), ja4_c_sha256, sizeof(ja4_c_sha256)); utstring_printf(ja4_a, "%02x%02x%02x%02x%02x%02x", ja4_c_sha256[0], ja4_c_sha256[1], ja4_c_sha256[2], ja4_c_sha256[3], ja4_c_sha256[4], ja4_c_sha256[5]); } else { utstring_printf(ja4_a, "000000000000"); } chello->ja4.value_sz=MIN(utstring_len(ja4_a), sizeof(chello->ja4)-1); memcpy(chello->ja4.value, utstring_body(ja4_a), chello->ja4.value_sz); utarray_free(cs_array); utarray_free(sa_array); utarray_free(ext_dup); utstring_free(ja4_a); utstring_free(ja4_b); utstring_free(ja4_c); } int ssl_parse_server_hello(struct ssl_server_hello *shello, unsigned char *payload, int payload_len) { int offset=0,one_ltv=0; shello->total_len = BtoL3BytesNum((const char *)(payload+1)); if(shello->total_len<0 || (shello->total_len+SERVER_HELLO_HDRLEN > payload_len-offset)) { return SSL_FALSE; } shello->version=ssl_get_hello_version((unsigned char *)payload, payload_len-offset); if(shello->version==0) { return SSL_FALSE; } offset+=(sizeof(shello->version)+SERVER_HELLO_HDRLEN); /*get client hello random*/ shello->random.gmt_time=(unsigned int)BtoL4BytesNum((const char *)(payload+offset)); offset+=sizeof(shello->random.gmt_time); if(payload_len-offset-SSL_RANDOM_SIZE<=0) { return SSL_FALSE; } shello->random.bytes.len=SSL_RANDOM_SIZE; shello->random.bytes.value=payload+offset; offset+=SSL_RANDOM_SIZE; /*get client hello session*/ one_ltv=ssl_parse_lv1(&(shello->session), payload+offset, payload_len-offset); if(one_ltv==-1) { return SSL_FALSE; } offset+=one_ltv; /*get client hello suites*/ shello->ciphersuites.len=2; shello->ciphersuites.value=payload+offset; offset+=2; /*get client hello compress*/ shello->compress_method.len=1; shello->compress_method.value=payload+offset; offset+=1; if(offset < payload_len) { /*get extension*/ unsigned short extensions_len=(unsigned short)BtoL2BytesNum((const char *)(payload+offset)); offset+=sizeof(extensions_len); unsigned short ex_offset=0; for(int i=0; (payload_len-offset >= 4) && ex_offsetextensions->value, &extension); } } return SSL_TRUE; } void ssl_shello_ja3s_generate(struct ssl_server_hello *shello) { if(shello==NULL) { return ; } UT_string *ja3s_string; utstring_new(ja3s_string); utstring_printf(ja3s_string, "%u,", shello->version); if(shello->ciphersuites.value!=NULL) { utstring_printf(ja3s_string, "%u,", ntohs(*(unsigned short *)(shello->ciphersuites.value))); } else { utstring_printf(ja3s_string, ","); } int32_t flag=SSL_FALSE; for(uint32_t i=0; iextensions->value); i++) { struct ssl_l2tv *ext=(struct ssl_l2tv *)utarray_eltptr(shello->extensions->value, i); if(ext==NULL || ssl_is_grease_value(ext->type)) { continue; } utstring_printf(ja3s_string, "%s%u", ((flag==SSL_FALSE) ? "" : "-"), ext->type); flag=SSL_TRUE; } shello->ja3s.value_sz=ja3_md5sum(utstring_body(ja3s_string), utstring_len(ja3s_string), shello->ja3s.value, sizeof(shello->ja3s.value)); shello->ja3s.value[shello->ja3s.value_sz]='\0'; utstring_free(ja3s_string); } void ssl_shello_ja4s_generate(struct ssl_server_hello *shello) { if(shello==NULL) { return ; } size_t alpn_first_proto_sz=0; char alpn_first_proto[4]={0}; char *supported_versions=NULL; for(uint32_t i=0; iextensions->value); i++) { struct ssl_l2tv *ext=(struct ssl_l2tv *)utarray_eltptr(shello->extensions->value, i); if(ext==NULL || ssl_is_grease_value(ext->type)) { continue; } switch(ext->type) { case ALPN_EXT_TYPE: alpn_first_proto_sz=ssl_hello_apln_first_protocol_get(ext, alpn_first_proto, sizeof(alpn_first_proto)); break; case SUPPORTED_VERSIONS: supported_versions=(char *)ssl_hello_supported_version_get(ext); break; default: break; } } if(supported_versions==NULL) { supported_versions=(char *)ssl_hello_version_convert(shello->version); } UT_string *ja4s_a; utstring_new(ja4s_a); utstring_printf(ja4s_a, "t%s%02u%s_%04x_", ((supported_versions==NULL) ? "00" : supported_versions), ((utarray_len(shello->extensions->value) > 99) ? 99 : utarray_len(shello->extensions->value)), ((alpn_first_proto_sz==0) ? "00" : alpn_first_proto), ((shello->ciphersuites.value!=NULL) ? ntohs(*(unsigned short *)(shello->ciphersuites.value)) : 0) ); UT_string *ja4s_c; utstring_new(ja4s_c); int32_t flag=SSL_FALSE; for(uint32_t i=0; iextensions->value); i++) { struct ssl_l2tv *ext=(struct ssl_l2tv *)utarray_eltptr(shello->extensions->value, i); if(ext==NULL || ssl_is_grease_value(ext->type)) { continue; } if(ext->type==ALPN_EXT_TYPE || ext->type==SERVER_NAME_EXT_TYPE) { continue; } utstring_printf(ja4s_c, "%s%04x", ((flag==SSL_FALSE) ? "" : ","), ext->type); flag=SSL_TRUE; } if(utstring_len(ja4s_c)>0) { unsigned char ja4s_c_sha256[SHA256_DIGEST_LENGTH]; ja4_sha256(utstring_body(ja4s_c), utstring_len(ja4s_c), ja4s_c_sha256, sizeof(ja4s_c_sha256)); utstring_printf(ja4s_a, "%02x%02x%02x%02x%02x%02x", ja4s_c_sha256[0], ja4s_c_sha256[1], ja4s_c_sha256[2], ja4s_c_sha256[3], ja4s_c_sha256[4], ja4s_c_sha256[5]); } else { utstring_printf(ja4s_a, "000000000000"); } shello->ja4s.value_sz=MIN(utstring_len(ja4s_a), sizeof(shello->ja4s)-1); memcpy(shello->ja4s.value, utstring_body(ja4s_a), shello->ja4s.value_sz); utstring_free(ja4s_a); utstring_free(ja4s_c); } int ssl_parse_new_session_ticket(struct ssl_new_session_ticket *new_session_ticket, char *payload, int payload_len) { int offset=0; new_session_ticket->total_len=BtoL3BytesNum((const char *)(payload+1)); if(new_session_ticket->total_len<0) { return SSL_FALSE; } /*4 means _type+len*/ if(new_session_ticket->total_len+SESSION_TICKET_HDRLEN > payload_len-offset) { /**packet trunked**/ return SSL_FALSE; } offset+=SESSION_TICKET_HDRLEN; new_session_ticket->lift_time=BtoL4BytesNum((const char *)(payload+offset)); offset+=sizeof(new_session_ticket->lift_time); new_session_ticket->ticket_len=BtoL2BytesNum((const char *)(payload+offset)); new_session_ticket->ticket=(unsigned char *)(payload+offset); return SSL_TRUE; } int ssl_parse_certificate_detail(const struct streaminfo *a_tcp, struct ssl_runtime_context *ssl_context, char *payload, int payload_len, int thread_seq, const void *a_packet) { if(!(g_ssl_runtime_para.ssl_interested_region_flag&SSL_CERTIFICATE_DETAIL) || g_ssl_runtime_para.parse_certificate_detail==0) { ssl_context->stream.certificate=NULL; return SSL_TRUE; } int certificate_num=0; struct ssl_certificate_chain cert_unit[CERT_MAXNUM]; int certificate_count=ssl_read_all_cert(payload, payload_len, cert_unit, CERT_MAXNUM); for(int i=0;istream.certificate, cert_unit[i].cert, cert_unit[i].cert_len, thread_seq); if(state==SSL_FALSE) { return SSL_FALSE; } ssl_context->stream.certificate->cert_len = cert_unit[i].cert_len; switch(certificate_num) { case 0: ssl_context->stream.certificate->cert_type=CERT_TYPE_INDIVIDUAL; break; case 1: if(certificate_count==2) { ssl_context->stream.certificate->cert_type=CERT_TYPE_ROOT; } else { ssl_context->stream.certificate->cert_type=CERT_TYPE_MIDDLE; } break; case 2: if(certificate_count==3) { ssl_context->stream.certificate->cert_type=CERT_TYPE_ROOT; } else { ssl_context->stream.certificate->cert_type=CERT_TYPE_CHAIN; } break; default: if(certificate_num==certificate_count-1) { ssl_context->stream.certificate->cert_type=CERT_TYPE_ROOT; } else { ssl_context->stream.certificate->cert_type=CERT_TYPE_CHAIN; } break; } ssl_call_plugins(a_tcp, ssl_context, (char *)(ssl_context->stream.certificate), sizeof(struct ssl_certificate), SSL_CERTIFICATE_DETAIL_MASK, thread_seq, a_packet); certificate_num++; ssl_context->stream.certificate->subject_alter.num=0; if(ssl_context->stream.certificate->subject_alter.name!=NULL) { dictator_free(thread_seq, ssl_context->stream.certificate->subject_alter.name); ssl_context->stream.certificate->subject_alter.name=NULL; } ssl_context->stream.certificate->subject_key.len=0; if(ssl_context->stream.certificate->subject_key.value!=NULL) { dictator_free(thread_seq, ssl_context->stream.certificate->subject_key.value); ssl_context->stream.certificate->subject_key.value=NULL; } memset(&(ssl_context->stream.certificate->issuer), 0, sizeof(struct ssl_rdn_sequence)); memset(&(ssl_context->stream.certificate->subject), 0, sizeof(struct ssl_rdn_sequence)); ssl_context->stream.certificate->serial.len=0; ssl_context->stream.certificate->version.len=0; ssl_context->stream.certificate->version.value=NULL; ssl_context->stream.certificate->validity.before[0]='\0'; ssl_context->stream.certificate->validity.after[0]='\0'; ssl_context->stream.certificate->signature_algorithm.len=0; ssl_context->stream.certificate->signature_algorithm.value[0]='\0'; } return SSL_TRUE; } int ssl_parse_certificate(const struct streaminfo *a_tcp, struct ssl_runtime_context *ssl_context, char *payload, int payload_len, int thread_seq, const void *a_packet) { int offset=0; ssl_context->stream.certificate->total_len=BtoL3BytesNum((const char *)(payload+4)); if(ssl_context->stream.certificate->total_len<0) { return SSL_FALSE; } /*7 means cert_type+len*/ if(ssl_context->stream.certificate->total_len+CERTIFICATE_HDRLEN>payload_len) { /**packet trunked**/ return SSL_TRUE; } /*2 means version*/ offset=CERTIFICATE_HDRLEN; if(g_ssl_runtime_para.ssl_interested_region_flag & SSL_CERTIFICATE) { ssl_call_plugins(a_tcp, ssl_context, payload+offset, payload_len-offset, SSL_CERTIFICATE_MASK, thread_seq, a_packet); } return ssl_parse_certificate_detail(a_tcp, ssl_context, payload+offset, payload_len-offset, thread_seq, a_packet); } int ssl_parse_handshake(const struct streaminfo *a_tcp, struct ssl_runtime_context *ssl_context, char *payload, int payload_len, int thread_seq, const void *a_packet) { int offset=0; int state=SSL_TRUE; while((payload_len-offset) > 0) { struct ssl_handshake_type *handshake=(struct ssl_handshake_type *)(payload+offset); if(CERTIFICATE == handshake->content_type) { if(payload_len-offsetstream.certificate=&certificate; state=ssl_parse_certificate(a_tcp, ssl_context, payload+offset, payload_len-offset, thread_seq, a_packet); if(state!=SSL_TRUE) { return state; } offset+=(certificate.total_len+CERTIFICATE_HDRLEN); ssl_context->stream.certificate=NULL; } /**analyse client_hello**/ else if (CLIENT_HELLO == handshake->content_type) { if ((payload_len-offset)value, &UT_ssl_hello_extension_icd); ssl_context->stream.chello=&chello; state=ssl_parse_client_hello(&chello, (unsigned char *)(payload+offset), payload_len-offset); if(state!=SSL_TRUE) { utarray_free(chello.extensions->value); free(chello.extensions); return state; } ssl_chello_ja3_generate(&chello); ssl_chello_ja4_generate(&chello); ssl_call_plugins(a_tcp, ssl_context, (char *)(payload+offset), chello.total_len+CLIENT_HELLO_HDRLEN, SSL_CLIENT_HELLO_MASK, thread_seq, a_packet); offset+=(chello.total_len+CLIENT_HELLO_HDRLEN); utarray_free(chello.extensions->value); free(chello.extensions); ssl_context->stream.chello=NULL; } /**analyse server_hello**/ else if (SERVER_HELLO == handshake->content_type) { if((payload_len-offset)value, &UT_ssl_hello_extension_icd); ssl_context->stream.shello=&shello; state=ssl_parse_server_hello(&shello, (unsigned char *)(payload+offset), payload_len-offset); if(state!=SSL_TRUE) { utarray_free(shello.extensions->value); free(shello.extensions); return state; } ssl_shello_ja3s_generate(&shello); ssl_shello_ja4s_generate(&shello); ssl_call_plugins(a_tcp, ssl_context, (char *)(payload+offset), shello.total_len+SERVER_HELLO_HDRLEN, SSL_SERVER_HELLO_MASK, thread_seq, a_packet); offset+=(shello.total_len+SERVER_HELLO_HDRLEN); utarray_free(shello.extensions->value); free(shello.extensions); ssl_context->stream.shello=NULL; } else if (NEW_SESSION_TICKET == handshake->content_type) { if((payload_len-offset) < SESSION_TICKET_HDRLEN) { break; } struct ssl_new_session_ticket new_session_ticket={0}; ssl_context->stream.new_session_ticket=&new_session_ticket; state=ssl_parse_new_session_ticket(&new_session_ticket, (payload+offset), (payload_len-offset)); if(state!=SSL_TRUE) { return state; } ssl_call_plugins(a_tcp, ssl_context, (char *)(payload+offset), new_session_ticket.total_len+SESSION_TICKET_HDRLEN, SSL_NEW_SESSION_TICKET_MASK, thread_seq, a_packet); offset+=(new_session_ticket.total_len+SESSION_TICKET_HDRLEN); ssl_context->stream.new_session_ticket=NULL; } else { break; } } return SSL_TRUE; } int ssl_parse_application_data(const struct streaminfo *a_tcp, struct ssl_runtime_context *ssl_context, char *payload, int payload_len, int thread_seq, const void *a_packet) { if (NULL==payload || payload_len<= 0) { return SSL_TRUE; } ssl_call_plugins(a_tcp, ssl_context, payload, payload_len, SSL_APPLICATION_DATA_MASK, thread_seq, a_packet); return SSL_TRUE; } int ssl_parse_alert(const struct streaminfo *a_tcp, struct ssl_runtime_context *ssl_context, char *payload, int payload_len, int thread_seq, const void *a_packet) { if(NULL==payload || payload_len<=0) { return SSL_TRUE; } ssl_call_plugins(a_tcp, ssl_context, payload, payload_len, SSL_ALERT_MASK, thread_seq, a_packet); return SSL_TRUE; } int ssl_identify_version(char *pcData, int payload_len) { if (NULL == pcData || payload_len < 1) { return 0; } else if (20 > pcData[0] || 23 < pcData[0]) { return 0; } if (01 == pcData[1] && 1 == pcData[2]) { /*TLCP 1.1*/ return TLCPV1_VERSION; } else if (03 == pcData[1] && 1 == pcData[2]) { /*TLS 1.0*/ return TLSV1_0_VERSION; } else if (03 == pcData[1] && 2 == pcData[2]) { /*TLS 1.1*/ return TLSV1_1_VERSION; } else if (03 == pcData[1] && 3 == pcData[2]) { /*TLS 1.2*/ return TLSV1_2_VERSION; } else if (03 == pcData[1] && 0 == pcData[2]) { /*SSL 3.0*/ return SSLV3_VERSION; } else if (0 == pcData[1] && 2 == pcData[2]) { /*SSL 2.0*/ return SSLV2_VERSION; } else if (0xfe == (unsigned char)pcData[1] && 0xff == (unsigned char)pcData[2]) { /*DTLS 1.0*/ return DTLSV1_0_VERSION; } else if (0x01 == (unsigned char)pcData[1] && 0x00 == (unsigned char)pcData[2]) { /*DTLS 1.0 (OpenSSL pre 0.9.8f)*/ return DTLSV1_0_VERSION_NOT; } return 0; } const char* ssl_get_version_name(unsigned short version) { for(unsigned int i=0;ssl_version_list[i].value!=0;i++) { if(ssl_version_list[i].value==version) { return (char*)ssl_version_list[i].string; } } return NULL; } int ssl_parse_version(const struct streaminfo *a_tcp, struct ssl_runtime_context *ssl_context, char *payload, int payload_len, int thread_seq, const void *a_packet) { if(UNKNOWN_VERSION==ssl_context->version) { ssl_context->version=(unsigned int)ssl_identify_version(payload, payload_len); /*jump the first pkt*/ if (UNKNOWN_VERSION==ssl_context->version) { if (ssl_context->first_pkt_flag==0) { ssl_context->first_pkt_flag=1; return SSL_CONTINUE; } else { return SSL_FALSE; } } /*version callback*/ for(int i=0; ssl_version_list[i].value!=0; i++) { if(ssl_version_list[i].value==ssl_context->version) { ssl_call_plugins(a_tcp, ssl_context, (char*)ssl_version_list[i].string, strlen(ssl_version_list[i].string), SSL_VERSION_MASK, thread_seq, a_packet); break; } } } return SSL_TRUE; } static void ssl_detain_frag_chello(const struct streaminfo *a_tcp) { if(g_ssl_runtime_para.detain_frag_chello_num == 0 || a_tcp->curdir != DIR_C2S || a_tcp->dir != DIR_DOUBLE)return; struct frag_chello *pkts = (struct frag_chello *)stream_bridge_async_data_get(a_tcp, g_ssl_runtime_para.frag_chello_exdata_idx); if (pkts == NULL) { pkts=(struct frag_chello *)calloc(sizeof(struct frag_chello), 1); if(g_ssl_runtime_para.fs)fieldstat_easy_counter_incrby(g_ssl_runtime_para.fs, a_tcp->threadnum, g_ssl_runtime_para.fs_metric_id[FS_METRIC_DETAIN_FRAG_CHELLO_SESSION], NULL, 0, 1); } if(pkts->finish == 1)return; const void *p = get_current_rawpkt_from_streaminfo(a_tcp); struct detain_pkt *dpkt=MESA_rawpkt_detain(a_tcp, p); if(dpkt && pkts->p_szp[pkts->p_sz]=dpkt; pkts->p_sz+=1; if(pkts->p_sz==g_ssl_runtime_para.detain_frag_chello_num)pkts->finish=1; if(g_ssl_runtime_para.fs)fieldstat_easy_counter_incrby(g_ssl_runtime_para.fs, a_tcp->threadnum, g_ssl_runtime_para.fs_metric_id[FS_METRIC_DETAIN_FRAG_CHELLO_NUM], NULL, 0, 1); } stream_bridge_async_data_put(a_tcp, g_ssl_runtime_para.frag_chello_exdata_idx, pkts); return; } static void ssl_detain_chello_finish(const struct streaminfo *a_tcp) { if(g_ssl_runtime_para.detain_frag_chello_num == 0)return; struct frag_chello *pkts = (struct frag_chello *)stream_bridge_async_data_get(a_tcp, g_ssl_runtime_para.frag_chello_exdata_idx); if(pkts)pkts->finish=1; if(g_ssl_runtime_para.fs)fieldstat_easy_counter_incrby(g_ssl_runtime_para.fs, a_tcp->threadnum, g_ssl_runtime_para.fs_metric_id[FS_METRIC_DETAIN_FRAG_CHELLO_FINISH], NULL, 0, 1); return; } int ssl_parse_message(const struct streaminfo *a_tcp, struct ssl_runtime_context *ssl_context, char *payload, int payload_len, int thread_seq, const void *a_packet) { int offset=0; int state=SSL_TRUE; state=ssl_parse_version(a_tcp, ssl_context, payload, payload_len, thread_seq, a_packet); if(state!=SSL_TRUE) { return state; } while(payload_len-offset > SSL_RECORD_HDRLEN) { int one_record_len=0; struct ssl_record_header *ssl_record=NULL; if(ssl_context->record.is_offset_header==1) { ssl_record=&(ssl_context->record.header); one_record_len=payload_len; } else { ssl_record=(struct ssl_record_header *)(payload+offset); one_record_len=htons(ssl_record->total_len); ssl_context->is_ssl_stream=SSL_TRUE; if((payload_len-offset) < one_record_len) { ssl_trunk_cache(ssl_context, payload+offset, payload_len-offset, thread_seq); if(*(unsigned char *)((ssl_context->record.cache_buff)+sizeof(struct ssl_record_header))==CLIENT_HELLO) ssl_detain_frag_chello(a_tcp); break; //cache } offset+=SSL_RECORD_HDRLEN; } switch (ssl_record->content_type) { case APPLICATION_DATA: state=ssl_parse_application_data(a_tcp, ssl_context, payload+offset, one_record_len, thread_seq, a_packet); break; case ALERT: state=ssl_parse_alert(a_tcp, ssl_context, payload+offset, one_record_len, thread_seq, a_packet); break; case HANDSHAKE: state=ssl_parse_handshake(a_tcp, ssl_context, payload+offset, one_record_len, thread_seq, a_packet); break; case CHANGE_CIPHER_SEP: break; default: if (ssl_context->is_ssl_stream==SSL_TRUE) { return SSL_TRUE; } else { return SSL_FALSE; } } if(state==SSL_FALSE) { offset=payload_len; break; } if(state==SSL_CONTINUE) { ssl_context->record.is_offset_header=1; ssl_context->record.header=*ssl_record; ssl_trunk_cache(ssl_context, payload+offset, payload_len-offset, thread_seq); if(*(unsigned char *)((ssl_context->record.cache_buff)+sizeof(struct ssl_record_header))==CLIENT_HELLO) ssl_detain_frag_chello(a_tcp); break; } offset+=one_record_len; } if(offset==payload_len) { if(ssl_context->record.cache_buff!=NULL && *(unsigned char *)((ssl_context->record.cache_buff)+sizeof(struct ssl_record_header))==CLIENT_HELLO) ssl_detain_chello_finish(a_tcp); ssl_trunk_free(ssl_context, thread_seq); } return state; } int ssl_parse_stream(const struct streaminfo *a_tcp, struct ssl_runtime_context *ssl_context, int thread_seq, const void *a_packet) { int payload_len = 0; char *payload=NULL; if(ssl_context==NULL) { return SSL_FALSE; } struct tcpdetail *tcp_detail=(struct tcpdetail *)a_tcp->pdetail; /**if buffered, copy new data to the buff**/ /**if lose packets, drop the buffered data**/ /*bad data fill the buffer, or lose pkt clear it*/ if(tcp_detail->lostlen>0 && ssl_context->record.cache_len>0) { ssl_trunk_free(ssl_context, thread_seq); } if(ssl_context->record.cache_len>0) { payload_len=MIN((int)tcp_detail->datalen, (g_ssl_runtime_para.max_cache_len - ssl_context->record.cache_len)); if(ssl_context->record.is_offset_header==1) { if(payload_len<=SSL_RECORD_HDRLEN) { ssl_trunk_free(ssl_context, thread_seq); return SSL_TRUE; } memcpy(ssl_context->record.cache_buff + ssl_context->record.cache_len, ((char *)(tcp_detail->pdata))+SSL_RECORD_HDRLEN, payload_len-SSL_RECORD_HDRLEN); ssl_context->record.cache_len += payload_len-SSL_RECORD_HDRLEN; } else { memcpy(ssl_context->record.cache_buff + ssl_context->record.cache_len, tcp_detail->pdata, payload_len); ssl_context->record.cache_len += payload_len; } payload_len=ssl_context->record.cache_len; payload=ssl_context->record.cache_buff; } else { payload=(char *)tcp_detail->pdata; payload_len=tcp_detail->datalen; } /**validaty check**/ if(NULL==payload || payload_len