쉘 스크립트에서 setuid 허용


184

setuid권한 비트 대신 집행의 소유자의 유효 사용자 ID로 프로그램을 실행하기 위해 리눅스를 알려줍니다 :

> cat setuid-test.c

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

int main(int argc, char** argv) {
    printf("%d", geteuid());
    return 0;
}

> gcc -o setuid-test setuid-test.c
> ./setuid-test

1000

> sudo chown nobody ./setuid-test; sudo chmod +s ./setuid-test
> ./setuid-test

65534

그러나 이것은 실행 파일에만 적용됩니다. 쉘 스크립트는 setuid 비트를 무시합니다.

> cat setuid-test2

#!/bin/bash
id -u

> ./setuid-test2

1000

> sudo chown nobody ./setuid-test2; sudo chmod +s ./setuid-test2
> ./setuid-test2

1000

위키 백과 는 말합니다 :

보안 결함 가능성이 높아짐에 따라 많은 운영 체제는 실행 가능한 쉘 스크립트에 적용될 때 setuid 속성을 무시합니다.

내가 그런 위험을 감수 할 것이라고 가정하면, 리눅스가 setuid 비트를 쉘 스크립트에서 실행 파일과 동일하게 취급하도록 할 수있는 방법이 있습니까?

그렇지 않은 경우이 문제에 대한 일반적인 해결 방법이 있습니까? 내 현재 솔루션은 암호 프롬프트를 피하기 위해 원하는 스크립트를 실행할 사용자로 지정된 스크립트를 실행할 sudoers수 있도록 항목 을 추가하는 ALLNOPASSWD입니다. 그것의 주요 단점은 sudoers내가 이것을하고 싶을 때마다 항목이 필요하고 발신자가 sudo some-script그냥 대신 해야한다는 것입니다.some-script

답변:


202

