gawk의 -i 옵션이나 @include 지시문을 안전하게 사용하는 방법은 무엇입니까?

gawk의 -i 옵션이나 @include 지시문을 안전하게 사용하는 방법은 무엇입니까?

gawk -i inplace some-awk-code some-file내부에서 (또는 스크립트 @include "inplace"에서 ) awk파일(또는 기타 확장자)을 편집합니다 .보안 구멍이다.

왜?

이 문제를 어떻게 해결할 수 있나요?

답변1

awkGNU에는 실행할 코드를 지정하는 측면에서 표준에 대한 일부 확장이 있습니다.

표준에서는 코드를 읽을 파일 경로로 간주 되는 하나 이상의 또는 첫 번째 비옵션 인수(예: gawk ) awk로만 코드를 전달할 수 있습니다 . gawk에는 더 많은 옵션이 있습니다.-f filepathfilepathawk -- 'literal code here'

  • -e 'literal code'(또는 --source 'literal code') 에서처럼 sed코드를 여러 매개변수로 분할하고 -f filepath해당 매개변수를 매개변수 간에 분산시킬 수 있습니다.
  • -E filepath(또는 --exec filepath), -f단 하나만 있을 수 있다는 점과 그 이후의 모든 항목은 옵션이나 변수 할당을 고려하지 않고 파일 경로(또는 -표준 입력)만 고려한다는 점을 제외하면 동일합니다.
  • --file filepath: 별칭 -f.
  • -i filepath(또는): --include filepath동작과 유사하지만 약간의 변경이 있습니다.-f설명서에 적힌대로.

이제 질문은 gawk,파일 경로위의 모든 항목이 항상 파일 경로로 간주되는 것은 아닙니다.

  1. 만약에파일 경로존재하지 않는 경우 gawk확장명이 추가된 동일한 파일을 열려고 시도합니다 .awk. 즉, 의도하지 않은 코드를 해석하게 될 수도 있지만 실행하려는 파일이 존재하지 않기 때문에 실제로 문제가 될 가능성은 거의 없습니다. --traditionalor 에서는 그렇게 하지 않지만 -W traditional대부분의 gawk 확장 기능을 사용할 수는 없습니다.
  2. 만약에파일 경로/문자를 포함하지 않는 경우(그리고 가 아닌 경우 -) awk 프로그램은 $AWKPATH쉘과 유사한 방식 으로 환경 변수를 찾거나 with 및 with 를 포함하여 모든 // ( 및 설명된 대로 없이 또는 with 에서 execvp()슬래시 없는 명령을 찾습니다. 위의 확장자가 추가된 경우).$PATH--posix--traditional-f-i-E.awk

두 번째 요점은 여기서 문제의 핵심입니다.

다음을 통해 기본 AWKPATH를 찾을 수 있습니다.

$ (unset -v AWKPATH && gawk 'BEGIN{print ENVIRON["AWKPATH"]}')
.:/usr/share/awk

(멘션에는 그런 변수가 없는데도 ENVIRON!)

현재 작업 디렉터리로 시작하고 그 뒤에 일부 확장이나 ..NET과 함께 제공되는 awk기타 타사 모듈이 포함된 시스템 위치가 옵니다 gawk. 이 시스템에서:

$ ls /usr/share/awk
Assert.awk getlong.awk intdiv0.awk ord.awk rewind.awk
bit2str.awk getopt.awk isnumeric.awk passwd.awk round.awk
Cliff_rand.awk gettime.awk Join.awk processarray.awk shellquote.awk
ctime.awk 그룹.awk libintl.awk fastsort.awk strtonum.awk
dpkg-awk.awk have_mpfr.awk nosign.awk 읽기 가능.awk walkarray.awk
ftrans.awk      제자리에.awk    ns_passwd.awk 파일 읽기.awk 제로 파일.awk

즉 , -f/ 의 경우 현재 작업 디렉터리에 로드 -E하려는 경우 필요하며 , 현재 작업 디렉터리에 없는 경우 다른 곳에서 로드할 수 있습니다 (또는). 셸에서 실행하려면 현재 작업 디렉터리가 필요한 것과 같습니다(보안상의 이유로 일반적으로 포함되지 않으며 위에 표시된 대로 로드하려고 시도한다는 점만 제외).filegawk -f ./filegawk -f filefilefile.awkfile./cmdcmd$PATH.gawkfile.awk

이는 일반적인 것 외에도 적용 됩니다 -i.-i포함하다이 경우 라이브러리의 gawk 확장하다그것들이 있어야 할 디렉토리에서 그것들을 찾을 것으로 예상하고,하다확장을 추가하고 싶습니다 .awk(해당 라이브러리 확장에는 일반적으로 이러한 확장이 있으므로).

