리눅스(Linux), 유닉스(Unix) 기반의 쉘스크립트를 작성하는 목적은 주요정보통신기반시설 기술적 취약점 분석 평가 방법 가이드라인의 각 항목을 자동으로 진단하여 취약점을 도출하기 위함이다.


즉, 각 항목별 양호/취약 판단, 취약점이 발생한 현황, 현황에 대한 설명 등을 자동으로 판단하여 엑셀 파일로 저장한다.


리눅스, 유닉스 취약점 진단 스크립트를 개발하는데 문제가 될 것은 없다. 하지만, 웹 서버(Resin, Webtob, Jeus, Tomcat 등)는 일반적인 주석인 #이 아니라 HTML 주석이므로 문제가 발생한다.

 

이외에 다양한 문제가 있어 쉘스크립트 개발 책을 참고했지만 그 책 조차도 언급이 없어 구글 검색으로 다양한 정보를 알게되어 쉘스크립트를 개발하는데 참고사항이 되었으면 하는 마음에 공유하기로 한다.


물론 아주 대단한 팁은 아니지만 이런 정보를 알고 있는 상태에서 개발한다면 시간을 단축시키는 효과가 있다.


HTML 주석 제거

리눅스, 유닉스 설정 파일의 주석은 #이므로 주석을 제거하려면 grep -v "#" 명령어를 입력하면 된다. 하지만, 시스템에 존재하는 설정 파일의 주석과 다른 웹 서버(Resin, Webtob, Jeus, Tomcat 등) 설정 파일은 주석이 <! -- -->이다.

 

그렇다고 grep -v <!-- 또는 grep -v --> 명령어를 입력해도 한 줄 라인의 주석만 제거되므로 여러 줄의 주석을 제거할 수 없다. 여러 줄의 주석을 제거하려면 하단에 명시된 두개의 명령어 중 하나를 사용하면 된다.


# cat tmp.html
    <!--
     |                                |
     |  This is a dummy dummy         |
     |      please  please            |
     |                                |
     |       help help ...            |
      ________________________________
     | -->

# cat tmp.html | sed -e :a -re 's/<!--.*?-->//g;/<!--/N;//ba' | egrep "[a-zA-Z0-9]|<"
# cat tmp.html | awk '/<!--/ {off=1} /-->/ {off=2} /([\s\S]*)/ {if (off==0) print; if (off==2) off=0}' | egrep "[a-zA-Z0-9]|<"

awk 명령어에서 범위 내 변수 사용

awk 명령어로 각 라인의 범위를 지정하는 문자는 '/범위시작/,/범위끝/'이다. ask 명령어로 범위를 지정할 때, 각 변수의 값을 대입하는데 '/$START/,/$END/'로 지정하면 awk 명령어 구문 오류가 발생한다.


범위 내에 변수를 대입할 때 반드시 변수명 사이에 싱글쿼터+더블쿼터인 '"$변수명"' 형식으로 감싸줘야 한다.


# START="100 START"
# END="100 END"

# cat tmp.txt | awk '/'"$START"'/,/'"$END"'/'

명령어(실행 파일) 존재 확인

인프라 취약점 진단 시 해시 값을 체크하는 구문이 존재하여 명령어의 존재 유무를 확인하는 코드를 삽입했다. 처음에 md5sum 명령어가 위치한 절대 경로를 시스템 배포판별로 모두 구해서 삽입했는데, 구글 검색해보니 아주 간단하게 명령어의 존재 유무를 체크할 수 있다.

 

command 뒤에 명령어를 입력하면 해당 명령어가 존재하는지 확인하는데, 만약 존재하지 않으면 스크립트를 종료한다. 명령어 존재 시 구문을 추가하고 싶다면 if 문 뒤에 !를 제거하면 된다.


if ! [ -x "$(command -v md5sum)" ]; then
  echo 'Error: md5sum is not installed.' >&2
  exit 1
fi

시스템 버전 파일로 저장

명령어의 버전(배시 버전, 웹 서버 버전 등) 정보를 텍스트 파일로 저장할 때 일부 명령어가 텍스트 파일로 저장되지 않는다.


버전 정보 출력 시 에러의 방향을 현재의 출력 방향으로 보내는 문구인 2>&1을 사용하지 않으면 버전 정보가 화면에 출력되고, 파일에 저장하지 않으므로 반드시 2>&1를 삽입해야 한다.


$SERVER_BIN -v >> tmp.txt 2>&1

FreeBSD에서 for(())문 에러 해결

FreeBSD에서 for((i=0;i<=5;i++)) 구문을 사용하면 에러가 발생하여 스크립트가 정상적으로 실행되지 않는다. 또한, 리눅스에서 지정한 숫자만큼 반복하는 seq 명령어가 있는데, FreeBSD는 해당 명령어가 존재하지 않는다.


