"인수를 명령으로 해석하는 명령에 신뢰할 수없는 데이터를 전달하는 명령을 실행하십시오"


18

findutils 매뉴얼에서 :

예를 들어이 두 명령과 같은 구성

# risky
find -exec sh -c "something {}" \;
find -execdir sh -c "something {}" \;

매우 위험합니다. 그 이유는 '{}'이 세미콜론이나 쉘에 특수한 다른 문자를 포함 할 수있는 파일 이름으로 확장 되었기 때문입니다. 예를 들어 누군가 파일 /tmp/foo; rm -rf $HOME을 작성하는 경우 위의 두 명령으로 누군가의 홈 디렉토리를 삭제할 수 있습니다.

따라서 신뢰할 수없는 데이터 (예 : 파일 이름)를 인수로 해석 할 명령 (예 : 'sh')으로 명령을 해석하는 명령을 실행하지 마십시오.

셸의 경우이 문제에 대한 영리한 해결 방법이 있습니다.

# safer
find -exec sh -c 'something "$@"' sh {} \;
find -execdir sh -c 'something "$@"' sh {} \;

이 방법으로 모든 문제를 피할 수는 없지만 공격자가 선택한 데이터를 셸 명령의 텍스트로 대체하는 것보다 훨씬 안전합니다.

  1. find -exec sh -c "something {}" \;대한 교체 {}가 인용되지 않아 단일 문자열로 취급되지 않는 문제의 원인 입니까?
  2. 솔루션 find -exec sh -c 'something "$@"' sh {} \;에서

    • first {}가 대체되었지만 {}인용되지 않았으므로 "$@"원래 명령과 동일한 문제가 있습니까? 예를 들어, "$@"확장하는 것 "/tmp/foo;", "rm", "-rf",와 "$HOME"?

    • {}탈출하거나 인용하지 않습니까?

  3. 동일한 종류의 문제와 해결책이 적용 sh -c되는 다른 사례 (여전히 또는없는 경우, find필요에 따라 또는 필요하지 않은 경우도 있음)를 제시 할 수 있습니까? 가능한 한 작은 산만? `bash -c`에 의해 실행되는 명령에 인수를 제공하는 방법을 참조하십시오

감사.


답변:


29

이것은 실제로 인용과 관련이 없으며 인수 처리와 관련이 있습니다.

위험한 예를 고려하십시오.

find -exec sh -c "something {}" \;
  • 이것은 여섯 개 단어로 쉘, 및 분할에 의해 구문 분석 : find, -exec, sh, -c, something {}(따옴표 더 이상) ;. 확장 할 것이 없습니다. 쉘은 find이 6 개의 단어를 인수로 사용합니다.

  • find프로세스 발견 뭔가 말하자면 foo; rm -rf $HOME, 그것은 대체 {}와 함께 foo; rm -rf $HOME, 그리고 실행 sh인수와 함께 sh, -c그리고 something foo; rm -rf $HOME.

  • sh이제을보고 -c결과적으로 something foo; rm -rf $HOME( 옵션이 아닌 첫 번째 인수 ) 구문 분석 하고 결과를 실행합니다.

이제 더 안전한 변형을 고려하십시오.

find -exec sh -c 'something "$@"' sh {} \;
  • 쉘 실행 find인수와 find, -exec, sh, -c, something "$@", sh, {}, ;.

  • 이제 때 find발견은 foo; rm -rf $HOME, 그것이 바꾸는 {}다시 및 실행 sh인수로 sh, -c, something "$@", sh, foo; rm -rf $HOME.

  • sh보고 -c및 파싱 something "$@"실행하는 명령으로, 그리고 shfoo; rm -rf $HOME위치 매개 변수 (로 시작$0 ), 확장 "$@"foo; rm -rf $HOME 하나의 값으로 , 그리고 실행 something하나의 인수 foo; rm -rf $HOME.

당신은 이것을 사용하여 이것을 볼 수 있습니다 printf. 새 디렉토리를 작성하고 입력 한 후 실행하십시오.

touch "hello; echo pwned"

다음과 같이 첫 번째 변형 실행

find -exec sh -c "printf \"Argument: %s\n\" {}" \;

생산

Argument: .
Argument: ./hello
pwned

반면에 두 번째 변형은

find -exec sh -c 'printf "Argument: %s\n" "$@"' sh {} \;

생산

Argument: .
Argument: ./hello; echo pwned

더 안전한 변형에서 왜 {}이스케이프되거나 인용되지 않습니까?
Tim

1
일반적으로 인용 할 필요는 없습니다. 다른 의미가있는 쉘에서만 인용하면되며 요즘에는 주요 쉘이 사용되는 것으로 생각하지 않습니다.
Stephen Kitt 2018 년

