wordpress wp-includes/http.php漏洞修复
在wp-includes/http.php中有一处检验规则漏洞,文件中的wp_http_validate_url函数对输入IP验证不当,导致黑客可构造类似于012.10.10.10这样的畸形IP绕过验证,进行SSRF。
源代码的第534行中,调用了preg_match方法对传入的IP地址进行校验,这里可以构造一个畸形的IP地址,以跳过if去执行else,从而使用了gethostbyname。
核心问题出在此正则表达式的校验比较弱,正确的IPv4地址是xxx.xxx.xxx.xxx,但是首位不能是0。
解决办法-:增强正则表达式。
^(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))$
function wp_http_validate_url( $url ) { 509 $original_url = $url; 510 $url = wp_kses_bad_protocol( $url, array( 'http', 'https' ) ); 511 if ( ! $url || strtolower( $url ) !== strtolower( $original_url ) ) 512 return false; 513 514 $parsed_url = @parse_url( $url ); 515 if ( ! $parsed_url || empty( $parsed_url['host'] ) ) 516 return false; 517 518 if ( isset( $parsed_url['user'] ) || isset( $parsed_url['pass'] ) ) 519 return false; 520 521 if ( false !== strpbrk( $parsed_url['host'], ':#?[]' ) ) 522 return false; 523 524 $parsed_home = @parse_url( get_option( 'home' ) ); 525 526 if ( isset( $parsed_home['host'] ) ) { 527 $same_host = ( strtolower( $parsed_home['host'] ) === strtolower( $parsed_url['host'] ) || 'localhost' === strtolower( $parsed_url['host'] ) ); 528 } else { 529 $same_host = false; 530 } 531 532 if ( ! $same_host ) { 533 $host = trim( $parsed_url['host'], '.' ); 534 if ( preg_match( '#^(([1-9]?\d|1\d\d|25[0-5]|2[0-4]\d)\.){3}([1-9]?\d|1\d\d|25[0-5]|2[0-4]\d)$#', $host ) ) { 535 $ip = $host; 536 } else { 537 $ip = gethostbyname( $host ); 538 if ( $ip === $host ) // Error condition for gethostbyname() 539 $ip = false; 540 } 541 if ( $ip ) { 542 $parts = array_map( 'intval', explode( '.', $ip ) ); 543 if ( 127 === $parts[0] || 10 === $parts[0] || 0 === $parts[0] 544 || ( 172 === $parts[0] && 16 <= $parts[1] && 31 >= $parts[1] ) 545 || ( 192 === $parts[0] && 168 === $parts[1] ) 546 ) { 547 // If host appears local, reject unless specifically allowed. 548 /** 549 * Check if HTTP request is external or not. 550 * 551 * Allows to change and allow external requests for the HTTP request. 552 * 553 * @since 3.6.0 554 * 555 * @param bool false Whether HTTP request is external or not. 556 * @param string $host IP of the requested host. 557 * @param string $url URL of the requested host. 558 */ 559 if ( ! apply_filters( 'http_request_host_is_external', false, $host, $url ) ) 560 return false; 561 } 562 } 563 } 564 565 if ( empty( $parsed_url['port'] ) ) 566 return $url; 567 568 $port = $parsed_url['port']; 569 if ( 80 === $port || 443 === $port || 8080 === $port ) 570 return $url; 571 572 if ( $parsed_home && $same_host && isset( $parsed_home['port'] ) && $parsed_home['port'] === $port ) 573 return $url; 574 575 return false; 576 }