잘 구성된 형식은 printf
일반적으로 작동하는 형식을 갖습니다.
$ var="Hello"
$ printf '%s\n' "$var"
Hello
하지만 형식을 제공하지 않으면 어떤 보안 문제가 발생할 수 있습니까?
$ printf "$var"
Hello
변수확장을 인용했으니 문제는 없겠죠?
답변1
존재하다:
printf "$var"
두 가지 질문이 있습니다:
- 형식으로 전달되는 변수 데이터입니다.
$var
공격자의 통제 하에 있는 경우 문제가 될 수 있음 - 옵션 구분 기호(
--
)가 누락되어$var
로 시작하면 옵션으로 처리될 수 있습니다-
.
상황은 더욱 악화됩니다:
printf $var
Bourne과 같은 셸의 대부분의 분할+글로브는 $var
확장하는 동안 실행되므로 위에서 언급한 보안 취약점이 발생합니다.bash/POSIX 쉘에서 변수를 인용하는 것을 잊어버리는 보안 위험.
여기에서는 모든 명령을 실행할 수 있습니다.
$ export var1='-va[1$(uname>&2)] x' var2='%d a[1$(uname>&2)]'
$ bash -c 'printf $var1'
Linux
$ ksh -c 'printf $var2'
Linux
0
임의의 uname
명령(다행히도 여기서는 무해함)은 에 의해 제공됩니다 printf
.
~을 위한
printf "$var"
그 자체로는 더 적은 수의 질문을 생각할 수 있습니다.
가장 확실한 것은 DoS로, var=%1000000000s
출력에 공백 문자를 많이 보내거나 더 나쁘게는 %.1000000000f
메모리와 CPU 시간을 많이 사용합니다.
$ var=%.1000000000f command time -f 'max mem: %MK, elapsed: %E' bash -c 'printf "$var"' | wc -c
max mem: 4885344K, elapsed: 0:12.33
1000000002
$var
다른 DoS는 구문 오류를 유발하는 잘못된 형식 또는 잘못된 옵션에 의해 유발될 수 있으며 , 이로 인해 해당 옵션이 활성화될 때 호출되는 스크립트 printf
및 오류가 발생할 수 있습니다.errexit
printf "$var"
이 옵션을 지원하는 유일한 세 개의 셸인 , 및 에서는 var='-va[1$(uname>&2)]'
문제가 되지 않는 것 같습니다. 이 셸은 형식으로 처리하고 다른 두 개는 구문 오류로 처리합니다(형식 누락으로 인해). bash
.ksh93
zsh
-v varname
zsh
export var='%(%Z %z)T\n'
ksh93 및 bash에는 스크립트의 시간대를 나타내는 작은 정보 유출이 있습니다 .
$ bash -c 'printf "$var"'
BST +0100
에서는 yash
요소가 여러 개인 배열인 경우 여러 인수를 사용하여 호출되지만 printf "$var"
산술 평가가 수행되지 않으며 어떤 경우에도 산술 평가가 동일한 유형의 영향을 받지 않습니다.printf
$var
yash
printf
ksh, bash 또는 zsh에 영향을 미치는 명령 주입 취약점.
ksh93 printf
은 가장 많은 확장 기능(모든 날짜 형식, 정규식 형식 변환, 문자 너비 기반 패딩, URI/HTML 인코딩...)을 갖춘 것이며 아직 실험적입니다. printf "$data"
수천 줄의 코드가 노출되었습니다.데이터. 거기에 임의의 명령 실행 경로가 있다고 해도 놀라지 않을 것입니다(아마도 일부 산술 표현식 평가를 통해 또는 자체 코드에서 일부 버그를 트리거함으로써). 물론 printf
이는 모든 구현에서 발생할 수 있습니다.
C 함수에서 변경 가능한 외부 데이터의 문제점은 스택에 있는 임의의 메모리 영역을 역참조하는 시퀀스가 printf()
포함되어 있을 때입니다 . var가 에 전달된 12번째 인수에 저장된 바이트 값을 인쇄하려고 할 때 . 다른 인수가 전달되지 않았기 때문에 스택에 뭔가 다른 것이 있게 되는데, 이는 아마도 민감한 정보를 담고 있는 메모리의 일부 영역에 대한 포인터일 것입니다. 와 함께 , 결국에는%
printf(var)
%12$s
printf
printf
%n
printf()
글쓰기거기에 몇 가지 숫자가 있습니다.
$ tcc -run -w -xc - $'%6$s\n' <<<'f(char*f){char*s="secret";printf(f);}main(int c,char**v){f(v[1]);}'
secret
$ tcc -run -w -xc - $'%p%p%p%p%p\n%s\n' <<<'f(char*f){char*s="secret";printf(f);}main(int c,char**v){f(v[1]);}'
0x7fff1182db380x7fff1182db500x7900000x80x562b5ec0ba6a
secret
printf
유틸리티는 결국 printf()
이 모든 것을 스스로 호출하거나 구현하게 될 수 있습니다(적어도 %b
어느 정도는 그래야 하며 printf()
숫자 형식의 경우 인수를 숫자로 변환해야 합니다).
호출하는 경우 printf()
형식 사양을 재정의할 수 있는 충분한 인수 없이 호출되는 것을 방지합니다. 이는 아무것도 출력하지 않거나 0을 출력하는 것과 같은 POSIX 요구 사항 printf "%s"
이므로 구현 시 충분한 빈 문자열 또는 0개의 숫자 인수를 에 전달해야 합니다.printf %d
printf
printf()
printf
잘못 작성된 구현이 이를 올바르게 수행할 수 없다고 상상할 수 있습니다 . 나는 아무것도 모르지만 과거에 awk
자체 구현이 영향을 받는 것을 보았습니다 (또한 처리에 의해 또는 관련 처리 에 의해 ).printf()
OFMT
CONVFMT
printf()
그러나 print "$var"
이 벡터를 통한 임의의 명령 주입 취약점이 존재합니다 zsh
. 거기에서 사용하는 것이 중요 print -- $var
하며, 일반적으로 print -r -- "$var"
원하는 곳에 사용하는 것도 중요합니다.
var='%(%.999999999999s)T'
² 예를 들어 Ubuntu 20.04와 함께 제공되는 ksh93이 포함된 SEGV를 받았습니다.
³ 오늘도 현재 버전에서는 busybox
출력 busybox awk -v OFMT='%#x %#x %#x %#x %g' 'BEGIN {print 1.1}'
과 0x1 0x4 0x4 0x4624bb30 1.1
세그 busybox awk -v OFMT='%n %g' 'BEGIN {print 1.1}'
폴트가 발생합니다.
답변2
printf
C 함수가 아닌 쉘 명령에 대해 이야기하고 있으므로 printf(3)
올바르게 인용하면 "$var"
쉘 명령은 C에서 수행할 수 있는 기존 스택 덤프를 허용하지 않습니다. 그러나 Stéphane의 답변에서 알 수 있듯이 아래 예와 같이 명령 주입에 인용되지 않은 확장을 사용하는 경우에도 몇 가지 위험이 있습니다.
어떻게사용하는 출력은 후속 처리에도 영향을 미칠 수 있습니다.
어리석은 예를 만들어보세요:
tst()
{
while [ "$x" == "" ]
do
read x
done
printf $x
}
"$x is notempty"라는 조건은 통과되지만 사용자가 입력하면 printf의 출력은 비어 있게 됩니다 %s
. 따라서 가정된 출력이 tst
비어 있지 않은 루틴은 실패할 수 있습니다. 저것할 수 있다예기치 않은 코드 경로가 사용됩니다.
저것할 수 있다코드의 나머지 부분에 따라 보안 문제가 발생합니다.
여기에는 주의사항과 함께 "ifs"와 "coulds"가 많이 있다는 점에 유의하세요. 이는 응용 프로그램에 따라 매우 다릅니다. 그래서 즉각적인 영향은 없을 것이라고 말씀드린 겁니다.
따라서 표준 방어 코딩 스타일에서는 입력을 신뢰할 수 없는 경우 형식 문자열을 지정하도록 제안합니다. 입력을 신뢰할 수 있는 경우에는 이것이 필요하지 않습니다.
비보안 관점에서 hello%sthere
볼 때 일반적으로 출력이 입력과 일치하기를 원합니다. 사용자가 입력하면 hellothere
.