Giới thiệu

Cách đây không lâu, CVE-2021-29447 được công khai thông qua báo cáo của @sonarsource trên hackerone. CVE-2021-29447 được gán cho lỗi XXE xảy ra thông qua thư viện ID3 của wordpress. Thư viện không an toàn khi chỉ bật tính năng ngăn chặn tải các thực thể XML entities với phiên bản php 8.0 trở xuống.Đối với phiên bản trên 8. thì vẫn cho phép tải các thực thể xml.

Chính về thế mà mức độ nghiêm trọng của bản báo cáo này chỉ nằm ở mức medium(4~6.9) vì nó phụ thuộc nhiều vào phiên bản phpwordpress.

Cùng xem qua một số điều về mã lỗi này:

Môi trường

  • >= php8.0
  • < wordpress 5.7.0

Có thể rebuild từ git này https://github.com/motikan2010/CVE-2021-29447

Mã gây lỗi chính:

wp-includes/ID3/getid3.lib.php

	public static function XML2array($XMLstring) {
		if (function_exists('simplexml_load_string') && function_exists('libxml_disable_entity_loader')) {
			if (PHP_VERSION_ID < 80000) {
				// http://websec.io/2012/08/27/Preventing-XEE-in-PHP.html
				// https://core.trac.wordpress.org/changeset/29378
				// This function has been deprecated in PHP 8.0 because in libxml 2.9.0, external entity loading is
				// disabled by default, so this function is no longer needed to protect against XXE attacks.
				$loader = libxml_disable_entity_loader(true);
			}
			$XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', LIBXML_NOENT);
			$return = self::SimpleXMLelement2array($XMLobject);
			if (PHP_VERSION_ID < 80000 && isset($loader)) {
				libxml_disable_entity_loader($loader);
			}
			return $return;
		}
		return false;
	}

Đối với php >= 8.0 thì simplexml_load_string thực hiện tải dữ liệu xml mà không thực hiện libxml_disable_entity_loader như các phiên bản php < 8.0.

Về cơ bản, thì lỗi này cũng không khó để khai thác, chỉ thực hiện tải các tải byte object của audio và dùng các hàm chính ví dụ như parseRIFFData, parseRIFF,… để phân tích các siêu dữ liệu của tệp.

wp-includes/ID3/module.audio-video.riff.php

	public function ParseRIFF($startoffset, $maxoffset) {
		$info = &$this->getid3->info;

		$RIFFchunk = false;
		$FoundAllChunksWeNeed = false;

		try {
			$this->fseek($startoffset);
			$maxoffset = min($maxoffset, $info['avdataend']);
			while ($this->ftell() < $maxoffset) {
				$chunknamesize = $this->fread(8);
				//$chunkname =                          substr($chunknamesize, 0, 4);
				$chunkname = str_replace("\x00", '_', substr($chunknamesize, 0, 4));  // note: chunk names of 4 null bytes do appear to be legal (has been observed inside INFO and PRMI chunks, for example), but makes traversing array keys more difficult
				$chunksize =  $this->EitherEndian2Int(substr($chunknamesize, 4, 4));
				//if (strlen(trim($chunkname, "\x00")) < 4) {
				if (strlen($chunkname) < 4) {
					$this->error('Expecting chunk name at offset '.($this->ftell() - 8).' but found nothing. Aborting RIFF parsing.');
					break;
				}
				if (($chunksize == 0) && ($chunkname != 'JUNK')) {
					$this->warning('Chunk ('.$chunkname.') size at offset '.($this->ftell() - 4).' is zero. Aborting RIFF parsing.');
					break;
				}
				if (($chunksize % 2) != 0) {
					// all structures are packed on word boundaries
					$chunksize++;
				}
				...

Stack trace triage lỗi:

getid3.lib.php:723, getid3_lib::XML2array()
module.audio-video.riff.php:428, getid3_riff->Analyze()
getid3.php:640, getID3->analyze()
media.php:3661, wp_read_audio_metadata()
media.php:321, media_handle_upload()
async-upload.php:94, {main}()

Vì lỗi này không quá nhiều technical nên mình sẽ nói qua một chút cách mà mã xml hoạt động trong tệp audio . Nguyên nhân cần dẫn đến lỗi này.

Đầu tiên thì cần xem qua định dạng của tệp WAV này.

Lấy ví dụ trực tiếp là đoạn audio wav (Wave files have the extension .wav and are the most common file format on the PC platform) có chứa mã khai thác xml như sau:

image-20210610121223501

Như ảnh trên, mã xml tức marker data của iXML chunk được tiêm vào audio với chiều dài 116 byte không làm thay đổi đến cấu trúc của tệp. Nội dung iXML này có thể tùy biến chỉnh sửa.

Nói một cách nôm na dễ hiểu, thì xml như một meta data, tương tự như khi bạn dùng exiftool thêm các trường author , date-time vào hình ảnh như một trường chú thích bổ sung. Để hiểu rõ hơn về iXML thì có thể đọc thêm iXML-LineUp.pdfxml chunks này.

Ngoài ra, mã xml còn có thể tiêm ở trong nhiều định dạng khác:ví dụ như XSL,SVG ( 2 loại này khá phổ biến đã được gán nhiều mã lỗi), ngoài ra còn có RDF , các loại tệp siêu văn bản khác.

Vì thế, đối với các ứng dụng, việc phân tích xử lý các field , marker,.. cần phải cẩn thận đối với các siêu dữ liệu độc hại như XML hoặc JS,…

Bản vá

libxml_disable_entity_loader được bật ngay cả khi php >= 8.0.

Tài liệu:

[1] https://wordpress.org/news/2021/04/wordpress-5-7-1-security-and-maintenance-release/

[2] https://core.trac.wordpress.org/changeset/29378L

[3] https://blog.wpscan.com/2021/04/15/wordpress-571-security-vulnerability-release.html

[4] https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-rv47-pc52-qrhh

[5] https://blog.sonarsource.com/wordpress-xxe-security-vulnerability/