Logo Search packages:      
Sourcecode: medusa version File versions  Download package

web-form.c

/***************************************************************************
 *   web-form.c                                                            *
 *   Copyright (C) 2007 by Luciano Bello                                   *
 *   luciano@debian.org.ar                                                 *
 *                                                                         *
 *   Implementation of a web form brute force module for                   *
 *   medusa. Module concept by the one-and-only Foofus.                    *
 *   Protocol stuff based on the original medusa http code by              *
 *   fizzgig (fizzgig@foofus.net).                                         *
 *                                                                         *
 *                                                                         *
 *   CHANGE LOG                                                            *
 *   08/10/2007 - Created by Luciano Bello (luciano@debian.org)            *
 *   08/24/2007 - Minor modification by JoMo-Kun                           *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License version 2,       *
 *   as published by the Free Software Foundation                          *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   http://www.gnu.org/licenses/gpl.txt                                   *
 *                                                                         *
 *   This program is released under the GPL with the additional exemption  *
 *   that compiling, linking, and/or using OpenSSL is allowed.             *
 *                                                                         *
 ***************************************************************************/

#include "module.h"

#define MODULE_NAME    "web-form.mod"
#define MODULE_AUTHOR  "Luciano Bello <luciano@linux.org.ar>"
#define MODULE_SUMMARY_USAGE  "Brute force module for web forms"
#define MODULE_VERSION    "0.9"
#define MODULE_VERSION_SVN "$Id: web-form.c 0 2007-07-25 22:22:41Z luciano $"
#define MODULE_SUMMARY_FORMAT  "%s : version %s"
#define MODULE_SUMMARY_FORMAT_WARN  "%s : version %s (%s)"
#define OPENSSL_WARNING "No usable OPENSSL. Module disabled."

#define FREE(x) \
        if (x != NULL) { \
           free(x); \
           x = NULL; \
        }

#ifdef HAVE_LIBSSL

#include "../http-ntlm.h"

#define HTTP_PORT 80
#define HTTPS_PORT 443

#define FORM_UNKNOWN 0
#define FORM_GET 1
#define FORM_POST 2

typedef struct __MODULE_DATA {
  char *szDir;
  char *szHostHeader;
  char *szUserAgent;
  int nFormType;
  char *szDenySignal;
  char *szFormData;
  char *szFormRest;
  char *szFormUser;
  char *szFormPass;
} _MODULE_DATA;

// Tells us whether we are to continue processing or not
enum HTTP_STATE
{
  HSTATE_NEW,
  HSTATE_RUNNING,
  HSTATE_EXITING
};

// Forward declarations
int tryLogin(int hSocket, _MODULE_DATA* _psSessionData, sLogin** login, int nPort, char* szLogin, char* szPassword);
int initHttp(_MODULE_DATA* _psSessionData, sLogin* login);

// Tell medusa how many parameters this module allows
int getParamNumber()
{
  return 0;    // we don't need no stinking parameters
}

// Displays information about the module and how it must be used
void summaryUsage(char **ppszSummary)
{
  // Memory for ppszSummary will be allocated here - caller is responsible for freeing it
  int  iLength = 0;

  if (*ppszSummary == NULL)
  {
    iLength = strlen(MODULE_SUMMARY_USAGE) + strlen(MODULE_VERSION) + strlen(MODULE_SUMMARY_FORMAT) + 1;
    *ppszSummary = (char*)malloc(iLength);
    memset(*ppszSummary, 0, iLength);
    snprintf(*ppszSummary, iLength, MODULE_SUMMARY_FORMAT, MODULE_SUMMARY_USAGE, MODULE_VERSION);
  }
  else
  {
    writeError(ERR_ERROR, "%s reports an error in summaryUsage() : ppszSummary must be NULL when called", MODULE_NAME);
  }

}

