env x = '() {:;}; 커맨드의 bash는 왜 안전하지 않은가?


237

bash에는 분명히 취약점 (CVE-2014-6271)이 있습니다. Bash 특수하게 조작 된 환경 변수 코드 삽입 공격

무슨 일이 일어나고 있는지 알아 내려고 노력하고 있지만 완전히 이해하고 있는지는 확실하지 않습니다. echo작은 따옴표로 어떻게 실행될 수 있습니까?

$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
vulnerable
this is a test

편집 1 : 패치 시스템은 다음과 같습니다

$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `x'
this is a test

편집 2 : 약간 다른 테스트를 사용하는 관련 취약점 / 패치 CVE-2014-7169 가 있습니다.

$ env 'x=() { :;}; echo vulnerable' 'BASH_FUNC_x()=() { :;}; echo vulnerable' bash -c "echo test"

패치되지 않은 출력 :

vulnerable
bash: BASH_FUNC_x(): line 0: syntax error near unexpected token `)'
bash: BASH_FUNC_x(): line 0: `BASH_FUNC_x() () { :;}; echo vulnerable'
bash: error importing function definition for `BASH_FUNC_x'
test

부분적으로 (이전 버전) 패치 된 출력 :

bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `x'
bash: error importing function definition for `BASH_FUNC_x()'
test

CVE-2014-7169 이하의 패치 된 출력 :

bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `BASH_FUNC_x'
test

편집 3 : 이야기는 다음과 같이 계속됩니다.


실행되는 에코가 아닙니다. x의 함수 정의. x에 정의 된 함수가 부적절한 작업을 수행하는 경우 함수 x가 실수 인 경우 bash가 반환 값을 확인할 수있는 방법이 없습니다. 테스트 코드에서 함수가 비어 있습니다. 확인되지 않은 반환 값은 스크립트 삽입으로 이어질 수 있습니다. 스크립트 삽입은 권한 에스컬레이션으로 이어지고 권한 에스컬레이션은 루트 액세스로 이어집니다. 이 패치는 x를 함수로 만들 수 없음
eyoung100

26
eyoung100, 에코 실행 되지 않습니다 . 단어 vulnerable가 출력에 표시되어 실행되고 있음을 알 수 있습니다 . 주요 문제는 bash가 함수 정의 후에 코드를 구문 분석하고 실행한다는 것입니다. 다른 예 /bin/idseclists.org/oss-sec/2014/q3/650 의 일부를 참조하십시오 .
Mikel

4
간단한 의견입니다. Red Hat은 릴리스 된 패치는 부분 패치 일 뿐이며 시스템을 여전히 위험에 노출 시킨다고 조언했습니다.
피터

2
@ eyoung100의 차이점은 함수 내부의 코드는 환경 변수가 명시 적으로 호출 될 때만 실행된다는 것입니다. 함수 정의 뒤의 코드는 새로운 Bash 프로세스가 시작될 때마다 실행됩니다.
David Farrell

1
자세한 내용은 stackoverflow.com/questions/26022248/… 를 참조 하십시오
Barmar

답변:


204

bash는 내 보낸 함수 정의를 환경 변수로 저장합니다. 내 보낸 함수는 다음과 같습니다.

$ foo() { bar; }
$ export -f foo
$ env | grep -A1 foo
foo=() {  bar
}

즉, 환경 변수 foo에는 리터럴 내용이 있습니다.

() {  bar
}

새로운 bash 인스턴스가 시작되면 특수하게 조작 된 환경 변수를 찾아 함수 정의로 해석합니다. 직접 작성해도 여전히 작동하는지 확인할 수 있습니다.

$ export foo='() { echo "Inside function"; }'
$ bash -c 'foo'
Inside function

불행히도 문자열 (환경 변수)에서 함수 정의를 구문 분석하면 의도 한 것보다 더 큰 영향을 줄 수 있습니다. 패치되지 않은 버전에서는 함수 정의 종료 후 발생하는 임의의 명령도 해석합니다. 이는 환경에서 허용 가능한 함수형 문자열을 결정하는 데 제약이 충분하지 않기 때문입니다. 예를 들면 다음과 같습니다.

$ export foo='() { echo "Inside function" ; }; echo "Executed echo"'
$ bash -c 'foo'
Executed echo
Inside function

bash 시작 중에 함수 정의 외부의 에코가 예기치 않게 실행되었습니다. 함수 정의는 평가 및 악용을 수행하는 단계 일 뿐이며 함수 정의 자체와 사용 된 환경 변수는 임의적입니다. 쉘은 환경 변수 foo를 살펴 보고 , 함수 정의가 무엇인지에 대해 알고있는 제약 조건을 충족시키는 것처럼 보이고, 의도하지 않게 에코를 실행하여 선을 평가합니다.

변수 자체가 변수에 포함 된 임의의 코드를 직접 호출하도록 일반적으로 허용되거나 예상되지 않기 때문에 이는 안전하지 않은 것으로 간주됩니다. 아마도 프로그램이 신뢰할 수없는 사용자 입력으로부터 환경 변수를 설정했을 것입니다. 이러한 환경 변수는 사용자가 코드에 선언 된 이유로 환경 변수를 사용하여 명시적인 의도없이 임의의 명령을 실행할 수있는 방식으로 조작 될 수 있다는 것은 매우 예상치 못한 일입니다.

다음은 실행 가능한 공격의 예입니다. 수명의 일부로 취약한 셸을 실행하는 웹 서버를 실행합니다. 이 웹 서버는 환경 변수를 bash 스크립트로 전달합니다. 예를 들어 CGI를 사용하는 경우 HTTP 요청에 대한 정보는 종종 웹 서버의 환경 변수로 포함됩니다. 예를 들어, HTTP_USER_AGENT사용자 에이전트의 내용으로 설정되었을 수 있습니다. 즉, 사용자 에이전트를 스푸핑하면 '() {:; }; 해당 쉘 스크립트가 실행될 때 echo foo ' echo foo가 실행됩니다. 다시 말하지만, echo foo악의적이든 아니든 상관 없습니다.


3
이것이 Zsh와 같은 다른 Bash와 같은 쉘에 영향을 줄 수 있습니까?
Amelio Vazquez-Reina

3
@ user815423426 아니요, zsh에는이 기능이 없습니다. Ksh는 기능이 있지만 다르게 구현되었으므로 환경이 아닌 쉘이 포크 인 경우에만 매우 좁은 환경에서만 기능을 전송할 수 있다고 생각합니다.
Gilles

20
@ user815423426 rc는 환경에서 함수를 전달하는 다른 쉘이지만 이름이 "fn_"인 변수를 갖는 다른 쉘이며 호출 될 때만 해석됩니다.
Stéphane Chazelas

18
@ StéphaneChazelas-버그를보고 해 주셔서 감사합니다.
Deer Hunter

13
@gnclmorais 당신은 달리기 보다는 export bar='() { echo "bar" ; }'; zsh -c bar그것을 표시 한다는 것을 의미 합니까? 호출중인 쉘과 테스트 설정에 사용중인 쉘을 혼동하지 않습니까? barzsh:1: command not found: bar
Gilles

85

이것은 무슨 일이 일어나고 있는지 더 보여줄 수 있습니다 :

$ export dummy='() { echo "hi"; }; echo "pwned"'
$ bash
pwned
$

취약한 쉘을 실행중인 경우 새 서브 쉘을 시작하면 (여기서는 단순히 bash 문을 사용하여) 임의 코드 ( echo "pwned")가 시작의 일부로 즉시 실행되는 것을 볼 수 있습니다. 분명히 쉘은 환경 변수 (더미)에 함수 정의가 포함되어 있음을 확인하고 해당 환경에서 해당 함수를 정의하기 위해 정의를 평가합니다 (함수가 실행 중이 아님을 유의하십시오 : 'hi'를 인쇄합니다).

불행히도, 그것은 함수 정의를 평가할뿐만 아니라 함수 정의를 따르는 악의적 진술을 포함하여 환경 변수 값의 전체 텍스트를 평가합니다. 초기 함수 정의가 없으면 환경 변수가 평가되지 않고 단지 환경에 텍스트 문자열로 추가됩니다. Chris Down이 지적했듯이, 이것은 내 보낸 셸 함수 가져 오기를 구현하는 특정 메커니즘입니다.

우리는 새로운 쉘에서 정의 된 함수를 볼 수 있으며, 그 함수는 익스포트 된 것으로 표시되어 실행할 수 있습니다. 또한 더미는 텍스트 변수로 가져 오지 않았습니다.

$ declare -f
dummy ()
{
    echo "hi"
}
declare -fx dummy
$ dummy
hi
$echo $dummy
$

이 기능을 생성하거나 실행하지 않아도 악용의 일부가 아니며 악용이 실행되는 수단 일뿐입니다. 요점은 공격자가 익스포트 된 환경 변수에 입력되는 텍스트 문자열에서 악성 코드를 최소한의 중요하지 않은 함수 정의로 제공 할 수 있으면 서브 쉘이 시작될 때 실행된다는 것입니다. 많은 스크립트에서. 또한 스크립트의 권한으로 실행됩니다.


17
받아 들인 대답은 실제로주의 깊게 읽으면이 말을하지만,이 대답은 문제 정의 인 (기능 자체를 실행하는 것이 아니라) 평가라는 것을 이해하는 데 더 명확하고 도움이됩니다 .
natevw

1
export다른 사람이 가지고있는 동안 왜이 예에 명령이 env있습니까? env다른 배쉬 쉘이 시작될 때 호출 될 환경 변수를 정의하는 데 사용되고 있다고 생각 했습니다. 그럼 어떻게이 작업은export
하리스

지금까지는 대답이 받아 들여지지 않았습니다. 아마 다른 며칠 동안 기다릴 것입니다. 이 답변의 단점은 원래 명령을 분해하지 않으며 질문의 원래 명령 에서이 답변의 명령으로 이동하여 동일한 명령을 표시하는 방법에 대해 설명합니다. 그 외에도 좋은 설명입니다.
jippie

@ralph - 모두 envexport수출 환경 정의는 그래서 그들은 서브 쉘에서 사용할 수 있습니다. 실제로 내 보낸 정의를 서브 쉘 환경으로 가져 오는 방법, 특히 함수 정의를 가져 오는 메커니즘에 문제가 있습니다.
sdenham

1
@ralph- env일부 옵션 및 환경 변수가 설정된 명령을 실행합니다. 원래 질문 예제에서 문자열로 env설정 하고 명령을 실행하여 실행합니다. 그렇게 하면 Vim이 시작되고 거기에서를 사용하여 포함 된 쉘 / 환경을 호출 할 수 있으며 인쇄합니다 .하지만 종료하면 및 종료되면 vim이 실행되는 동안에 만 존재하므로 정의되지 않습니다 명령을 통해 . 실행 서브 쉘하도록 명령 대신 현재 환경에서 지속적인 값을 설정 이상이 그들을 사용합니다. xbash -cenv x='foo' vim!echo $xfooecho $xenvexport
Gary Fixler

72

나는 이것을 Chris Down의 위의 훌륭한 답변에 대한 튜토리얼 스타일의 재구성으로 작성했습니다.


bash에서는 다음과 같은 쉘 변수를 가질 수 있습니다

$ t="hi there"
$ echo $t
hi there
$

기본적으로 이러한 변수는 하위 프로세스에서 상속되지 않습니다.

$ bash
$ echo $t

$ exit

그러나 내보내기로 표시하면 bash는 하위 프로세스 환경으로 이동한다는 것을 의미하는 플래그를 설정합니다 ( envp매개 변수는별로 보이지 않지만 mainC 프로그램에는 세 개의 매개 변수가 있습니다. main(int argc, char *argv[], char *envp[])마지막 포인터 배열은 배열입니다) 정의가있는 쉘 변수).

t다음과 같이 내보내십시오 .

$ echo $t
hi there
$ export t
$ bash
$ echo $t
hi there
$ exit

위의 내용 t은 서브 쉘에서 정의되지 않았지만 이제는 내보내기 후에 나타납니다 ( export -n t내보내기를 중지하려는 경우에 사용 ).

그러나 bash의 기능은 다른 동물입니다. 다음과 같이 선언하십시오.

$ fn() { echo "test"; }

이제 다른 쉘 명령 인 것처럼 호출하여 함수를 호출 할 수 있습니다.

$ fn
test
$

다시 한번, 서브 쉘을 생성하면 함수가 내보내지지 않습니다 :

$ bash
$ fn
fn: command not found
$ exit

다음과 export -f같이 함수를 내보낼 수 있습니다 .

$ export -f fn
$ bash
$ fn
test
$ exit

까다로운 부분은 다음과 같습니다 . fn셸 변수 내보내기 t가 위와 같이 내 보내진 함수 가 환경 변수로 변환됩니다 . 이것은 fn로컬 변수 일 때 발생하지 않지만 내보내기 후에는 셸 변수로 볼 수 있습니다. 그러나 동일한 이름의 일반 (예 : 함수가 아닌) 쉘 변수를 가질 수도 있습니다. bash는 변수의 내용에 따라 구별됩니다.

$ echo $fn

$ # See, nothing was there
$ export fn=regular
$ echo $fn
regular
$ 

이제 env내보내기로 표시된 모든 셸 변수를 표시하고 일반 fn및 함수가 모두 fn표시됩니다.

$ env
.
.
.
fn=regular
fn=() {  echo "test"
}
$

하위 셸은 두 가지 정의를 수집합니다. 하나는 일반 변수이고 다른 하나는 함수입니다.

$ bash
$ echo $fn
regular
$ fn
test
$ exit

fn위에서와 같이 또는 직접 일반 변수 할당으로 정의 할 수 있습니다 .

$ fn='() { echo "direct" ; }'

이것은 매우 특이한 일입니다. 일반적으로 fn위와 같이 fn() {...}구문 으로 함수 를 정의 합니다. 그러나 bash는 환경을 통해 파일을 내보내므로 위의 일반 정의로 직접 "바로 가기"할 수 있습니다. (직관을 고려하여) 이것은 현재 쉘에서 사용 가능한 새로운 기능을 제공 하지 않습니다fn . 그러나 만약 당신이 ** 서브 ** 쉘을 스폰한다면, 그렇게 될 것입니다.

함수 내보내기를 취소 fn하고 fn위와 같이 새로운 규칙을 그대로 유지 합시다 .

$ export -nf fn

이제 함수 fn는 더 이상 내 보내지 않지만 일반 변수 fn는 내보내고 그 안에 들어 () { echo "direct" ; }있습니다.

이제 서브 쉘이 시작하는 일반 변수를 ()보면 나머지 변수를 함수 정의로 해석합니다. 그러나 이것은 새로운 쉘이 시작될 때만 입니다. 위에서 보았 듯이, 일반 쉘 변수로 시작 ()한다고해서 함수처럼 작동하지는 않습니다. 서브 쉘을 시작해야합니다.

이제 "shellshock"버그 :

방금 보았 듯이 새 쉘이 일반 변수의 정의를 수집 할 때 ()함수로 해석합니다. 그러나 함수를 정의하는 닫는 중괄호 뒤에 더 많은 것이 있으면 거기에있는 모든 것을 실행 합니다.

이것들은 다시 한 번 요구 사항입니다.

  1. 새로운 배쉬가 생성됩니다
  2. 환경 변수가 수집됩니다
  3. 이 환경 변수는 "()"로 시작하고 중괄호 안에 함수 본문을 포함하고 이후에 명령을가집니다.

이 경우 취약한 bash는 후자의 명령을 실행합니다.

예:

$ export ex='() { echo "function ex" ; }; echo "this is bad"; '
$ bash
this is bad
$ ex
function ex
$

정기적으로 내 보낸 변수 ex는 함수로 해석 된 서브 쉘로 전달 ex되었지만 this is bad서브 쉘이 생성 될 때 후행 명령이 실행되었습니다 ( ).


매끄러운 한 줄 테스트 설명

Shellshock 취약점을 테스트하는 데 널리 사용되는 단일 라이너는 @jippie의 질문에서 인용 된 것입니다.

env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

여기에 고장이 있습니다. 먼저 :bash는의 약어입니다 true. true그리고 :둘 다 bash에서 true로 평가합니다 (추측).

$ if true; then echo yes; fi
yes
$ if :; then echo yes; fi
yes
$

둘째, env(또한 bash는 내장) 명령을 사용하여 환경 변수를 출력 (우리가 위에서 본 바와 같이)뿐만 아니라 그 명령에 주어진 보낸 변수 (또는 변수)와 하나의 명령을 실행하는 데 사용할 수 있습니다, 그리고 bash -c에서 하나의 명령을 실행의 명령 줄 :

$ bash -c 'echo hi'
hi
$ bash -c 'echo $t'

$ env t=exported bash -c 'echo $t'
exported
$

이 모든 것들을 함께 꿰매어 bash를 명령으로 실행하고 (예와 같이 bash -c echo this is a test) 더미 작업을 수행 ()하고 서브 쉘이 함수로 해석하도록 시작하는 변수를 내보낼 수 있습니다. 쉘 쇼크가 존재하면 서브 쉘에서 후행 명령도 즉시 실행합니다. 우리가 전달하는 함수는 우리와 관련이 없기 때문에 (구문 분석해야합니다!) 상상할 수있는 가장 짧은 유효한 함수를 사용합니다.

$ f() { :;}
$ f
$ 

여기서 함수 f:명령을 실행하여 true를 반환하고 종료합니다. 이제 "악한"명령을 추가하고 정규 변수를 서브 쉘로 내 보내면 승리합니다. 다시 한 줄짜리가 있습니다.

$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

따라서 끝 xecho vulnerable붙은 간단한 유효한 함수를 사용하여 일반 변수로 내보내집니다 . 이것은 bash에 전달되며 bash x는 함수 (우리가 신경 쓰지 않는)로 해석 한 다음 아마도 echo vulnerableshellshock이 있는 경우 if를 실행합니다 .

this is a test메시지 를 제거하여 원 라이너를 약간 줄일 수 있습니다 .

$ env x='() { :;}; echo vulnerable' bash -c :

이것은 귀찮게하지 않고 this is a test자동 :명령을 다시 실행 합니다. (이를 그대로두면 -c :서브 쉘에 앉아 수동으로 종료해야합니다.) 아마도 가장 사용자 친화적 인 버전은 다음과 같습니다.

$ env x='() { :;}; echo vulnerable' bash -c "echo If you see the word vulnerable above, you are vulnerable to shellshock"

12
좋은 설명입니다. 이 질문은 많은 견해를 겪고 있으며 (아마도 모두가 다른 사람들처럼 배쉬에 능숙하지는 않을 것 { :;};입니다.) 내 의견으로는 당신의 대답에 좋은 추가가 될 것입니다. 예에서 문제의 원래 명령으로 어떻게 넘어가는지 설명해 주시겠습니까?
jippie

20

프로그램에 임의의 환경 변수를 제공 할 수 있으면 선택한 라이브러리를로드하여 거의 모든 작업을 수행 할 수 있습니다. 대부분의 경우 이는 환경 변수를받는 프로그램의 취약점이 아니라 외부인이 임의의 환경 변수를 제공 할 수있는 메커니즘으로 간주됩니다.

그러나 CVE-2014-6271은 다릅니다.

환경 변수에 신뢰할 수없는 데이터가있는 것은 아무 문제가 없습니다. 프로그램 동작을 수정할 수있는 환경 변수에 들어 가지 않도록해야합니다. 좀 더 추상적으로 말하면, 특정 호출에 대해 외부 변수에 의해 직접 지정 될 수있는 환경 변수 이름의 화이트리스트를 작성할 수 있습니다.

CVE-2014-6271과 관련하여 제시된 예는 로그 파일을 구문 분석하는 데 사용되는 스크립트입니다. 환경 변수에서 신뢰할 수없는 데이터를 전달해야하는 매우 합법적 인 요구가있을 수 있습니다. 물론 그러한 환경 변수의 이름은 부정적인 영향을 미치지 않도록 선택됩니다.

그러나이 특정 bash 취약점에 대해 나쁜 점이 있습니다. 변수 이름을 통해 이용할 수 있습니다. 이라는 환경 변수를 만들면 GET_REQUEST_TO_BE_PROCESSED_BY_MY_SCRIPT자신의 스크립트 이외의 다른 프로그램이 해당 환경 변수의 내용을 해석하지 않을 것입니다. 그러나이 bash 버그를 악용함으로써 모든 단일 환경 변수가 공격 경로가됩니다.

이것이 환경 변수의 이름이 비밀로 예상되는 것은 아닙니다. 관련된 환경 변수의 이름을 알면 공격이 쉬워지지 않습니다.

만약 program1통화 program2차례 호출에 program3, 다음 program1에 데이터를 전달할 수있는 program3환경 변수를 통해. 각 프로그램에는 설정 한 특정 환경 변수 목록과 실행되는 특정 목록이 있습니다. 당신이하지에서 인식 할 수있는 이름을 선택하는 경우 program2, 당신은 데이터를 전달할 수 있습니다 program1에 대한 program3이 어떤 부정적인에 영향을 미치는 문제에 대해 걱정하지 않고 program2.

내 보낸 변수의 정확한 이름 program1과 해석되는 변수의 이름을 알고있는 공격자 program2는 이름 집합간에 겹치지 않으면 'program2'의 동작을 수정하기 위해이 지식을 이용할 수 없습니다.

그러나이 버그로 인해 모든 환경 변수를 코드로 해석 하기 때문에 스크립트 인 경우 program2bash문제가 발생했습니다 bash.


1
"모든 단일 환경 변수가 공격 벡터가됩니다"– 그것이 내가 놓친 부분입니다. 감사.
wrschneider

9

그것은 당신이 링크 한 기사에 설명되어 있습니다 ...

bash 쉘을 호출하기 전에 특수하게 조작 된 값으로 환경 변수를 작성할 수 있습니다. 이 변수에는 코드가 포함될 수 있으며,이 코드는 쉘이 호출 되 자마자 실행됩니다.

이것은 호출 된 bash -c "echo this is a test"가 호출 될 때 작은 따옴표로 코드 를 실행 한다는 것을 의미합니다 .

Bash는 다소 제한된 구현에서 함수를 가지고 있으며 이러한 bash 함수를 환경 변수에 넣을 수 있습니다. 이 결함은 이러한 기능 정의 끝에 enivronment 변수에 추가 코드가 추가 될 때 발생합니다.

게시 한 코드 예제는 할당 된 bash가 할당을 수행 한 후이 문자열 평가를 중지하지 않는다는 사실을 악용한다는 것을 의미합니다. 이 경우 기능 할당.

내가 이해했듯이 게시 한 코드 스 니펫에 대한 실제로 특별한 것은 실행하려는 코드 앞에 함수 정의를 사용하여 일부 보안 메커니즘을 우회 할 수 있다는 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.