CVE-2014-4501 (published 2014-07-23T14:55Z)

Multiple stack-based buffer overflows in sgminer before 4.2.2, cgminer before 4.3.5, and BFGMiner before 3.3.0 allow remote pool servers to have unspecified impact via a long URL in a client.reconnect stratum message to the (1) extract_sockaddr or (2) parse_reconnect functions in util.c.

Severity: HIGH
CVSS v2 base score: 10.0 (AV:N/AC:L/Au:N/C:C/I:C/A:C)

Weakness information:
cwe_id: CWE-119
cwe_name: Improper Restriction of Operations within the Bounds of a Memory Buffer
description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
extended_description: 
            Certain languages allow direct addressing of memory locations and do not automatically ensure that these locations are valid for the memory buffer that is being referenced. This can cause read or write operations to be performed on memory locations that may be associated with other variables, data structures, or internal program data.
         
         

Fix information:
repo_url: https://github.com/luke-jr/bfgminer
commit_details: repo_url: https://github.com/luke-jr/bfgminer
author: Luke Dashjr
author_date: 2014-06-05 17:05:12+00:00
msg: Stratum: extract_sockaddr: Truncate overlong addresses rather than stack overflow

Thanks to Mick Ayzenberg <mick@dejavusecurity.com> for finding this!
repository: repo_url: https://github.com/luke-jr/bfgminer
repo_name: luke-jr/bfgminer
description: Modular ASIC/FPGA miner written in C, featuring overclocking, monitoring, fan speed control and remote interface capabilities.
repo_language: C
owner: luke-jr
file_changes: filename: util.c
change_type: ModificationType.MODIFY
diff: @@ -1002,6 +1002,13 @@ bool extract_sockaddr(struct pool *pool, char *url)
 
 	if (url_len < 1)
 		return false;
+	
+	if (url_len >= sizeof(url_address))
+	{
+		applog(LOG_WARNING, "%s: Truncating overflowed address '%.*s'",
+		       __func__, url_len, url_begin);
+		url_len = sizeof(url_address) - 1;
+	}
 
 	sprintf(url_address, "%.*s", url_len, url_begin);
 

programming_language: C
repo_url: https://github.com/ckolivas/cgminer
commit_details: repo_url: https://github.com/ckolivas/cgminer
author: Con Kolivas
author_date: 2014-06-10 11:36:22+10:00
msg: Do some random sanity checking for stratum message parsing
repository: repo_url: https://github.com/ckolivas/cgminer
repo_name: ckolivas/cgminer
description: ASIC and FPGA miner in c for bitcoin
repo_language: C
owner: ckolivas
file_changes: filename: util.c
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,5 @@
 /*
- * Copyright 2011-2013 Con Kolivas
+ * Copyright 2011-2014 Con Kolivas
  * Copyright 2010 Jeff Garzik
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -720,6 +720,34 @@ bool hex2bin(unsigned char *p, const char *hexstr, size_t len)
 	return ret;
 }
 
+static bool _valid_hex(char *s, const char *file, const char *func, const int line)
+{
+	bool ret = false;
+	int i, len;
+
+	if (unlikely(!s)) {
+		applog(LOG_ERR, "Null string passed to valid_hex from"IN_FMT_FFL, file, func, line);
+		return ret;
+	}
+	len = strlen(s);
+	if (unlikely(!len)) {
+		applog(LOG_ERR, "Zero length string passed to valid_hex from"IN_FMT_FFL, file, func, line);
+		return ret;
+	}
+	for (i = 0; i < len; i++) {
+		unsigned char idx = s[i];
+
+		if (unlikely(hex2bin_tbl[idx] < 0)) {
+			applog(LOG_ERR, "Invalid char %x passed to valid_hex from"IN_FMT_FFL, idx, file, func, line);
+			return ret;
+		}
+	}
+	ret = true;
+	return ret;
+}
+
+#define valid_hex(s) _valid_hex(s, __FILE__, __func__, __LINE__)
+
 static const int b58tobin_tbl[] = {
 	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
@@ -1375,7 +1403,7 @@ bool extract_sockaddr(char *url, char **sockaddr_url, char **sockaddr_port)
 	if (url_len < 1)
 		return false;
 
-	sprintf(url_address, "%.*s", url_len, url_begin);
+	snprintf(url_address, 254, "%.*s", url_len, url_begin);
 
 	if (port_len) {
 		char *slash;
@@ -1685,14 +1713,13 @@ static bool parse_notify(struct pool *pool, json_t *val)
 	ntime = __json_array_string(val, 7);
 	clean = json_is_true(json_array_get(val, 8));
 
-	if (!job_id || !prev_hash || !coinbase1 || !coinbase2 || !bbversion || !nbit || !ntime) {
+	if (!valid_hex(job_id) || !valid_hex(prev_hash) || !valid_hex(coinbase1) ||
+	    !valid_hex(coinbase2) || !valid_hex(bbversion) || !valid_hex(nbit) ||
+	    !valid_hex(ntime)) {
 		/* Annoying but we must not leak memory */
