diff options
author | Luca Deri <deri@ntop.org> | 2024-10-31 21:20:26 +0100 |
---|---|---|
committer | Luca Deri <deri@ntop.org> | 2024-10-31 21:20:46 +0100 |
commit | 412ca8700fc53da705c6aa386c736a400279a614 (patch) | |
tree | 39ada41ae2b12ea7abf4e1243a4ae5f73d8bf3c3 /src | |
parent | bcc1874e581de9d59514248a27f525cb56a0ec31 (diff) |
Added HTTP credentials extraction
Diffstat (limited to 'src')
-rw-r--r-- | src/include/ndpi_api.h | 1 | ||||
-rw-r--r-- | src/include/ndpi_typedefs.h | 3 | ||||
-rw-r--r-- | src/lib/ndpi_main.c | 11 | ||||
-rw-r--r-- | src/lib/ndpi_utils.c | 13 | ||||
-rw-r--r-- | src/lib/protocols/http.c | 74 |
5 files changed, 91 insertions, 11 deletions
diff --git a/src/include/ndpi_api.h b/src/include/ndpi_api.h index d304ceb5d..ae5679e71 100644 --- a/src/include/ndpi_api.h +++ b/src/include/ndpi_api.h @@ -109,6 +109,7 @@ extern "C" { void * ndpi_calloc(unsigned long count, size_t size); void * ndpi_realloc(void *ptr, size_t old_size, size_t new_size); char * ndpi_strdup(const char *s); + char * ndpi_strndup(const char *s, size_t size); void ndpi_free(void *ptr); void * ndpi_flow_malloc(size_t size); void ndpi_flow_free(void *ptr); diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h index 4dfddc5f0..dc3f110ce 100644 --- a/src/include/ndpi_typedefs.h +++ b/src/include/ndpi_typedefs.h @@ -1349,12 +1349,13 @@ struct ndpi_flow_struct { struct { ndpi_http_method method; u_int8_t request_version; /* 0=1.0 and 1=1.1. Create an enum for this? */ - u_int8_t websocket:1, _pad:7; + u_int8_t websocket:1, request_header_observed:1, first_payload_after_header_observed:1, is_form:1, _pad:4; u_int16_t response_status_code; /* 200, 404, etc. */ char *url, *content_type /* response */, *request_content_type /* e.g. for POST */, *user_agent, *server; char *detected_os; /* Via HTTP/QUIC User-Agent */ char *nat_ip; /* Via HTTP X-Forwarded-For */ char *filename; /* Via HTTP Content-Disposition */ + char *username, *password; } http; ndpi_multimedia_flow_type flow_multimedia_type; diff --git a/src/lib/ndpi_main.c b/src/lib/ndpi_main.c index 98873e959..700016378 100644 --- a/src/lib/ndpi_main.c +++ b/src/lib/ndpi_main.c @@ -6762,6 +6762,12 @@ void ndpi_free_flow_data(struct ndpi_flow_struct* flow) { if(flow->http.filename) ndpi_free(flow->http.filename); + if(flow->http.username) + ndpi_free(flow->http.username); + + if(flow->http.password) + ndpi_free(flow->http.password); + if(flow->kerberos_buf.pktbuf) ndpi_free(flow->kerberos_buf.pktbuf); @@ -9333,6 +9339,8 @@ void ndpi_parse_packet_line_info(struct ndpi_detection_module_struct *ndpi_str, if((packet->payload[a] == 0x0d) && (packet->payload[a+1] == 0x0a)) { /* If end of line char sequence CR+NL "\r\n", process line */ + flow->http.request_header_observed = 1; + if(((a + 3) < packet->payload_packet_len) && (packet->payload[a+2] == 0x0d) && (packet->payload[a+3] == 0x0a)) { @@ -9401,8 +9409,7 @@ void ndpi_parse_packet_line_info_any(struct ndpi_detection_module_struct *ndpi_s for(a = 0; a < end; a++) { if(packet->payload[a] == 0x0a) { - packet->line[packet->parsed_lines].len = (u_int16_t)( - ((size_t) &packet->payload[a]) - ((size_t) packet->line[packet->parsed_lines].ptr)); + packet->line[packet->parsed_lines].len = (u_int16_t)(((size_t) &packet->payload[a]) - ((size_t) packet->line[packet->parsed_lines].ptr)); if(a > 0 && packet->payload[a - 1] == 0x0d) packet->line[packet->parsed_lines].len--; diff --git a/src/lib/ndpi_utils.c b/src/lib/ndpi_utils.c index 86aee7c7f..782f85f16 100644 --- a/src/lib/ndpi_utils.c +++ b/src/lib/ndpi_utils.c @@ -3885,3 +3885,16 @@ const char* ndpi_print_os_hint(u_int8_t os_hint) { return("Unknown"); } + +/* ************************************************************** */ + +char* ndpi_strndup(const char *s, size_t size) { + char *ret = (char*)ndpi_malloc(size+1); + + if(ret == NULL) return(NULL); + + memcpy(ret, s, size); + ret[size] = '\0'; + + return(ret); +} diff --git a/src/lib/protocols/http.c b/src/lib/protocols/http.c index cb146fcc0..1c468165c 100644 --- a/src/lib/protocols/http.c +++ b/src/lib/protocols/http.c @@ -108,7 +108,7 @@ static void ndpi_analyze_content_signature(struct ndpi_detection_module_struct * - ndpi_search_portable_executable - ndpi_search_shellscript */ - + if((flow->initial_binary_bytes_len >= 2) && (flow->initial_binary_bytes[0] == 0x4D) && (flow->initial_binary_bytes[1] == 0x5A)) set_risk = 1, msg = "Found DOS/Windows Exe"; /* Win executable */ else if((flow->initial_binary_bytes_len >= 4) && (flow->initial_binary_bytes[0] == 0x7F) && (flow->initial_binary_bytes[1] == 'E') @@ -224,12 +224,11 @@ static void ndpi_validate_http_content(struct ndpi_detection_module_struct *ndpi len = packet->payload_packet_len - (double_ret - packet->payload); - if(ndpi_strnstr((const char *)packet->content_line.ptr, "text/", packet->content_line.len) + if(flow->http.is_form + || ndpi_strnstr((const char *)packet->content_line.ptr, "text/", packet->content_line.len) || ndpi_strnstr((const char *)packet->content_line.ptr, "/json", packet->content_line.len) - || ndpi_strnstr((const char *)packet->content_line.ptr, "x-www-form-urlencoded", packet->content_line.len) ) { /* This is supposed to be a human-readeable text file */ - packet->http_check_content = 1; if(len >= 8 /* 4 chars for \r\n\r\n and at least 4 charts for content guess */) { @@ -462,6 +461,7 @@ static void setHttpUserAgent(struct ndpi_flow_struct *flow, char *ua) { else if(!strcmp(ua, "Windows NT 6.2")) ua = "Windows 8"; else if(!strcmp(ua, "Windows NT 6.3")) ua = "Windows 8.1"; else if(!strcmp(ua, "Windows NT 10.0")) ua = "Windows 10"; + else if(!strcmp(ua, "Windows NT 11.0")) ua = "Windows 11"; /* Good reference for future implementations: * https://github.com/ua-parser/uap-core/blob/master/regexes.yaml */ @@ -641,6 +641,42 @@ static void ndpi_http_parse_subprotocol(struct ndpi_detection_module_struct *ndp flow->http.user_agent && strstr(flow->http.user_agent, "Valve/Steam HTTP Client")) { ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_STEAM, master_protocol, NDPI_CONFIDENCE_DPI); } + + + if(flow->http.request_header_observed) { + if(flow->http.first_payload_after_header_observed == 0) { + /* Skip the last part of the HTTP request */ + flow->http.first_payload_after_header_observed = 1; + } else if(flow->http.is_form && (packet->payload_packet_len > 0)) { + /* Response payload */ + char *dup = ndpi_strndup((const char *)packet->payload, packet->payload_packet_len); + + if(dup) { + char *key, *value, *tmp; + + key = strtok_r(dup, "=", &tmp); + + while((key != NULL) + && ((flow->http.username == NULL) || (flow->http.password == NULL))) { + value = strtok_r(NULL, "&", &tmp); + + if(!value) + break; + + if((strcmp(key, "user") == 0) || (strcmp(key, "username") == 0)) { + if(!flow->http.username) flow->http.username = ndpi_strdup(value); + } else if((strcmp(key, "pwd") == 0) || (strcmp(key, "password") == 0)) { + if(!flow->http.password) flow->http.password = ndpi_strdup(value); + ndpi_set_risk(flow, NDPI_CLEAR_TEXT_CREDENTIALS, "Found password"); + } + + key = strtok_r(NULL, "=", &tmp); + } + + ndpi_free(dup); + } + } + } } /* ************************************************************* */ @@ -986,13 +1022,32 @@ static void check_content_type_and_change_protocol(struct ndpi_detection_module_ } if(packet->authorization_line.ptr != NULL) { + const char *a = NULL, *b = NULL; + NDPI_LOG_DBG2(ndpi_struct, "Authorization line found %.*s\n", packet->authorization_line.len, packet->authorization_line.ptr); - if(ndpi_strncasestr((const char*)packet->authorization_line.ptr, - "Basic", packet->authorization_line.len) - || ndpi_strncasestr((const char*)packet->authorization_line.ptr, - "Digest", packet->authorization_line.len)) { + if((a = ndpi_strncasestr((const char*)packet->authorization_line.ptr, + "Basic", packet->authorization_line.len)) + || (b = ndpi_strncasestr((const char*)packet->authorization_line.ptr, + "Digest", packet->authorization_line.len))) { + size_t content_len; + u_int len = b ? 7 : 6; + u_char *content = ndpi_base64_decode((const u_char*)&packet->authorization_line.ptr[len], + packet->authorization_line.len - len, &content_len); + + if(content != NULL) { + char *double_dot = strchr((char*)content, ':'); + + if(double_dot) { + double_dot[0] = '\0'; + flow->http.username = ndpi_strdup((char*)content); + flow->http.password = ndpi_strdup(&double_dot[1]); + } + + ndpi_free(content); + } + ndpi_set_risk(flow, NDPI_CLEAR_TEXT_CREDENTIALS, "Found credentials in HTTP Auth Line"); } @@ -1013,6 +1068,9 @@ static void check_content_type_and_change_protocol(struct ndpi_detection_module_ packet->content_line.len); flow->http.request_content_type[packet->content_line.len] = '\0'; } + + if(ndpi_strnstr(flow->http.request_content_type, "x-www-form-urlencoded", packet->content_line.len)) + flow->http.is_form = 1; } } else { /* Response */ |