에서 gnu.org/software/findutils/manual/html_mono/... : "이 구조물 (모두 ;{}.) 필요 (A '\'로) 탈출 또는 쉘에 의해 확장에서 그들을 보호하기 위해 인용되는" bash에 대해 올바르지 않다는 것을 의미합니까?
Tim

3
나는 일반적으로 껍질이 잘못되었음을 의미합니다. POSIX를 참조하십시오 . 에서 그 특정 명령문 findutils은 인용에 전혀 해를 끼치하지 않습니다 물론 수동 날짜는 ... 적어도 1996 백업 {}으로하거나, '{}'또는 "{}",하지만 필요가 없습니다.
Stephen Kitt 2018 년

@Tim 당신은 직관이 아직 두 가지 매우 다른 유형의 인수 처리가 있다는 사실을 설명하지 않는 일반적인 상황에 처해 있습니다 : 이 텍스트 줄에서 인수를 구문 분석 할 때 / 방법 인용 문제는 원시 운영 체제 인수 전달과 다릅니다 (따옴표는 단지 ​​일반 문자 임). 쉘 명령 find some/path -exec sh -c 'something "$@"' {} \;에는 실제로 3 개의 인수 처리 / 통과 계층, 2 개의 쉘 종류 및 기본 원시 운영 체제 종류가 있습니다.
mtraceur 2016 년

3

1 부:

find 텍스트 대체 만 사용합니다.

예, 인용 부호가없는 경우 다음과 같이하십시오.

find . -type f -exec sh -c "echo {}" \;

공격자라는 파일을 생성 할 수 있었다 ; echo owned, 그것은 간부 인 것이다

sh -c "echo ; echo owned"

이는 실행 쉘 초래 echo다음 echo owned.

그러나 따옴표를 추가하면 공격자는 따옴표를 끝내고 다음과 같은 파일을 만들어 악의적 인 명령을 넣을 수 있습니다 '; echo owned.

find . -type f -exec sh -c "echo '{}'" \;

이는 쉘 실행이 발생할 것입니다 echo '', echo owned.

큰 따옴표를 작은 따옴표로 바꾸면 공격자가 다른 유형의 따옴표도 사용할 수 있습니다.


2 부:

에서 find -exec sh -c 'something "$@"' sh {} \;{}처음 쉘에 의해 해석되지 않습니다, 그것은 직접 실행될 것 execve때문에 쉘 따옴표를 추가, 도움이되지 것입니다.

find -exec sh -c 'something "$@"' sh "{}" \;

쉘은 실행하기 전에 큰 따옴표를 제거하므로 효과가 없습니다 find.

find -exec sh -c 'something "$@"' sh "'{}'" \;

쉘이 특별히 취급하지 않는 인용 부호를 추가하므로 대부분의 경우 명령이 원하는 것을 수행하지 않음을 의미합니다.

이 확장되어 데 /tmp/foo;, rm, -rf, $HOME사람들은 인자이기 때문에, 문제가되지 않습니다 something, 그리고 something아마도 실행 명령으로 인수를 취급하지 않습니다.


3 부 :

나는 유사한 고려 사항, 예를 들어, 신뢰할 수없는 입력을 받아 (의 일부) 명령으로 실행되는 모든 신청을 가정 xargs하고 parallel.


xargs-I또는 -J사용하는 경우에만 위험합니다 . 정상적인 작동에서와 마찬가지로 목록의 끝에 인수 만 추가하는 것입니다 -exec ... {} +.
찰스 더피

1
( parallel이것은 많은 토론의 대상이되고있어 문제이다; 대조적으로, 취약한면을 증가, 사용자로부터 명시적인 요청없이 기본적으로 쉘을 실행 참조 unix.stackexchange.com/questions/349483/... 에 대한 설명, 그리고 list.gnu.org/archive/html/bug-parallel/2015-05/msg00005.html에 포함 된 링크 ).
Charles Duffy

@CharlesDuffy " 취약한 표면 감소 "를 의미 합니다. OP가 설명하는 공격은 기본적으로 GNU Parallel 에서는 작동 하지 않습니다 .
Ole Tange

1
그래, 난 당신이 SSH를 통해 패리티 위해 필요 알 - 경우 원격 산란하지 않았다 parallel어떤 소켓의 다른 쪽 끝의 "수신기"프로세스를 덜 쉘 직접적인을 할 수 execv. 당신의 신발에 도구를 구현한다면 정확히 내가했을 것입니다. 비 대화식 프로그램을 갖는 것은 현재의 가치에 따라 다르게 행동하는 SHELL것이 거의 놀랍지 않습니다.
찰스 더피

1
예- 사용자가 명시 적으로 쉘을 시작하지 않으면 파이프와 리디렉션 불가능 해야한다고 생각합니다.이 시점에서 명시 적으로 시작한 쉘의 명시 적 동작 만 얻습니다. execv제공 하는 것만 기대할 수 있다면 더 간단하고 놀랍지 않으며 위치 / 런타임 환경에서 변경되지 않는 규칙입니다.
Charles Duffy

