setuid 비트를 올바르게 사용하기


45

일반 사용자가 실행할 때 루트 권한이 필요한 프로세스가 있습니다. 분명히 "setuid bit"를 사용하여이 작업을 수행 할 수 있습니다. POSIX 시스템에서이를 수행하는 올바른 방법은 무엇입니까?

또한 인터프리터 (bash, perl, python, php 등)를 사용하는 스크립트로 어떻게 할 수 있습니까?

답변:


53

의 setuid 비트를 실행할 때 다른 경우, 프로그램이 파일 대신 실제 사용자의 소유자의 권한이 때문에 이렇게 실행 파일에 설정할 수 있습니다. 유효 uid (사용자 ID)와 실제 uid 의 차이점 입니다.

와 같은 일부 일반적인 유틸리티 passwd는 루트를 소유하고 필요에 따라이 방식으로 구성 됩니다 (루트 만 읽을 수있는 passwd액세스 가 필요함 /etc/shadow).

이 작업을 수행 할 때 가장 좋은 전략은 바로 수퍼 유저로 수행해야하는 모든 작업을 수행 한 다음 권한을 낮추어 루트를 실행하는 동안 버그 나 오용이 발생하지 않도록하는 것입니다. 이를 위해 프로세스의 유효 uid를 실제 uid로 설정합니다. POSIX C에서 :

#define _POSIX_C_SOURCE 200112L // Needed with glibc (e.g., linux).
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

void report (uid_t real) {
    printf (
        "Real UID: %d Effective UID: %d\n",
        real,
        geteuid()
    );
}

int main (void) {
    uid_t real = getuid();
    report(real);
    seteuid(real);
    report(real);
    return 0;
}

POSIX 시스템에서 일반적으로 사용되는 경우 가장 높은 수준의 언어에서 동등한 기능을 가져야하는 관련 기능 :

  • getuid(): 실제 UID를 가져 옵니다 .
  • geteuid(): 효과적인 UID를 얻으십시오 .
  • seteuid(): 유효 UID를 설정합니다 .

setuid 비트가 실행 파일에 설정되어있는 경우를 제외하고 는 실제 uid에 부적합한 마지막 것은 아무것도 할 수 없습니다 . 이것을 시도하려면 컴파일하십시오 gcc test.c -o testuid. 그런 다음 권한이 있어야합니다.

chown root testuid
chmod u+s testuid

마지막은 setuid 비트를 설정합니다. 이제 ./testuid일반 사용자로 실행 하면 기본적으로 프로세스가 유효 uid 0, 루트로 실행되는 것을 볼 수 있습니다.

스크립트는 어떻습니까?

이것은 플랫폼마다 다르지만 Linux에서는 바이트 코드를 포함하여 인터프리터가 필요한 것은 인터프리터에 설정되어 있지 않으면 setuid 비트를 사용할 수 없습니다 (매우 어리 석습니다). 위의 C 코드를 모방 한 간단한 펄 스크립트는 다음과 같습니다.

#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);

print "Real UID: $< Effective UID: $>\n";
$> = $<; # Not an ASCII art greedy face, but genuine perl...
print "Real UID: $< Effective UID: $>\n"; 

* nixy 근본 인 펄은 유효 uid ( $>)와 실제 uid ( $<)를 위한 특수 변수를 만들었습니다 . 그러나이 같은 시도하는 경우 chownchmod컴파일 된 실행 파일 (C, 앞의 예에서), 그것은 어떤 차이를하지 않습니다와 함께 사용합니다. 스크립트가 권한을 얻을 수 없습니다.

이에 대한 대답은 setuid 바이너리를 사용하여 스크립트를 실행하는 것입니다.

#include <stdio.h>
#include <unistd.h> 

int main (int argc, char *argv[]) {
    if (argc < 2) {
        puts("Path to perl script required.");
        return 1;
    }
    const char *perl = "perl";
    argv[0] = (char*)perl;
    return execv("/usr/bin/perl", argv);
}

