[Ultramonkey-l7-develop 572] l7vsd の X-Forwarded-For 付加機能の不具合報告

Back to archive index

TATEISHI Katsuyuki tatei****@oss*****
2010年 2月 10日 (水) 11:29:39 JST


立石です。

UltraMonkey-L7 2.1.3-0 の l7vsd における X-Forwarded-For の付
加機能に不具合を見つけましたので報告します。
X-Forwarded-For 付加機能(各モジュールの-F/--forwarded-forオ
プション)は使用しないほうがいいです。

●詳細
UltraMonkey-L7 のバックエンドのリアルサーバへの HTTP リクエ
ストに X-Forwarded-For ヘッダを付加するという機能があります。

これは l7directord.cf の virtual セクション内、module の設定
においてmodule名に渡すオプションとして --forwarded-for または
-Fオプションをつけると最終的に l7vsd がロードするモジュールに
オプションとして渡され、有効になります。

この機能は一見有効に動作しますが、ユーザがPOSTでデータを送る
際、以下の条件を満たすと不正な動作をします。

 1. ユーザデータ内に HTTP ヘッダらしき文字列(リクエストライ
    ン)の並びがある(※)
 2. POSTリクエストが複数のパケットに分割されている
 3. 1のデータにおけるリクエストラインの開始部が2の分割部の切
    れ目と一致する

上記の条件を満たすと、1のリクエストラインのCRLFの後ろ(直感的
に言えば次の行)に
X-Forwarded-For: <クライアントのIPアドレス><CR><LF>
という文字列を挿入してユーザデータを破壊してしまいます。

 ※ 正確には以下の perl 正規表現にマッチする場合にリクエスト
    ラインであるとみなされます。
============================================================
(GET|HEAD|POST|PUT|PROP(FIND|PATCH)|OPTIONIS|CONNECT|COPY|TRACE|DELETE|LOCK|UNLOCK|MOVE|MKCOL) [^ \r\n]* HTTP\/1.[01]\r\n
============================================================

再現コードは添付のとおりです。このコードは
============================================================
!!MARKER!! START POST DATA !!MARKER!!
USER_DATA_USER_DATA_USER_DATA_USER_DATA_USER_DA
TA_USER_DATA_USER_DATA_USER_DATA_USER_DATA_USER
GET / HTTP/1.1
USER_DATA_USER_DATA_USER_DATA_USER_DATA_USER_DA
TA_USER_DATA_USER_DATA_USER_DATA_USER_DATA_USER
!!MARKER!! END OF POST DATA !!MARKER!!
============================================================
というデータをPOSTするプログラムですが、192.168.0.1というIP
アドレスからのクライアントから実行し、UM-L7を通過すると
============================================================
!!MARKER!! START POST DATA !!MARKER!!
USER_DATA_USER_DATA_USER_DATA_USER_DATA_USER_DA
TA_USER_DATA_USER_DATA_USER_DATA_USER_DATA_USER
GET / HTTP/1.1
X-Forwarded-For: 192.168.0.1
USER_DATA_USER_DATA_USER_DATA_USER_DATA_USER_DA
TA_USER_DATA_USER_DATA_USER_DATA_USER_DATA_USER
!!MARKER!! END OF POST DATA !!MARKER!!
============================================================
というデータになってしまいます。

影響範囲は、UltraMonkey-L7 2.1.3-0 で、かつ X-Forwarded-For
機能を有効にしている場合です。

今のところ回避策は X-Forward-For 付加機能を使わないというもの
だけです。

なお、[Ultramonkey-l7-users 240] で報告されているSSLProxyの不
具合と同様の不具合です。

以上です。

