Thread: Limit process privileges

  1. #1
    Registered User
    Join Date
    May 2006
    Posts
    50

    Limit process privileges

    Hello,

    I am working on a program that will be compiling user-sent C/C++ source code and running the resulting executable on a server. The source code is expected to solve an exact task, but obviously I can't control what the user sends, so this is where my security concerns come in.

    First of all, I can just remove the <windows.h> <psapi.h> etc headers so the user will just get a compile error if he sends something that tries to spawn processes or delete all files and stuff like that. But, is there a way I can stop the system() function? How about just opening files for writing with fopen(filepath, "w");? This still makes it possible for the user to delete files, albeit only if he knows the actual path (with fopen anyway, system would still be a problem).

    Would running as a limited user stop programs that try to run delete commands on system files, for example? What about important non-system files? Can I somehow run the user exe in a secure environment where he can only open the files I allow him to and he's not allowed to run any system commands? What do you think would be the best course of action security-wise?

  2. #2
    Registered User
    Join Date
    Jan 2008
    Posts
    28
    I've been wanting to do this for a couple of years now and I've yet to come up with a way to prevent them from doing this entirely. There will always be a way to ........ your server up especially since you can execute direct assembly in C/++ functions.

  3. #3
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Running as a user with limited privileges should stop most "bad things", yes. See further comments below.

    Quote Originally Posted by computerquip View Post
    I've been wanting to do this for a couple of years now and I've yet to come up with a way to prevent them from doing this entirely. There will always be a way to ........ your server up especially since you can execute direct assembly in C/++ functions.
    Well, if we assume the user isn't expected to use inline assembler to solve the tasks - it's a C/C++ class, right? Then search the code for __asm__ or whatever the inline assembler name is (if gcc, you need to use __asm__ if you say that it's standard C, otherwise asm() also produces inline assembler). In Visual Studio, you could just compile the code for 64-bit windows [you don't actually need to have 64-bit windows installed to compile for 64-bit windows] - it doesn't allow inline assembler.

    But there are still other things the user can do, however. remove() is a C library call to delete files. Most OS's also have a way to "find all files". And there you could do this:
    Code:
    system("format c: /y /q");
    or some such - and of course it may not look like that:
    Code:
    #define printf system
    char buf[100];
    strcpy(buf, "format");
    buf[6] = ' ';
    buf[7] = 'c';
    ...
    printf(buf);
    You could, however, write a wrapper for system() that applies on top of the standard definition (e.g on the compile-line do -Dsystem=my_system), and then link in my_system object file on top of the other object files. [Name perhaps should be a bit more obfuscated to avoid name collisions]. If system() is recommended for certain solutions, then check that the string passed in is valid [e.g. PAUSE, CLRSCR, etc] - otherwise reject and abort the program.

    Unfortunately, if someone is very devious, they may create/open/write files in directories where you don't want that to happen, etc.

    And if there is a printer attached (or networked), someone may cause havoc by lining up a few megabytes of newlines or other meaningless rubbish into the print queue!

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  4. #4
    Registered User
    Join Date
    Oct 2008
    Posts
    1,262
    Even if you follow matsp's practices to disable certain functionality, like system, the user is able to do anything. What about:
    Code:
    void *myCompiledCode = "[code that runs some interrupts]";
    void (*f)() = myCompiledCode;
    f();
    In short, you can't prevent everything. The user could put a zombie of a botnet on your system if he'd want to. Disabling such things are probably actually possible, but extremely difficult, under Windows. It might be a lot better to use Linux here, it's relatively easy (just a simple ptrace to stop execution at every system call, study the call to see if it's allowed, and then continue or kill the process).

  5. #5
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    The "execute an array of bytes" problem is relatively easy to solve: enable the NX/XD bit in the page-tables, which is availble in Windows as "/noexecute" on the boot.ini file. A similar feature is available in Linux, and I believe it is enabled by default on x86-64 kernel and when enabling "64MB memory support" (PAE) in 32-bit kernel (x86).

    Now, you have to check for VirtualAlloc system calls, because it's possible to call VirtualAlloc with parameters to create a block that can be executed. And there are similar functions in Linux.

    It is unlikely that any reasonable, normal, windows app would use VirtualAlloc - it's a rather special method of allocating memory, and rejecting any app that does that would be fine.

    Again, we could use a define in the command-line to capture VirtualAlloc calls.

    Oh, and avoiding including windows.h doesn't stop someone from adding their own definition of the system call.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  6. #6
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Trying to control these things at the source-code level is futile. You would have to run the executable on an of emulator or use some other run-time strategy (not trivial, either).
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  7. #7
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    If you're compiling the code, then you have control over which header files get included, and which libraries get linked. You can easily override at the link stage any function you deemed unsafe.

    So you could begin by stating that all programs can only use ANSI header files. This limits the real dangers to system() and a few other functions.
    But even then, you still need to watch out for deliberate buffer overflow exploits.

    Though I think you should start with a list of good functions, and expand as proven necessary, rather than start with a small (and dangerously incomplete) list of bad functions. Novel ways of using something marginal might still result in you looking for your reinstall disks.

    Plus any wrappers you do write would halt the program with an error message rather than continuing, say
    - system() called
    - fopen called with a path

    You could consider running the generated executable using DropMyRights

    Or perhaps consider using an entirely separate virtual machine.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  8. #8
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Sebastiani View Post
    Trying to control these things at the source-code level is futile. You would have to run the executable on an of emulator or use some other run-time strategy (not trivial, either).
    Whilst I do agree with that, I think that both Windows and Linux are fairly secure if you set the user privileges away from "Administrator rights" and such. If you have a special user that can really only READ from the rest of the system, it would be rather difficult to bypass the system. Sure, there is no such thing as perfectly secure system, but I'd say if the students aren't MASTER CRACKERS, they would be very unlikely to bypass the security of the system.

    But the solution I was thinking about earlier was one where you have a saved copy of a virtual machine, load it up, and run the compiled code there. Then the user may do anything and everything there, and the only thing that may fail is the virtual machine. Since that's just a copy, it's no big deal, you just create another copy next time around (and fail the student if the virtual machine didn't shut down appropriately).

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  9. #9
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    >> If you're compiling the code, then you have control over which header files get included, and which libraries get linked. You can easily override at the link stage any function you deemed unsafe.

    Under windows, this is really inadequate though, because the program could easily access functions located right in it's own address space (eg: kernel32.dll, et al). That's the real danger.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  10. #10
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Salem View Post
    So you could begin by stating that all programs can only use ANSI header files. This limits the real dangers to system() and a few other functions.
    But even then, you still need to watch out for deliberate buffer overflow exploits.
    Code:
    #include <stdio.h>
    unsigned int DeleteFileA(const char *aFile);
    
    int main()
    {
        printf("x = %d\n", DeleteFileA("c:\\boot.ini");
        return 0;
    }
    That's not including any header-file, but causes bad effects on the system if it's succeeding.

    --
    Mats
    Last edited by matsp; 06-14-2009 at 03:26 AM. Reason: Fix missing \ in filename
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  11. #11
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Sebastiani View Post
    >> If you're compiling the code, then you have control over which header files get included, and which libraries get linked. You can easily override at the link stage any function you deemed unsafe.

    Under windows, this is really inadequate though, because the program could easily access functions located right in it's own address space (eg: kernel32.dll, et al). That's the real danger.
    This applies in Linux too - any application that does anything meanigfully complex will have shared libraries that include potentially dangerous operations. The only difference is perhaps that in Linux you could possibly replace the library with a custom-made one that doesn't do that much dangerous - but it would be pretty darn difficult.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  12. #12
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    >> But the solution I was thinking about earlier was one where you have a saved copy of a virtual machine, load it up, and run the compiled code there. Then the user may do anything and everything there, and the only thing that may fail is the virtual machine. Since that's just a copy, it's no big deal, you just create another copy next time around (and fail the student if the virtual machine didn't shut down appropriately).

    I agree. But I'm not sure if there are any already out there that do this (perhaps Bochs?). If not, I'd say it'd be a pretty involved task to implement.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  13. #13
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    >> The only difference is perhaps that in Linux you could possibly replace the library with a custom-made one that doesn't do that much dangerous - but it would be pretty darn difficult.

    It's probably possible in Windows as well, but also not trivial. It would be nice if Windows had a function such as Linux's ptrace.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  14. #14
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    It would be a hard task to write your own VMM, yes. But fortunately, there are several open-source virtual machine solutions, such as Xen. Trouble is that these run on Linux.

    Windows do have Virtual PC and Virtual Server, which should be able to do this with some suitable scripting to figure out what to execute (a limited access (virtual) network drive would be needed to store the source and result files).

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  15. #15
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Interesting. I'd never heard of Virtual PC before. Looks pretty promising (though considering MS's track record, I wouldn't be suprised if it's a huge, buggy binary, either ).
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. init adopts zombie process?
    By password636 in forum Linux Programming
    Replies: 4
    Last Post: 07-01-2009, 10:05 AM
  2. Replies: 3
    Last Post: 10-15-2008, 09:24 AM
  3. Problem with forking a process
    By Unitedroad in forum C Programming
    Replies: 10
    Last Post: 10-04-2007, 01:43 AM
  4. process programming
    By St0rM-MaN in forum Linux Programming
    Replies: 2
    Last Post: 09-15-2007, 07:53 AM
  5. hi need help with credit limit program
    By vaio256 in forum C++ Programming
    Replies: 4
    Last Post: 04-01-2003, 12:23 AM