/* Display module usage information */
void showUsage()
{
  writeVerbose(VB_NONE, "%s (%s) %s :: %s\n", MODULE_NAME, MODULE_VERSION, MODULE_AUTHOR, MODULE_SUMMARY_USAGE);
  writeVerbose(VB_NONE, "Available module options:");
  writeVerbose(VB_NONE, "  USER-AGENT:?       User-agent value. Default: \"I'm not Mozilla, I'm Ming Mong\".");
  writeVerbose(VB_NONE, "  FORM:?             Target form to request. Default: \"/\"");
  writeVerbose(VB_NONE, "  DENY-SIGNAL:?      Authentication failure message. Attempt flagged as successful if text is not present in");
  writeVerbose(VB_NONE, "                     server response. Default: \"Login incorrect\"");
  writeVerbose(VB_NONE, "  FORM-DATA:<METHOD>?<FIELDS>");
  writeVerbose(VB_NONE, "                     Methods and fields to send to web service. Valid methods are GET and POST. The actual form");
  writeVerbose(VB_NONE, "                     data to be submitted should also be defined here. Specifically, the fields: username and");
  writeVerbose(VB_NONE, "                     password. Default: \"post?username=&password=\"");
  writeVerbose(VB_NONE, "");
  writeVerbose(VB_NONE, "Usage example: \"-M web-form -m USER-AGENT:\"g3rg3 gerg\" -m FORM:\"webmail/index.php\" -m DENY-SIGNAL:\"deny!\"");
  writeVerbose(VB_NONE, "                 -m FORM-DATA:\"post?user=&pass=&submit=True\"");
}

// The "main" of the medusa module world - this is what gets called to actually do the work
int go(sLogin* logins, int argc, char *argv[])
{
  int i;
  char *strtok_ptr, *pOpt, *pOptTmp;
  _MODULE_DATA *psSessionData;
  psSessionData = malloc(sizeof(_MODULE_DATA));
  memset(psSessionData, 0, sizeof(_MODULE_DATA));

  if ( !( 0 <= argc <= 2) )
  {
    // Show usage information
    writeError(ERR_ERROR, "%s: Incorrect number of parameters passed to module.", MODULE_NAME);
    return FAILURE;
  }
  else
  {
    // Parameters are good - make module go now
    writeError(ERR_DEBUG, "OMG teh %s module has been called!!", MODULE_NAME);

    for (i=0; i<argc; i++) {
      pOptTmp = malloc( strlen(argv[i]) + 1);
      memset(pOptTmp, 0, strlen(argv[i]) + 1);
      strncpy(pOptTmp, argv[i], strlen(argv[i]));
      writeError(ERR_DEBUG_MODULE, "Processing complete option: %s", pOptTmp);
      pOpt = strtok_r(pOptTmp, ":", &strtok_ptr);
      writeError(ERR_DEBUG_MODULE, "Processing option: %s", pOpt);

      if (strcmp(pOpt, "FORM") == 0)
      {
        pOpt = strtok_r(NULL, "\0", &strtok_ptr);
        writeError(ERR_DEBUG_MODULE, "Processing option parameter: %s", pOpt);

        if ( pOpt )
        {
          psSessionData->szDir = malloc(strlen(pOpt) + 1);
          memset(psSessionData->szDir, 0, strlen(pOpt) + 1);
          strncpy(psSessionData->szDir, pOpt, strlen(pOpt));
        }
        else
          writeError(ERR_WARNING, "Method FORM requires value to be set.");
      }
      else if (strcmp(pOpt, "DENY-SIGNAL") == 0)
      {
        pOpt = strtok_r(NULL, "\0", &strtok_ptr);
        writeError(ERR_DEBUG_MODULE, "Processing option parameter: %s", pOpt);

        if ( pOpt )
        {
          psSessionData->szDenySignal= malloc(strlen(pOpt) + 1);
          memset(psSessionData->szDenySignal, 0, strlen(pOpt) + 1);
          strncpy(psSessionData->szDenySignal, pOpt, strlen(pOpt));
        }
        else
          writeError(ERR_WARNING, "Method DENY-SIGNAL requires value to be set.");
      }
      else if (strcmp(pOpt, "FORM-DATA") == 0)
      {
        pOpt = strtok_r(NULL, "\0", &strtok_ptr);
        writeError(ERR_DEBUG_MODULE, "Processing option parameter: %s", pOpt);

        if ( pOpt )
        {
          psSessionData->szFormData = malloc(strlen(pOpt) + 1);
          memset(psSessionData->szFormData, 0, strlen(pOpt) + 1);
          strncpy(psSessionData->szFormData, pOpt, strlen(pOpt));
        }
        else
          writeError(ERR_WARNING, "Method FORM-DATA requires value to be set.");
      }
      else if (strcmp(pOpt, "USER-AGENT") == 0)
      {
        pOpt = strtok_r(NULL, "\0", &strtok_ptr);
        writeError(ERR_DEBUG_MODULE, "Processing option parameter: %s", pOpt);

        if ( pOpt )
        {
          psSessionData->szUserAgent = malloc(strlen(pOpt) + 1);
          memset(psSessionData->szUserAgent, 0, strlen(pOpt) + 1);
          strncpy(psSessionData->szUserAgent, pOpt, strlen(pOpt));
        }
        else
          writeError(ERR_WARNING, "Method USER-AGENT requires value to be set.");
      }
      else
      {
        writeError(ERR_WARNING, "Invalid method: %s.", pOpt);
      }

      free(pOptTmp);
    }
    initHttp(psSessionData, logins);
  }

  return SUCCESS;
}

