CVE-2017-7566 (PHP, published 2017-04-06T16:59Z)
Description: value: MyBB before 1.8.11 allows remote attackers to bypass an SSRF protection mechanism.
Severity: MEDIUM (CVSS v3 base score: 7.7)
Weakness: CWE-918 \u2014 Server-Side Request Forgery (SSRF)
The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
Repository: https://github.com/mybb/mybb
Files changed:
admin/modules/config/smilies.php
inc/class_parser.php
inc/functions.php
Commit message: MyBB 1.8.11


Vulnerable code:
					$form_container->output_cell("<img src=\"../".$path.$file."\" alt=\"\" /><br /><small>{$file}</small>", array("class" => "align_center", "width" => 1));
					$form_container->output_cell($form->generate_text_box("name[{$file}]", $name, array('id' => 'name', 'style' => 'width: 98%')));
			$image = $smilie['image'];
			$image = "../".$smilie['image'];
			$image = $smilie['image'];
			$image = "../".$smilie['image'];
			$callback_mycode['email_simple']['regex'] = "#\[email\](.*?)\[/email\]#i";
			$callback_mycode['email_complex']['regex'] = "#\[email=(.*?)\](.*?)\[/email\]#i";
		(!empty($url_components['port']) && !in_array($url_components['port'], array(80, 8080, 443))) ||
		$addresses = gethostbynamel($url_components['host']);
		if($addresses)
			foreach($config['disallowed_remote_addresses'] as $disallowed_address)
				$ip_range = fetch_ip_range($disallowed_address);
				foreach($addresses as $address)
					$packed_address = my_inet_pton($address);

					if(is_array($ip_range))
					{
						if(strcmp($ip_range[0], $packed_address) <= 0 && strcmp($ip_range[1], $packed_address) >= 0)
						{
							return false;
						}
					}
					elseif($address == $disallowed_address)
					{
						return false;
					}
		$can_followlocation = @ini_get('open_basedir') === '' && !$mybb->safemode;

		$request_header = $max_redirects != 0 && !$can_followlocation;
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_HEADER, $request_header);
		curl_setopt($ch, CURLOPT_TIMEOUT, 10);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
		if($max_redirects != 0 && $can_followlocation)
			curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
			curl_setopt($ch, CURLOPT_MAXREDIRS, $max_redirects);
			curl_setopt($ch, CURLOPT_POST, 1);
			curl_setopt($ch, CURLOPT_POSTFIELDS, $post_body);
		if($request_header)
		if(!isset($url_components['port']))
		{
			$url_components['port'] = 80;
		}
		$fp = @fsockopen($scheme.$url_components['host'], $url_components['port'], $error_no, $error, 10);
		if($max_redirects != 0 && (strstr($status_line, ' 301 ') || strstr($status_line, ' 302 ')))
	else if(empty($post_data))
	{
		return @implode("", @file($url));
	}