-		if (job_id)
-			free(job_id);
-		if (coinbase1)
-			free(coinbase1);
-		if (coinbase2)
-			free(coinbase2);
+		free(job_id);
+		free(coinbase1);
+		free(coinbase2);
 		goto out;
 	}
 
@@ -1880,7 +1907,7 @@ static bool parse_reconnect(struct pool *pool, json_t *val)
 	if (!port)
 		port = pool->stratum_port;
 
-	sprintf(address, "%s:%s", url, port);
+	snprintf(address, 254, "%s:%s", url, port);
 
 	if (!extract_sockaddr(address, &sockaddr_url, &stratum_port))
 		return false;
@@ -2551,14 +2578,14 @@ resend:
 	if (!sessionid)
 		applog(LOG_DEBUG, "Failed to get sessionid in initiate_stratum");
 	nonce1 = json_array_string(res_val, 1);
-	if (!nonce1) {
-		applog(LOG_INFO, "Failed to get nonce1 in initiate_stratum");
+	if (!valid_hex(nonce1)) {
+		applog(LOG_INFO, "Failed to get valid nonce1 in initiate_stratum");
 		free(sessionid);
 		goto out;
 	}
 	n2size = json_integer_value(json_array_get(res_val, 2));
-	if (!n2size) {
-		applog(LOG_INFO, "Failed to get n2size in initiate_stratum");
+	if (n2size < 2 || n2size > 16) {
+		applog(LOG_INFO, "Failed to get valid n2size in initiate_stratum");
 		free(sessionid);
 		free(nonce1);
 		goto out;

programming_language: C
repo_url: https://github.com/sgminer-dev/sgminer
commit_details: repo_url: https://github.com/sgminer-dev/sgminer
author: Noel Maersk
author_date: 2014-06-05 21:45:01+03:00
msg: stratum: parse_reconnect(): treat pool-sent URL as untrusted.

Thanks to Mick Ayzenberg <mick@dejavusecurity.com> for reminding
that this existed and highlighting the offender.

Also to Luke-jr for actually fixing this in bfgminer. :D
repository: repo_url: https://github.com/sgminer-dev/sgminer
repo_name: sgminer-dev/sgminer
description: Scrypt GPU miner
repo_language: C
owner: sgminer-dev
file_changes: filename: util.c
change_type: ModificationType.MODIFY
diff: @@ -1718,15 +1718,14 @@ static void __suspend_stratum(struct pool *pool)
 
 static bool parse_reconnect(struct pool *pool, json_t *val)
 {
-	char *sockaddr_url, *stratum_port, *tmp;
-	char *url, *port, address[256];
-
 	if (opt_disable_client_reconnect) {
-		applog(LOG_WARNING, "Stratum client.reconnect forbidden, aborting.");
+		applog(LOG_WARNING, "Stratum client.reconnect received but is disabled, not reconnecting.");
 		return false;
 	}
 
-	memset(address, 0, 255);
+	char *url, *port, address[256];
+	char *sockaddr_url, *stratum_port, *tmp; /* Tempvars. */
+
 	url = (char *)json_string_value(json_array_get(val, 0));
 	if (!url)
 		url = pool->sockaddr_url;
@@ -1735,8 +1734,7 @@ static bool parse_reconnect(struct pool *pool, json_t *val)
 	if (!port)
 		port = pool->stratum_port;
 
-	sprintf(address, "%s:%s", url, port);
-
+	snprintf(address, sizeof(address), "%s:%s", url, port);
 	if (!extract_sockaddr(address, &sockaddr_url, &stratum_port))
 		return false;
 

programming_language: C
repo_url: https://github.com/sgminer-dev/sgminer
commit_details: repo_url: https://github.com/sgminer-dev/sgminer
author: Luke Dashjr
author_date: 2014-06-05 17:05:12+00:00
msg: Stratum: extract_sockaddr: Truncate overlong addresses rather than stack overflow

Thanks to Mick Ayzenberg <mick@dejavusecurity.com> for finding this!
repository: repo_url: https://github.com/sgminer-dev/sgminer
repo_name: sgminer-dev/sgminer
description: Scrypt GPU miner
repo_language: C
owner: sgminer-dev
file_changes: filename: util.c
change_type: ModificationType.MODIFY
diff: @@ -1219,6 +1219,13 @@ bool extract_sockaddr(char *url, char **sockaddr_url, char **sockaddr_port)
 
 	if (url_len < 1)
 		return false;
+	
+	if (url_len >= sizeof(url_address))
+	{
+		applog(LOG_WARNING, "%s: Truncating overflowed address '%.*s'",
+		       __func__, url_len, url_begin);
+		url_len = sizeof(url_address) - 1;
+	}
 
 	sprintf(url_address, "%.*s", url_len, url_begin);
 

programming_language: C