이를 해결하려고 for문이 아닌 while문으로 변경했더니 정상적으로 실행된다. 다음 코드의 예시는 체크리스트를 미리 만들어 놓고, 체크리스의 숫자만큼 반복하여 체크리스트의 목록을 출력하는 예이다.


AV_1="UNIX1-01"
AV_2="UNIX1-02"
AV_3="UNIX1-03"
AV_4="UNIX1-04"
AV_5="UNIX1-05"

i=1
while [ $i -le 5 ]
do
  eval "AV=\${AV_$i}"
  echo $AV
  i=`expr $i + 1`
done

상호작용 시 /dev/tty 사용

일반적인 쉘스크립트로 사용자에게 입력을 요구할 때, read 명령어를 사용한다. 하지만, 해당 스크립트를 난독화를 적용하고 실행하게 되면 에러가 발생한다.


사용자에게 입력을 요구하는 문자열을 출력할 때, /dev/tty를 지정하고, 사용자가 입력한 문자열을 변수에 저장할 때, /dev/tty를 지정하면 에러가 발생하지 않는다. /dev/tty는 터미널을 의미하는 것으로 입력한 데이터를 현재의 터미널로 보낸다는 의미이다.


echo "웹 서버 종류 입력" 
echo -n "    (ex. resin ...) : " > /dev/tty 
read SERVER < /dev/tty

에러 메시지를 d/ev/null로 버림

스크립트를 실행 중 에러가 발생하면 화면에 에러 메시지가 출력된다. 난독화된 스크립트를 실행하다가 이런 에러 메시지를 발생하면 스크립트에서 문자열 검색을 하면 로직을 파악하는 단서가 될 수 있다.


항상 명령어를 실행 중 에러(2>)가 발생하면 /dev/null로 전송하여 출력되지 않도록 한다.


$BIN version | grep "4" >> tmp.txt 2> /dev/null

텍스트 파일 비교 시 comm 사용

스크립트로 여러 개의 HTML 주석을 처리하는 방법을 알지 못했을 때 사용한 방법으로 comm 명령어는 두개의 텍스트 파일을 비교하는 명령어이다.


즉, 두개의 텍스트 파일의 공통점과 다른점을 찾아주며 다양한 옵션이 있는데, 옵션으로 -3을 지정하면 다른점을 찾은 결과만 저장한다.


SERVER_USER=`comm -3 tmp1.txt tmp2.txt` 

PATH 환경변수에 점(.) 제거하는 스크립트

PATH 환경변수 앞 부분에 점이 있으면 현재 디렉토리에서 임의의 파일을 실행할 수 있는 장점이 존재한다. 하지만, 관리자 입장에서 매우 편리한 기능이지만 보안상 좋지 않는 설정이다.


쉘 설정 파일에서 PATH 환경변수에 점이 없는데, echo $PATH 명령어를 실행하면 점이 존재하는 경우가 있다. 이 경우 권고 사항에 따라 조치를 하려고 해도 할 수 없다.


PATH 환경변수에 점을 제거하는 스크립트를 실행하면 정상적으로 점이 제거된다.


# vi ~/.bash_profile

DOT=. 
PATH="$(echo "$PATH" |sed -e "s#\(^\|:\)$(echo "$DOT" |sed -e 's/[^^]/[&]/g' -e 's/\^/\\^/g')\(:\|/\{0,1\}$\)#\1\2#" -e 's#:\+#:#g' -e 's#^:\|:$##g')" 
export PATH 

# source ~/.bash_profile
# echo $PATH 

▶ Linux 쉘스크립트 문법 및 예제

▶ OpenSSL 파일 암호화/복호화

▶ 스크립트 난독화 및 암호화 (bash obfuscate)

▶ expect 스크립트 자동화


  • 카카오톡-공유
  • 네이버-블로그-공유
  • 네이버-밴드-공유
  • 페이스북-공유
  • 트위터-공유
  • 카카오스토리-공유

댓글을 달아 주세요

  1. noname
    2020.07.01 00:48

    last 명령 후 결과를 입력리다이렉션으로 저장했습니다. 이제 이 결과를 가지고 사용자별로 작업한 시간을 모두 합산해야하는데 어떻게 해야할지 모르겠습니다. awk 명령어와 스크립트를 사용해서 출력을 해야하는데 도와주실 수 있나요...

    • dazemonkey "'"'
      2020.07.01 11:28 신고

      안녕하세요.

      last 명령어 결과 사용자의 아이디를 각 변수에 저장하고, 사용자에 해당하는 시간값을 awk 명령어를 이용해 카운트 하시면 됩니다.