Linux는 해석 된 모든 실행 파일 (즉, #!줄로 시작하는 실행 파일)에서 setuid¹ 비트를 무시합니다 . comp.unix.questions 자주 묻는 질문은 이 setuid 쉘 스크립트와 보안 문제를 설명합니다. 이러한 문제는 두 가지 종류가 있습니다 : shebang 관련 및 shell 관련; 자세한 내용은 아래를 참조하십시오.

보안에 신경 쓰지 않고 setuid 스크립트를 허용하려면 Linux에서 커널을 패치해야합니다. 3.x를 커널로, 나는 당신이 전화를 추가 할 필요가 있다고 생각 install_exec_credsload_script호출하기 전에, 기능 open_exec,하지만 테스트하지 않았습니다.


세이트 세방

shebang ( #!)이 일반적으로 구현 되는 방식에 고유 한 경쟁 조건이 있습니다 .

  1. 커널은 실행 파일을 열고로 시작하는 것을 찾습니다 #!.
  2. 커널은 실행 파일을 닫고 인터프리터를 대신 엽니 다.
  3. 커널은 스크립트의 경로를 인수 목록 ( argv[1])에 삽입하고 인터프리터를 실행합니다.

이 구현에서 setuid 스크립트가 허용되는 경우 공격자는 기존 setuid 스크립트에 대한 심볼릭 링크를 생성하고 실행하여 커널이 1 단계를 수행 한 후 및 인터프리터가 도착하기 전에 링크를 변경하여 임의의 스크립트를 호출 할 수 있습니다. 첫 번째 논쟁을여십시오. 이러한 이유로, 대부분의 유니스 는 셰방을 감지 할 때 setuid 비트를 무시합니다 .

이 구현을 보호하는 한 가지 방법은 커널이 인터프리터가 파일을 열 때까지 스크립트 파일을 잠그는 것입니다 (이것은 파일을 링크 해제하거나 덮어 쓰는 것뿐만 아니라 경로의 디렉토리 이름을 바꾸는 것을 방지해야 함). 그러나 유닉스 시스템은 필수 잠금에서 벗어나는 경향이 있으며 기호 링크는 올바른 잠금 기능을 특히 어렵고 침습적으로 만듭니다. 아무도 그렇게하지 않는다고 생각합니다.

몇 유닉스 시스템 (설정 커널을 필요로 모두 주로 오픈 BSD, NetBSD의 및 Mac OS X가 활성화 될)를 구현 보안이 setuid 오두막을 추가 기능을 사용 : 경로가 이미 파일 기술자에 열린 파일을 참조 N (그래서 개방 입니다 대략)와 같습니다 . Linux를 포함한 많은 유닉스 시스템 에는 setuid 스크립트 가 있지만 설정되어 있지 않습니다./dev/fd/N/dev/fd/Ndup(N)/dev/fd

  1. 커널은 실행 파일을 열고로 시작하는 것을 찾습니다 #!. 실행 파일의 파일 디스크립터가 3이라고 가정하십시오.
  2. 커널이 인터프리터를 엽니 다.
  3. 커널 /dev/fd/3은 인수 목록 ( argv[1])을 삽입 하고 인터프리터를 실행합니다.

Sven Mascheck의 shebang 페이지 에는 setuid 지원을 포함하여 unices에 대한 shebang 에 대한 많은 정보가있습니다.


Setuid 통역사

OS가 setuid shebang을 지원하거나 기본 바이너리 래퍼 (예 :)를 사용했기 때문에 프로그램을 루트로 실행했다고 가정 해 봅시다 sudo. 보안 구멍을 열었습니까? 아마 . 여기서 문제는 해석 된 프로그램과 컴파일 된 프로그램에 관한 것이 아닙니다 . 문제는 권한으로 실행되는 경우 런타임 시스템 이 안전하게 동작 하는지 여부 입니다.

  • 동적으로 연결된 네이티브 바이너리 실행 파일은 프로그램에 필요한 동적 라이브러리를로드하는 동적 로더 (예 :)에 의해 해석되는 방식 /lib/ld.so입니다. 많은 유니스에서 환경을 통해 동적 라이브러리의 검색 경로를 구성하고 ( LD_LIBRARY_PATH환경 변수의 일반적인 이름 임) 실행 된 모든 바이너리 ( LD_PRELOAD)에 추가 라이브러리를로드 할 수도 있습니다. 프로그램의 호출자는 특수하게 조작 libc.so$LD_LIBRARY_PATH(다른 전술 중에서) 배치함으로써 해당 프로그램의 컨텍스트에서 임의의 코드를 실행할 수 있습니다 . 모든 제정신 시스템 LD_*은 setuid 실행 파일 의 변수를 무시합니다 .

  • 에서 껍질 같은 SH, CSH 및 유도체, 환경 변수 자동 쉘 매개된다. PATH, 등의 매개 변수를 통해 IFS스크립트 호출자는 쉘 스크립트 컨텍스트에서 임의의 코드를 실행할 수있는 많은 기회가 있습니다. 일부 셸은 스크립트가 권한으로 호출 된 것을 감지하면 이러한 변수를 적절한 기본값으로 설정하지만, 신뢰할만한 특정 구현이 있는지는 알 수 없습니다.

  • 대부분의 런타임 환경 (기본, 바이트 코드 또는 해석)은 비슷한 기능을 가지고 있습니다. setuid 실행 파일에서 특별한 예방 조치를 취하는 사람은 거의 없지만 기본 코드를 실행하는 작업은 동적 연결 (예방 조치가 필요함)보다 더 멋진 일을하지 않는 경우가 많습니다.

  • Perl 은 주목할만한 예외입니다. 그것은 명시 적으로이 setuid 스크립트를 지원하는 안전한 방법입니다. 실제로, OS에서 스크립트의 setuid 비트를 무시하더라도 스크립트는 setuid를 실행할 수 있습니다. 이는 perl에 필요한 검사를 수행하고 원하는 권한으로 원하는 스크립트에서 인터프리터를 다시 호출하는 setuid 루트 도우미와 함께 제공되기 때문입니다. 이것은 perlsec 매뉴얼에 설명되어 있습니다. 그것은이 setuid 펄 스크립트가 필요하다는로 사용 #!/usr/bin/suidperl -wT대신 #!/usr/bin/perl -wT하지만, 대부분의 현대 시스템에 #!/usr/bin/perl -wT충분하다.

네이티브 이진 래퍼 를 사용하면 이러한 문제를 방지하기 위해 자체적으로 아무 것도 수행하지 않습니다 . 실제로 런타임 환경이 특권으로 호출되고 런타임 구성 가능성을 무시하고 있음을 감지하지 못할 수 있으므로 상황을 악화시킬 수 있습니다.

랩퍼 가 환경을 삭제하면 원시 바이너리 랩퍼가 쉘 스크립트를 안전하게 만들 수 있습니다 . 스크립트는 너무 많은 가정을하지 않도록주의해야합니다 (예 : 현재 디렉토리). 환경을 위생 처리하도록 설정된 경우 sudo를 사용할 수 있습니다. 블랙리스트 변수는 오류가 발생하기 쉬우므로 항상 화이트리스트에 올리십시오. sudo를 함께, 있는지 확인 env_reset옵션 즉, 온 setenv오프, 그리고 그 env_fileenv_keep단지 무해한 변수가 포함되어 있습니다.


TL, DR :

  • Setuid shebang은 안전하지 않지만 일반적으로 무시됩니다.
  • 특권 (sudo 또는 setuid를 통해)으로 프로그램을 실행하는 경우 원시 코드 또는 perl을 작성하거나 환경을 삭제하는 랩퍼 (예 : env_reset옵션을 사용하여 sudo)로 프로그램을 시작하십시오 .

¹ 이 설명은 "setgid"를 "setuid"로 대체하는 경우에도 동일하게 적용됩니다. 스크립트에서 Linux 커널에 의해 무시됩니다.


2
@Josh : 보안 setuid 셸 스크립트가 가능하지만 셸 구현 자와 스크립트 작성기 모두 매우주의해야합니다. 필자는 구현자가 네이티브 코드 대신 스크립트 작성자가 거의 노력하지 않아도 setuid 스크립트를 안전하게 보호 할 수 있도록주의를 기울인 Perl을 추천합니다.
Gilles

3
분명히 suidperl물건은 더 이상 사용되지 않으며 수년간 제거 대상으로 표시되었습니다 (그러나 계속
유지됨

7
실제로 suidperlperl 5.11 (5.12 안정)에서 제거되었습니다. perl5110delta :> "suidperl"이 제거되었습니다. 제대로 지원하지 않는 시스템에서 setuid 권한 비트를 에뮬레이트하는 메커니즘을 제공했습니다. perl5120delta :> "suidperl"은 더 이상 Perl의 일부가 아닙니다. 제대로 지원하지 않는 시스템에서 setuid 권한 비트를 에뮬레이트하는 메커니즘을 제공했습니다.
랜디 스타 너

5
또한 perl 5.6.1 문서 (약 10 년 전)의이 줄에 주목하십시오. suidperl의 사용은 권장하지 않습니다 . 필요하다고 생각되면 먼저 sudo와 같은 대안을 시도하십시오. courtesan.com/sudo를 참조하십시오 .
랜디 스타 너

3
이해가되지 않습니다 : 이것은 문제의 이유에 대한 설명 인 것 같지만 여기에 OP의 질문에 대한 실제 대답이 있습니까? OS에 setuid 쉘 스크립트를 실행하도록 지시하는 방법이 있습니까?
Tom

55

이 문제를 해결하는 한 가지 방법은 setuid 비트를 사용할 수있는 프로그램에서 쉘 스크립트를 호출하는 것입니다.
sudo와 같은 것. 예를 들어, C 프로그램에서이를 수행하는 방법은 다음과 같습니다.

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    setuid( 0 );   // you can set it at run time also
    system( "/home/pubuntu/setuid-test2.sh" );
    return 0;
 }

setuid-test2.c로 저장하십시오.
컴파일
이제이 프로그램 바이너리에서 setuid를 수행하십시오.

su - nobody   
[enter password]  
chown nobody:nobody a.out  
chmod 4755 a.out  

이제 실행할 수 있어야하며 아무도 권한없이 스크립트가 실행되는 것을 볼 수 있습니다.
그러나 여기서는 스크립트 경로를 하드 코딩하거나 위의 exe에 명령 줄 arg로 전달해야합니다.


25
필자는 프로그램을 실행할 수있는 모든 사람에게 정의 된 사용자로 스크립트를 실행할 수있는 기능을 기본적으로 제공하므로 스크립트를 명령 줄 인수로 전달할 것을 제안하지 않는 것이 좋습니다.
dsp

39
스크립트에 대한 전체 경로가 하드 코드되어 있어도 이것은 보안입니다. 쉘은 환경으로부터 변수를 상속받으며, 많은 변수는 호출자가 임의의 코드를 주입 ​​할 수있게합니다. PATH그리고 LD_LIBRARY_PATH명백한 벡터입니다. 어떤 쉘을 실행 $ENV하거나 $BASHENV또는 ~/.zshenv스크립트 내에서 이들 모두로부터 보호 할 수 있도록, 그들이 스크립트가 적절한 실행을 시작도하기 전에. 특권으로 쉘 스크립트를 호출하는 유일한 안전한 방법 은 환경정리하는 것입니다 . Sudo는 안전하게 수행하는 방법을 알고 있습니다. 따라서 자신의 래퍼를 쓰지 말고 sudo를 사용하십시오 .
Gilles

28
나는 그가 이것에 대해 갑자기 하향 조정되고 있다는 것이 안타깝습니다. 특히 안전하지 않은 버전도 듣고 싶다고 말했고 쉘 스크립트 인수를 말했을 때 실행 파일을 상상하고있었습니다. 분명히 그것은 매우 안전하지 않지만, 어떤 가능성이 있는지 알고 싶었습니다
Michael Mrozek

8
@Gilles : 참고로 리눅스 LD_LIBRARY_PATH는 setuid 비트를 만나면 무엇보다도 설정을 해제합니다 .
grawity

3
대신에 가족 system중 하나를 사용하는 것이 더 간단하고 효율적일 수 있습니다 . 이렇게하면 새 프로세스를 만들거나 셸을 시작할 수 없으며 권한있는 스크립트가 안전하게 인수를 처리 할 수 ​​있다고 가정하여 인수를 전달할 수 있습니다. execexecve
Toby Speight

23

이 보트에있는 몇 가지 스크립트를 접두어로 사용합니다.

#!/bin/sh
[ "root" != "$USER" ] && exec sudo $0 "$@"

이것은 사용하지 않고 setuid단순히로 현재 파일을 실행합니다 sudo.


4
이것은 setuid를 사용하지 않고 단지 sudo프롬프트를 제공 합니다. (나에게 setuid의 핵심은 sudo를 필요로하지 않고 루트로 실행할 수있게하는 것이다.)
Luc

12

전화하지 않으려면 다음을 sudo some_script수행하십시오.

  #!/ust/bin/env sh

  sudo /usr/local/scripts/your_script

SETUID 프로그램은 루트 권한으로 실행되며 사용자가 제어 할 수 있으므로 매우주의해서 설계해야합니다. 그들은 모든 것을 온전히 점검해야합니다. 다음과 같은 이유로 스크립트를 사용하여 수행 할 수 없습니다.

  • 쉘은 사용자와 크게 상호 작용하는 큰 소프트웨어입니다. 모든 코드를 검사하는 것은 거의 불가능합니다. 특히 대부분의 코드가 그러한 모드에서 실행되도록 설계되지 않았기 때문입니다.
  • 스크립트는 대부분 빠른 해결책이며 대개 setuid를 허용하도록주의를 기울이지 않습니다. 잠재적으로 위험한 기능이 많이 있습니다.
  • 그들은 다른 프로그램에 크게 의존합니다. 쉘을 점검하는 것만으로는 충분하지 않습니다. sed, awk등도 확인해야합니다.

sudo온전한 검사 를 제공하지만 충분하지는 않습니다. 모든 코드를 자신의 코드로 확인하십시오.

마지막으로, 기능 사용을 고려하십시오. 일반적으로 루트 권한이 필요한 특수 권한을 사용자로 실행하는 프로세스를 제공 할 수 있습니다. 그러나 예를 들어 ping네트워크를 조작해야 하지만 파일에 액세스 할 필요는 없습니다. 그러나 그들이 상속되는지 확실하지 않습니다.


2
나는 /ust/bin/env이어야한다 /usr/bin/env.
Bob

4

sudo + 스크립트 이름에 대한 별명을 작성할 수 있습니다. 물론 별명도 설정해야하므로 sudo를 입력하지 않아도되므로 설정이 훨씬 더 수월합니다.

그러나 끔찍한 보안 위험에 신경 쓰지 않으면 setuid 셸을 셸 스크립트의 해석기로 사용하십시오. 그것이 당신에게 효과가 있는지는 모르겠지만, 아마 그럴 것 같습니다.

그래도 실제로이 작업을 수행하지 말 것을 권고합니다. 나는 단지 교육 목적으로 언급하고있다 ;-)


5
작동합니다. 당신이 말한대로 끔찍합니다. SETUID 비트는 소유자 권한으로 실행할 수 있습니다. Setuid 셸 (setuid와 함께 작동하도록 설계되지 않은 경우)은 모든 사용자의 루트로 실행됩니다. 즉 누구나 실행할 수 있습니다 rm -rf /(시리즈의 다른 명령은 집에서 IT를 수행하지 마십시오 ).
Maciej Piechotka

9
에 의해 @MaciejPiechotka는 AT HOME하지 마 당신 말은 그 직장에서 할 주시기 바랍니다? :)
peterph

