
Runtime libc 함수 감사기로, LD_PRELOAD를 통해 파일시스템 시스템 콜을 후킹하여 파일 접근 경쟁 조건 및 심볼릭 링크 취약점을 탐지하고 잠재적 권한 상승 경로를 로깅합니다.
PathAuditor는 libc 함수를 감사하여 파일 액세스 관련 취약점을 찾는 도구입니다.
아이디어는 대략 다음과 같습니다:
LD_PRELOAD를 사용하여 모든 파일 시스템 관련 라이브러리 호출을 후킹하고 발견된 위반 사항을 syslog에 기록합니다.
이것은 공식적으로 지원되는 Google 제품이 아닙니다.
이 도구가 탐지할 수 있는 취약점 유형의 예를 살펴보겠습니다. CVE-2019-3461은 /tmp를 순회하며 오래된 파일을 삭제하는 도구인 tmpreaper의 버그였습니다. 일반적으로 루트로 cron 작업으로 실행됩니다. tmp 외부의 파일을 삭제하지 않기 위해 디렉토리가 마운트 지점인지 확인하는 다음 코드를 사용했습니다:
if (S_ISDIR (sb.st_mode)) {
char *dst;
if ((dst = malloc(strlen(ent->d_name) + 3)) == NULL)
message (LOG_FATAL, "malloc failed.\n");
strcpy(dst, ent->d_name);
strcat(dst, "/X");
rename(ent->d_name, dst);
if (errno == EXDEV) {
free(dst);
message (LOG_VERBOSE,
"File on different device skipped: `%s/%s'\n",
dirname, ent->d_name);
continue;
}
// [...]
요약하자면, 이 코드는 rename("/tmp/foo", "/tmp/foo/x")를 호출하며, "/tmp/foo"가 마운트 지점이면 EXDEV를 반환합니다.
PathAuditor는 "/tmp/foo"가 루트를 제외한 사용자 소유인 경우 이 호출을 잠재적 취약점으로 플래그 지정합니다.
그 이유를 이해하려면 rename 시스템 호출이 실행될 때 커널에서 발생하는 일을 생각해 봐야 합니다(간략화):
"/tmp/foo"를 탐색합니다."/tmp/foo/x"를 탐색합니다.여기에는 "/tmp/foo"가 두 번 해석되므로 경쟁 조건이 있습니다. 사용자가 제어할 수 있는 경우 사용자는 언제든지 다른 파일로 대체할 수 있습니다.
특히, tmpreaper 코드의 if(S_ISDIR) 검사를 통과하려면 처음에 "/tmp/foo"가 디렉토리여야 합니다.
그런 다음 코드가 시스템 호출에 들어가기 직전에 파일로 대체합니다.
커널이 첫 번째 인수를 해석할 때 사용자가 제어하는 내용의 파일을 보게 됩니다.
이제 다시 대체하는데, 이번에는 동일한 파일 시스템의 임의 디렉토리에 대한 심볼릭 링크로 대체합니다.
커널은 경로를 두 번째로 해석하고 심볼릭 링크를 따라 제어된 파일을 선택한 디렉토리로 이동합니다.
동일한 파일 시스템 제한은 rename이 파일 시스템 간에 작동하지 않기 때문입니다. 그러나 일부 Linux 배포판에서는 /tmp가 기본적으로 rootfs의 폴더일 뿐이며 이 버그를 사용하여 파일을 /etc/cron으로 이동할 수 있으며, 이 파일은 루트로 실행됩니다.
시도하려면 bazel로 libpath_auditor.so를 빌드하고 LD_PRELOAD를 사용하여 바이너리에 로드해야 합니다. 모든 위반 사항은 syslog에 기록되므로 syslog가 실행 중인지 확인하십시오.
bazel build //pathauditor/libc:libpath_auditor.so
LD_PRELOAD=/path/to/bazel-bin/pathauditor/libc/libpath_auditor.so cat /tmp/foo/bar
tail /var/log/syslog
시스템의 모든 프로세스에서 실행하려면 /etc/ld.so.preload에 추가할 수도 있습니다. 다만, 이는 불안정을 초래할 수 있으므로 테스트 시스템에서만 권장됩니다.
빠른 시작으로 이 프로젝트에 포함된 docker 컨테이너를 사용해 볼 수 있습니다:
docker build -t pathauditor-example .
docker run -it pathauditor-example
# LD_PRELOAD=/pathauditor/bazel-bin/pathauditor/libc/libpath_auditor.so cat /tmp/foo/bar
# cat /var/log/syslog