1

HTTPOXY漏洞说明

 2 years ago
source link: https://www.laruence.com/2016/07/19/3101.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

HTTPOXY漏洞说明

好久没写文章了, 博客都长草了, 早上起来本来想去上班, 一看这么大雨, 这要上路了不得堵死啊.
再加上有同学对我昨天转发的微博HTTPOXY漏洞表示不理解, 问会不会影响普通应用, 于是就写篇文章介绍下, 等早高峰过了吧;).....
不过要注意的是, 这里我只是介绍PHP这个角度, 关于Go和Python等其他角度的,因为我也不是"很"懂,你们还是看原文吧 🙂

漏洞原文在这里, https://httpoxy.org/, 没看懂的一定都是英语没过6级的 🙂
这里有一个核心的背景是, 长久一来我们习惯了使用一个名为"http_proxy"的环境变量来设置我们的请求代理, 比如在命令行我们经常这么用:

  1. http_proxy=127.0.0.1:9999 wget http://www.laruence.com/

通过设置一个http_proxy的环境变量, 让wget使用代理请求http://www.laruence.com/
有据可考的是, 这样的设定最初来自1994年的CERN libwww 2.15, 我猜测大概是当时很多工具是基于这个类库做的, 于是就慢慢成了一个既定标准吧. 只不过这些应用都要求http_proxy是全部小写的, 还不足以造成今天这个漏洞.
但估计是因为环境变量习惯都是大写的原因吧, 后来有的类库开始支持大写的HTTP_PROXY, 比如yum: https://www.centos.org/docs/5/html/yum/sn-yum-proxy-server.html
再后来很多的类库, 各种语言的, 都开始支持这种配置, 有的支持大写的, 有的支持小写的, 还有的都支持.

包括我们自己, 也很有可能在日常的工作中写出如下的代码(我就曾经在写爬虫的时候写过):

  1. <?php
  2. $http_proxy = getenv("HTTP_PROXY");
  3. if ($http_proxy) {
  4.     $context = array(
  5.         'http' => array(
  6.             'proxy' => $http_proxy,
  7.             'request_fulluri' => true,
  8.     $s_context = stream_context_create($context);
  9. } else {
  10.     $s_context = NULL;
  11. $ret = file_get_contents("http://www.laruence.com/", false, $s_context);

那么问题来了, 在CGI(RFC 3875)的模式的时候, 会把请求中的Header, 加上HTTP_ 前缀, 注册为环境变量, 所以如果你在Header中发送一个Proxy:xxxxxx, 那么PHP就会把他注册为HTTP_PROXY环境变量, 于是getenv("HTTP_PROXY")就变成可被控制的了. 那么如果你的所有类似的请求, 都会被代理到攻击者想要的地址,之后攻击者就可以伪造,监听,篡改你的请求了...
比如:

  1.  curl -H "Proxy:127.0.0.1:8000" http://host.com/httpoxy.php

所以, 这个漏洞要影响你, 有几个核心前提是:

  • 你的服务会对外请求资源
  • 你的服务使用了HTTP_PROXY(大写的)环境变量来代理你的请求(可能是你自己写,或是使用一些有缺陷的类库)
  • 你的服务跑在PHP的CGI模式下(cgi, php-fpm)

如果你没有满足上面的条件, 那么恭喜你,你不受此次漏洞影响 :).

后记: 在微博上有同学提醒, 我可能把这个问题的影响潜意识的让大家觉得危害不大, 但实际上, 延伸一下: 所有HTTP_开头的环境变量在CGI下都是不可信的, 千万不要用于敏感操作, 另外一点就是, 我深刻的体会过, 做安全的同学想象力非常丰富, 虽然看似很小的一个点, 但到了安全的同学手里, 配合他们丰富的想象力, 强大的社工能力, 也是能做出巨大攻击效果的....

那知道了原理修复起来也很简单了, 以Nginx为例, 在配置中加入:

  1.    fastcgi_param HTTP_PROXY "";

所以建议, 即使你不受此次漏洞影响, 也应该加入这个配置.
而如果你是一个类库的作者,或者你因为什么原因没有办法修改服务配置, 那么你就需要在代码中加入对sapi的判断, 除非是cli模式, 否则永远不要相信http_proxy环境变量,

  1. <?php
  2. if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) {
  3.    //只有CLI模式下, HTTP_PROXY环境变量才是可控的

就好比Guzzle的这个修复:Addressing HTTP_PROXY security vulnerability
补充: 从PHP5.5.38开始, getenv增加了第二个参数, local_only = false, 如果这个参数为true, 则只会从系统本地的环境变量表中获取, 从而修复这个问题, 并且默认的PHP将拦截HTTP_PROXY: fix
thanks


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK