쉘 산술 평가에서 정리되지 않은 데이터 사용의 보안 영향

쉘 산술 평가에서 정리되지 않은 데이터 사용의 보안 영향

안에논평최근 질문, Stéphane Chazelas는 이중 대괄호 산술에는 다음과 같은 보안 위험이 있다고 언급했습니다.

x=$((1-$x))

대부분의 껍질에.

내 Google 기술이 녹슬어서 아무것도 찾을 수 없는 것 같습니다. 이중 괄호 산술이 보안에 미치는 영향은 무엇입니까?

답변1

문제는 애플리케이션의 콘텐츠가 $x삭제되지 않았고 공격자가 제어할 수 있는 데이터가 포함된 경우 쉘 코드가 결국 권한 상승 컨텍스트(예: setuid에 의해 호출되는 스크립트) 애플리케이션에서 사용될 수 있다는 것입니다. sudoers 스크립트 또는 직간접적으로 사용됩니다. 오프 그리드 데이터(CGI, DHCP 후크...)를 처리합니다.

만약에:

x='(PATH=2)'

그 다음에:

x=$((1-$x)))

PATH이를 2(공격자가 제어할 가능성이 있는 상대 경로)로 설정하면 부작용이 있습니다. 다음 PATH으로 대체할 수 있습니다 . bash, zsh 또는 ksh (변수에서 숫자 상수만 허용하는 dash 또는 yash 아님) 에서도 동일한 일이 발생합니다 .LD_LIBRARY_PATHIFSx=$((1-x))

알아채다:

x=$((1-$x))

$x(POSIX에 따라 선택 사항) (감소) 연산자를 구현하는 일부 셸에서는 음수 값에 대해 제대로 작동하지 않습니다 --( 에서와 같이 이는 셸에서 산술 표현식을 x=-1평가해야 함을 의미함 ). 확장은 산술 평가의 일부로 수행되므로 문제가 없습니다 (이전은 아님).1--1"$((1-x))"x

bash, zshksh( dash또는 아님 yash) 에서 다음과 같은 경우 x:

x='a[0$(uname>&2)]'

$((1-$x))그런 다음 or 의 확장으로 인해 $((1-x))명령이 실행됩니다( 의 경우 배열 변수여야 하지만 예를 들어 사용할 수 있습니다).unamezshapsvar

요약하자면, 초기화되지 않았거나 삭제되지 않은 외부 데이터는 쉘 산술 표현식에 사용되어서는 안 됩니다.

$((...))$[...]산술 평가는 ( in 또는 라고도 함)을 통해 수행할 수 있지만 bash내장 함수 zsh의 셸 , 배열 인덱싱 및 구성 등에 따라 달라집니다.let[testdeclare/typeset/export...returnbreakcontinueexitprintfprint((..))[[...]]

ksh// 의 배열 인덱싱에서 작동하기 때문에 변수 이름을 인수로 사용하는 모든 내장 함수( / with , , , // , / with , ... ) zsh에서도 작동합니다.bash[test-vreadunsetexporttypesetreadonlyprintprintf-vgetopts

숫자 테스트 연산자의 피연산자가 내장 함수가 있든 [[...]]없든 산술 표현식으로 처리된다는 사실은 OR에서 일반적으로 후자가 선호되는 이유 중 하나입니다.[testbashzsh

비교하다:

$ a='x[1$(uname>&2)]' bash -c '[ "$a" -eq "$b" ]'
bash: line 0: [: x[1$(uname>&2)]: integer expression expected

(보안):

$ a='x[1$(uname>&2)]' bash -c '[[ "$a" -eq "$b" ]]'
Linux

( uname실행되었습니다).

( ksh, 및 에 문제 [가 있습니다 .)[[ ... ]]

변수에 리터럴 십진 정수가 포함되어 있는지 확인하려면 POSIXly를 사용할 수 있습니다.

case $var in
  ("" | - | *[!0123456789-]* | ?*-*) echo >&2 not a valid number; exit 1;;
esac

일부 로케일에서는 [0-9][[:digit:]]가 0123456789 이상으로 일치하므로 모든 형태의 입력 유효성 검사/삭제를 피해야 합니다.

또한 어떤 경우에는 앞에 0이 붙은 숫자가 8진수( 010때때로 10, 때로 8)로 처리된다는 점을 염두에 두십시오. 위 검사에서는 시스템보다 더 큰 시스템이 허용될 수도 있습니다(또는 지원되는 가장 큰 정수를 사용함). 어떤 응용 프로그램이든). 해당 정수를 사용하십시오. 예를 들어 bash는 18446744073709551616 을 0으로 처리합니다. 왜냐하면 2 64 이기 때문입니다 . 따라서 위의 사례 설명에 추가 검사를 추가해야 할 수도 있습니다. 예를 들면 다음과 같습니다.

(0?* | -0?*)
  echo >&2 'Only decimal numbers without leading 0 accepted'; exit 1;;
(-??????????* | [!-]?????????*)
  echo >&2 'Only numbers from -999999999 to 999999999 supported'; exit 1;;

예:

$ export 'x=psvar[0$(uname>&2)]'
$ ksh93 -c 'echo "$((x))"'
Linux
ksh93: psvar: parameter not set
$ ksh93 -c '[ x -lt 2 ]'
Linux
ksh93: [: psvar: parameter not set
$ bash -c 'echo "$((x))"'
Linux
0
$ bash -c '[[ $x -lt 2 ]]'
Linux
$ bash -c 'typeset -i a; export a="$x"'
Linux
$ bash -c 'typeset -a a=([x]=1)'
Linux
$ bash -c '[ -v "$x" ]'
Linux
$ bash -c 'read "$x"' < /dev/null
Linux
$ env psvar= bash -c 'unset "$x"'
Linux
$ mksh -c '[[ $x -lt 2 ]]'
Linux
$ zsh -c 'echo "$((x))"'
Linux
0
$ zsh -c 'printf %d $x'
Linux
0
$ zsh -c 'integer x'
Linux
$ zsh -c 'exit $x'
Linux

추가 자료:

관련 정보