cellog Thu Jun 4 20:00:01 2009 UTC Modified files: /php-src/ext/phar tar.c /php-src/ext/phar/tests/tar bignames_overflow.phpt /php-src/ext/phar/tests/tar/files make.dangerous.tar.php.inc Log: MFPECL: fix security vulnerability in phar's handling of long tar filenames
http://cvs.php.net/viewvc.cgi/php-src/ext/phar/tar.c?r1=1.68&r2=1.69&diff_format=u Index: php-src/ext/phar/tar.c diff -u php-src/ext/phar/tar.c:1.68 php-src/ext/phar/tar.c:1.69 --- php-src/ext/phar/tar.c:1.68 Wed May 13 20:26:18 2009 +++ php-src/ext/phar/tar.c Thu Jun 4 20:00:01 2009 @@ -330,16 +330,19 @@ if (!old && hdr->prefix[0] != 0) { char name[256]; + int i, j; - strcpy(name, hdr->prefix); - /* remove potential buffer overflow */ - if (hdr->name[99]) { - strncat(name, hdr->name, 100); - } else { - strcat(name, hdr->name); + for (i = 0; i < 155; i++) { + name[i] = hdr->prefix[i]; + if (name[i] == '\0') { + break; + } + } + for (j = 0; j < 100; j++) { + name[i+j] = hdr->name[j]; } - entry.filename_len = strlen(hdr->prefix) + 100; + entry.filename_len = i+j; if (name[entry.filename_len - 1] == '/') { /* some tar programs store directories with trailing slash */ @@ -347,8 +350,16 @@ } entry.filename = pestrndup(name, entry.filename_len, myphar->is_persistent); } else { - entry.filename = pestrdup(hdr->name, myphar->is_persistent); - entry.filename_len = strlen(entry.filename); + int i; + + /* calculate strlen, which can be no longer than 100 */ + for (i = 0; i < 100; i++) { + if (hdr->name[i] == '\0') { + break; + } + } + entry.filename_len = i; + entry.filename = pestrndup(hdr->name, i, myphar->is_persistent); if (entry.filename[entry.filename_len - 1] == '/') { /* some tar programs store directories with trailing slash */ http://cvs.php.net/viewvc.cgi/php-src/ext/phar/tests/tar/bignames_overflow.phpt?r1=1.1&r2=1.2&diff_format=u Index: php-src/ext/phar/tests/tar/bignames_overflow.phpt diff -u /dev/null php-src/ext/phar/tests/tar/bignames_overflow.phpt:1.2 --- /dev/null Thu Jun 4 20:00:01 2009 +++ php-src/ext/phar/tests/tar/bignames_overflow.phpt Thu Jun 4 20:00:01 2009 @@ -0,0 +1,40 @@ +--TEST-- +Phar: tar with huge filenames, buffer overflow +--SKIPIF-- +<?php if (!extension_loaded("phar")) die("skip"); ?> +--INI-- +phar.require_hash=0 +--FILE-- +<?php +$fname = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.tar'; +$fname2 = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.2.tar'; +$pname = 'phar://' . $fname; + +include dirname(__FILE__) . '/files/make.dangerous.tar.php.inc'; + +$tar = new danger_tarmaker($fname, 'none'); +$tar->init(); +$tar->addFile(str_repeat('a', 101), 'hi'); +$tar->addFile(str_repeat('a', 255), 'hi2'); +$tar->close(); + +$p1 = new PharData($fname); +foreach ($p1 as $file) { + echo $file->getFileName(), "\n"; +} +echo $p1[str_repeat('a', 101)]->getContent() . "\n"; +echo $p1[str_repeat('a', 255)]->getContent() . "\n"; + +?> +===DONE=== +--CLEAN-- +<?php +unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.tar'); +unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.2.tar'); +?> +--EXPECT-- +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +hi +hi2 +===DONE=== http://cvs.php.net/viewvc.cgi/php-src/ext/phar/tests/tar/files/make.dangerous.tar.php.inc?r1=1.1&r2=1.2&diff_format=u Index: php-src/ext/phar/tests/tar/files/make.dangerous.tar.php.inc diff -u /dev/null php-src/ext/phar/tests/tar/files/make.dangerous.tar.php.inc:1.2 --- /dev/null Thu Jun 4 20:00:01 2009 +++ php-src/ext/phar/tests/tar/files/make.dangerous.tar.php.inc Thu Jun 4 20:00:01 2009 @@ -0,0 +1,170 @@ +<?php +// stolen from PEAR2_Pyrus_Developer_Creator_Tar by Greg Beaver, the original author, for use in unit tests +// this tarmaker makes a malicious tar with a header designed to overflow the buffer +class danger_tarmaker +{ + /** + * Path to archive file + * + * @var string + */ + protected $archive; + /** + * Temporary stream used for creating the archive + * + * @var stream + */ + protected $tmp; + protected $path; + protected $compress; + function __construct($path, $compress = 'zlib') + { + $this->compress = $compress; + if ($compress === 'bz2' && !function_exists('bzopen')) { + throw new PEAR2_Pyrus_Developer_Creator_Exception( + 'bzip2 extension not available'); + } + if ($compress === 'zlib' && !function_exists('gzopen')) { + throw new PEAR2_Pyrus_Developer_Creator_Exception( + 'zlib extension not available'); + } + $this->path = $path; + } + + /** + * save a file inside this package + * + * This code is modified from Vincent Lascaux's File_Archive + * package, which is licensed under the LGPL license. + * @param string relative path within the package + * @param string|resource file contents or open file handle + */ + function addFile($path, $fileOrStream, $stat = null) + { + clearstatcache(); + if ($stat === null) { + if (is_resource($fileOrStream)) { + $stat = fstat($fileOrStream); + } else { + $stat = array( + 'mode' => 0x8000 + 0644, + 'uid' => 0, + 'gid' => 0, + 'size' => strlen($fileOrStream), + 'mtime' => time(), + ); + } + } + + $link = null; + if ($stat['mode'] & 0x4000) { + $type = 5; // Directory + } else if ($stat['mode'] & 0x8000) { + $type = 0; // Regular + } else if ($stat['mode'] & 0xA000) { + $type = 1; // Link + $link = @readlink($current); + } else { + $type = 9; // Unknown + } + + $filePrefix = ''; + if (strlen($path) > 255) { + throw new Exception( + "$path is too long, must be 255 characters or less" + ); + } else if (strlen($path) > 100) { + $filePrefix = substr($path, 0, strlen($path)-100); + $path = substr($path, -100); + } + + $block = pack('a100a8a8a8a12A12', + $path, + '12345678', // have a mode that allows the name to overflow + sprintf('%6s ',decoct($stat['uid'])), + sprintf('%6s ',decoct($stat['gid'])), + sprintf('%11s ',decoct($stat['size'])), + sprintf('%11s ',decoct($stat['mtime'])) + ); + + $blockend = pack('a1a100a6a2a32a32a8a8a155a12', + $type, + $link, + 'ustar', + '00', + 'Pyrus', + 'Pyrus', + '', + '', + $filePrefix, + '123456789abc'); // malicious block + + $checkheader = array_merge(str_split($block), str_split($blockend)); + if (!function_exists('_pear2tarchecksum')) { + function _pear2tarchecksum($a, $b) {return $a + ord($b);} + } + $checksum = 256; // 8 * ord(' '); + $checksum += array_reduce($checkheader, '_pear2tarchecksum'); + + $checksum = pack('a8', sprintf('%6s ', decoct($checksum))); + + fwrite($this->tmp, (binary)$block . $checksum . $blockend, 512); + if (is_resource($fileOrStream)) { + stream_copy_to_stream($fileOrStream, $this->tmp); + if ($stat['size'] % 512) { + fwrite($this->tmp, (binary)str_repeat("\0", 512 - $stat['size'] % 512)); + } + } else { + fwrite($this->tmp, (binary)$fileOrStream); + if (strlen($fileOrStream) % 512) { + fwrite($this->tmp, (binary)str_repeat("\0", 512 - strlen($fileOrStream) % 512)); + } + } + } + + /** + * Initialize the package creator + */ + function init() + { + switch ($this->compress) { + case 'zlib' : + $this->tmp = gzopen($this->path, 'wb'); + break; + case 'bz2' : + $this->tmp = bzopen($this->path, 'w'); + break; + case 'none' : + $this->tmp = fopen($this->path, 'wb'); + break; + default : + throw new Exception( + 'unknown compression type ' . $this->compress); + } + } + + /** + * Create an internal directory, creating parent directories as needed + * + * @param string $dir + */ + function mkdir($dir) + { + $this->addFile($dir, "", array( + 'mode' => 0x4000 + 0644, + 'uid' => 0, + 'gid' => 0, + 'size' => 0, + 'mtime' => time(), + )); + } + + /** + * Finish saving the package + */ + function close() + { + fwrite($this->tmp, pack('a1024', '')); + fclose($this->tmp); + } +} \ No newline at end of file
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php