전문
먼저 문제를 해결하는 올바른 방법이 아니라고 말하고 싶습니다. " 그렇지 않으면 감옥에 갈 것이기 때문에 사람들을 살해해서는 안됩니다 "라고 말하는 것과 비슷 합니다 .
마찬가지로 보안 취약점을 도입하고 있으므로 변수를 인용하지 않습니다. 변수가 잘못되어 있기 때문에 변수를 인용하십시오 (그러나 감옥에 대한 두려움이 도움이된다면 왜 그렇지 않을까요).
방금 기차에 뛰어든 사람들을위한 간단한 요약입니다.
대부분의 셸에서 변수 확장을 따옴표로 묶지 않은 상태로두면 (및이 답변의 나머지 부분이 명령 대체 ( `...`
또는 $(...)
) 및 산술 확장 ( $((...))
또는 $[...]
) 에도 적용됨 ) 매우 특별한 의미가 있습니다. 이를 설명하는 가장 좋은 방법은 일종의 암시 적 split + glob 연산자 ¹를 호출하는 것과 같습니다 .
cmd $var
다른 언어로 다음과 같이 작성됩니다.
cmd(glob(split($var)))
$var
는 $IFS
특수 매개 변수 ( 분할 부분)와 관련된 복잡한 규칙에 따라 단어 목록으로 먼저 분할 된 다음 해당 분할로 인한 각 단어 는 일치하는 파일 목록 ( 글로브 부분)으로 확장 되는 패턴으로 간주됩니다 .
경우 예를 들어, $var
포함 *.txt,/var/*.xml
및 $IFS
포함 ,
, cmd
인수의 수, 첫 번째 존재로 호출 될 수 cmd
와있는 다음 사람 txt
은 현재 디렉토리에있는 파일과 xml
의 파일을 /var
.
cmd
두 개의 문자 인수 cmd
와 로만 호출 *.txt,/var/*.xml
하려면 다음 과 같이 작성하십시오.
cmd "$var"
다른 친숙한 언어로 된 것입니다.
cmd($var)
쉘의 취약점은 무엇을 의미 합니까?
결국, 쉘 스크립트는 보안에 민감한 컨텍스트에서 사용되어서는 안되는 것으로 알려져 있습니다. 분명히, 인용되지 않은 변수를 남겨 두는 것은 버그이지만 그렇게 많은 해를 끼칠 수는 없습니까?
글쎄, 누군가가 쉘 스크립트를 웹 CGI에 사용해서는 안된다는 사실이나 고맙게도 대부분의 시스템은 요즘 setuid / setgid 쉘 스크립트를 허용하지 않는다는 사실에도 불구하고 쉘 쇼크 (원격으로 악용 가능한 bash 버그) 2014년 9월)의 헤드 라인 공개 쉘은 여전히 광범위하게 사용되는 곳들은 아마 안 : CGI를에서, DHCP에서 클라이언트 후크 호출의 sudoers 명령에 스크립트 에 의해 (그렇지 않은 경우 등 )의 setuid 명령 ...
때때로 무의식적으로. 예를 들어 system('cmd $PATH_INFO')
, php
/ perl
/ python
CGI 스크립트에서 셸을 호출하여 해당 명령 줄을 해석합니다 ( cmd
자체 스크립트는 쉘 스크립트 일 수 있으며 작성자는 CGI에서 호출 한 것으로 예상하지 않았을 수도 있음).
권한 에스컬레이션을위한 경로가있을 때, 즉 누군가 ( 공격 자라고 함 )가 의도하지 않은 일을 할 수있을 때 취약점 이 있습니다.
그것은 공격자가 데이터를 제공 한다는 것을 의미 하며 , 권한이없는 사용자 / 프로세스에 의해 데이터가 처리되고 있다는 것을 의미 합니다 .
기본적으로 버그가있는 코드 가 공격자 가 제어하는 데이터를 처리 할 때 문제가 발생합니다 .
이제 데이터의 출처를 항상 명확하게 알 수있는 것은 아니며 코드에서 신뢰할 수없는 데이터를 처리 할 수 있는지 여부를 판단하기가 어려운 경우가 많습니다.
변수에 관한 한 CGI 스크립트의 경우 데이터는 CGI GET / POST 매개 변수와 쿠키, 경로, 호스트 ... 매개 변수와 같은 것들입니다.
setuid 스크립트 (다른 사용자가 호출 할 때 한 사용자로 실행)의 경우 이는 인수 또는 환경 변수입니다.
또 다른 매우 일반적인 벡터는 파일 이름입니다. 디렉토리에서 파일 목록을 얻는 경우 공격자 가 파일을 심었을 수 있습니다 .
(파일을 처리 할 때 그 점에서, 심지어 대화 형 쉘의 프롬프트에서, 당신은 취약 할 수 있습니다 /tmp
또는 ~/tmp
예를 들어).
~/.bashrc
예를 들어 클라이언트가 제어하는 일부 변수가
있는 서버 배포 에서 유사 하게 실행되도록 bash
호출 할 때 a 도 취약 할 수 있습니다 .ssh
ForcedCommand
git
이제는 신뢰할 수없는 데이터를 처리하기 위해 스크립트를 직접 호출 할 수 없지만 다른 명령을 통해 호출 할 수 있습니다. 또는 잘못된 코드를 스크립트에 복사하여 붙여 넣을 수 있습니다 (3 년 동안 줄이나 동료 중 한 사람). 코드의 복사본이 어디에 있는지 알 수 없으므로 Q & A 사이트에서 특히 중요 합니다.
사업으로; 얼마나 나쁩니 까?
인용되지 않은 변수 (또는 명령 대체)를 남겨 두는 것은 쉘 코드와 관련된 보안 취약점의 가장 큰 원인입니다. 이러한 버그는 종종 취약점으로 번역되기도하지만 인용되지 않은 변수를 보는 것이 일반적이기 때문입니다.
실제로 쉘 코드에서 취약점을 찾을 때 가장 먼저 할 일은 인용되지 않은 변수를 찾는 것입니다. 공격자가 제어 할 수있는 데이터를 쉽게 추적 할 수 있습니다.
인용되지 않은 변수가 취약점으로 변할 수있는 방법은 무한합니다. 여기서 몇 가지 일반적인 추세를 설명하겠습니다.
정보 공개
대부분의 사람들은 분할 부분으로 인해 인용되지 않은 변수와 관련된 버그에 부딪 칠 것입니다 (예를 들어, 파일 이름은 요즘 이름에 공백이 있고 공백은 기본값 IFS입니다). 많은 사람들이 글로브 부분 을 간과합니다
. 글로브의 부분은 최소한으로 위험
분할 부분.
비위생 외부 입력에 대해 글 로빙을 수행 하면 공격자 가 모든 디렉토리의 내용을 읽을 수 있습니다.
에서:
echo You entered: $unsanitised_external_input
$unsanitised_external_input
포함 된 경우 공격자 가의 콘텐츠를 볼 수 /*
있음을 의미 합니다/
. 별거 아냐 그래도 더 흥미하게 함께 /home/*
있는 시스템에 당신에게 사용자 이름의 목록을 제공 /tmp/*
, /home/*/.forward
다른 위험한 관행에서 힌트를 위해 /etc/rc*/*
사용할 서비스 ... 필요가 개별적으로 이름을 없습니다. 값은 /*
/*/* /*/*/*...
전체 파일 시스템을 나열합니다.
서비스 거부 취약점.
이전 사례를 너무 멀리 가져 가면 DoS가 있습니다.
실제로, 입력되지 않은 입력을 가진 목록 컨텍스트에서 인용되지 않은 변수 는 적어도 DoS 취약점입니다.
전문가 쉘 스크립터조차도 일반적으로 다음과 같은 것을 인용하는 것을 잊습니다.
#! /bin/sh -
: ${QUERYSTRING=$1}
:
no-op 명령입니다. 무엇이 잘못 될 수 있습니까?
그것은
설정되지 않은 경우 에 할당 $1
하기 위한 것 입니다. 명령 줄에서 CGI 스크립트를 호출 할 수있는 빠른 방법입니다.$QUERYSTRING
$QUERYSTRING
그것은 $QUERYSTRING
여전히 확장되어 있고 인용되지 않았기 때문에 split + glob 연산자가 호출됩니다.
이제 확장하기 위해 특히 비싼 글로브가 있습니다. /*/*/*/*
는 아래 4 단계까지 리스팅 디렉토리를 의미로 하나는 충분히 나쁘다. 디스크 및 CPU 활동 외에도 수만 개의 파일 경로 (여기서는 최소 서버 VM에 40k,이 중 10k는 디렉토리)를 저장해야합니다.
이제 /*/*/*/*/../../../../*/*/*/*
40k x 10k를 의미
/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*
하며 가장 강력한 기계조차 무릎을 꿇기에 충분합니다.
직접 해보십시오 (컴퓨터가 충돌하거나 멈출 수 있도록 준비하십시오).
a='/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*' sh -c ': ${a=foo}'
물론 코드가 다음과 같은 경우 :
echo $QUERYSTRING > /some/file
그런 다음 디스크를 채울 수 있습니다.
쉘 cgi 또는 bash cgi 또는 ksh cgi 에서 Google 검색을 수행하면 쉘 에서 CGI를 작성하는 방법을 보여주는 몇 페이지가 있습니다. 프로세스 매개 변수의 절반이 얼마나 취약한 지 확인하십시오.
심지어 데이빗 콘의 고유 한
(쿠키 처리보고) 취약합니다.
임의의 코드 실행 취약성까지
공격자 가 어떤 명령을 실행할 수 있다면 , 그가 할 수있는 일에 제한이 없기 때문에 임의 코드 실행은 최악의 취약점 유형입니다 .
즉 일반적으로의 분할 에게 리드 부분. 이렇게 분할하면 하나의 인수 만 예상 될 때 여러 인수가 명령에 전달됩니다. 이들 중 첫 번째는 예상 된 컨텍스트에서 사용되지만 다른 것은 다른 컨텍스트에 있으므로 잠재적으로 다르게 해석됩니다. 예를 들어 더 나은 :
awk -v foo=$external_input '$2 == foo'
여기서는 $external_input
쉘 변수 의 내용을 변수에 할당
하려고했습니다 foo
awk
.
지금:
$ external_input='x BEGIN{system("uname")}'
$ awk -v foo=$external_input '$2 == foo'
Linux
분할의 두 번째 단어 는 코드에 $external_input
할당되지 않고 코드로 foo
간주됩니다 awk
(여기서는 임의 명령을 실행 함 uname
).
즉, 다른 명령을 실행할 수있는 명령 특히 문제의 ( awk
, env
, sed
(GNU 일) perl
, find
...) 특히 (인수 후 옵션을 적용)는 GNU의 변종. 때로는 같은 다른 실행할 수 있도록 명령을 의심하지 않을 ksh
, bash
또는 zsh
년대 [
또는 printf
...
for file in *; do
[ -f $file ] || continue
something-that-would-be-dangerous-if-$file-were-a-directory
done
라는 디렉토리를 만들면 x -o yes
테스트하는 것과 완전히 다른 조건식이기 때문에 테스트가 긍정적이됩니다.
더 나쁜 것은, x -a a[0$(uname>&2)] -gt 1
모든 ksh 구현을 가진 ( sh
최상의 상용 Unices와 일부 BSD 를 포함하여) 라는 파일을 만들면 uname
해당 쉘이 [
명령 의 숫자 비교 연산자에 대한 산술 평가를 수행하기 때문에 실행 됩니다.
$ touch x 'x -a a[0$(uname>&2)] -gt 1'
$ ksh -c 'for f in *; do [ -f $f ]; done'
Linux
같은 bash
파일 이름과 동일합니다 x -a -v a[0$(uname>&2)]
.
물론, 그들이 임의의 실행을 얻지 못하면 공격자 는 더 적은 피해를 입수 할 수 있습니다 (임의의 실행을 도울 수 있습니다). 파일을 쓰거나 권한, 소유권을 변경하거나 주요 또는 부작용을 가질 수있는 모든 명령을 악용 할 수 있습니다.
파일 이름으로 모든 종류의 작업을 수행 할 수 있습니다.
$ touch -- '-R ..'
$ for file in *; do [ -f "$file" ] && chmod +w $file; done
그리고 당신은 ..
(재귀 적으로 GNU와 함께 chmod
) 쓰기 가능
하게 만듭니다 .
공개적으로 쓰기 가능한 영역에서 파일을 자동으로 처리하는 스크립트 /tmp
는 매우 신중하게 작성해야합니다.
는 어때 [ $# -gt 1 ]
그것은 내가 몹시 싫어하는 것입니다. 어떤 사람들은 따옴표를 생략 할 수 있는지 결정하기 위해 특정 확장이 문제가 될 수 있는지 궁금해하는 모든 어려움을 겪습니다.
말하는 것과 같습니다. 이봐, $#
split + glob 연산자를 적용 할 수없는 것 같습니다 . shell에게 split + glob을 요청합시다 . 또는 버그가 발생하지 않기 때문에 잘못된 코드를 작성해 봅시다 .
지금은 얼마나 가능성이 적습니까? OK $#
(또는 $!
, $?
또는 모든 산술 대체)에는 숫자 (또는 -
일부) 만 포함될 수 있으므로 glob 부분이 빠져 있습니다. 를 들어 분할 부분은하지만 뭔가를 위해, 우리가 필요로하는 모든입니다 $IFS
숫자를 포함 (거나 -
).
일부 셸을 사용하면 $IFS
환경에서 상속 될 수 있지만 환경이 안전하지 않은 경우 어쨌든 게임입니다.
이제 다음과 같은 함수를 작성하면
my_function() {
[ $# -eq 2 ] || return
...
}
의미하는 것은 함수의 동작이 호출되는 컨텍스트에 달려 있다는 것입니다. 다시 말하면, $IFS
입력 중 하나가됩니다. 엄밀히 말하면 함수에 대한 API 문서를 작성할 때 다음과 같아야합니다.
# my_function
# inputs:
# $1: source directory
# $2: destination directory
# $IFS: used to split $#, expected not to contain digits...
그리고 함수를 호출하는 코드 $IFS
는 숫자를 포함하지 않아야합니다. 이 두 개의 큰 따옴표 문자를 입력하고 싶지 않기 때문에 모든 것.
이제 해당 [ $# -eq 2 ]
버그가 취약점 $IFS
이 되려면 공격자가 제어 할 수 있는 가치가 있어야 합니다 . 공격자가 다른 버그를 악용 하지 않으면 일반적으로 발생하지 않습니다 .
그것은 들어 본 적이 없습니다. 사람들이 산술 표현에 데이터를 사용하기 전에 데이터를 삭제하는 것을 잊는 경우가 일반적입니다. 우리는 이미 일부 셸에서 임의의 코드를 실행할 수 있다는 것을 이미 보았지만, 모든 셸 에서 공격자 가 변수에 정수 값을 제공 할 수 있습니다
.
예를 들어 :
n=$(($1 + 1))
if [ $# -gt 2 ]; then
echo >&2 "Too many arguments"
exit 1
fi
그리고와 $1
값 (IFS=-1234567890)
, 산술 평가 설정 IFS의 부작용을 가지고, 다음 [
명령에 대한 체크 의미하지 너무 많은 인수가 바이 패스된다.
어떤 경우에 대한 분할 + 글로브의 연산자가 호출되지 않는 이유는 무엇입니까?
변수 및 기타 확장에 따옴표가 필요한 또 다른 경우가 있습니다 : 패턴으로 사용될 때.
[[ $a = $b ]] # a `ksh` construct also supported by `bash`
case $a in ($b) ...; esac
여부를 테스트하지 않습니다 $a
와 $b
(있는 경우를 제외하고 동일 zsh
)하지만 경우 $a
에 패턴과 일치 $b
. 그리고 당신은 인용 할 필요 $b
는 문자열로 비교하려는 경우 (같은 일에 "${a#$b}"
또는 "${a%$b}"
또는 "${a##*$b*}"
어디 $b
그렇지 않은 패턴으로 수행 될 경우 인용한다).
어떤 것을 의미하는 것은 즉 [[ $a = $b ]]
여기서 경우에 true를 반환 할 수있다 $a
상이하다 $b
(예를 들어 때 $a
이다 anything
하고 $b
있다 *
) 또는 동일한 경우 (예컨대 둘 때 false를 반환 할 수있다 $a
및 $b
있다 [a]
).
보안 취약점이 생길 수 있습니까? 예, 다른 버그와 마찬가지로 여기서 공격자 는 스크립트의 논리적 코드 흐름을 변경하거나 스크립트가 만들고있는 가정을 어길 수 있습니다. 예를 들어 다음과 같은 코드를 사용하십시오.
if [[ $1 = $2 ]]; then
echo >&2 '$1 and $2 cannot be the same or damage will incur'
exit 1
fi
공격자 는를 통과하여 확인을 우회 할 수 있습니다 '[a]' '[a]'
.
패턴 일치 나 split + glob 연산자를 모두 적용 하지 않으면 변수를 따옴표로 묶지 않는 위험은 무엇입니까?
나는 내가 쓰는 것을 인정해야한다.
a=$b
case $a in...
인용문은 해를 끼치 지 않지만 꼭 필요한 것은 아닙니다.
그러나 이러한 경우 (예 : Q & A 답변에서) 따옴표를 생략하면 부작용으로 초보자에게 잘못된 메시지를 보낼 수 있습니다 . 변수를 인용하지 않는 것이 좋습니다.
예를 들어, 그들은 a=$b
OK이면 목록 컨텍스트에서 명령 에 대한 인수에있는 것처럼 많은 쉘에 있지는 않을export a=$b
것이라고 생각하기 시작할 수 있습니다 .export
env a=$b
무엇에 대해 zsh
?
zsh
대부분의 디자인 어색함을 수정했습니다. 에서 zsh
(적어도 SH / KSH 에뮬레이션 모드의 경우) 당신이 원하는 경우, 분할 , 또는 로빙 또는 패턴 매칭을 , 당신은 명시 적으로 요청해야 : $=var
분할하고, $~var
glob에 나 변수의 컨텐츠로 취급 패턴.
그러나 인용되지 않은 명령 대체시 (와 같이 echo $(cmd)
) 여전히 분할 (글로브하지 않음)은 암시 적으로 수행됩니다 .
또한 변수를 인용하지 않는 경우에 따라 원하지 않는 부작용이 빈 제거 입니다. 이 zsh
동작은 globbing을 모두 비활성화 set -f
하고 (로 IFS=''
) 분리 () 하여 다른 쉘에서 수행 할 수있는 것과 유사합니다 . 아직도 안에:
cmd $var
아무 없습니다 분할 + 글로브 하지만, 경우 $var
대신 하나 개의 빈 인수를 수신, 비어있는, cmd
전혀 인수를받을 수 없습니다.
이로 인해 버그가 발생할 수 있습니다 (예 : 명백한 것 [ -n $var ]
). 스크립트의 기대와 가정을 깨뜨려 취약점을 일으킬 수는 있지만 지금 당장 가져 오지 않은 예제를 만들 수는 없습니다.)
당신이 경우에 대해 할 필요가 분할 + 글로브 연산자를?
그렇습니다. 일반적으로 변수를 인용하지 않은 상태로 두려고합니다. 그러나 스플릿 및 글로브 연산자를 사용하기 전에 올바르게 조정해야 합니다. glob 부분이 아닌 split 부분 만 원한다면 (대부분의 경우) globbing ( / ) 을 비활성화 하고 수정해야 합니다. 그렇지 않으면 위에서 언급 한 David Korn의 CGI 예제와 같이 취약점도 발생할 수 있습니다.set -o noglob
set -f
$IFS
결론
간단히 말해서, 쉘에 변수 (또는 명령 대체 또는 산술 확장)를 인용 부호로 남겨 두는 것은 특히 잘못된 컨텍스트에서 수행 될 때 실제로 매우 위험 할 수 있으며, 어떤 잘못된 컨텍스트인지 알기가 매우 어렵습니다.
그것이 나쁜 습관 으로 여겨지는 이유 중 하나입니다 .
지금까지 읽어 주셔서 감사합니다. 머리 위로 넘어가더라도 걱정하지 마십시오. 모든 사람이 코드를 작성하는 방식에 따른 코드 작성의 모든 의미를 이해할 것을 기대할 수는 없습니다. 그렇기 때문에 우리는 모범 사례 권장 사항 을 가지고
있으므로 이유를 이해하지 않고도 따를 수 있습니다.
(그리고 아직 명확하지 않은 경우, 쉘에 보안 관련 코드를 작성하지 마십시오).
그리고 이 사이트에 귀하의 답변에 변수를 인용하십시오!
¹In ksh93
및 pdksh
유도체는 중괄호 확장 로빙이 비활성화가 아니면 (경우에 수행되는 ksh93
버전 짝수 때까지 + ksh93u braceexpand
옵션을 사용할 수 없음).