fix pingback's security issue

ref: https://joychou.org/web/typecho-ssrf-analysis-and-exploit.html
This commit is contained in:
joyqi 2017-10-13 01:01:11 +08:00
parent 0e7b399ba8
commit eeedef972a
4 changed files with 77 additions and 3 deletions

View File

@ -22,7 +22,7 @@ define('__TYPECHO_MB_SUPPORTED__', function_exists('mb_get_info') && function_ex
class Typecho_Common
{
/** 程序版本 */
const VERSION = '1.1/17.8.17';
const VERSION = '1.1/17.10.13';
/**
* 允许的属性
@ -1105,6 +1105,67 @@ EOF;
return array($type, $header, $body);
}
/**
* 检查是否是一个安全的主机名
*
* @param $host
* @return bool
*/
public static function checkSafeHost($host)
{
if ('localhost' == $host) {
return false;
}
$address = gethostbyname($host);
$inet = inet_pton($address);
if ($inet === false) {
// 有可能是ipv6的地址
$records = dns_get_record($host, DNS_AAAA);
if (empty($records)) {
return false;
}
$address = $records[0]['ipv6'];
$inet = inet_pton($address);
}
if (strpos($address, '.')) {
// ipv4
// 非公网地址
$privateNetworks = array(
'10.0.0.0|10.255.255.255',
'172.16.0.0|172.31.255.255',
'192.168.0.0|192.168.255.255',
'169.254.0.0|169.254.255.255',
'127.0.0.0|127.255.255.255'
);
$long = ip2long($address);
foreach ($privateNetworks as $network) {
list ($from, $to) = explode('|', $network);
if ($long >= ip2long($from) && $long <= ip2long($to)) {
return false;
}
}
} else {
// ipv6
// https://en.wikipedia.org/wiki/Private_network
$from = inet_pton('fd00::');
$to = inet_pton('fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff');
if ($inet >= $from && $inet <= $to) {
return false;
}
}
return true;
}
/**
* 获取图片
*

View File

@ -294,8 +294,8 @@ abstract class Typecho_Http_Client_Adapter
*
* @access public
* @param string $url 请求地址
* @param string $rfc 请求协议
* @return string
* @throws Typecho_Http_Client_Exception
*/
public function send($url)
{
@ -307,6 +307,10 @@ abstract class Typecho_Http_Client_Adapter
throw new Typecho_Http_Client_Exception('Unknown Host', 500);
}
if (!in_array($params['scheme'], array('http', 'https'))) {
throw new Typecho_Http_Client_Exception('Unknown Scheme', 500);
}
if (!empty($params['path'])) {
$this->path = $params['path'];
}

View File

@ -140,7 +140,6 @@ class Widget_Service extends Widget_Abstract_Options implements Widget_Interface
->setHeader('User-Agent', $this->options->generator)
->setTimeout(3)
->setData($input)
->setIp('127.0.0.1')
->send(Typecho_Common::url('/action/service', $this->options->index));
} catch (Typecho_Http_Client_Exception $e) {

View File

@ -2057,6 +2057,16 @@ class Widget_XmlRpc extends Widget_Abstract_Contents implements Widget_Interface
$pathInfo = Typecho_Common::url(substr($target, strlen($this->options->index)), '/');
$post = Typecho_Router::match($pathInfo);
/** 检查源地址是否合法 */
$params = parse_url($source);
if (false === $params || !in_array($params['scheme'], array('http', 'https'))) {
return new IXR_Error(16, _t('源地址服务器错误'));
}
if (!Typecho_Common::checkSafeHost($params['host'])) {
return new IXR_Error(16, _t('源地址服务器错误'));
}
/** 这样可以得到cid或者slug*/
if (!($post instanceof Widget_Archive) || !$post->have() || !$post->is('single')) {
return new IXR_Error(33, _t('这个目标地址不存在'));