yTNEF/Evolution TNEF Attachment decoder plugin directory traversal & buffer overflow vulnerabilities

--=-1rIS48eoy7iDmqyvYLuH
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

------------------------------------------------------------------------
yTNEF/Evolution TNEF Attachment decoder plugin directory traversal &
buffer overflow vulnerabilities
------------------------------------------------------------------------
Yorick Koster, June 2009

------------------------------------------------------------------------
Abstract
------------------------------------------------------------------------

yTNEF & the Evolution TNEF Attachment decoder plugin are affected by
several directory traversal and buffer overflow vulnerabilities. The=20
directory traversal vulnerabilities allow attackers to overwrite or=20
create local files with the privileges of the target user. Exploiting=20
the buffer overflow vulnerabilities allows for arbitrary code execution=20
with the privileges of the target user.

------------------------------------------------------------------------
See also
------------------------------------------------------------------------

- #2009-013 yTNEF/Evolution TNEF attachment decoder input sanitization=20
errors [2]

------------------------------------------------------------------------
Tested version
------------------------------------------------------------------------

These vulnerabilities were discovered using the latest (stable) versions
of Evolution (currently 2.62.2) and yTNEF (currently 2.6). The=20
vulnerabilities were verified on the following Linux distributions:

 - GNOME version of Mandriva Linux 2009 Spring running Evolution=20
2.26.1.1 (Evolution plugin installed by default)
 - Ubuntu 9.04 running Evolution 2.26.1 (with=20
evolution-plugins-experimental package installed)

------------------------------------------------------------------------
Affected functions
------------------------------------------------------------------------

The following functions are affected by these issues:

Evolution plugin:
	* processTnef()
	* saveVCard()
	* saveVCalendar()
	* saveVTask()

yTNEF:
	* ProcessTNEF()
	* SaveVCard()
	* SaveVCalendar()
	* SaveVTask()

------------------------------------------------------------------------
Fix
------------------------------------------------------------------------

There is currently no fix available.

------------------------------------------------------------------------
Introduction
------------------------------------------------------------------------

Transport Neutral Encapsulation Format (TNEF) is a proprietary e-mail=20
attachment format used by Microsoft Outlook and Microsoft Exchange=20
Server. A plugin [3] for Evolution exists that provides basic support=20
for TNEF encoded e-mails. This plugin uses the ytnef library [4]=20
(libytnef) for processing TNEF messages. It borrows code from the ytnef=20
program, which is a program to work with procmail to decode TNEF streams
(winmail.dat attachments). Both applications share (almost) code and=20
are, because of this, both affected by the issues described in this=20
document.

------------------------------------------------------------------------
Evolution TNEF Attachment decoder plugin
------------------------------------------------------------------------

The plugin is started on e-mail attachments that have a MIME type of=20
either application/vnd.ms-tnef or application/ms-tnef. It creates a=20
temporary directory under ~/.evolution/cache/tmp using the format=20
tnef-attachment-XXXXXX. The TNEF attachment is saved as=20
.evo-attachment.tnef.