4

super [-r reqpath] 명령 [args]

Super는 지정된 사용자가 마치 루트 인 것처럼 스크립트 (또는 다른 명령)를 실행할 수 있도록합니다. 또는 명령을 실행하기 전에 명령별로 uid, gid 및 / 또는 보충 그룹을 설정할 수 있습니다. 스크립트를 setuid root로 만드는 대신 안전한 대안이 될 것입니다. Super는 또한 일반 사용자가 다른 사용자가 실행할 명령을 제공 할 수 있도록합니다. 이들은 명령을 제공하는 사용자의 uid, gid 및 그룹으로 실행됩니다.

Super는``super.tab ''파일을 참조하여 사용자가 요청 된 명령을 실행할 수 있는지 확인합니다. 권한이 부여되면 super는 pgm [args]를 실행합니다. 여기서 pgm은이 명령과 관련된 프로그램입니다. 루트는 기본적으로 실행이 허용되지만 규칙에서 루트를 제외하면 여전히 거부 될 수 있습니다. 일반 사용자는 기본적으로 실행이 허용되지 않습니다.

command가 수퍼 프로그램에 대한 심볼릭 링크 (또는 하드 링크) 인 경우 % command args를 입력하는 것은 % super command args를 입력하는 것과 같습니다 (명령은 super가 아니어야합니다. 링크.)