int initHttp(_MODULE_DATA *_psSessionData, sLogin* login)
{
  int hSocket = -1;
  enum HTTP_STATE nState = HSTATE_NEW;
  char* bufReceive;
  int i = 0;
  char *pPass;
  char *pTemp;
  int nPort, nBufLength = 0;
  sUser* user = login->psUser;
  sConnectParams params;

  memset(&params, 0, sizeof(sConnectParams));
  if (login->psServer->psAudit->iPortOverride > 0)
    params.nPort = login->psServer->psAudit->iPortOverride;
  else if (login->psServer->psHost->iUseSSL > 0)
    params.nPort = HTTPS_PORT;
  else
    params.nPort = HTTP_PORT; 
  initConnectionParams(login, &params);

  if (user != NULL)
  {
    writeError(ERR_DEBUG, "[%s] module started for host: %s:%d, user: '%s'", MODULE_NAME, login->psServer->pHostIP, params.nPort, user->pUser);
  }
  else
  {
    writeError(ERR_DEBUG, "null user");
  }

  pPass = getNextPass(login->psServer->psAudit, user);
  if (pPass == NULL)
  {
    writeVerbose(VB_GENERAL, "[%s] out of passwords for user '%s' at host '%s', bailing", MODULE_NAME, user->pUser, login->psServer->pHostIP);
  }

  while(NULL != pPass)
  {
    switch(nState)
    {
      case HSTATE_NEW:
        // Already have an open socket - close it
        if (hSocket > 0)
          medusaDisconnect(hSocket);

        if (login->psServer->psHost->iUseSSL > 0)
          hSocket = medusaConnectSSL(&params);
        else
          hSocket = medusaConnect(&params);

        if (hSocket < 0)
        {
          writeError(ERR_NOTICE, "%s: failed to connect, port %d was not open on %s", MODULE_NAME, params.nPort, login->psServer->pHostIP);
          login->iResult = LOGIN_RESULT_UNKNOWN;
          login->iStatus = LOGIN_DONE;
          setPassResult(login, pPass);
          return FAILURE;
        }

        /* Set request parameters */
        if (!_psSessionData->szDir) {
          _psSessionData->szDir = malloc(1);
          memset(_psSessionData->szDir, 0, 1);
          sprintf(_psSessionData->szDir, "/");
        }

        if (!_psSessionData->szHostHeader) {
          nBufLength = strlen(login->psServer->psHost->pHost) + 1 + log(params.nPort) + 1;
          _psSessionData->szHostHeader = malloc(nBufLength + 1);
          memset(_psSessionData->szHostHeader, 0, nBufLength + 1);
          sprintf(_psSessionData->szHostHeader, "%s:%d", login->psServer->psHost->pHost, params.nPort);
        }

        if (!_psSessionData->szFormData) {
          _psSessionData->szFormRest = malloc(1);
          memset(_psSessionData->szFormRest, 0, 1);
          
          _psSessionData->szFormUser = malloc(10);
          memset(_psSessionData->szFormUser, 0, 10);
          sprintf(_psSessionData->szFormUser, "username=");
          
          _psSessionData->szFormPass = malloc(10);
          memset(_psSessionData->szFormPass, 0, 10);
          sprintf(_psSessionData->szFormPass, "password=");

          _psSessionData->nFormType = FORM_POST;
        }
        else {
          pTemp = strtok(_psSessionData->szFormData, "?");
          
          writeError(ERR_DEBUG_MODULE, "[%s] User-supplied Form Action Method: %s", MODULE_NAME, pTemp);
          if(strncasecmp(pTemp, "POST", 4) == 0) 
            _psSessionData->nFormType=FORM_POST;
          else if(strncasecmp(pTemp, "GET", 3) == 0) 
            _psSessionData->nFormType=FORM_GET;
          else 
            _psSessionData->nFormType=FORM_UNKNOWN;
 
          _psSessionData->szFormUser = strtok(NULL, "&");
          _psSessionData->szFormPass = strtok(NULL, "&");
          _psSessionData->szFormRest = strtok(NULL, "");

          writeError(ERR_DEBUG_MODULE, "[%s] User-supplied Form User Field: %s", MODULE_NAME, _psSessionData->szFormUser);
          writeError(ERR_DEBUG_MODULE, "[%s] User-supplied Form Pass Field: %s", MODULE_NAME, _psSessionData->szFormPass);
          writeError(ERR_DEBUG_MODULE, "[%s] User-supplied Form Rest Field: %s", MODULE_NAME, _psSessionData->szFormRest);
          
          if ((_psSessionData->nFormType == FORM_UNKNOWN) || (_psSessionData->szFormUser == NULL) || (_psSessionData->szFormPass == NULL)) 
          {
            writeError(ERR_WARNING, "Invalid FORM-DATA format. Using default format: \"post?username=&password=\"");
            _psSessionData->szFormRest = malloc(1);
            memset(_psSessionData->szFormRest, 0, 1);
            
            _psSessionData->szFormUser = malloc(10);
            memset(_psSessionData->szFormUser, 0, 10);
            sprintf(_psSessionData->szFormUser, "username=");

            _psSessionData->szFormPass = malloc(10);
            memset(_psSessionData->szFormPass, 0, 10);
            sprintf(_psSessionData->szFormPass, "password=");

            _psSessionData->nFormType=FORM_POST;
          }
        }

        if (!_psSessionData->szUserAgent) {
          _psSessionData->szUserAgent = malloc(31);
          memset(_psSessionData->szUserAgent, 0, 31);
          sprintf(_psSessionData->szUserAgent, "I'm not Mozilla, I'm Ming Mong");
        }

        if (!_psSessionData->szDenySignal) {
          _psSessionData->szDenySignal = malloc(19);
          memset(_psSessionData->szDenySignal, 0, 19);
          sprintf(_psSessionData->szDenySignal, "Login Incorrect");
        }

        nState = HSTATE_RUNNING;
        break;
      case HSTATE_RUNNING:
        nState = tryLogin(hSocket, _psSessionData, &login, params.nPort, user->pUser, pPass);
        medusaDisconnect(hSocket);
        hSocket = -1;
        pPass = getNextPass(login->psServer->psAudit, user);
        if (pPass == NULL)
        {
          writeError(ERR_DEBUG_MODULE, "Done with passwords, exiting http");
          nState = HSTATE_EXITING;
        }
        else
        {
          nState = HSTATE_NEW;
        }
        break;
    case HSTATE_EXITING:
      if (hSocket > 0)
        medusaDisconnect(hSocket);
      hSocket = -1;
    default:
      writeError(ERR_CRITICAL, "Unknown HTTP module state (%d). Exiting...", nState);
      login->iResult = LOGIN_RESULT_UNKNOWN;
      login->iStatus = LOGIN_DONE;
      setPassResult(login, pPass);
    }
  }

  /* clean up memory */
  FREE(_psSessionData->szDir);
  FREE(_psSessionData->szHostHeader);
  FREE(_psSessionData->szUserAgent);
  FREE(_psSessionData->szDenySignal);
  FREE(_psSessionData->szFormData);
  /* Ignore since they may point to data within szFormData (strtok) */ 
  /* FREE(_psSessionData->szFormRest); */
  /* FREE(_psSessionData->szFormUser); */
  /* FREE(_psSessionData->szFormPass); */
  FREE(_psSessionData);

  login->iStatus = LOGIN_DONE;
  return SUCCESS;
}