--
TATEISHI Katsuyuki <tatei****@oss*****>
-------------- next part --------------
/*
 * client:c
 * allmost code are from example in the getaddrinfo(3)
 *
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>     
#include <stdio.h>     
#include <stdlib.h>    
#include <unistd.h>    
#include <string.h>    

/* user data for crafted packet */
static char postdata0[] =
"!!MARKER!! START POST DATA !!MARKER!!\r\n"
"USER_DATA_USER_DATA_USER_DATA_USER_DATA_USER_DA\r\n"
"TA_USER_DATA_USER_DATA_USER_DATA_USER_DATA_USER\r\n";

static char postdata1[] =
"GET / HTTP/1.1\r\n";

static char postdata2[] =
"USER_DATA_USER_DATA_USER_DATA_USER_DATA_USER_DA\r\n"
"TA_USER_DATA_USER_DATA_USER_DATA_USER_DATA_USER\r\n"
"!!MARKER!! END OF POST DATA !!MARKER!!\r\n";

/* real post request */
static char method[] =
"POST / HTTP/1.1\r\n"
"User-Agent: crafted-packet client.c\r\n"
"Accept: */*\r\n"
"Host: 192.168.122.3\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: %d\r\n" /* content length of postdata* */
"\r\n";			/* hearder-content delimiter */

int
main(int argc, char *argv[])
{                           
    struct addrinfo hints;  
    struct addrinfo *result, *rp;
    int sfd, s, j;               
    size_t len;                  
    ssize_t nread;               
    char buf[BUFSIZ];          

    if (argc < 3) {
        fprintf(stderr, "Usage: %s host port\n", argv[0]);
        exit(EXIT_FAILURE);                                      
    }                                                            

    /* Obtain address(es) matching host/port */

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
    hints.ai_socktype = SOCK_STREAM; /* Datagram socket */   
    hints.ai_flags = 0;                                     
    hints.ai_protocol = 0;          /* Any protocol */      

    s = getaddrinfo(argv[1], argv[2], &hints, &result);
    if (s != 0) {                                      
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
        exit(EXIT_FAILURE);                                   
    }                                                         

    /* getaddrinfo() returns a list of address structures.
       Try each address until we successfully connect(2).  */
    if (s != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
        exit(EXIT_FAILURE);
    }

    /* getaddrinfo() returns a list of address structures.
       Try each address until we successfully connect(2).
       If socket(2) (or connect(2)) fails, we (close the socket
       and) try the next address. */                           

    for (rp = result; rp != NULL; rp = rp->ai_next) {
        sfd = socket(rp->ai_family, rp->ai_socktype, 
                     rp->ai_protocol);               
        if (sfd == -1)                               
            continue;                                

        if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
            break;                  /* Success */           

        close(sfd);
    }              

    if (rp == NULL) {               /* No address succeeded */
        fprintf(stderr, "Could not connect\n");
        exit(EXIT_FAILURE);
    }

    freeaddrinfo(result);           /* No longer needed */

    sprintf(buf, method,
    	strlen(postdata0) + strlen(postdata1) + strlen(postdata2));
    len = strlen(buf);
    if (write(sfd, buf, len) != len) {
	fprintf(stderr, "partial/failed write\n");
	exit(EXIT_FAILURE);
    }

    len = strlen(postdata0);
    if (write(sfd, postdata0, len) != len) {
	fprintf(stderr, "partial/failed write\n");
	exit(EXIT_FAILURE);
    }

    sleep(1);

    len = strlen(postdata1);
    if (write(sfd, postdata1, len) != len) {
	fprintf(stderr, "partial/failed write\n");
	exit(EXIT_FAILURE);
    }

    sleep(1);

    len = strlen(postdata2);
    if (write(sfd, postdata2, len) != len) {
	fprintf(stderr, "partial/failed write\n");
	exit(EXIT_FAILURE);
    }

    nread = read(sfd, buf, BUFSIZ);
    if (nread == -1) {
        perror("read");
        exit(EXIT_FAILURE);
    }

    printf("Received %ld bytes: %s\n", (long) nread, buf);

    exit(EXIT_SUCCESS);
}



Ultramonkey-l7-develop メーリングリストの案内
Back to archive index