thc-hydra/hydra-http-form.c

712 lines
21 KiB
C

/*
Hydra Form Module
-----------------
The hydra form can be used to carry out a brute-force attack on simple
web-based login forms that require username and password variables via
either a GET or POST request.
The module works similarly to the HTTP basic auth module and will honour
proxy mode (with authenticaion) as well as SSL. The module can be invoked
with the service names of "http-get-form", "http-post-form",
"https-get-form" and "https-post-form".
Here's a couple of examples: -
./hydra -l "<userID>" -P pass.txt 10.221.64.12 http-post-form
"/irmlab2/testsso-auth.do:ID=^USER^&Password=^PASS^:Invalid Password"
./hydra -S -s 443 -l "<username>" -P pass.txt 10.221.64.2 https-get-form
"/irmlab1/vulnapp.php:username=^USER^&pass=^PASS^:incorrect"
The option field (following the service field) takes three ":" separated
values and an optional fourth value, the first is the page on the server
to GET or POST to, the second is the POST/GET variables (taken from either
the browser, or a proxy such as PAROS) with the varying usernames and passwords
in the "^USER^" and "^PASS^" placeholders, the third is the string that it
checks for an *invalid* or *valid* login - any exception to this is counted
as a success.
So please:
* invalid condition login should be preceded by "F="
* valid condition login should be preceded by "S=".
By default, if no header is found the condition is assume to be a fail,
so checking for *invalid* login.
The fourth optional value, can be a 'C' to define a different page to GET
initial cookies from.
If you specify the verbose flag (-v) it will show you the response from the
HTTP server which is useful for checking the result of a failed login to
find something to pattern match against.
Module initially written by Phil Robinson, IRM Plc (releases@irmplc.com),
rewritten by David Maciejak
Fix and issue with strtok use and implement 1 step location follow if HTTP
3xx code is returned (david dot maciejak at gmail dot com)
Added fail or success condition, getting cookies, and allow 5 redirections by david
*/
#include "hydra-mod.h"
/* HTTP Request Options */
#define OPT_PROXY_AUTH 1
#define OPT_PROXY_NOAUTH 2
#define OPT_NOPROXY 4
#define OPT_GETCOOKIES 8
/* Default HTTP Headers */
#define DEFAULT_HOST 1
#define DEFAULT_USER_AGENT 2
#define DEFAULT_CONTENT_TYPE 4
#define MAX_REQ_LEN 1030
extern char *HYDRA_EXIT;
char *buf;
char *cond;
char http_request[MAX_REQ_LEN];
typedef struct header_node {
char *header;
char *value;
struct header_node *next;
}t_header_node, *ptr_header_node;
int success_cond = 0;
int getcookie = 1;
int auth_flag = 0;
char redirected_url_buff[2048] = "";
int redirected_flag = 0;
#define MAX_REDIRECT 8
int redirected_cpt = MAX_REDIRECT;
char cookie[4096] = "", cmiscptr[1024];
extern char *webtarget;
extern char *slash;
int webport, freemischttpform = 0;
int http_opts;
char bufferurl[1024], cookieurl[1024] = "", userheader[1024] = "", *url, *variables, *optional1;
ptr_header_node ptr_head = NULL;
void prepare_httpreq(char * method, char * path){
char tail[] = " HTTP/1.0";
int req_len = (strlen(method) + strlen(path) + 1) <= MAX_REQ_LEN ? (strlen(method) + strlen(path) + 1) : MAX_REQ_LEN;
memset(http_request, 0, MAX_REQ_LEN);
if(strcmp(method, "GET") == 0)
strcat(http_request, "GET ");
else if(strcmp(method, "POST") == 0)
strcat(http_request, "POST ");
strncat(http_request, path, MAX_REQ_LEN - sizeof(tail) - 5);
strcat(http_request, tail);
}
/*
* Returns 1 if specified header exists, or 0 otherwise.
*/
int header_exists(char * header){
ptr_header_node cur_ptr = NULL;
int header_exists = 0;
for(cur_ptr = ptr_head; cur_ptr && !header_exists; cur_ptr = cur_ptr->next)
if(cur_ptr->header && strcmp(cur_ptr->header, header) == 0)
header_exists = 1;
return header_exists;
}
/*
* Returns 1 if success, or 0 otherwise (out of memory).
*/
int add_header(char *header, char *value){
ptr_header_node cur_ptr = NULL;
// get to the last header
for(cur_ptr = ptr_head; cur_ptr && cur_ptr->next; cur_ptr = cur_ptr->next);
ptr_header_node new_ptr = (ptr_header_node) malloc(sizeof(t_header_node));
char * new_header = strdup(header);
char * new_value = strdup(value);
if(new_ptr && new_header && new_value){
// create a new item and append it to the list
new_ptr->header = new_header;
new_ptr->value = new_value;
new_ptr->next = NULL;
}else{
// we're out of memory, so forcefully end
return 0;
}
if(cur_ptr)
cur_ptr->next = new_ptr;
else
// head is NULL, so the list is empty
ptr_head = new_ptr;
return 1;
}
/*
* List layout:
* +----------+ +--------+ +--------+ +--------+
* | ptr_head | --> | next | --> | next | --> | NULL |
* | | | header | | header | | NULL |
* | | | value | | value | | NULL |
* +----------+ +--------+ +--------+ +--------+
*
* Returns 1 if success, or 0 otherwise (out of memory).
*/
int add_or_replace_header(char *header, char *value){
int ret = 1;
ptr_header_node cur_ptr = NULL;
char * new_header = strdup(header);
char * new_value = strdup(value);
if(new_header && new_value){
for(cur_ptr = ptr_head; cur_ptr; cur_ptr = cur_ptr->next){
if(strcmp(cur_ptr->header, new_header) == 0){
free(cur_ptr->value);
cur_ptr->value = new_value;
break;
}
}
}
if(cur_ptr == NULL){
ret = add_header(header, value);
// add_header() will create a new copy so we can safely free them
free(new_header);
free(new_value);
}
return ret;
}
/*
* Replace in all headers' values every occurrence of oldvalue by newvalue.
*/
void hdrrep(char * oldvalue, char * newvalue){
ptr_header_node cur_ptr = NULL;
for(cur_ptr = ptr_head; cur_ptr; cur_ptr = cur_ptr->next){
if(strstr(cur_ptr->value, oldvalue)){
cur_ptr->value = (char *) realloc(cur_ptr->value, strlen(newvalue));
if(cur_ptr->value)
strcpy(cur_ptr->value, newvalue);
else{
hydra_report(stderr, "[ERROR] Out of memory.");
hydra_child_exit(1);
}
}
}
}
/*
* Concat all the headers in the list in a single string, and clean the whole list.
*/
char * stringify_headers_and_clean(){
char * headers_str = NULL;
ptr_header_node cur_ptr = ptr_head, tmp_ptr = NULL;
int ttl_size = strlen(http_request);
while(cur_ptr){
if(cur_ptr->header && cur_ptr->value){ // Check for NULLs
ttl_size += strlen(cur_ptr->header) + strlen(cur_ptr->value) + 1;
if(headers_str)
headers_str = (char *) realloc(headers_str, sizeof(char) * ttl_size);
else{
// Garbage appears when strcat()-ing, if we don't blank newly allocated memory
headers_str = (char *) malloc(sizeof(char) * ttl_size);
if(headers_str)
memset(headers_str, 0, sizeof(char) * ttl_size);
}
if(headers_str){ // Check for errors
strcat(headers_str, cur_ptr->header);
strcat(headers_str, ": ");
strcat(headers_str, cur_ptr->value);
strcat(headers_str, "\r\n");
}else{
// Error: out of memory
hydra_report(stderr, "Out of memory for HTTP headers");
hydra_child_exit(1);
}
}
// Clean it up and get to the next header
tmp_ptr = cur_ptr;
cur_ptr = cur_ptr->next;
free(tmp_ptr->header);
free(tmp_ptr->value);
free(tmp_ptr);
}
return headers_str;
}
int strpos(char *str, char *target) {
char *res = strstr(str, target);
if (res == NULL)
return -1;
else
return res - str;
}
char *html_encode(char *string) {
char *ret = string;
if (ret == NULL)
return NULL;
if (index(ret, '%') != NULL)
ret = hydra_strrep(ret, "%", "%25");
if (index(ret, ' ') != NULL)
ret = hydra_strrep(ret, " ", "%20");
if (index(ret, '&') != NULL)
ret = hydra_strrep(ret, "&", "%26");
if (index(ret, '#') != NULL)
ret = hydra_strrep(ret, "&", "%23");
return ret;
}
/*
int analyze_server_response(int socket)
return 0 or 1 when the cond regex is matched
return -1 if no response from server
*/
int analyze_server_response(int s) {
int runs = 0;
while ((buf = hydra_receive_line(s)) != NULL) {
runs++;
//check for http redirection
if (strstr(buf, "HTTP/1.1 3") != NULL || strstr(buf, "HTTP/1.0 3") != NULL || strstr(buf, "Status: 3") != NULL) {
redirected_flag = 1;
} else if (strstr(buf, "HTTP/1.1 401") != NULL || strstr(buf, "HTTP/1.0 401") != NULL) {
auth_flag = 1;
} else if ((strstr(buf, "HTTP/1.1 403") != NULL) || (strstr(buf, "HTTP/1.1 404") != NULL) || (strstr(buf, "HTTP/1.0 403") != NULL) || (strstr(buf, "HTTP/1.0 404") != NULL)) {
return 0;
}
if (hydra_strcasestr(buf, "Location: ") != NULL) {
char *startloc, *endloc;
char str[2048];
startloc = hydra_strcasestr(buf, "Location: ") + strlen("Location: ");
strncpy(str, startloc, sizeof(str) - 1);
str[sizeof(str) - 1] = 0;
endloc = strchr(str, '\n');
if (endloc != NULL)
*endloc = 0;
endloc = strchr(str, '\r');
if (endloc != NULL)
*endloc = 0;
strcpy(redirected_url_buff, str);
}
//there can be multiple cookies
if (hydra_strcasestr(buf, "Set-Cookie: ") != NULL) {
char *cookiebuf = buf;
do {
char *startcookie, *endcookie1, *endcookie2;
char str[1024], tmpcookie[4096] = "", tmpname[128] = "", *ptr, *ptr2;
memset(str, 0, sizeof(str));
startcookie = hydra_strcasestr(cookiebuf, "Set-Cookie: ") + strlen("Set-Cookie: ");
strncpy(str, startcookie, sizeof(str) - 1);
str[sizeof(str) - 1] = 0;
endcookie1 = strchr(str, '\n');
endcookie2 = strchr(str, ';');
//terminate string after cookie data
if (endcookie1 != NULL && endcookie1 < endcookie2)
*endcookie1 = 0;
else if (endcookie2 != NULL)
*endcookie2 = 0;
// is the cookie already there? if yes, remove it!
if (index(startcookie, '=') != NULL && (ptr = index(startcookie, '=')) - startcookie + 1 <= sizeof(tmpname)) {
strncpy(tmpname, startcookie, sizeof(tmpname) - 2);
tmpname[sizeof(tmpname) - 2] = 0;
ptr = index(tmpname, '=');
*(++ptr) = 0;
// is the cookie already in the cookiejar? (so, does it have to be replaced?)
if ((ptr = hydra_strcasestr(cookie, tmpname)) != NULL) {
// yes it is.
// if the cookie is not in the beginning of the cookiejar, copy the ones before
if (ptr != cookie && *(ptr - 1) == ' ') {
strncpy(tmpcookie, cookie, ptr - cookie - 2);
tmpcookie[ptr - cookie - 2] = 0;
}
ptr += strlen(tmpname);
// if there are any cookies after this one in the cookiejar, copy them over
if ((ptr2 = strstr(ptr, "; ")) != NULL) {
ptr2 += 2;
strncat(tmpcookie, ptr2, sizeof(tmpcookie) - strlen(tmpcookie) - 1);
}
if (debug)
printf("[DEBUG] removing cookie %s in jar\n before: %s\n after: %s\n", tmpname, cookie, tmpcookie);
strcpy(cookie, tmpcookie);
}
}
ptr = index(str, '=');
// only copy the cookie if it has a value (otherwise the server wants to delete the cookie
if (ptr != NULL && *(ptr + 1) != ';' && *(ptr + 1) != 0 && *(ptr + 1) != '\n' && *(ptr + 1) != '\r') {
if (strlen(cookie) > 0)
strncat(cookie, "; ", sizeof(cookie) - strlen(cookie) - 1);
strncat(cookie, str, sizeof(cookie) - strlen(cookie) - 1);
}
cookiebuf = startcookie;
} while (hydra_strcasestr(cookiebuf, "Set-Cookie: ") != NULL);
}
#ifdef HAVE_PCRE
if (hydra_string_match(buf, cond) == 1) {
#else
if (strstr(buf, cond) != NULL) {
#endif
free(buf);
// printf("DEBUG: STRING %s FOUND!!:\n%s\n", cond, buf);
return 1;
}
// else printf("DEBUG: STRING %s NOT FOUND:\n%s\n", cond, buf);
free(buf);
}
if (runs == 0) {
if (debug)
hydra_report(stderr, "DEBUG: no response from server\n");
return -1;
}
return 0;
}
void hydra_reconnect(int s, char *ip, int port, unsigned char options) {
if (s >= 0)
s = hydra_disconnect(s);
if ((options & OPTION_SSL) == 0) {
s = hydra_connect_tcp(ip, port);
} else {
s = hydra_connect_ssl(ip, port);
}
}
int start_http_form(int s, char *ip, int port, unsigned char options, char *miscptr, FILE * fp, char *type) {
char *empty = "";
char *login, *pass, clogin[256], cpass[256];
char *buffer; // Buffer for HTTP headers
char header[8096], *upd3variables, cuserheader[1024];
int found = !success_cond, i, j;
memset(header, 0, sizeof(header));
cookie[0] = 0; // reset cookies from potential previous attempt
// Take the next login/pass pair
if (strlen(login = hydra_get_next_login()) == 0)
login = empty;
if (strlen(pass = hydra_get_next_password()) == 0)
pass = empty;
strncpy(clogin, html_encode(login), sizeof(clogin) - 1);
clogin[sizeof(clogin) - 1] = 0;
strncpy(cpass, html_encode(pass), sizeof(cpass) - 1);
cpass[sizeof(cpass) - 1] = 0;
// Replace the user/pass placeholders in the command line options
upd3variables = hydra_strrep(variables, "^USER^", clogin);
upd3variables = hydra_strrep(upd3variables, "^PASS^", cpass);
// Replace the user/pass placeholders in the user-supplied headers
hdrrep("^USER^", clogin);
hdrrep("^PASS^", cpass);
//////////////////////////////////////////////////////////////////////////////////////////////////////////
buffer = stringify_headers_and_clean();
hydra_report(stdout, "HTTP headers:\n%s\r\n%s", http_request, buffer);
hydra_child_exit(1);
return 1;
}
void service_http_form(char *ip, int sp, unsigned char options, char *miscptr, FILE * fp, int port, char *type) {
int run = 1, next_run = 1, sock = -1;
int myport = PORT_HTTP, mysslport = PORT_HTTP_SSL;
char *ptr, *ptr2;
hydra_register_socket(sp);
if (webtarget != NULL && (webtarget = strstr(miscptr, "://")) != NULL) {
webtarget += strlen("://");
if ((ptr2 = index(webtarget, ':')) != NULL) { /* step over port if present */
*ptr2 = 0;
ptr2++;
ptr = ptr2;
if (*ptr == '/' || (ptr = index(ptr2, '/')) != NULL)
miscptr = ptr;
else
miscptr = slash; /* to make things easier to user */
} else if ((ptr2 = index(webtarget, '/')) != NULL) {
if (freemischttpform == 0) {
freemischttpform = 1;
miscptr = malloc(strlen(ptr2) + 1);
strcpy(miscptr, ptr2);
*ptr2 = 0;
}
} else
webtarget = NULL;
}
if (cmdlinetarget != NULL && webtarget == NULL)
webtarget = cmdlinetarget;
else if (webtarget == NULL && cmdlinetarget == NULL)
webtarget = hydra_address2string(ip);
if (port != 0)
webport = port;
else if ((options & OPTION_SSL) == 0)
webport = myport;
else
webport = mysslport;
sprintf(bufferurl, "%.1000s", miscptr);
url = bufferurl;
ptr = url;
while (*ptr != 0 && (*ptr != ':' || *(ptr - 1) == '\\'))
ptr++;
if (*ptr != 0)
*ptr++ = 0;
variables = ptr;
while (*ptr != 0 && (*ptr != ':' || *(ptr - 1) == '\\'))
ptr++;
if (*ptr != 0)
*ptr++ = 0;
cond = ptr;
while (*ptr != 0 && (*ptr != ':' || *(ptr - 1) == '\\'))
ptr++;
if (*ptr != 0)
*ptr++ = 0;
optional1 = ptr;
if (strstr(url, "\\:") != NULL) {
if ((ptr = malloc(strlen(url))) != NULL) {
strcpy(ptr, hydra_strrep(url, "\\:", ":"));
url = ptr;
}
}
if (strstr(variables, "\\:") != NULL) {
if ((ptr = malloc(strlen(variables))) != NULL) {
strcpy(ptr, hydra_strrep(variables, "\\:", ":"));
variables = ptr;
}
}
if (strstr(cond, "\\:") != NULL) {
if ((ptr = malloc(strlen(cond))) != NULL) {
strcpy(ptr, hydra_strrep(cond, "\\:", ":"));
cond = ptr;
}
}
if (url == NULL || variables == NULL || cond == NULL /*|| optional1 == NULL */ )
hydra_child_exit(2);
//printf("url: %s, var: %s, cond: %s, opt: %s\n", url, variables, cond, optional1);
if (*cond == 0) {
fprintf(stderr, "[ERROR] invalid number of parameters in module option\n");
hydra_child_exit(2);
}
sprintf(cookieurl, "%.1000s", url);
//conditions now have to contain F or S to set the fail or success condition
if (*cond != 0 && (strpos(cond, "F=") == 0)) {
success_cond = 0;
cond += 2;
} else if (*cond != 0 && (strpos(cond, "S=") == 0)) {
success_cond = 1;
cond += 2;
} else {
//by default condition is a fail
success_cond = 0;
}
// Prepare the HTTP requests that will follow
// Add the default HTTP headers
http_opts = 0;
if(use_proxy == 1 && proxy_authentication != NULL){
// proxy with authentication
// TODO Add default headers here
}else if(use_proxy == 1){
// proxy without authentication
// TODO Add default headers here
}else{
// direct web server, no proxy
add_header("Host", webtarget);
add_header("User-Agent", "Mozilla/5.0 (Hydra)");
}
prepare_httpreq(type, url);
char *header = NULL, *value = NULL;
while ( /*(optional1 = strtok(NULL, ":")) != NULL */ *optional1 != 0) {
switch (optional1[0]) {
case 'c': // fall through
case 'C':
ptr = optional1 + 2;
while (*ptr != 0 && (*ptr != ':' || *(ptr - 1) == '\\'))
ptr++;
if (*ptr != 0)
*ptr++ = 0;
sprintf(cookieurl, "%.1000s", hydra_strrep(optional1 + 2, "\\:", ":"));
optional1 = ptr;
break;
case 'h':
// add a new header at the end
ptr = optional1 + 2;
while (*ptr != 0 && (*ptr != ':' || *(ptr - 1) == '\\'))
ptr++;
if (*ptr != 0)
*ptr++ = 0;
ptr2 = ptr;
while (*ptr2 != 0 && (*ptr2 != ':' || *(ptr2 - 1) == '\\'))
ptr2++;
if (*ptr2 != 0)
*ptr2++ = 0;
/*
* At this point:
* - (optional1 + 2) contains the header's name
* - ptr contains the header's value
*/
if(add_header(optional1 + 2, hydra_strrep(ptr, "\\:", ":"))){
// Success: break the switch and go ahead
optional1 = ptr2;
break;
}
// Error: abort execution
hydra_report(stderr, "[ERROR] Out of memory for HTTP headers.");
hydra_child_exit(1);
break;
case 'H':
// add a new header, or replace an existing one's value
ptr = optional1 + 2;
while (*ptr != 0 && (*ptr != ':' || *(ptr - 1) == '\\'))
ptr++;
if (*ptr != 0)
*ptr++ = 0;
ptr2 = ptr;
while (*ptr2 != 0 && (*ptr2 != ':' || *(ptr2 - 1) == '\\'))
ptr2++;
if (*ptr2 != 0)
*ptr2++ = 0;
/*
* At this point:
* - (optional1 + 2) contains the header's name
* - ptr contains the header's value
*/
if(add_or_replace_header(optional1 + 2, hydra_strrep(ptr, "\\:", ":"))){
// Success: break the switch and go ahead
optional1 = ptr2;
break;
}
// Error: abort execution
hydra_report(stderr, "[ERROR] Out of memory for HTTP headers.");
hydra_child_exit(1);
break;
// no default
}
}
while (1) {
if (run == 2) {
if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0) {
if (freemischttpform)
free(miscptr);
freemischttpform = 0;
hydra_child_exit(1);
}
}
switch (run) {
case 1: /* connect and service init function */
{
if (sock >= 0)
sock = hydra_disconnect(sock);
if ((options & OPTION_SSL) == 0) {
if (port != 0)
myport = port;
sock = hydra_connect_tcp(ip, myport);
port = myport;
} else {
if (port != 0)
mysslport = port;
sock = hydra_connect_ssl(ip, mysslport);
port = mysslport;
}
if (sock < 0) {
hydra_report(stderr, "[ERROR] Child with pid %d terminating, can not connect\n", (int) getpid());
if (freemischttpform)
free(miscptr);
freemischttpform = 0;
hydra_child_exit(1);
}
next_run = 2;
break;
}
case 2: /* run the cracking function */
next_run = start_http_form(sock, ip, port, options, miscptr, fp, type);
break;
case 3: /* clean exit */
if (sock >= 0)
sock = hydra_disconnect(sock);
if (freemischttpform)
free(miscptr);
freemischttpform = 0;
hydra_child_exit(0);
break;
case 4: /* silent error exit */
if (sock >= 0)
sock = hydra_disconnect(sock);
if (freemischttpform)
free(miscptr);
freemischttpform = 0;
hydra_child_exit(1);
break;
default:
if (freemischttpform)
free(miscptr);
freemischttpform = 0;
hydra_report(stderr, "[ERROR] Caught unknown return code, exiting!\n");
hydra_child_exit(0);
}
run = next_run;
}
if (freemischttpform)
free(miscptr);
}
void service_http_get_form(char *ip, int sp, unsigned char options, char *miscptr, FILE * fp, int port) {
////
service_http_form(ip, sp, options, miscptr, fp, port, "GET");
}
void service_http_post_form(char *ip, int sp, unsigned char options, char *miscptr, FILE * fp, int port) {
////
service_http_form(ip, sp, options, miscptr, fp, port, "POST");
}
int service_http_form_init(char *ip, int sp, unsigned char options, char *miscptr, FILE * fp, int port) {
// called before the childrens are forked off, so this is the function
// which should be filled if initial connections and service setup has to be
// performed once only.
//
// fill if needed.
//
// return codes:
// 0 all OK
// -1 error, hydra will exit, so print a good error message here
return 0;
}