에서 (또는 시스템에 설치된 위치) 찾고 gawk -i inplace 'some code' some-file싶지만 여기서 문제는 기본 AWKPATH입니다.gawk/usr/share/awk/inplace.awkinplace.awk시작and .이므로 gawkand에서 먼저 조회됩니다../inplace./inplace.awk

/tmp쓰기 가능하거나 이미 다른 사람이 쓸 수 있거나 일반적으로 신뢰할 수 없는 디렉터리에서 이 파일을 실행 하면 악성 코드가 실행될 수 있습니다.

예를 들어 다음을 실행합니다.

echo 'BEGIN{system("reboot")}' > /tmp/inplace

awk -i inplace현재 작업 디렉터리에서 실행되는 모든 스크립트는 /tmp시스템을 재부팅 한다는 것을 알 수 있습니다 !

이 문제를 해결하려면:

  • inplace각 시스템이나 Gawk 배포에 맞게 경로를 조정해야 할 수도 있지만 awk -i /usr/share/awk/inplace.awk확장 경로를 하드코딩하는 대신 사용하세요 .awk -i inplace

  • 또는 .모든 상대 경로 구성 요소를 제거하십시오 $AWKPATH.

    export AWKPATH="$(LC_ALL=C gawk 'BEGIN {
      n = split(ENVIRON["AWKPATH"], dirs, ":")
      for (i = 1; i <= n; i++)
        if (substr(dirs[i], 1, 1) == "/") {
          newawkpath = newawkpath sep dirs[i]
          sep = ":"
        }
      if (newawkpath == "") newawkpath = "/dev/null"
      print newawkpath}')"
    

    현재 작업 디렉터리에서 파일을 사용 gawk -f ./file하거나 로드 해야 한다는 점을 명심하세요 ( 위에 표시된 변경 사항 없이도 이미 이 작업을 수행하고 있을 수 있습니다). 또한 4.1.2 이전의 gawk 버전이 검토 중이라는 점에 유의하세요 .awk -E ./file$AWKPATH$AWKPATH

    이 방법 은 시작 시 환경에 이미 있어야 하므로 ,를 #! /usr/bin/gawk -E사용하는 스크립트 에서는 사용할 수 없습니다 . 따라서 를 사용하는 스크립트가 있는 경우 사용자 에게 확장 경로를 변경하거나 위와 같이 확장 경로를 하드코딩하도록 지시해야 합니다 .@include$AWKPATHgawkgawk@include "some-extension"$AWKPATH

  • 또는 수십 년 동안 perl사용 가능했던 -i내부 편집 옵션을 사용하여 awk가능한 모든 작업을 수행하고 더 스마트한 구문²과 더 적은 이식성 문제로 더 많은 작업을 수행할 수 있습니다. 그러나 --in 을 잊지 마십시오 perl -i -ne 'perl code' -- *.txt. 그렇지 않으면 코드 주입 취약점이 발생할 수도 있습니다(또는 를 사용하십시오 ./*.txt. 참조).perl -ne '...' 실행의 보안 위험 *)!


¹ 그렇지 않은 경우파일 경로-이 경우 대부분의 구현에서는 awk이를 표준 입력에서 코드를 읽는 것으로 해석합니다.

s와 동일 하다고 생각할 수 있는 ² perl옵션 은 다른 상대 경로를 포함하지도 않고 포함하지도 않는 기본 검색 경로를 사용합니다(참조) 또는에서 모듈 찾기-Mgawk-iM$PERL5LIB$PERLLIB(unset -v PERL5LIB PERLLIB && perl -le 'print for @INC'.

답변2

먼저, 제가 글을 쓰는 모든 포럼에서 제가 수년 동안 말해온 내용을 말씀해주신 @StephaneChazelas에게 감사 sed -i드립니다 awk -i inplace.

이미 말씀하신 내용에 추가하여(이것은 나에게 새로운 내용이며 생각했던 것보다 더 나쁩니다):

  1. "-제자리에"? 설마!

    sed -i둘 다 awk -i inplace"제자리에서" 편집하는 척하지만 그렇지 않습니다. 실제로 그들은 (숨겨진) 임시 파일을 출력으로 생성하고 결국 이동하여 원본 파일을 덮어씁니다. 기본적으로 POSIX 확인 변형을 사용하는 것과 동일하지만 자동입니다. 이것은 좋은 생각처럼 들리지만 "내부" 관점에서 볼 때 소유권 및 파일 모드뿐만 아니라 inode 번호도 보존하고 싶습니다.그렇지 않다!실제로 올바른 전제 조건이 충족되면 세 가지 속성이 모두 변경됩니다(예: 사용자가 파일에 쓸 수 있지만 파일과 다른 기본 그룹, 고정 비트가 있는 디렉터리 등).

    오해하지 마세요. 이런 일이 발생하는 데에는 아무런 문제가 없습니다. 프로세스가 임시 파일에 쓴 다음 자체적으로 복사되는 경우에도 같은 방식으로 발생합니다. 하지만 이 경우라면 나는이것을 깨닫다그리고 변경 후에는 파일 모드 등이 수정되었는지 확인하세요. 왜냐면 이 척은 효과가 있거든제자리에사용자는 이 효과를 인식하지 못할 가능성이 높습니다.

  2. 존재하지 않는 임시 파일

    다음 질문은: 그 과정에서 파일이 수정되고 임시 파일이 생성되면 예방 조치를 취하겠습니다. 임시 파일을 저장할 공간이 충분해야 하며 그 후에는 반드시 임시 파일을 삭제하겠습니다. 임시 파일이 어디로 가는지 모르기 때문에(맨 페이지에 이에 대한 정보가 없고 모든 것이 "그 자리에서" 발생한다고 가정함) 이를 제어할 수 없으며 스크립트에서 시스템이 충돌하는 경우(이러한 일이 발생함) ) 디스크 공간을 차지하는 일부 아티팩트를 남겨두었는지 모르겠습니다.

답변3

또한 gawk에는 환경에서 찾을 수 없는 경우 기본값을 갖는 AWKLIBPATH 변수가 있습니다. 이 변수는 @load "library"라이브러리 파일이 발견되는 위치를 제어합니다. 공유 라이브러리 로드

.기본값은 (내가 설치한 버전의 경우) 디렉토리를 사용하지 않는 것 같지만 변경될 수도 있다고 생각합니다.

답변4

지금 @include은 내cppawk. C 전처리기, 해당 #include매크로 및 모든 것을 사용할 수 있게 해주는 awk 주변의 작은 쉘 스크립트 래퍼입니다 .

#include현재 디렉토리는 검색되지 않습니다. 한 가지 더 나은 기능이 있습니다. 헤더 이름이 큰따옴표로 묶인 경우 #include지시문이 있는 파일과 동일한 디렉터리에서 해당 이름을 찾습니다. 이를 통해 cppawk여러 파일이 포함된 프로그램을 쉽게 만들 수 있습니다. 기본 파일은 #include "..."지시문의 상대 경로를 사용하여 다른 파일을 쉽게 찾을 수 있습니다.

cppawk자체 라이브러리 헤더가 일부 있지만 내부 파일 편집을 위한 솔루션을 제공하는 것은 없습니다. 이 유틸리티를 사용하면 솔루션을 쉽게 재사용할 수 있습니다.

이것은 품질이 낮은 프로토타입입니다.

$ cat file.bak
alpha
bravo
charlie
$ cp file.bak file
$ cppawk '
#include "inplace.h"
{ out(NR, $0) }
' file
$ cat file
1 alpha
2 bravo
3 charlie

콘텐츠 inplace.h:

BEGIN {
  __inplace_tmpfile = "xyz.tmp"
  __inplace_origfile = ARGV[1]
}

END {
  close(__inplace_tmpfile)
  system("mv " __inplace_tmpfile " " __inplace_origfile)
}

#define out(...) print __VA_ARGS__ >  __inplace_tmpfile

여기에는 최소한 다음이 필요합니다. 임시 파일을 가져오고 내용을 쉘에서 이스케이프하여 명령에 ARGV[1]안전하게 삽입할 수 있는 더 좋은 방법입니다.mv

out리디렉션이 없는 기본 구현을 가질 수 있습니다. 그런 다음 코드가 포함될 때 코드를 수정할 필요가 없도록 print프로그램 대신 이를 사용하는 습관을 갖게 됩니다 .cppawkinplace.h

-f스크립트 자료가 포함될 수 있으므로 전처리 없이 이러한 목표 중 일부를 달성할 수 있습니다 . inplace.h헤더 대신 다음 내용이 포함된 파일을 준비합니다 inplace.awk.

BEGIN {
  inplace = "xyz.tmp"
  __inplace_origfile = ARGV[1]
}

END {
  close(inplace)
  system("mv " inplace " " __inplace_origfile)
}

임시 파일을 보유하는 변수 이름의 익명화를 해제했으며 이제 이는 인터페이스의 일부입니다.

불행하게도 명령줄 내 스크립팅 항목과 포함을 혼합할 수 있으려면 -fGNU 특정 -e옵션이 필요합니다.

$ mv file.bak file
$ awk -f inplace.awk -e '{ print NR, $0 > inplace }' file
$ cat file
1 alpha
2 bravo
3 charlie

인용 방법에 대한 질문도 있습니다 inplace.awk. 그것을 어디에 두고 어떻게 찾을 수 있나요? #include그런 문제는 없습니다. 코드와 함께 보내면 자체 옆에서 찾을 수 있습니다. 라이브러리 헤더로 넣으면 다시 문제 cppawk가 되지 <inplace.h>않습니다. 또한 cppawk --prepro-only전처리기 없이 실행할 수 있는 전체 "번역 단위" 캡처를 사용하는 옵션도 있습니다 cpp.

관련 정보