Fixed code:
					$file = htmlspecialchars_uni($file);

					$form_container->output_cell("<img src=\"../".htmlspecialchars_uni($path).$file."\" alt=\"\" /><br /><small>{$file}</small>", array("class" => "align_center", "width" => 1));
					$form_container->output_cell($form->generate_text_box("name[{$file}]", htmlspecialchars_uni($name), array('id' => 'name', 'style' => 'width: 98%')));
			$image = htmlspecialchars_uni($smilie['image']);
			$image = "../".htmlspecialchars_uni($smilie['image']);
			$image = htmlspecialchars_uni($smilie['image']);
			$image = "../".htmlspecialchars_uni($smilie['image']);
			$callback_mycode['email_simple']['regex'] = "#\[email\]((?:[a-zA-Z0-9-_\+\.]+?)@[a-zA-Z0-9-]+\.[a-zA-Z0-9\.-]+(?:\?.*?)?)\[/email\]#i";
			$callback_mycode['email_complex']['regex'] = "#\[email=((?:[a-zA-Z0-9-_\+\.]+?)@[a-zA-Z0-9-]+\.[a-zA-Z0-9\.-]+(?:\?.*?)?)\](.*?)\[/email\]#i";
	if(!my_validate_url($url, true))
	{
		return false;
	}

	if(!isset($url_components['scheme']))
	{
		$url_components['scheme'] = 'https';
	}
	if(!isset($url_components['port']))
	{
		$url_components['port'] = $url_components['scheme'] == 'https' ? 443 : 80;
	}

		(!in_array($url_components['port'], array(80, 8080, 443))) ||
	$addresses = get_ip_by_hostname($url_components['host']);
	$destination_address = $addresses[0];

		foreach($config['disallowed_remote_addresses'] as $disallowed_address)
			$ip_range = fetch_ip_range($disallowed_address);

			$packed_address = my_inet_pton($destination_address);

			if(is_array($ip_range))
				if(strcmp($ip_range[0], $packed_address) <= 0 && strcmp($ip_range[1], $packed_address) >= 0)
					return false;
			elseif($destination_address == $disallowed_address)
			{
				return false;
			}
		$fetch_header = $max_redirects > 0;
		$curlopt = array(
			CURLOPT_URL => $url,
			CURLOPT_HEADER => $fetch_header,
			CURLOPT_TIMEOUT => 10,
			CURLOPT_RETURNTRANSFER => 1,
			CURLOPT_FOLLOWLOCATION => 0,
		);

		if($ca_bundle_path = get_ca_bundle_path())
			$curlopt[CURLOPT_SSL_VERIFYPEER] = 1;
			$curlopt[CURLOPT_CAINFO] = $ca_bundle_path;
		}
		else
		{
			$curlopt[CURLOPT_SSL_VERIFYPEER] = 0;
		}

		$curl_version_info = curl_version();
		$curl_version = $curl_version_info['version'];

		if(version_compare($curl_version, '7.0.7', '>=') && version_compare($curl_version, '7.49', '>='))
		{
			$curlopt[CURLOPT_CONNECT_TO] = array(
				$url_components['host'].':'.$url_components['port'].':'.$destination_address
			);
		}
		elseif(version_compare(PHP_VERSION, '5.5', '>='))
		{
			$curlopt[CURLOPT_RESOLVE] = array(
				$url_components['host'].':'.$url_components['port'].':'.$destination_address
			);
			$curlopt[CURLOPT_POST] = 1;
			$curlopt[CURLOPT_POSTFIELDS] = $post_body;
		curl_setopt_array($ch, $curlopt);

		if($fetch_header)
		if(function_exists('stream_context_create'))
		{
			if($url_components['scheme'] == 'https' && $ca_bundle_path = get_ca_bundle_path())
			{
				$context = stream_context_create([
					'ssl' => [
						'verify_peer' => true,
						'verify_peer_name' => true,
						'peer_name' => $url_components['host'],
						'cafile' => $ca_bundle_path,
					],
				]);
			}
			else
			{
				$context = stream_context_create([
					'ssl' => [
						'verify_peer' => false,
						'verify_peer_name' => false,
					],
				]);
			}

			$fp = @stream_socket_client($scheme.$destination_address.':'.(int)$url_components['port'], $error_no, $error, 10, STREAM_CLIENT_CONNECT, $context);
		}
		else
		{
			$fp = @fsockopen($scheme.$url_components['host'], (int)$url_components['port'], $error_no, $error, 10);
		}

		if($max_redirects > 0 && (strstr($status_line, ' 301 ') || strstr($status_line, ' 302 ')))
/**
 * Resolves a hostname into a set of IP addresses.
 *
 * @param string $hostname The hostname to be resolved
 * @return array|bool The resulting IP addresses. False on failure
 */
function get_ip_by_hostname($hostname)
{
	$addresses = gethostbynamel($hostname);

	if(!$addresses)
	{
		$result_set = dns_get_record($hostname, DNS_A | DNS_AAAA);

		if($result_set)
		{
			$addresses = array_column($result_set, 'ip');
		}
		else
		{
			return false;
		}
	}

	return $addresses;
}

/**
 * Returns the location of the CA bundle defined in the PHP configuration.
 *
 * @return string|bool The location of the CA bundle, false if not set
 */
function get_ca_bundle_path()
{
	if($path = ini_get('openssl.cafile'))
	{
		return $path;
	}
	if($path = ini_get('curl.cainfo'))
	{
		return $path;
	}

	return false;
}