Exemplo n.º 1
0
/* Prevent Cross-site Request Forgery
 */
int prevent_csrf(t_session *session) {
	char *referer, *slash;
	int i, n;

	if (strcmp(session->method, "POST") != 0) {
		return 0;
	}

	if ((referer = get_http_header("Referer:", session->http_headers)) == NULL) {
		return 0;
	}

	if (strncmp(referer, "http://", 7) == 0) {
		referer += 7;
	} else if (strncmp(referer, "https://", 8) == 0) {
		referer += 8;
	} else {
		session->cookie = NULL;

		log_error(session, "invalid referer while checking for CSRF");

		return 1;
	}

	if ((slash = strchr(referer, '/')) != NULL) {
		n = slash - referer;
	} else {
		n = strlen(referer);
	}

	for (i = 0; i < session->host->hostname.size; i++) {
		if (strncasecmp(referer, *(session->host->hostname.item + i), n) == 0) {
			return 0;
		}
	}

	session->cookie = NULL;

	if (session->body != NULL) {
		log_exploit_attempt(session, "CSRF", session->body);
#ifdef ENABLE_TOMAHAWK
		increment_counter(COUNTER_EXPLOIT);
#endif
#ifdef ENABLE_MONITOR
		if (session->config->monitor_enabled) {
			monitor_counter_exploit_attempt(session);
		}
#endif
	} else {
		log_error(session, "CSRF attempt detected with no request body");
	}

#ifdef ENABLE_MONITOR
	if (session->config->monitor_enabled) {
		monitor_counter_exploit_attempt(session);
	}
#endif

	return 1;
}
Exemplo n.º 2
0
/* Validate URL
 */
bool validate_url(t_session *session) {
	if (valid_uri(session->uri, session->host->allow_dot_files)) {
		if (session->host->secure_url == false) {
			return true;
		} else if (strstr(session->request_uri, "%00") == NULL) {
			return true;
		} else {
			session->return_code = 403;
		}
	} else {
		session->return_code = (session->request_method == PUT) ? 403 : 404;
	}

	log_exploit_attempt(session, "invalid URL", NULL);
#ifdef ENABLE_TOMAHAWK
	increment_counter(COUNTER_EXPLOIT);
#endif
#ifdef ENABLE_MONITOR
	if (session->config->monitor_enabled) {
		monitor_counter_exploit_attempt(session);
	}
#endif

	session->error_cause = ec_INVALID_URL;

	return false;
}
Exemplo n.º 3
0
/* Prevent SQL injection
 */
static int prevent_sqli_str(t_session *session, char *str, int length) {
	char *data;
	t_sqli_pattern *pattern;
	int result = 0;

	if ((str == NULL) || (length <= 0)) {
		return 0;
	}

	if ((data = (char*)malloc(length + 1)) == NULL) {
		return -1;
	}
	memcpy(data, str, length);
	data[length] = '\0';
	url_decode(data);

	pattern = sqli_patterns;
	while (pattern != NULL) {
		if (regexec(&(pattern->regex), data, 0, NULL, 0) != REG_NOMATCH) {
			log_exploit_attempt(session, "SQLi", str);
#ifdef ENABLE_TOMAHAWK
			increment_counter(COUNTER_EXPLOIT);
#endif
#ifdef ENABLE_MONITOR
			if (session->config->monitor_enabled) {
				monitor_counter_exploit_attempt(session);
			}
#endif

			result = 1;
			break;
		}

		pattern = pattern->next;
	}

	free(data);

	return result;
}
Exemplo n.º 4
0
/* Prevent cross-site scripting.
 */
int prevent_xss(t_session *session) {
	int result = 0;
	short low, high;
	char *str, value;

	if ((str = session->vars) == NULL) {
		return 0;
	}

	while (*str != '\0') {
		if ((value = *str) == '%') {
			if ((high = hex_to_int(*(str + 1))) != -1) {
				if ((low = hex_to_int(*(str + 2))) != -1) {
					value = (char)(high<<4) + low;
				}
			}
		}

		if ((value == '\"') || (value == '<') || (value == '>') || (value == '\'')) {
			*str = '_';
			result += 1;
		}
		str++;
	}

	if (result > 0) {
		log_exploit_attempt(session, "XSS", session->vars);
#ifdef ENABLE_TOMAHAWK
		increment_counter(COUNTER_EXPLOIT);
#endif
#ifdef ENABLE_MONITOR
		if (session->config->monitor_enabled) {
			monitor_counter_exploit_attempt(session);
		}
#endif
	}

	return result;
}
Exemplo n.º 5
0
/* Serve the client that connected to the webserver
 */