int digits(int num)
{
  if ( num/10 < 1 ) return 1;
  else return (1+digits( (int) (num/10) ));
}

/* Module Specific Functions */

int sendPost(int hSocket, _MODULE_DATA* _psSessionData, char* szLogin, char* szPassword)
{
  char* bufSend = NULL;
  char* bufForm = NULL;
  int nSendBufferSize = 0;
  int nFormBufferSize = 0;
  int nRet = SUCCESS;

  nFormBufferSize = asprintf(&bufForm, "%s%s&%s%s&%s", _psSessionData->szFormUser, szLogin, _psSessionData->szFormPass, szPassword, _psSessionData->szFormRest); 
  nSendBufferSize = asprintf(&bufSend, "POST /%s HTTP/1.1\r\nHost: %s\r\nUser-Agent: %s\r\nConnection: close\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: %i\r\n\r\n%s", _psSessionData->szDir, _psSessionData->szHostHeader, _psSessionData->szUserAgent, nFormBufferSize, bufForm);
  
  if (medusaSend(hSocket, bufSend, nSendBufferSize, 0) < 0)
  {
    writeError(ERR_ERROR, "%s failed: medusaSend was not successful", MODULE_NAME);
    nRet = FAILURE;  
  }
  
  free(bufSend);
  free(bufForm);
  return nRet;
}

