httpoxy 漏洞预警及修复方案

影响范围

PHP、Go、Python等开启CGI(Client)模式的脚本语言

Language环境依赖HTTP Client
PHPphp-fpmmod_phpGuzzle 4+Artax
Pythonwsgiref.handlers.CGIHandlertwisted.web.twcgi.CGIScriptrequests
Gonet/http/cginet/http

漏洞原理

在CGI(RFC 3875)的模式的时候,server 会把请求中的 Header, 加上 HTTP_ 前缀, 注册为环境变量,且重名变量会被覆盖污染,若该变量被脚本调用,即可利用,该漏洞在15年前在LWP中已被发现并修复http://www.nntp.perl.org/group/perl.libwww/2001/03/msg2249.html。

如控制 HTTPHOST 进行 URL 跳转,或者直接控制 HTTP_PROXY 让流量到代理服务器中转,将 HTTP 控制进入环境变量进行shellshock攻击等,所以,所有 HTTP_ 开头的环境变量在CGI模式下都是不可信不安全的。

利用过程如图所示:

可通过

ngrep -q -i -d any -W byline 'proxy' 'dst port 80'

捕获扫描

<?php
$http_proxy = getenv("HTTP_PROXY");
if ($http_proxy) { $context = array( 'http' => array( 'proxy' => $http_proxy, 'request_fulluri' => true, ), ); $s_context = stream_context_create($context); } else { $s_context = NULL; } $ret = file_get_contents("http://www.chaitin.cn/", false, $s_context);
PoC
curl "http://target" -H "Proxy: test env"

利用条件

  • 后端信任接收的 Header
  • 污染控制的变量被脚本调用
  • 开启CGI(Client)模式
  • 服务器能对外通信

修复方案

最为有效的方式就是在脚本调用变量之前及时阻断或者限制内部调用时的可信变量

Nginx

在配置中加入

fastcgi_param HTTP_PROXY "";

Apache

根据官方建议patchhttps://www.apache.org/security/asf-httpoxy-response.txt

IIS

运行 appcmd set config /section:requestfiltering /+requestlimits.headerLimits.[header='proxy',sizelimit='0'] 来阻止恶意代理被调用

如果要清理header,可以使用如下规则

<system.webServer>
    <rewrite>
        <rules> <rule name="Erase HTTP_PROXY" patternSyntax="Wildcard"> <match url="*.*" /> <serverVariables> <set name="HTTP_PROXY" value="" /> </serverVariables> <action type="None" /> </rule> </rules> </rewrite> </system.webServer>

参考来源

https://httpoxy.org/

https://www.symfony.fi/entry/httpoxy-vulnerability-hits-php-installations-using-fastcgi-and-php-fpm-and-hhvm

附其他语言测试用例

bash
#!/bin/bash

export http_proxy=$HTTP_PROXY
`curl http://www.chaitin.cn`
python
#!/usr/bin/python

import requests
import os
import sys
from wsgiref.handlers import CGIHandler if sys.version_info < (3,): def b(x): return x else: import codecs def b(x): return codecs.latin_1_encode(x)[0] def application(environ, start_response): status = '200 OK' r = requests.get("http://www.chaitin.cn/") output = """ Made internal subrequest to http://www.chaitin.cn/ and got: os.environ[HTTP_PROXY]: %(proxy)s os.getenv('HTTP_PROXY'): %(getenv-proxy)s wsgi Proxy header: %(wsgi-env-proxy)s status code: %(status)d text: %(text)s """ % { 'proxy': os.environ['HTTP_PROXY'] if 'HTTP_PROXY' in os.environ else 'none', 'getenv-proxy': os.getenv('HTTP_PROXY', 'none'), 'wsgi-env-proxy': environ['HTTP_PROXY'] if 'HTTP_PROXY' in environ else 'none', 'status': r.status_code, 'text': r.text } response_headers = [('Content-type', 'text/plain'), ('Content-Length', str(len(b(output))))] start_response(status, response_headers) return [b(output)] if __name__ == '__main__': CGIHandler().run(application)
Go
package main

import (
    "fmt"
    "io"
    "log"
    "net/http"
    "net/http/cgi" ) func main() { if err := cgi.Serve(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { header := w.Header() header.Set("Content-Type", "text/plain; charset=utf-8") response, err := http.Get("http://www.chaitin.cn/") if err != nil { log.Fatal(err) } else { defer response.Body.Close() fmt.Fprint(w, "Response body from internal subrequest:") _, err := io.Copy(w, response.Body) fmt.Fprintln(w, "") if err != nil { log.Fatal(err) } } fmt.Fprintln(w, "Method:", r.Method) fmt.Fprintln(w, "URL:", r.URL.String()) query := r.URL.Query() for k := range query { fmt.Fprintln(w, "Query", k+":", query.Get(k)) } r.ParseForm() form := r.Form for k := range form { fmt.Fprintln(w, "Form", k+":", form.Get(k)) } post := r.PostForm for k := range post { fmt.Fprintln(w, "PostForm", k+":", post.Get(k)) } fmt.Fprintln(w, "RemoteAddr:", r.RemoteAddr) if referer := r.Referer(); len(referer) > 0 { fmt.Fprintln(w, "Referer:", referer) } if ua := r.UserAgent(); len(ua) > 0 { fmt.Fprintln(w, "UserAgent:", ua) } for _, cookie := range r.Cookies() { fmt.Fprintln(w, "Cookie", cookie.Name+":", cookie.Value, cookie.Path, cookie.Domain, cookie.RawExpires) } })); err != nil { fmt.Println(err) } }


http://www.tuicool.com/articles/Brmm6zm
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值