이 컴파일 gcc --std=c99 whatever.c -o perlsuidchown root perlsuid && chmod u+s perlsuid. 이제 실행할 수 있는 , 0의 유효 uid와 함께 펄 스크립트를 관계없이 누구 소유의.

PHP, python 등에서도 비슷한 전략이 적용됩니다 .

# Think hard, very important:
>_< # Genuine ASCII art "Oh tish!" face

제발 이런 종류의 물건을 두지 마십시오 . 대부분의 경우 실제로 스크립트 이름을 절대 경로 로 컴파일하려고 합니다 . 즉, 모든 코드를 다음 main()과 같이 바꿉니다.

    const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
    return execv("/usr/bin/perl", (char * const*)args);

/opt/suid_scripts그들은 루트가 아닌 사용자를 위해 그 안의 모든 것이 읽기 전용 인지 확인하십시오 . 그렇지 않으면 누군가가를 대신 할 수 whatever.pl있습니다.

또한 많은 스크립팅 언어를 사용하면 환경 변수가 스크립트 실행 방식을 변경할 수 있습니다 . 예를 들어, 환경 변수로 인해 호출자가 제공 한 라이브러리가로드되어 호출자가 임의 코드를 루트로 실행할 수 있습니다. 따라서 인터프리터와 스크립트 자체가 가능한 모든 환경 변수에 대해 강력하다는 것을 알지 못하면 이 작업을 수행하지 마십시오 .

대신 어떻게해야합니까?

루트가 아닌 사용자가 루트로 스크립트를 실행할 수있게하는보다 안전한 방법은 sudo 규칙 을 추가 하고 사용자를 실행하는 것 sudo /path/to/script입니다. Sudo는 대부분의 환경 변수를 제거하고 관리자가 명령을 실행할 수있는 사람과 인수를 세부적으로 선택할 수 있도록합니다. 암호 프롬프트없이 특정 프로그램을 루트로 실행하는 방법을 참조하십시오 . 예를 들어.


1
쉘로 호출되면 많은 쉘이 다르게 작동 -privileged하며, 책임있는 스크립트에서 호출 된 경우에도 안전하게 호출 suid root할 수 있습니다. 하지만 책임감있는 대본 의 개념이 실제 세계에서는 약간의 꿈이라고 생각합니다. 어쨌든, 권한이 상승한 상태에서 가능하다면 모든 종류의 글을 피하는 것이 얼마나 중요한지 언급 할 가치가 있습니다.
mikeserv

@ mikeserv " -privilegedshell"과 "responsible script" 의 개념에 익숙하지 않습니다 . 쉘에 대한 다른 답변을 원하십니까? )이 작업을 수행하는 많은 좋은 이유가되지 않습니다 - 내가 당신에게 물론 진드기를 장담 할 수 없어 sudo더 나은 옵션이 가능한 경우는 - 나는 PHP에서 시스템 암호 인증에 대한 초기 질문에 따른 일반적인 답변으로 쓴 .
goldilocks

1
나는 정말로 그렇게하기를 원하지 않습니다- 책임있는 스크립트정말 어렵습니다 . 하지만 난의 분석에 동의하지 않는 말을해야 sudo내가 생각 - sudo같은 정신병 이 척한다는 점에서 하지root. 쉘 글로브를에 넣을 수도 있습니다 sudoers. 라이브 응용 프로그램에는 좋지만 su스크립팅 목적으로 더 좋습니다 sudo. 그러나 어느 쪽도 기호가있는 스크립트로 명시 적으로하지 #!/bin/ksh -pbangline 또는 무엇이든 suid ksh에가 $PATH있다.
mikeserv

이 답변이 문제를 해결하는 동안 setuid 스크립트는 로컬 권한 에스컬레이션 취약점을 열 수있는 레시피입니다. 스크립트를 쉽게 setuid로 만들 수없는 이유가 있습니다. 정답은 sudo입니다.
mdadm