static int serve_client(t_session *session) {
	int result, length, auth_result;
	char *search, *qmark, chr, *client_ip;
	t_host *host_record;
	t_access access;
	t_deny_body *deny_body;
	t_req_method request_method;
	t_ip_addr ip;
#ifdef ENABLE_TOOLKIT
	int i;
	t_toolkit_options toolkit_options;
#endif
#ifdef ENABLE_RPROXY
	t_rproxy *rproxy;
#endif

#ifdef ENABLE_DEBUG
	session->current_task = "fetch & parse request";
#endif

	if ((result = fetch_request(session)) != 200) {
		session->request_method = GET;
		return result;
	} else if ((result = parse_request(session, session->header_length + session->content_length)) != 200) {
		session->request_method = GET;
		return result;
	}

#ifdef ENABLE_DEBUG
	session->current_task = "serve client";
#endif

	session->time = time(NULL);

	/* Hide reverse proxies
	 */
	if (in_iplist(session->config->hide_proxy, &(session->ip_address))) {
		if ((client_ip = get_http_header(hs_forwarded, session->http_headers)) != NULL) {
			if ((search = strrchr(client_ip, ',')) != NULL) {
				client_ip = search + 1;
			}

			while ((*client_ip == ' ') && (*client_ip != '\0')) {
				client_ip++;
			}

			if (*client_ip != '\0') {
				if (parse_ip(client_ip, &ip) != -1) {
					if (reposition_client(session, &ip) != -1) {
						copy_ip(&(session->ip_address), &ip);
					}
				}
			}
		}
	}

	/* Find host record
	 */
	if (session->hostname != NULL) {
		remove_port_from_hostname(session->hostname, session->binding);

		if ((host_record = get_hostrecord(session->config->first_host, session->hostname, session->binding)) != NULL) {
			session->host = host_record;
#ifdef ENABLE_TOMAHAWK
			session->last_host = host_record;
#endif
		}
	}
	session->host->access_time = session->time;

#ifdef ENABLE_SSL
	/* SSL client authentication
	 */
	if (session->binding->use_ssl) {
		if ((session->host->ca_certificate != NULL) && (ssl_has_peer_cert(&(session->ssl_context)) == false)) {
			log_error(session, "missing client SSL certificate");
			return 440;
		}
	}
#endif

	/* Enforce usage of SSL
	 */
#ifdef ENABLE_SSL
	if (session->host->require_ssl && (session->binding->use_ssl == false)) {
		if ((qmark = strchr(session->uri, '?')) != NULL) {
			*qmark = '\0';
			session->vars = qmark + 1;
			session->uri_len = strlen(session->uri);
		}
		session->cause_of_301 = require_ssl;
		return 301;
	}
#endif

	/* Deny matching bodies
	 */
	if (session->body != NULL) {
		chr = *(session->body + session->content_length);
		*(session->body + session->content_length) = '\0';

		deny_body = session->host->deny_body;
		while (deny_body != NULL) {
			if (strpcmp(session->body, &(deny_body->pattern)) == 0) {
				if ((session->config->ban_on_denied_body > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) {
					ban_ip(&(session->ip_address), session->config->ban_on_denied_body, session->config->kick_on_ban);
					log_system(session, "Client banned because of denied body");
#ifdef ENABLE_MONITOR
					if (session->config->monitor_enabled) {
						monitor_counter_ban(session);
					}
#endif
				}

				log_exploit_attempt(session, "denied body", session->body);
#ifdef ENABLE_TOMAHAWK
				increment_counter(COUNTER_EXPLOIT);
#endif
#ifdef ENABLE_MONITOR
				if (session->config->monitor_enabled) {
					monitor_counter_exploit_attempt(session);
				}
#endif

				*(session->body + session->content_length) = chr;

				return 403;
			}
			deny_body = deny_body->next;
		}

		*(session->body + session->content_length) = chr;
	}

#ifdef ENABLE_RPROXY
	/* Reverse proxy
	 */
	rproxy = session->host->rproxy;
	while (rproxy != NULL) {
		if (rproxy_match(rproxy, session->request_uri)) {
			if (rproxy_loop_detected(session->http_headers)) {
				return 508;
			}

			if ((qmark = strchr(session->uri, '?')) != NULL) {
				*qmark = '\0';
				session->vars = qmark + 1;
			}

			if (validate_url(session) == false) {
				return -1;
			}

			if ((session->vars != NULL) && (session->host->secure_url)) {
				if (forbidden_chars_present(session->vars)) {
					return 403;
				}
			}

			if (duplicate_host(session) == false) {
				return 500;
			} else if ((result = uri_to_path(session)) != 200) {
				return result;
			} else if (load_user_config(session) == -1) {
				return 500;
			} else if ((result = copy_directory_settings(session)) != 200) {
				return result;
			}

			switch (access = allow_client(session)) {
				case deny:
					log_error(session, fb_accesslist);
					return 403;
				case allow:
					break;
				case pwd:
				case unspecified:
					if ((auth_result = http_authentication_result(session, access == unspecified)) != 200) {
						return auth_result;
					}
			}

			if (session->host->prevent_xss) {
				prevent_xss(session);
			}

			if (session->host->prevent_csrf) {
				prevent_csrf(session);
			}

			if (session->host->prevent_sqli) {
				if ((result = prevent_sqli(session)) != 0) {
					return result;
				}
			}

			return proxy_request(session, rproxy);
		}

		rproxy = rproxy->next;
	}
#endif

	/* Actions based on request method
	 */
	switch (session->request_method) {
		case TRACE:
			if (session->binding->enable_trace == false) {
				return 501;
			}
			return handle_trace_request(session);
		case PUT:
		case DELETE:
			if ((session->binding->enable_alter == false) && (session->host->webdav_app == false)) {
				return 501;
			}
			break;
		case unknown:
			return 400;
		case unsupported:
			if (session->host->webdav_app == false) {
				return 501;
			}
			break;
		default:
			break;
	}

#ifdef ENABLE_TOOLKIT
	/* URL toolkit
	 */
#ifdef ENABLE_SSL
	init_toolkit_options(&toolkit_options, session->host->website_root, session->config->url_toolkit,
	                     session->binding->use_ssl, session->host->allow_dot_files, session->http_headers);
#else
	init_toolkit_options(&toolkit_options, session->host->website_root, session->config->url_toolkit,
	                     session->host->allow_dot_files, session->http_headers);
#endif

	if ((session->request_method != PUT) && (session->request_method != DELETE)) {
		for (i = 0; i < session->host->toolkit_rules.size; i++) {
			if ((result = use_toolkit(session->uri, session->host->toolkit_rules.item[i], &toolkit_options)) == UT_ERROR) {
				return 500;
			}

			if ((toolkit_options.ban > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) {
				ban_ip(&(session->ip_address), toolkit_options.ban, session->config->kick_on_ban);
				log_system(session, "Client banned because of URL match in UrlToolkit rule");
#ifdef ENABLE_MONITOR
				if (session->config->monitor_enabled) {
					monitor_counter_ban(session);
				}
#endif
				return 403;
			}

			session->toolkit_fastcgi = toolkit_options.fastcgi_server;
			if (toolkit_options.new_url != NULL) {
				if (register_tempdata(&(session->tempdata), toolkit_options.new_url, tc_data) == -1) {
					free(toolkit_options.new_url);
					return 500;
				}
				session->uri = toolkit_options.new_url;
			}

			if (result == UT_REDIRECT) {
				if ((session->location = strdup(toolkit_options.new_url)) == NULL) {
					return -1;
				}
				session->cause_of_301 = location;
				return 301;
			}

			if (result == UT_DENY_ACCESS) {
				log_error(session, "access denied via URL toolkit rule");
				return 403;
			}

			if (toolkit_options.expire > -1) {
				session->expires = toolkit_options.expire;
			}
		}
	}
#endif

	/* Find GET data
	 */
	if ((qmark = strchr(session->uri, '?')) != NULL) {
		*qmark = '\0';
		session->vars = qmark + 1;
	}

	url_decode(session->uri);
	session->uri_len = strlen(session->uri);

	if ((session->vars != NULL) && (session->host->secure_url)) {
		if (forbidden_chars_present(session->vars)) {
			return 403;
		}
	}

	if (duplicate_host(session) == false) {
		return 500;
	}

	if (validate_url(session) == false) {
		return -1;
	}

	if ((result = uri_to_path(session)) != 200) {
		return result;
	}

	/* Load configfile from directories
	 */
	if (load_user_config(session) == -1) {
		return 500;
	}

	if ((result = copy_directory_settings(session)) != 200) {
		return result;
	}

	switch (access = allow_client(session)) {
		case deny:
			log_error(session, fb_accesslist);
			return 403;
		case allow:
			break;
		case pwd:
		case unspecified:
			if ((auth_result = http_authentication_result(session, access == unspecified)) != 200) {
				return auth_result;
			}
	}

	switch (is_directory(session->file_on_disk)) {
		case error:
			return 500;
		case yes:
			session->uri_is_dir = true;
			break;
		case no:
			if (((session->request_method != PUT) || session->host->webdav_app) && (session->host->enable_path_info)) {
				if ((result = get_path_info(session)) != 200) {
					return result;
				}
			}
			break;
		case no_access:
			log_error(session, fb_filesystem);
			return 403;
		case not_found:
			if (session->request_method == DELETE) {
				return 404;
			}
	}

#ifdef ENABLE_TOOLKIT
	if ((session->toolkit_fastcgi == NULL) && session->uri_is_dir) {
#else
	if (session->uri_is_dir) {
#endif
		length = strlen(session->file_on_disk);
		if (*(session->file_on_disk + length - 1) == '/') {
			strcpy(session->file_on_disk + length, session->host->start_file);
		} else {
			return 301;
		}
	}

	if (get_target_extension(session) == -1) {
		return 500;
	}

	if (((session->request_method != PUT) && (session->request_method != DELETE)) || session->host->webdav_app) {
		check_target_is_cgi(session);
	}

	/* Handle request based on request method
	 */
	request_method = session->request_method;
	if (session->host->webdav_app) {
		if ((request_method == PUT) || (request_method == DELETE)) {
			request_method = POST;
		}
	}

	switch (request_method) {
		case GET:
		case HEAD:
			if (session->cgi_type != no_cgi) {
				session->body = NULL;
				result = execute_cgi(session);
#ifdef ENABLE_XSLT
			} else if (can_transform_with_xslt(session)) {
				result = handle_xml_file(session);
#endif
			} else {
				result = send_file(session);
			}
			if (result == 404) {
#ifdef ENABLE_XSLT
				if ((session->host->show_index != NULL) && (session->uri[session->uri_len - 1] == '/')) {
					result = show_index(session);
				}
#endif
#ifdef ENABLE_MONITOR
			} else if (session->config->monitor_enabled) {
				if ((result == 200) && (session->host->monitor_host)) {
					unlink(session->file_on_disk);
				}
#endif
			}

			if ((session->request_method == GET) && (session->cgi_type == no_cgi) && (session->directory != NULL)) {
				if (session->directory->run_on_download != NULL) {
					run_program(session, session->directory->run_on_download, result);
				}
			}
			break;
		case POST:
		case unsupported:
			if (session->cgi_type != no_cgi) {
				result = execute_cgi(session);
#ifdef ENABLE_XSLT
			} else if (can_transform_with_xslt(session)) {
				result = handle_xml_file(session);
#endif
			} else {
				result = 405;
			}
			break;
		case PUT:
			result = handle_put_request(session);
			if (((result == 201) || (result == 204)) && (session->host->run_on_alter != NULL)) {
				run_program(session, session->host->run_on_alter, result);
			}
			break;
		case DELETE:
			result = handle_delete_request(session);
			if ((result == 204) && (session->host->run_on_alter != NULL)) {
				run_program(session, session->host->run_on_alter, result);
			}
			break;
		default:
			result = 400;
	}

	return result;
}

/* Handle timeout upon sending request
 */
static void handle_timeout(t_session *session) {
	if ((session->config->ban_on_timeout > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) {
		ban_ip(&(session->ip_address), session->config->ban_on_timeout, session->config->kick_on_ban);
		log_system(session, "Client banned because of connection timeout");
#ifdef ENABLE_MONITOR
		if (session->config->monitor_enabled) {
			monitor_counter_ban(session);
		}
#endif
	} else {
		log_system(session, "Timeout while waiting for request");
	}
}

/* Request has been handled, handle the return code.
 */
static void handle_request_result(t_session *session, int result) {
	char *hostname;

#ifdef ENABLE_DEBUG
	session->current_task = "handle request result";
#endif

	if (result == -1) switch (session->error_cause) {
		case ec_MAX_REQUESTSIZE:
			log_system(session, "Maximum request size reached");
			session->return_code = 413;
			send_code(session);
			if ((session->config->ban_on_max_request_size > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) {
				ban_ip(&(session->ip_address), session->config->ban_on_max_request_size, session->config->kick_on_ban);
				log_system(session, "Client banned because of sending a too large request");
#ifdef ENABLE_MONITOR
				if (session->config->monitor_enabled) {
					monitor_counter_ban(session);
				}
#endif
			}
			break;
		case ec_TIMEOUT:
			if (session->kept_alive == 0) {
				session->return_code = 408;
				send_code(session);
				handle_timeout(session);
			}
			break;
		case ec_CLIENT_DISCONNECTED:
			if (session->kept_alive == 0) {
				log_system(session, "Client disconnected");
			}
			break;
		case ec_SOCKET_READ_ERROR:
			if (errno != ECONNRESET) {
				log_system(session, "Error while reading request");
			}
			break;
		case ec_SOCKET_WRITE_ERROR:
			log_request(session);
			break;
		case ec_FORCE_QUIT:
			log_system(session, "Client kicked");
			break;
		case ec_SQL_INJECTION:
			if ((session->config->ban_on_sqli > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) {
				ban_ip(&(session->ip_address), session->config->ban_on_sqli, session->config->kick_on_ban);
				hostname = (session->hostname != NULL) ? session->hostname : unknown_host;
				log_system(session, "Client banned because of SQL injection on %s", hostname);
#ifdef ENABLE_MONITOR
				if (session->config->monitor_enabled) {
					monitor_counter_ban(session);
				}
#endif
			}
			session->return_code = 441;
			send_code(session);
			log_request(session);
			break;
		case ec_INVALID_URL:
			if ((session->config->ban_on_invalid_url > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) {
				ban_ip(&(session->ip_address), session->config->ban_on_invalid_url, session->config->kick_on_ban);
				hostname = (session->hostname != NULL) ? session->hostname : unknown_host;
				log_system(session, "Client banned because of invalid URL on %s", hostname);
#ifdef ENABLE_MONITOR
				if (session->config->monitor_enabled) {
					monitor_counter_ban(session);
				}
#endif
			}
			send_code(session);
			break;
		default:
			if (session->data_sent == false) {
				session->return_code = 500;
				send_code(session);
			}
	} else switch (result) {
		case 200:
			break;
		case 201:
		case 204:
		case 304:
		case 412:
			if (session->data_sent == false) {
				session->return_code = result;
				send_header(session);
				send_buffer(session, "Content-Length: 0\r\n\r\n", 21);
			}
			break;
		case 411:
		case 413:
			session->keep_alive = false;
			if (session->data_sent == false) {
				session->return_code = result;
				send_header(session);
				send_buffer(session, "Content-Length: 0\r\n\r\n", 21);
			}
			break;
		case 400:
			log_garbage(session);
			if (session->data_sent == false) {
				session->return_code = 400;
				if (send_code(session) == -1) {
					session->keep_alive = false;
				}
			}
			if ((session->config->ban_on_garbage > 0) && (ip_allowed(&(session->ip_address), session->config->banlist_mask) != deny)) {
				ban_ip(&(session->ip_address), session->config->ban_on_garbage, session->config->kick_on_ban);
				log_system(session, "Client banned because of sending garbage");
#ifdef ENABLE_MONITOR
				if (session->config->monitor_enabled) {
					monitor_counter_ban(session);
				}
#endif
			}
			break;
		case 401:
		case 403:
		case 404:
		case 501:
		case 503:
			if (session->data_sent == false) {
				switch (handle_error(session, result)) {
					case -1:
						session->keep_alive = false;
						break;
					case 200:
						break;
					default:
						if (session->data_sent == false) {
							session->return_code = result;
							if (send_code(session) == -1) {
								session->keep_alive = false;
							}
						}
				}
			}
			break;
		case 500:
			session->keep_alive = false;
		default:
			if (session->data_sent == false) {
				session->return_code = result;
				send_code(session);
			}
	}

	if ((result > 0) && (result != 400)) {
		log_request(session);
	} else {
		session->keep_alive = false;
	}
}