Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

dear monks, i'm a quite new perl user moreover english isn't my first language... but i'll try to explain properly my problem.

well, i'm setting up a script that display html page thru a socket.

but as basic it is, it permit to go up in directories and let display dangerous data like /etc/passwd etc.. So i intended to chroot the directory to secure it .this where i'm getting trouble i tried to put such lines:

my $user="nobody"; unless ($uid =(getpwnam($user))2){ die "tentative de lancer user inexistant ou root :p\n";

and just before the treatment : chroot($docroot) or die " chroot() a échoué : $!\n"; $> = $uid ;

here is the script hope that you may help me..

#! /usr/bin/perl use Socket; #config serveur my $docroot = "/var/www/html"; my $addr_serv =""; my $port_serv ="34000"; my $protocole ="tcp"; my $user="nobody"; $SIG{CHLD}="IGNORE"; unless ($uid =(getpwnam($user))[2]){ die "tentative de lancer user inexistant ou root :p\n"; } #definition socket my $proto =getprotobyname ($protocole); $proto = getprotobynumber ($protocole) unless defined ($proto); die "Protocole : $!\n" unless (defined ($proto)); my $port = $port_serv if ($port_serv !~ /\D/); $port = getservbyname ($port_serv, $proto) unless (defined ($port)); die "Services : $!\n" unless (defined ($port)); my $adr = gethostbyname ($addr_serv); $adr = gethostbyaddr ($addr_serv, AF_INET ) unless (defined ($adr)); $adr = INADDR_ANY unless (defined ($adr)); socket SOCK_SRV, PF_INET, SOCK_STREAM, $proto or die "socket : $!\n"; #bind le port et l'addresse setsockopt (SOCK_SRV , SOL_SOCKET, SO_REUSEADDR, pack ("l" , 1)); bind (SOCK_SRV, sockaddr_in ($port, $adr)) or die "bind : $!\n"; listen (SOCK_SRV, 5); chroot('/var/www/html') or die " chroot() a échoué : $!\n"; $> = $uid ; #ouvre le père jusqu'a ctrl+C for (;;) { accept SOCK, SOCK_SRV or last; ($port, $adr) = unpack_sockaddr_in getpeername SOCK; #fork le fils if (fork !=0) { close SOCK ; next; } close SOCK_SERV; select SOCK; $| = 1; chroot($docroot) or die " chroot() a échoué : $!\n"; $> = $uid ; #traitement de la requete while (<SOCK>) { #ignore tt requete sauf celle précédé de GET last if (/^\s*$/); next unless ( /^GET /); $path = (split (/\s+/)) [1]; # ouvre le fichier demandé if (open (FILE, "$docroot$path")) { my @lines = <FILE>; foreach $l(@lines){ print $l; } close (FILE); print "\n"; } # ou affiche une erreur si inexistant else { print "error" ; exit (0); } } exit (0) ; }

Replies are listed 'Best First'.
Re: chroot a directory...
by idsfa (Vicar) on Sep 09, 2004 at 16:23 UTC

    Note that you try to chroot twice:

    chroot('/var/www/html') or die " chroot() a échoué : $!\n";
    and
    chroot($docroot) or die " chroot() a échoué : $!\n";

    This probably is not what you want. Also, be careful about chroot. You may want to add a chdir as well. From the documentation:

    (It doesn't change your current working directory, which is unaffected.)

    Lastly, chroot changes root. You should not try to open $docroot$file, just $file.


    If anyone needs me I'll be in the Angry Dome.
      "Lastly, chroot changes root. You should not try to open $docroot$file, just $file."

      That's it !! As looking for $docroot in the chrooted env. the page was not displayed. Thanks a lot for your aswers,without i think i had spent a lot of time to find the solution.
Re: chroot a directory...
by muntfish (Chaplain) on Sep 09, 2004 at 16:11 UTC

    If I've understood correctly, the problem is that someone could use the .. parent directory syntax to request a file/page outside of your $docroot ?

    If so, then you may want to consider checking $path before opening the file. Use a regex to see if it contains .. preceded or followed by a / (there may be other possibilities, I haven't thought about it a great deal). If so, then deny the request.

    One other security hole you should definitely close, is to use the 3-argument open():

    open (FILE, '<', "$docroot$path")

    Otherwise you are open to people crafting a request ending with | which could allow someone to run arbitrary code on your machine.

    This is just a couple of things I thought of, there are probably other issues to be aware of as well...


    s^^unp(;75N=&9I<V@`ack(u,^;s|\(.+\`|"$`$'\"$&\"\)"|ee;/m.+h/&&print$&
      Well, if the OP goes with his plan to use chroot() instead of your suggestion, "the other" security hole isn't. There won't be executable available to run. chroot is a far better approach than "Use a regex to see if it contains .. preceded or followed by a / (there may be other possibilities, I haven't thought about it a great deal)." Not knowing whether there are other possibilities isn't exactly providing security.

        Fair enough about chroot... but there still isn't a reason to NOT use 3-arg open() - is there? Especially when the user input is untrusted.

        As per the OP's latest node, if they use:

        open (FILE, "$path")

        (rather than $docroot$path or whatever) then a client could request a $path of ">important.file" which could (depending on permissions) clobber the important file. Using the 3-arg open() suppresses the "special" interpretation of the first character.


        s^^unp(;75N=&9I<V@`ack(u,^;s|\(.+\`|"$`$'\"$&\"\)"|ee;/m.+h/&&print$&