int sendGet(int hSocket, _MODULE_DATA* _psSessionData, char* szLogin, char* szPassword)
{
  char* bufSend = NULL;
  int nSendBufferSize = 0;
  int nRet = SUCCESS;

  nSendBufferSize = asprintf(&bufSend, "GET /%s?%s%s&%s%s&%s HTTP/1.1\r\nHost: %s\r\nUser-Agent: %s\r\nConnection: close\r\n\r\n", _psSessionData->szDir, _psSessionData->szFormUser, szLogin, _psSessionData->szFormPass, szPassword, _psSessionData->szFormRest, _psSessionData->szHostHeader, _psSessionData->szUserAgent);
  
  if (medusaSend(hSocket, bufSend, nSendBufferSize, 0) < 0)
  {
    writeError(ERR_ERROR, "%s failed: medusaSend was not successful", MODULE_NAME);
    nRet = FAILURE;  
  }
  
  free(bufSend);
  return nRet;
}

int tryLogin(int hSocket, _MODULE_DATA* _psSessionData, sLogin** login, int nPort, char* szLogin, char* szPassword)
{
  char* pReceiveBuffer = NULL;
  int nReceiveBufferSize, nRet;
  char* pTemp = NULL;

  switch(_psSessionData->nFormType)
  {
    case FORM_GET:
      writeError(ERR_DEBUG_MODULE, "[%s] Sending Web Form Authentication (GET).", MODULE_NAME);
      nRet = sendGet(hSocket, _psSessionData, szLogin, szPassword);
      break;
    case FORM_POST:
      writeError(ERR_DEBUG_MODULE, "[%s] Sending Web Form Authentication (POST).", MODULE_NAME);
      nRet = sendPost(hSocket, _psSessionData, szLogin, szPassword);
      break;
    default:
      break;
  }

  if (nRet == FAILURE)
  {
    writeError(ERR_ERROR, "[%s] Failed during sending of authentication data.", MODULE_NAME);
    (*login)->iResult = LOGIN_RESULT_UNKNOWN;
    setPassResult(*login, szPassword);
    return HSTATE_RUNNING;  
  }

  writeError(ERR_DEBUG_MODULE, "[%s] Retrieving server response.", MODULE_NAME);
  pReceiveBuffer = medusaReceiveLine(hSocket, &nReceiveBufferSize);

  if ((pReceiveBuffer == NULL) || (pReceiveBuffer[0] == '\0'))
  {
    writeError(ERR_ERROR, "[%s] No data received", MODULE_NAME);
    (*login)->iResult = LOGIN_RESULT_UNKNOWN;
    setPassResult(*login, szPassword);
    return HSTATE_RUNNING;  
  }

  pTemp = ((char*)index(pReceiveBuffer, ' ')) + 1;
  if (strncmp(pTemp, "200 OK", 6) != 0 )
  {
    writeError(VB_GENERAL, "The answer was NOT successfully received, understood, and accepted: error code was %.6s", pTemp);
    (*login)->iResult = LOGIN_RESULT_UNKNOWN;
    setPassResult(*login, szPassword);
    return HSTATE_RUNNING;
  }

  while ((strcasestr(pReceiveBuffer, _psSessionData->szDenySignal) == NULL) && (pReceiveBuffer[0] != '\0'))
  {
    free(pReceiveBuffer);
    pReceiveBuffer = medusaReceiveLine(hSocket, &nReceiveBufferSize);
  }

  if (strcasestr(pReceiveBuffer, _psSessionData->szDenySignal) != NULL)
  {
    (*login)->iResult = LOGIN_RESULT_FAIL;
    setPassResult(*login, szPassword);
    return HSTATE_RUNNING;
  }
   
  writeError(ERR_DEBUG, "Login Successful");
  (*login)->iResult = LOGIN_RESULT_SUCCESS;
  setPassResult(*login, szPassword);
  return HSTATE_RUNNING;   
}