http://www.ucolick.org/~will/RUE/super/README

http://manpages.ubuntu.com/manpages/utopic/en/man1/super.1.html


안녕하세요. 사이트에 오신 것을 환영합니다! 답변이 더 자세 할 것으로 기대합니다. 답 을 편집 하고이 프로그램이 무엇이며 OP 문제를 해결하는 데 어떻게 도움이되는지 설명해 주시겠습니까?
terdon

다른 사용자가 파일 사용자로 계정을 실행할 수 있도록 super와 같은 것을 찾으려고 몇 시간 동안 Nizam에게 감사합니다.
차드

2

어떤 이유로 sudo사용할 수없는 경우 C로 씬 래퍼 스크립트를 작성할 수 있습니다.

#include <unistd.h>
int main() {
    setuid(0);
    execle("/bin/bash","bash","/full/path/to/script",(char*) NULL,(char*) NULL);
}

컴파일하면 그리고로 설정 setuid과 함께 chmod 4511 wrapper_script.

이것은 게시 된 다른 답변과 비슷하지만 깨끗한 환경에서 스크립트를 실행하고에서 /bin/bash호출하는 쉘 대신 명시 적으로 사용 system()하므로 잠재적 인 보안 허점을 닫습니다.

이것은 환경을 완전히 버립니다. 취약점을 열지 않고 일부 환경 변수를 사용하려면 실제로 사용하면 sudo됩니다.