void
org_gnome_format_tnef(void *ep, EMFormatHookTarget *t)
{
	[...]
=09
	tmpdir =3D e_mkdtemp("tnef-attachment-XXXXXX");
	if (tmpdir =3D=3D NULL)
		return;
=09
	filepath =3D tmpdir;
=09
	name =3D g_build_filename(tmpdir, ".evo-attachment.tnef",=20
NULL);
=09
	out =3D camel_stream_fs_new_with_name(name, O_RDWR|O_CREAT, 0666);


The saved file is parsed by TNEFParseFile(), the result is stored in a=20
struct of the type TNEFStruct. This struct is passed to the function=20
processTnef(), which tries to extract all relevant data and attachments=20
from the TNEF stream. Each relevant part of the TNEF stream is stored=20
within the previously created temporary directory that are made=20
available to the end user as separate e-mail attachments.

	/* Extracting the winmail.dat */
	TNEFInitialize(tnef);
	tnef->Debug =3D verbose;
	if (TNEFParseFile(name, tnef) =3D=3D -1) {
		printf("ERROR processing file
");
	}
	processTnef(tnef);
=09
	TNEFFree(tnef);
	/* Extraction done */

------------------------------------------------------------------------
yTNEF
------------------------------------------------------------------------

yTNEF processes TNEF files in a similar manner. It receives a file name=20
from the command line, calls TNEFParseFile() that creates a struct=20
TNEFStruct after which ProcessTNEF() is called. If ProcessTNEF() finds=20
attachments it can process, these attachments will be saved locally. The
ProcessTNEF() function is almost the same as the processTnef() function
of the Evolution plugin.

int main(int argc, char ** argv) {
	[...]
=09
	for(i=3D1; i<argc; i++) {
		[...]
=09
		TNEFInitialize(&TNEF);
		TNEF.Debug =3D verbose;
		if (TNEFParseFile(argv[ i], &TNEF) =3D=3D -1) {
			printf("ERROR processing file
");
			continue;
		}
		ProcessTNEF(TNEF);
		TNEFFree(&TNEF);
	}
}

------------------------------------------------------------------------
Directory traversal
------------------------------------------------------------------------

If a TNEF file is processed, both yTNEF and the Evolution plugin will=20
save certain types of TNEF structures. Special processing functions are=20
available for Contacts, Tasks & Appointments. These functions are=20
called if the Message Class is set to a certain value.

void processTnef(TNEFStruct *tnef) {
	[...]
=09
	/* First see if this requires special processing. */
	/* ie: its a Contact Card, Task, or Meeting request (vCal/vCard)=20
*/
	if (tnef->messageClass[0] !=3D 0)  {
		if (strcmp(tnef->messageClass, "IPM.Contact") =3D=3D 0) {
			saveVCard(tnef);
		}
		if (strcmp(tnef->messageClass, "IPM.Task") =3D=3D 0) {
			saveVTask(tnef);
		}
		if (strcmp(tnef->messageClass, "IPM.Appointment") =3D=3D 0) {
			saveVCalendar(tnef);
			foundCal =3D 1;
		}
	}
=09
	if ((filename =3D MAPIFindUserProp(&(tnef->MapiProperties),
				PROP_TAG(PT_STRING8,0x24))) !=3D MAPI_UNDEFINED) {
		if (strcmp(filename->data, "IPM.Appointment") =3D=3D 0) {
			/* If its "indicated" twice, we dont want to=20
save 2 calendar entries. */
			if (foundCal =3D=3D 0) {
				saveVCalendar(tnef);
			}
		}
	}

There is also code that treats TNEF structures with the Message Class=20
set to IPM.Microsoft Mail.Note. In the Evolution plugin, this code is=20
never called as the global variable saveRTF is set to zero. In case of=20
yTNEF this global variable is controlled by the command line.

if (strcmp(TNEF.messageClass, "IPM.Microsoft Mail.Note") =3D=3D 0)
 {
	if ((saveRTF =3D=3D 1) && (TNEF.subject.size > 0)) {
		// Description
		if ((filename=3DMAPIFindProperty(&(TNEF.MapiProperties),
				PROP_TAG(PT_BINARY, PR_RTF_COMPRESSED)))
			!=3D MAPI_UNDEFINED) {
[...]

After the structures mentioned before have been processed, all other=20
attachments are also saved locally. The file names used to save the=20
attachments are obtained from the TNEF data. In case of normal=20
attachments, the code first looks if the TNEF data contains MAPI=20
properties and if so, it will look for specific properties. If these=20
exists, a file name is extracted from these properties. If the=20
properties do not exist, the attachments title is used. This title
is also set through a TNEF structure. If this title is also not=20
available, a default file name will be used instead.

if ((RealAttachment =3D=3D 1) || (saveintermediate =3D=3D 1)) {
/* Ok, its not an embedded stream, so now we */
/* process it. */
if ((filename =3D MAPIFindProperty(&(p->MAPI),=20
		PROP_TAG(30,0x3707)))=20
	=3D=3D MAPI_UNDEFINED) {
	if ((filename =3D MAPIFindProperty(&(p->MAPI),=20
				PROP_TAG(30,0x3001)))=20
			=3D=3D MAPI_UNDEFINED) {
		filename =3D &(p->Title);
	}
}
if (filename->size =3D=3D 1) {
	filename =3D (variableLength*)malloc(sizeof(variableLength));
	filename->size =3D 20;
	filename->data =3D (char*)malloc(20);
	sprintf(filename->data, "file_%03i.dat", count);
}
if (filepath =3D=3D NULL) {
	sprintf(ifilename, "%s", filename->data);
} else {
	sprintf(ifilename, "%s/%s", filepath, filename->data);
}
for(i=3D0; i<strlen(ifilename); i++)=20
	if (ifilename[ i] =3D=3D  )=20
		ifilename[ i] =3D _;
=09
	if ((fptr =3D fopen(ifilename, "wb"))=3D=3DNULL) {
		printf("ERROR: Error writing file to disk!");
	} else {
		if (object =3D=3D 1) {
		fwrite(filedata->data + 16,=20
			sizeof(BYTE),=20
			filedata->size - 16,=20
			fptr);
		} else {
		fwrite(filedata->data,=20
			sizeof(BYTE),=20
			filedata->size,=20
			fptr);
		}
		fclose(fptr);
	}
}

Before a new file is created, all spaces within the file name are=20
replaced with the underscore character. No additional sanitation is=20
performed on the file name. Because of this, it is possible to traverse=20
outside of the temporary directory and create or overwrite any file with
the privileges of the target user. This allows an attacker to execute=20
arbitrary code for example by overwriting ~/.bashrc.

------------------------------------------------------------------------
Buffer overflow
------------------------------------------------------------------------

Beside the directory traversal, it is also possible to trigger a buffer=20
overflow by supplying an overly long file name. This is possible,=20
because the file name is copied in a fixed size buffer (256 bytes). In=20
the Evolution plugin, this triggers a buffer overflow on the heap. In=20
case of yTNEF the file name is copied in a buffer on the stack, thus=20
allowing for a stack-based buffer overflow to occur.

Evolution plugin:

void processTnef(TNEFStruct *tnef) {
	[...]
	ifilename =3D (char *) g_malloc(sizeof(char) * 256);
	[...]
		if (filepath =3D=3D NULL) {
			sprintf(ifilename, "%s", filename->data);
		} else {
			sprintf(ifilename, "%s/%s", filepath, filename->data);
		}

yTNEF:

void ProcessTNEF(TNEFStruct TNEF) {
	[...]	   =20
	char ifilename[256];
	[...]
		if (filepath =3D=3D NULL) {
			sprintf(ifilename, "%s", filename->data);
		} else {
			sprintf(ifilename, "%s/%s", filepath, filename->data);
		}

------------------------------------------------------------------------
References
------------------------------------------------------------------------

[1] http://www.akitasecurity.nl/advisory.php?id=3DAK20090601
[2] http://www.ocert.org/advisories/ocert-2009-013.html
[3] http://www.go-evolution.org/Tnef
[4] http://sourceforge.net/projects/ytnef/

------------------------------------------------------------------------
--=20
------------------------------------------------------------------------
Akita Software Security (Kvk 37144957)
http://www.akitasecurity.nl/
------------------------------------------------------------------------
Key fingerprint =3D 5FC0 F50C 8B3A 4A61 7A1F  2BFF 5482 D26E D890 5A65
http://keyserver.pgp.com/vkd/DownloadKey.event?keyid=3D0x5482D26ED8905A65

--=-1rIS48eoy7iDmqyvYLuH
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: This is a digitally signed message part

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)

iEYEABECAAYFAkqjk68ACgkQVILSbtiQWmWwswCeOGUoGqEmCA6L/Dl7CKnf+qCk
8SwAmQHxJF7Lf2VVEqvau1CeChXxDscm
=YQp9
-----END PGP SIGNATURE-----

--=-1rIS48eoy7iDmqyvYLuH--