#else

void summaryUsage(char **ppszSummary)
{
  // Memory for ppszSummary will be allocated here - caller is responsible for freeing it
  int  iLength = 0;

  if (*ppszSummary == NULL)
  {
    iLength = strlen(MODULE_SUMMARY_USAGE) + strlen(MODULE_VERSION) + strlen(MODULE_SUMMARY_FORMAT) + strlen(OPENSSL_WARNING) + 1;
    *ppszSummary = (char*)malloc(iLength);
    memset(*ppszSummary, 0, iLength);
    snprintf(*ppszSummary, iLength, MODULE_SUMMARY_FORMAT_WARN, MODULE_SUMMARY_USAGE, MODULE_VERSION, OPENSSL_WARNING);
  }
  else
  {
    writeError(ERR_ERROR, "%s reports an error in summaryUsage() : ppszSummary must be NULL when called", MODULE_NAME);
  }
}

void showUsage()
{
  writeVerbose(VB_NONE, "%s (%s) %s :: %s\n", MODULE_NAME, MODULE_VERSION, MODULE_AUTHOR, MODULE_SUMMARY_USAGE);
  writeVerbose(VB_NONE, "** Module was not properly built. Is OPENSSL installed correctly? **");
  writeVerbose(VB_NONE, "");
  return FAILURE;
}

int go(sLogin* logins, int argc, char *argv[])
{
  writeVerbose(VB_NONE, "%s (%s) %s :: %s\n", MODULE_NAME, MODULE_VERSION, MODULE_AUTHOR, MODULE_SUMMARY_USAGE);
  writeVerbose(VB_NONE, "** Module was not properly built. Is OPENSSL installed correctly? **");
  writeVerbose(VB_NONE, "");
  return FAILURE;
}

#endif

Generated by  Doxygen 1.6.0   Back to index