적어도 환경 변수에 대한 큰 취약점을 언급하기 위해 귀하의 답변을 편집 할 자유를 얻었습니다. 나는 이것이 안전하지 않은 방법 (스크립트의 setuid 래퍼)에 많은 혼란을 겪기 때문에 정식 답변만큼 좋아하지 않습니다. sudo를 추천하고 다른 스레드에 대한 확장 토론을 남겨 두는 것이 가장 좋습니다 (나는 거기 에서 다루었습니다 ).
질 'SO-정지 존재 악마'

11

그렇습니다, 그러나 아마 아주 나쁜 생각 입니다. 일반적으로 실행 파일에 SUID 비트를 직접 설정하지는 않지만 sudo (8) 또는 su (1) 을 사용하여 실행합니다 (누가 실행할 수 있는지 제한).

그러나 일반 사용자가 프로그램 (특히 스크립트!)을 루트로 실행할 수있게하는 데 많은 보안 문제가 있습니다. 대부분의 경우 프로그램이이를 처리하기 위해 구체적이고 매우 신중하게 작성되지 않는 한 악의적 인 사용자는이 프로그램을 사용하여 루트 셸을 쉽게 얻을 수 있습니다.

따라서 그렇게하더라도 환경, 명령 줄 매개 변수, STDIN 및 처리하는 모든 파일, 네트워크 연결에서 오는 데이터를 포함하여 루트로 실행하려는 프로그램의 모든 입력을 먼저 삭제하는 것이 좋습니다. 열림 등을하기가 매우 어렵습니다. 심지어 제대로하기가 더 어렵습니다.

예를 들어 편집기를 사용하여 일부 파일 만 편집하는 사용자를 허용 할 수 있습니다. 그러나 편집기는 외부 헬퍼의 명령 실행 또는 일시 중단을 허용하여 사용자에게 원하는대로 루트 쉘을 제공합니다. 또는 보안 성이 뛰어난 쉘 스크립트를 실행하고있을 수도 있지만 사용자는 수정 된 $ IFS 또는 다른 환경 변수를 사용하여 동작을 완전히 변경하여이를 실행할 수 있습니다. 또는 "ls"를 실행해야하는 PHP 스크립트 일 수 있지만 사용자는 $ PATH를 수정하여 실행하므로 "ls"라는 쉘을 실행합니다. 또는 수십 가지의 다른 보안 문제.

프로그램이 실제로 작업의 일부를 루트로 수행해야하는 경우, 일반 사용자로 실행되도록 수정하는 것이 가장 좋지만 suid helper 프로그램을 통해 루트가 필요한 부분은 (가능한 한 위생 처리 한 후) 실행하십시오.

모든 로컬 사용자를 완전히 신뢰하더라도 (루트 암호를 제공하는 것과 같이) 사용자가 의도하지 않은 보안 감시 (예 : 온라인으로 PHP 스크립트를 사용하거나 암호를 약하게하거나 원격 공격자가 전체 시스템을 완전히 손상시킬 수 있습니다.


1
둘째. 투시 안전 호출을위한 POSIX 프로그래머의 일련의 예를 man sh을 할 실패하더라도와 - command -p env -i .... 결국하는 것은 꽤 어렵다. 그러나 경우 올바르게 수행, 명시 적 목적을 위해, 더 나은을 할 수 있습니다 suid유능한 통역 사용보다 susudo- 둘 중 하나의 동작은 항상 스크립트의 통제 밖에 될 수있는 바와 같이 비록 ( su적게는 경향이 커브 볼을 던질 것입니다 약간 미치도록) . 전체 패키지를 번들로하면 유일하고 확실한 내기이다 - 그것은 단지 더 나은했다 있는지 전부입니다.
mikeserv
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.