#!/usr/bin/perl -w ##################################################################### # # Webshell # http://www.konetzka.de/webshell.html # # Copyright (c) 2003-2004 by Helge Konetzka # This program is free software; you can redistribute it # and/or modify it under the same terms as Perl itself. # # An interface to execute programs on servers without telnet access # Also upload, download and edit funcionality # Just for Unix-style servers # # Webshell comes with ABSOLUTELY NO WARRANTY! # # v1.4.2 (28.09.04): Bugfix der urlencode-Subroutine # v1.4.1 (09.09.04): Die automatische Konfiguration erfordert jetzt die # zweimalige Passworteingabe # v1.4 (06.09.04): Es werden keine Klartextpassworte oder Klartextonces mehr # auf dem Server gespeichert, wenn cgi-bin schreibbar ist # (Hinweis von Amelie Zapf) # v1.3.1 (05.09.04): Bugfix fuer Download (durch Ralf Zimmermann) # v1.3 (29.05.04): Fix fuer Webserver mit untersch. IDs fuer Upload und CGi # v1.2 (10.05.04): Richtiges Passwort nicht mehr in GET-Request enthalten, # waere bei weltlesbaren Logdateien toedlich # v1.1 (25.04.04): Downloadmoeglichkeit, Texteditor, Filebrowser # v1.0 (30.01.04): Kommandozeile am Webserver (nicht interaktiv), # Uploadmoeglichkeit, Pfadpersistenz # ##################################################################### # # Konfiguration # # Nur auf '0' setzen, falls keine Schreibmoeglichkeit fuer Skript in cgi-bin my $cgi_writeable = 1; # Nur dann das Passwort angeben, wenn $cgi_writeable = 0 my $origpass = ""; # ##################################################################### use strict; use CGI qw/:all/; use Cwd; # Skriptnamen extrahieren $0 =~ m%([^/]+)$%; my $cgi = $1; my $oncefile = "$cgi.once"; my $cryptfile = "$cgi.passwd"; my $pass = param('pass'); my $authenticate = authenticate(\$pass); my $cgi_pass = $cgi."?pass=$pass"; if ($authenticate) { # In das aktuelle Verzeichnis wechseln my $dir; if (param("dir")) { $dir = param("dir"); chdir $dir; } if (param("edit")) { my $file = param("edit"); if (param("content")) { my $old = $file.".old"; system("/bin/cp -p $file $old"); open (FILE,">$file"); my $content = param("content"); $content =~ s/\f\r\n/\n/g; $content =~ s/\n\f\r/\n/g; $content =~ s/\f\r/\n/g; $content =~ s/\r\n/\n/g; $content =~ s/\n\r/\n/g; $content =~ s/\r/\n/g; print FILE $content; close FILE; } print header; print start_html("Web-Editor $file"); print p("Web-Editor $file. Nach 5 Minuten verfaellt die Sitzung. Bitte vorher speichern!"); open FILE,$file; print start_form(-action=>$cgi); print ""; print br; print submit("Save"); print reset("Reset"); print hidden(-name=>"pass"); print hidden(-name=>"edit"); print hidden(-name=>"linebreak"); print hidden(-name=>"listdir"); print hidden(-name=>"dir"); print end_form; print start_form(-action=>$cgi); print hidden(-name=>"pass"); print hidden(-name=>"linebreak"); print hidden(-name=>"listdir"); print hidden(-name=>"dir"); print submit("Quit Editing"); print end_form; print end_html; } elsif (param("down")) { open (FILE,param("down")); my $file = param("down"); print header(-type=>"application/octet-stream",-attachment=>"$file"); my $buffer; # I/O - wichtig ist der erste Parameter von read, genau so! while (read(FILE,$buffer,1024)) { print $buffer; } # Datei schliessen close FILE; } else { # Ausgabe beginnen print header; print start_html(-title=>"Webshell"); print "
";
# Die Kommandozeile ausfuehren
if (param("command")) {
my $command = param("command");
# Darstellung der Ausgabe des Kommandos vorbereiten
print "$ENV{'SERVER_NAME'}:$dir\$ $command\n\n";
# Verzeichniswechsel durchfuehren
if ($command =~ /\s*cd\s*(\S*)/) {
my $newdir = "";
$newdir = $1;
chdir $newdir;
}
# Befehl ausfuehren
else {
# Fehlgeschlagenes Fork nicht abfangen wg. CGI
open (COMMAND,"$command 2>&1 |");
while (my $output = ) {
# Tabulatoren sind i.a. 8 Leerzeichen lang
$output =~ s/\t/ /g;
my $line = 0;
do {
# Umbruch wie Emacs
if (param("linebreak")) {
print "\\\n" unless $line == 0;
}
# Zeile abknabbern
my $suboutput = substr($output,$line*80,80);
# Zeichen mit Bedeutung in HTML-Code korrekt darstellen
$suboutput =~ s/&/&/g;
$suboutput =~ s/</g;
$suboutput =~ s/>/>/g;
print "$suboutput";
# Nach Rest schauen
} while (substr($output,++$line*80,80))
}
# Fehlgeschlagene Ausfuehrung nicht abfangen wg. CGI
close COMMAND;
}
}
# Falls sich das aktuelle Verzeichnis geaendert hat, merken
$dir = cwd();
$dir .= "/" unless $dir =~ m%/$%;
if (param("listdir")) {
my @files = <*>;
push @files,<.*>;
@files = sort @files;
my $newdir = urlencode($dir);
open (COMMAND,"ls -la 2>&1 |");
print "Inhalt von $dir:\n\n";
my $total;
while (my $output = ) {
if ($output =~ /^total/) {
$total = $output;
next;
}
my $file = shift @files;
$file =~ s/^\s*//;
$file =~ s/\s*$//;
my $linebreak;
if (param("linebreak")) { $linebreak = "on"; }
else { $linebreak = 0; }
my $listdir;
if (param("listdir")) { $listdir = "on"; }
else { $listdir = 0; }
if ($output =~ / $file\s*$/ && !(-d $file)) {
my $newfile = urlencode($file);
print "Edit ";
$output =~ s%$file$%$file%;
}
elsif ($output =~ / $file\s*$/ && (-d $file)) {
my $newfile = urlencode($file);
print "Enter ";
}
print $output;
}
print $total;
close COMMAND;
}
print " "; print checkbox(-name=>"linebreak",-label=>" Zeilenumbruch in der Ausgabe "); print checkbox(-name=>"listdir",-label=>" ls -la "); print br,"$ENV{'SERVER_NAME'}:$dir\$ "; print textfield(-name=>"command",-value=>"",-size=>"50",-override=>"1"); print submit("EXEC"); print "
"; print end_form; print end_html; } } # Start oder Falsches Passwort else { print header; # Konfiguration ist OK if ((-e $cryptfile && !$origpass) || (!$cgi_writeable && $origpass)) { sleep(5) if param("pass"); print start_html("Log In"); print h1("Log In"); print start_form(-action=>$cgi); print p("Bitte Passwort eingeben: ", password_field(-name=>"pass",-override=>"1"), submit("OK")); print hidden(-name=>"listdir",-value=>"on",-override=>"1"); print end_form; print end_html; } # Ups, da hat jemand ein Klartextpasswort in das Skript eingefuegt (Vor oder nach der Konfiguration moeglich).! elsif ($cgi_writeable && $origpass) { print start_html("Konfigurationsproblem"); print h1("Konfigurationsproblem"); print p("Das Passwort darf bei cgi_writeable = 1 nicht im Klartext eingegeben sein! Beachten Sie den Konfigurationsabschnitt im Skript."); print end_html; } # Es existiert keine Passwortdatei elsif ($cgi_writeable && !(-e $cryptfile)) { # Sie wird jetzt geschrieben if (param("pass2") && param("pass2") eq param("pass")) { open CRYPT,">$cryptfile"; my $newcrypt = crypt(param("pass"),"NE"); print CRYPT $newcrypt; close CRYPT; if (-e $cryptfile) { print start_html("Automatische Konfiguration erfolgreich"); print h1("Automatische Konfiguration erfolgreich"); print p("Das Passwort wurde gespeichert"); print start_form(-action=>$cgi); print p("Bitte jetzt ", hidden(-name=>"pass"), submit("Anmelden")); print hidden(-name=>"listdir",-value=>"on",-override=>"1"); print end_form; print end_html; } else { print start_html("Automatische Konfiguration nicht erfolgreich"); print h1("Automatische Konfiguration nicht erfolgreich"); print p("Das Passwort wurde nicht gespeichert"); print p("Das CGI-Verzeichnis ist fuer Sie nicht schreibbar, bitte lesen Sie den Abschnitt zu Konfigurationsproblemen auf http://www.konetzka.de/webshell.html!"); print end_html; } } # Das Passwort fuer die Datei muss abgefragt werden else { print start_html("Automatische Konfiguration"); print h1("Automatische Konfiguration"); print p("Die eingegebenen Passworte waren nicht gleich!") if param("pass2"); print p("Bitte wählen Sie ein Passwort"); print start_form(-action=>$cgi); print p("Bitte Passwort eingeben: ",br, password_field(-name=>"pass",-override=>"1"),br, "Bitte das Passwort nochmals eingeben: ",br, password_field(-name=>"pass2",-override=>"1")); print p(submit("OK")); print end_form; print end_html; } } elsif (!$cgi_writeable && !$origpass) { print start_html("Konfigurationsproblem"); print h1("Konfigurationsproblem"); print p("Das Passwort muss bei cgi_writeable = 0 im Klartext eingegeben sein! Beachten Sie den Konfigurationsabschnitt im Skript."); print end_html; } else { print start_html("WADDEHADDEDUDDEDA"); print p("WADDEHADDEDUDDEDA"); print end_html; } } sub urlencode { my ($string) = @_; my $newstring; while ($string || $string eq "0") { my $char = substr($string,0,1); $string =~ s/^$char//; my $dez = ord($char); if (($dez >= ord('a') && $dez <= ord('z')) || ($dez >= ord('A') && $dez <= ord('Z')) || ($dez >= ord('0') && $dez <= ord('9')) || $dez eq ord('_') || $dez eq ord('*') || $dez eq ord('-') || $dez eq ord('.')) { $newstring .= $char; } elsif ($char eq " ") { $newstring .= "+"; } else { my $hex = sprintf "%lx",$dez; $newstring .= "%".$hex; } } return $newstring; } sub authenticate { my ($pass) = @_; return 0 unless $$pass; # Onces und crypt werden benutzt open CRYPT,$cryptfile; my $crypt =