代码之家  ›  专栏  ›  技术社区  ›  DieDummheitInPerson

git http错误'fatal:协议错误:行长度错误字符:'

  •  0
  • DieDummheitInPerson  · 技术社区  · 6 年前

    我目前正在尝试在C中创建一个简单的GitHTTP服务器,而不使用已经存在的Web服务器。目前我唯一要做的就是创建一个服务器套接字,并使用来自客户机请求的环境变量执行git-http后端cgi脚本。拉请求已经起作用,但只适用于空存储库。当我尝试用内容克隆存储库时,客户端出现了以下错误:

    fatal: protocol error: bad line length character: 
    

    以下是客户端和服务器之间的通信日志:

    C: GET /test.git/info/refs?service=git-upload-pack HTTP/1.1
    C: Host: localhost:9000
    C: User-Agent: git/2.20.1
    C: Accept: */*
    C: Accept-Encoding: deflate, gzip
    C: Accept-Language: en-US, *;q=0.9
    C: Pragma: no-cache
    C:
    
    S: HTTP/1.1 200 OK
    S: Expires: Fri, 01 Jan 1980 00:00:00 GMT
    S: Pragma: no-cache
    S: Cache-Control: no-cache, max-age=0, must-revalidate
    S: Content-Type: application/x-git-upload-pack-advertisement
    S: 
    S: 001e# service=git-upload-pack
    S: 000000fadd3fba560f4afe000e70464ac3a7a9991ad13eb0
    S: HEAD003fdd3fba560f4afe000e70464ac3a7a9991ad13eb0 refs/heads/master
    S: 0000
    

    只是一点小提示:http/1.1200 OK是手动添加的,其余部分来自CGI脚本。你也可以找到我的密码 here . 首先,我有一个理论,即服务器响应的内容错误地放置了新行(例如,头部应该更高一行),但事实证明事实并非如此。所以我的问题是:我能做些什么吗?在C语言中,将这个响应编辑成一个好的格式是相当复杂的,特别是在响应时间较长的情况下。

    1 回复  |  直到 6 年前
        1
  •  0
  •   mmlr    6 年前

    首先,请确保您了解将由外部参与者控制的数据传递给类似函数的安全含义。 popen . 通过向请求行添加shell特殊字符,shell注入可以利用您现在拥有的实现。即使只使用带有精心设计的存储库名称的Git,您当前的代码也允许在服务器上执行任意命令。试试这个例子:

    git clone "$(echo -e 'http://localhost:9000/;echo\tunexpected\t>helloworld;cat\t/etc/passwd;exit;.git')"
    

    这将在服务器的工作目录中创建一个包含字符串“unexpected”的文件,并将返回 /etc/passwd 到客户机(使用wireshark查看)。

    为了避免这种情况,您需要确保正确地转义输入数据,这样就不会发生shell注入。理想情况下,您可以使用以下机制 execve 它允许您将环境变量和可能的命令行参数作为缓冲区提交,而不是生成可能不安全的字符串,然后由shell进行分析。当然,这样的解决方案需要更多的参与,因为它意味着重组您的程序。

    然后使用不安全的方式连接字符串。 strcat 无法知道目标缓冲区有多大,因此,如果输入足够多,它将愉快地覆盖超出缓冲区的堆栈。这是一个经典的堆栈溢出,然后可以利用它。使用更安全的替代品,如 strlcat 或者最好是一个合适的字符串库。

    现在回到你原来的问题:

    你得到的输出 git http-backend 是原始二进制输出,包括空字节。在示例响应中,在 HEAD 分离支持的功能列表。您可以通过手动运行命令并将其传输到类似 xxd 或者将它转储到一个文件中,然后用十六进制编辑器查看它。

    在从管道读取然后将输出连接到响应缓冲区的循环中,截断数据是因为 字符串连接函数 对以空字节结尾的C字符串进行操作。其余的 行和空字节本身永远不会响应,这会破坏git协议。

    你可以使用 fread 将管道中的原始数据直接读取到缓冲区中。然后,您需要使用一个不以空字节停止的函数将该缓冲区复制到响应缓冲区,比如 memcpy . 为了使这项工作正常,您还需要跟踪已经读取的字节以及响应缓冲区中还有多少空间。

    或者,由于您实际上不在最终响应缓冲区上进行任何处理,所以您也可以直接将从管道中读取的数据发送到客户机套接字。这样您就不必担心响应缓冲区的大小以及跟踪偏移量和剩余空间。这是一个适用于初始请求的版本 git 做:

            char response[10000] = "HTTP/1.1 200 OK\r\n";
            send(client_socket, response, strlen(response), 0);
            while (!feof(g)) {
                size_t bytes_read = fread(response, 1, sizeof(response), g);
                if (bytes_read == 0)
                    break;
    
                send(client_socket, response, bytes_read, 0);
            }
    

    随后的POST请求失败。