분명히, 스크립트 자체는 루트에 의해서만 쓰기 가능해야합니다.


-3

나는 모든 대답으로 확신 할 수없는이 질문을 처음 발견했습니다. 여기서 더 좋은 질문이 있습니다.

설명이 필요합니다.

#include <string>
#include <unistd.h>

template <typename T, typename U>
T &replace (
          T &str, 
    const U &from, 
    const U &to)
{
    size_t pos;
    size_t offset = 0;
    const size_t increment = to.size();

    while ((pos = str.find(from, offset)) != T::npos)
    {
        str.replace(pos, from.size(), to);
        offset = pos + increment;
    }

    return str;
}

int main(int argc, char* argv[])
{
    // Set UUID to root
    setuid(0);

    std::string script = 
R"(#!/bin/bash
whoami
echo $1
)";

    // Escape single quotes.
    replace(script, std::string("'"), std::string("'\"'\"'"));

    std::string command;
    command = command + "bash -c '" + script + "'"; 

    // Append the command line arguments.
    for (int a = 0; a < argc; ++a)
    {
        command = command + " " + argv[a];
    }

    return system(command.c_str());
}

그런 다음 실행

g++ embedded.cpp -o embedded
sudo chown root embedded
sudo chmod u+s embedded

왜 downvotes ???
Theodore R. Smith

2
아마도 문자열 조작을 수행하고 쉘 코드를 포함하는 지나치게 복잡한 솔루션이기 때문일 것입니다. 간단한 런처를 만들거나 sudo를 사용하십시오. 이것은 모든 옵션 중 최악입니다.
siride
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.