Bourne과 같은 일부 쉘은 연관 배열을 지원합니다: ( ksh93
1993년부터), zsh
(1998년부터), bash
(2009년부터), 하지만 3 사이에는 동작에 약간의 차이가 있습니다.
일반적인 용도는 특정 문자열의 발생 횟수를 계산하는 것입니다.
그러나 나는 다음과 같은 것을 발견했습니다.
조판-A 개수((count[$var]++))
특정 가치를 위해 일하지 마세요 $var
.모든 명령 실행취약성콘텐츠가 $var
공격자의 통제하에 있거나 그럴 가능성이 있는 경우.
왜 그런 겁니까? 문제가 있는 값은 무엇입니까? 이 문제를 어떻게 해결할 수 있나요?
답변1
문제는 다음과 같은 쉘 산술 표현식에 있습니다.
- 내부
$((...))
(POSIX), ((...))
(ksh/bash/zsh)- 배열 인덱스
- 일부 쉘 내장 함수의 일부 매개변수
- 내부 숫자 비교 연산자의 피연산자
[[...]]
단어 확장( ${param}
, $((...))
, $[...]
, $(...)
, `...`
, ${ ...; }
실행첫 번째, 결과 텍스트를 산술 표현식으로 해석합니다.
이 경우 $((...))
이는 POSIX 요구사항이기도 합니다.
이를 통해 이와 같은 op=+; echo "$(( 1 $op 2 ))"
작업이 가능하며 출력이 평가되는 표현식 이기 때문에 a=1+1; echo "$(($a * 2))"
출력이 3
아닌 이유도 설명합니다.4
1+1 * 2
이것도 일부산술 표현식에서 정제되지 않은 데이터를 사용하는 것이 보안 취약점인 이유일반적으로 말하면.
다음과 같은 것에도 적용된다는 점을 간과하기 쉽습니다.
(( assoc[$var]++ ))
위를 제외하고 ksh93
(편집하다이제 bash 5.2+에서는 아래 참조) $var
먼저 확장한 다음 결과를 해석하십시오.
이는 or가 $var
포함된 경우 or 표현식이 평가되고 /가 거기에서 특별한 의미를 갖는다는 것을 의미합니다. 이면 가 됩니다 .@
*
assoc[@]++
assoc[*]++
@
*
$var
x] + 2 + assoc[y
assoc[x] + 2 + assoc[y]
이제 일반적으로 에서는 그런 것이 포함되어 $(( $var ))
있어도 2차 확장이 발생하지 않고 실행되지 않습니다. 하지만 이미 본 것처럼$var
$(reboot)
reboot
쉘 산술 평가에서 정리되지 않은 데이터 사용의 보안 영향word[...]
, 재귀 확장을 허용하기 위해 내부적으로 발생하는 경우 예외가 존재합니다. 문제의 근원은 불행하다특징Korn 쉘의 if에는 var
산술 표현식이 포함되어 있으며, $((var))
의 산술 표현식은 $var
재귀적으로(예: when var2='var3 + 1' var='var2 + 1'
) 평가됩니다. 이는 POSIX에서 허용되지만 요구되지 않는 연산입니다.
이는 배열 멤버로 확장되므로 배열 인덱스의 내용이 결국 재귀적으로 계산된다는 의미입니다. 따라서 이면 $var
결국 에는 으로 호출 $(reboot)
됩니다 .(( assoc[$var]++ ))
reboot
ksh93
어느 정도 해결 방법이 있는 것 같지만 $var
포함되지 않은 경우에만 해당됩니다 $
. 따라서 ksh93은 var=']'
, var='@'
또는 와 함께 사용할 수 있지만 var='`reboot`'
와 함께 사용할 수는 없습니다 $(reboot)
.
예를 들어, reboot
harmless로 바꾸면 다음과 같습니다 uname>&2
.
$ var='1$(uname>&2)' ksh -c 'typeset -A a; (( a[$var]++ )); typeset -p a'
Linux
typeset -A a=([1]=1)
$ var='1$(uname>&2)' bash -c 'typeset -A a; (( a[$var]++ )); typeset -p a'
Linux
Linux
declare -A a=([1]="1" )
$ var='1$(uname>&2)' zsh -c 'typeset -A a; (( a[$var]++ )); typeset -p a'
Linux
Linux
typeset -A a=( [1]=1 )
명령 은 uname
결국 실행되었습니다( bash
(<5.2)에서 두 번, zsh
현재 값을 얻기 위해 한 번, 할당을 수행하기 위해 두 번째로 추측).
버전 5.0에서 bash는 assoc_expand_once
동작을 변경하는 옵션을 추가했습니다.
$ var='1$(uname>&2)' bash -O assoc_expand_once -c 'typeset -A a; ((a[$var]++)); typeset -p a'
declare -A a=(["1\$(uname>&2)"]="1" )
이제 작동하지만 , 또는 의 문제를 해결하지 못하므로 @
해결 *
되지 ]
않습니다.모든 명령 실행취약점:
$ var='x]+b[1$(uname>&2)' bash -O assoc_expand_once -c 'typeset -A a; ((a[$var]++)); typeset -p a'
Linux
declare -A a
(이번에는 uname
평가의 일환으로 실행분명히array( b
) 인덱스 평가).
문제가 있는 문자 목록은 셸마다 다릅니다. $
는 세 가지 모두에 대한 문제이고, \
, `
는 및 에 대한 문제 [
입니다 ]
. 와 동일하며 null 값입니다. 또한 일부 로케일에서는 일부 문자의 인코딩에 의 인코딩이 포함되거나 적어도 포함되지 않아 문제가 발생할 수 있습니다. 이를 탈출하는 방법은 세 가지 쉘 모두에서 다르게 수행되어야 합니다.bash
zsh
"
'
bash
@
*
\
`
[
]
이 문제를 해결하려면 다음을 수행할 수 있습니다.
assoc[$var]=$(( ${assoc[$var]} + 1 ))
대신에. 그건:
- 연관 배열 구성원에 대한 할당이 수행되지 않습니다.의 일부로산술 표현식이지만 연관 배열 구성원 할당만 수행합니다. 즉, 연관 배열 구성원을 대상으로 하는
=
,++
,--
,+=
, ... 산술 연산자를 사용하지 마십시오./=
- 산술 표현식에서 연관 배열을 참조할 때 또는 단순한 숫자가 아닌 산술 표현식을 포함하려는 경우에는 ,
assoc[$var]
그러나${assoc[$var]}
(또는$assoc[$var]
inzsh
) 을 사용하지 마십시오.(${assoc[$var]})
하지만 언제나처럼값연관 배열 구성원은 사용자가 제어해야 하며 일반 숫자가 바람직하며 다른 매개변수 확장과 마찬가지로 주위에 공백을 두는 것이 가장 좋습니다. 예를 들어 음수 값 에 문제가 발생할 수 있는 후자 ((1 - $var))
보다 선호됩니다 ( .((1-$var))
((1--1))
--
1
주목해야 할 또 다른 점은 $var
비어 있을 때 in은 (( 1 + var ))
여전히 var
산술 표현식 구문의 토큰이고 해당 값은 if입니다 0
. 그러나 에서는 (( 1 + $var ))
산술 표현식이 가 되는데 1 +
이는 구문 오류입니다( 그러나 단항 연산자를 호출하면 (( $var + 1 ))
가 되기 때문에 문제가 되지 않습니다 ).+ 1
+
기타 방법 bash
(5.1 이하, assoc_expand_once
옵션이 다음과 같은 경우)아니요활성화됨) 또는 zsh
(그러나 그렇지 않음ksh93
]
캐릭터 에는 \
여전히 문제가 있습니다), 위에서 언급한 두 번째 재귀 해석이 나올 때까지 확장이 지연됩니다.
(( assoc[\$var]++ ))
let 'assoc[$var]++'
(반드시 사용하세요.하나의여기에 인용됨)incr='assoc[$var]++'; (($incr))
(심지어((incr))
)((' assoc[$var]++ '))
또는(( assoc['$var']++ ))
(bash
만).
이는 산술 평가에 의해 생성된 종료 상태를 유지한다는 장점이 있습니다(성공0이 아닌 경우) 다음을 수행할 수 있습니다.
if (( assoc[\$var]++ )); then
printf '%s\n' "$var was already seen"
fi
이제 이는 쉘 관련 문제를 남깁니다 bash
. bash
연관 배열은 널 키를 지원하지 않습니다. 둘 다 and (not ) assoc[]=x
에서는 실패 하지만 isempty는 or 에서는 작동 하지만 or에서는 작동하지 않습니다 . 이제 bash-5.1에서도 지원 되지만 .bash
zsh
ksh93
assoc[$var]
$var
zsh
ksh93
bash
zsh
assoc+=('' value)
bash
따라서 독점적으로 사용되고 bash
null 키가 가능한 값 중 하나인 경우 유일한 옵션은 고정 접두사/접미사를 추가하는 것입니다. 예를 들어 다음을 사용하십시오.
assoc[.$var]=$(( ${assoc[.$var]} + 1 ))
또는:
let 'assoc[.$var]++'
(( assoc[.\$var]++ ))
...
2023년 편집
위의 해결 방법 중 대부분은 더 이상 bash-5.2에서 작동하지 않습니다.
bash-5.2.21에서는 (( assoc[$var]++ ))
(비어 있지 않은 경우 ) 및 , 또는 을 포함하는 경우에도 $var
안전한 것으로 보입니다 .(( assoc[.$var]++ ))
$var
]
\
*
@
이는 여전히 ACE 취약점의 이전 버전입니다.
5.2 및 5.2 이전 버전에서 작동하는 유일한 방법은 다음과 같습니다.
let 'assoc[$var]++'
assoc[$var]=$(( ${assoc[$var]} + 1 ))
ref='assoc[$var]'; (( $ref++ ))
- Bash 위키의 @ormaaj가 제공하는 팁:
(( assoc${-+[}\$var]++ ))
.
이 중 두 번째만 ksh93, zsh 및 bash에서 작동합니다.
특히, (( assoc[\$var] ))
zsh에서 작동했던 메서드는 더 이상 bash-5.2에서 작동하지 않습니다.