3

1. find -exec sh -c "something {}" \;대체품에 대한 {}견적이 인용되지 않아 단일 문자열로 취급되지 않는 문제의 원인이 있습니까?

어떤 의미에서 인용은 여기서 도움이 될 수 없습니다 . 대신 대체되는 파일 이름은 인용 부호를 포함한 모든 문자를 포함{} 할 수 있습니다 . 어떤 형태의 인용이 사용 되었든, 파일 이름은 같은 것을 포함 할 수 있으며 인용에서 "분리"됩니다.

2. ... 그러나 {}인용 부호가 없기 "$@"때문에 원래 명령과 같은 문제가 있습니까? 예를 들어 ? "$@"로 확장됩니다 "/tmp/foo;", "rm", "-rf", and "$HOME".

아니요 "$@". 위치 매개 변수가 별도의 단어로 확장되어 더 이상 분리 되지 않습니다 . 여기서는 그 자체에 {}대한 인수 find이며 find현재 파일 이름도에 대한 고유 한 인수로 전달합니다 sh. 쉘 스크립트에서 변수로 직접 사용할 수 있으며 쉘 명령 자체로는 처리되지 않습니다.

... 왜 {}탈출하거나 인용하지 않습니까?

대부분의 껍질에있을 필요는 없습니다. 당신이 실행하는 경우 fish, 그것은 할 필요가 : fish -c 'echo {}'빈 줄을 인쇄합니다. 그러나 인용하면 문제가되지 않습니다. 쉘은 인용 부호를 제거합니다.

3. 다른 예를 들어 주시겠습니까?

파일 이름 (또는 제어되지 않는 다른 문자열)을 어떤 종류의 코드 (*) 로 사용되는 문자열 내부에서 그대로 확장하면 임의의 명령이 실행될 가능성이 있습니다.

예를 들어, 이것은 $fPerl 코드를 직접 확장 하므로 파일 이름에 큰 따옴표가 있으면 문제가 발생할 수 있습니다. 파일 이름의 인용 부호는 Perl 코드의 인용 부호를 끝내고 나머지 파일 이름에는 Perl 코드가 포함될 수 있습니다.

touch '"; print "HELLO";"'
for f in ./*; do
    perl -le "print \"size: \" . -s \"$f\""
done

(Perl은 실행하기 전에 전체 코드를 미리 분석하기 때문에 파일 이름이 약간 이상해야합니다. 따라서 구문 분석 오류를 피해야합니다.)

이것이 인수를 통해 안전하게 전달하는 동안 :

for f in ./*; do
    perl -le 'print "size: " . -s $ARGV[0]' "$f"
done

(쉘에서 직접 다른 쉘을 실행하는 것은 이치에 맞지 않지만, 그렇다면 쉘과 비슷합니다 find -exec sh ...)

(* 어떤 종류의 코드에는 SQL이 포함되므로 필수 XKCD : https://xkcd.com/327/ 및 설명 : https://www.explainxkcd.com/wiki/index.php/Little_Bobby_Tables )


1

GNU Parallel이 인용문을 인용하는 이유는 바로 당신의 관심사입니다.

touch "hello; echo pwned"
find . -print0 | parallel -0 printf \"Argument: %s\\n\" {}

이 실행되지 않습니다 echo pwned.

쉘을 실행하므로 명령을 확장해도 갑자기 놀라지 않을 것입니다.

# This _could_ be run without spawining a shell
parallel "grep -E 'a|bc' {}" ::: foo
# This cannot
parallel "grep -E 'a|bc' {} | wc" ::: foo

셸 생성 문제에 대한 자세한 내용은 다음을 참조 하십시오. https://www.gnu.org/software/parallel/parallel_design.html#Always-running-commands-in-a-shell


1
... 즉, find . -print0 | xargs -0 printf "Argument: %s\n"마찬가지로 안전합니다 (또는 줄 -print0바꿈으로 파일 이름을 올바르게 처리하기 때문에 오히려 더 안전 합니다). 병렬 인용은 쉘이 없을 때 전혀 존재하지 않는 문제에 대한 해결 방법입니다 .
Charles Duffy

그러나 | wc명령에 추가하자마자 안전을 위해 농구대를 뛰어 넘어야합니다. GNU Parallel은 기본적으로 안전합니다.
Ole Tange

ITYM : 모든 것을 신중하게 인용하는 복잡한 과정으로 쉘 언어의 모든 문제를 해결하려고합니다. 코드와 혼합되지 않은 변수에 데이터를 올바르게 저장하는 것만 큼 안전하지는 않습니다. 자, 그것을 자동 foo {} | wc으로 sh -c 'foo "$1" | wcsh {}` 로 변환한다면 , 그것은 다를 수 있습니다.
ilkkachu 2018 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.