Alexander (HH): /Sicherheit: Untaint my Vars und per sudo Ausführen

Beitrag lesen

Moin Moin!

Warum so umständlich? Und warum so viele Rechte für das Programm?

Starte einen Prozess, der Port 80 als Server öffnet, mit Root-Rechten. Werfe in dem Prozess alle Rechte weg und werde zu nobody / www / wwwrun. Ersetze den Prozess durch einen anderen, der den noch offenen Port bedient.

Lektüre: http://cr.yp.to/ucspi-tcp.html, insbesondere http://cr.yp.to/ucspi-tcp/tcpserver.html, das fast genauso arbeitet. Allerdingss wird dort erst die fertig aufgebaute Client-Verbindung weitergereicht, nicht schon der offene Server-Port.

Mein Programm prüft nun die Portnummer und gegebenenfalls an Hand der Umgebungsvariablen USER, ob es mit root-Rechten ausgeführt wird.

Warum anhand von $ENV{'USER'}? Lies bitte mal in perlvar nach, was $< und $> bedeuten.

Falls dies nicht zutrifft, ruft sich das Programm selbst via sudo erneut auf.

Warum?

Wenn der User keine Root-Rechte hat und auch nicht haben darf, hilft sudo auch nicht.

Wenn der User sudo ausführen darf, dann soll er das bitte explizit machen, damit er sich bewußt bleibt, dass er mehr Rechte als normal hat.

Die Frage ist allerdings, welche weiteren Sicherheitslücken ich noch übersehen habe.

Du hast neue eingebaut. Angefangen bei sudo.

Fällt euch etwas auf:

Oh ja.

#! /usr/bin/perl -Tw

$ENV{'PATH'} = '/usr/bin';      # sicherer path

PATH ist nicht die einzige Environment-Variable, die Du auf sichere Werte setzen solltest. Lies bitte [link:http://perldoc.perl.org/perlsec.html@title=perlsec].

use strict;
use IO::Socket;

my $port        = ($ARGV[0] || 2999);   # Port
my $app;

untainting $0 and @ARGV:

if ($0 =~ /^(./[[:alpha:]]+)/) {      # lasse nur lokale Dateien zu
        $app = $1

Was genau läßt Du hier zu?

.
gefolgt von
/
gefolgt von
mindestens einem alphanumerischen Zeichen
gefolgt von
beliebig vielen beliebigen Zeichen

Das trifft z.B. auch auf ./a/../../../../.. zu.

}
else {
        $app = './WebServer.pl'         # Standard Skriptname
}

Und wenn das Pattern nicht paßt, nimmst Du willkürlich einen Namen, von dem Du nicht einmal prüfst, ob er paßt.

$ARGV[0] = $1 if ($ARGV[0] =~ /^([1-9]{1}[0-9]*)$/);

if ($port < 1024 && $ENV{'USER'} ne 'root') {
        print 'sudo benoetigt um auf Port ', $port, " zuzugreifen\n";
        exec 'sudo', './WebServer.pl', @ARGV

Wozu überhaupt das Theater, wenn Du ohnehin den festen Namen benutzt?

Wo war noch gleich sudo? /home/foken/bin/evil/sudo?

OK, Du hast $ENV{'PATH'} gesetzt, und endest damit zwangsläufig bei /usr/bin/sudo.

Und wo war gleich noch ./WebServer.pl?

Du hast ein /home/robert/WebServer.pl.

Ich bringe dich dazu, in /tmp zu arbeiten.

Dort habe ich MEIN WebServer.pl abgelegt. Das macht böse Dinge.

Du startest in /tmp dein Script, als robert.

perl -Tw /home/robert/WebServer.pl
oder meinetwegen
/home/robert/WebServer.pl

Du endest bei der Zeile exec 'sudo','./WebServer.pl'.

sudo fragt Dich nach Deinem Passwort. Du gibst es ein, denn schließlich startet das ja nur Dein Script, oder?

Das aktuelle Verzeichnis ist /tmp, nicht /home/robert.

sudo startet /tmp/WebServer.pl mit root-Rechten.

MEIN WebServer.pl, dass mir die volle Kontrolle über Deinen Rechner verschafft. Nicht DEIN WebServer.pl.

Du stehst mit runtergelassener Hose da. Sorry.

Die außerdem bestehende [link:http://en.wikipedia.org/wiki/Time-of-check-to-time-of-use@title=TOCTOU]-Lücke findest Du in der WP erklärt. Hinweis: Ich ersetze Dein Script zwischen dem Aufruf von /usr/bin/perl und dem Aufruf von sudo durch ein anderes Programm.

# schlaegt fehl falls @ARGV nicht untainted werden konnte

Und im Fehlerfall ignorierst Du den Fehler komplett? Keine Meldung, einfach fröhlich weiter im Text? Und hoffen, dass irgendwann die fehlenden Root-Rechte einen Fehler erzeugen?

}

  
Alexander

-- 
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".