前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python http.server open redirect vulnerability

python http.server open redirect vulnerability

作者头像
phith0n
发布2020-10-15 11:39:03
5900
发布2020-10-15 11:39:03
举报
文章被收录于专栏:离别歌 - 信息安全与代码审计

Github账号被封了以后,Vulhub也无法继续更新了,余下很多时间,默默看了点代码,偶然还能遇上一两个漏洞,甚是有趣。

这个漏洞出现在python核心库http中,发送给官方团队后被告知撞洞了,且官方也认为需要更多人看看怎么修复这个问题,所以我们来分析一下。

0x01 http.server库简单分析

众所周知Python有一个一键启动Web服务器的方法:

代码语言:javascript
复制
python3 -m http.server

在任意目录执行如上命令,即可启动一个web文件服务器。其实这个方法就用到了http.server模块。这个模块包含几个比较重要的类:

  1. HTTPServer这个类继承于socketserver.TCPServer,说明其实HTTP服务器本质是一个TCP服务器
  2. BaseHTTPRequestHandler,这是一个处理TCP协议内容的Handler,目的就是将从TCP流中获取的数据按照HTTP协议进行解析,并按照HTTP协议返回相应数据包。但这个类解析数据包后没有进行任何操作,不能直接使用。如果我们要写自己的Web应用,应该继承这个类,并实现其中的do_XXX等方法。
  3. SimpleHTTPRequestHandler,这个类继承于BaseHTTPRequestHandler,从父类中拿到解析好的数据包,并将用户请求的path返回给用户,等于实现了一个静态文件服务器。
  4. CGIHTTPRequestHandler,这个类继承于SimpleHTTPRequestHandler,在静态文件服务器的基础上,增加了执行CGI脚本的功能。

简单来说就是如下:

代码语言:javascript
复制
+-----------+          +------------------------+    
| TCPServer |          | BaseHTTPRequestHandler |
+-----------+          +------------------------+ 
     ^                            |
     |                            v
     |                +--------------------------+
     +----------------| SimpleHTTPRequestHandler |
     |                +--------------------------+
     |                            |
     |                            v
     |                 +-----------------------+
     +-----------------| CGIHTTPRequestHandler |
                       +-----------------------+

我们看看SimpleHTTPRequestHandler的源代码:

代码语言:javascript
复制
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
    server_version = "SimpleHTTP/" + __version__

    def do_GET(self):
        """Serve a GET request."""
        f = self.send_head()
        if f:
            try:
                self.copyfile(f, self.wfile)
            finally:
                f.close()

    # ...

    def send_head(self):
        path = self.translate_path(self.path)
        f = None
        if os.path.isdir(path):
            parts = urllib.parse.urlsplit(self.path)
            if not parts.path.endswith('/'):
                # redirect browser - doing basically what apache does
                self.send_response(HTTPStatus.MOVED_PERMANENTLY)
                new_parts = (parts[0], parts[1], parts[2] + '/',
                             parts[3], parts[4])
                new_url = urllib.parse.urlunsplit(new_parts)
                self.send_header("Location", new_url)
                self.end_headers()
                return None
            for index in "index.html", "index.htm":
                index = os.path.join(path, index)
                if os.path.exists(index):
                    path = index
                    break
            else:
                return self.list_directory(path)
        # ...

前面HTTP解析的部分不再分析,如果我们请求的是GET方法,将会被分配到do_GET函数里,在do_GET()中调用了send_head()方法。

send_head()中调用了self.translate_path(self.path)将request path进行一个标准化操作,目的是获取用户真正请求的文件。如果这个path是一个已存在的目录,则进入if语句。

如果用户请求的path不是以/结尾,则进入第二个if语句,这个语句中执行了HTTP跳转的操作,这就是我们当前漏洞的关键点了。

0x02 任意URL跳转漏洞

如果我们请求的是一个已存在的目录,但PATH没有以/结尾,则将PATH增加/并用301跳转。

这就涉及到了一个有趣的问题:在chrome、firefox等主流浏览器中,如果url以//domain开头,浏览器将会默认认为这个url是当前数据包的协议。比如,我们访问http://example.com,跳转到//baidu.com/,则浏览器会默认认为跳转到http://baidu.com,而不是跳转到.//baidu.com/目录。

所以,如果我们发送的请求的是GET //baidu.com HTTP/1.0\r\n\r\n,那么将会被重定向到//baidu.com/,也就产生了一个任意URL跳转漏洞。

在此前,由于目录baidu.com不存在,我们还需要绕过if os.path.isdir(path)这条if语句。绕过方法也很简单,因为baidu.com不存在,我们跳转到上一层目录即可:

代码语言:javascript
复制
GET //baidu.com/%2f.. HTTP/1.0\r\n\r\n

如何测试这个漏洞呢?其实也很简单,直接用python3 -m http.server启动一个HTTP服务器即可。访问http://127.0.0.1:8000//example.com/%2f%2e%2e即可发现跳转到了http://example.com/%2f../

0x03 web.py任意URL跳转漏洞

那么,虽然说python核心库存在这个漏洞,不过通常情况下不会有人直接在生产环境用python -m http.server

Python框架web.py在处理静态文件的代码中继承并使用了SimpleHTTPRequestHandler类,所以也会受到影响。

我们可以简单测试一下,我们用web.py官网的示例代码创建一个web应用:

代码语言:javascript
复制
import web

urls = (
    '/(.*)', 'hello'
)
app = web.application(urls, globals())


class hello:
    def GET(self, name):
        if not name:
            name = 'World'
        return 'Hello, ' + name + '!'


if __name__ == "__main__":
    app.run()

然后模拟真实环境,创建一个static目录,和一些子目录:

代码语言:javascript
复制
static
├── css
│   └── app.css
└── js
    └── app.js

运行后,直接访问http://127.0.0.1:8080////static%2fcss%2f@www.example.com/..%2f即可发现已成功跳转。

web.py的具体分析我就不多说了,由于请求必须有/static/前缀,所以利用方法有些不同,不过核心原理也无差别。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x01 http.server库简单分析
  • 0x02 任意URL跳转漏洞
  • 0x03 web.py任意URL跳转漏洞
相关产品与服务
云服务器
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档