프로젝트 제목
CVE-2021-3156 sudo 익스플로잇 생성기
설치
- 의존성 설치: ./install.sh
- 익스플로잇 생성기 실행: ./run.sh
- 시스템의 sudo 버전이 취약한 경우 설치된 버전에 대한 익스플로잇이 생성됩니다.
- 익스플로잇은 exploit.c와 바이너리 ./exploit로 생성됩니다.
요구 사항
- gdb gcc make python3 python3-distro
- tcache가 있는 glibc
- 현재 Ubuntu, Debian 운영 체제.
테스트 완료
- Ubuntu 20.04.1
- Ubuntu 20.04.2
- Ubuntu 20.10
- Ubuntu 18.04.5
- Debian 10.7.0
저자
취약점
- sudo는 parse_args 함수에서 main의 메타 문자를 이스케이프합니다.
- 이후 set_cmnd에서 공백이 뒤따르지 않는 이스케이프 문자를 제거합니다.
- sudo는 parse_args가 set_cmnd의 전제 조건이라고 가정합니다.
- 문제는 각 파서의 조건이 다르다는 것입니다. 첫 번째 파서는 모드가 (MODE_RUN && MODE_SHELL)일 때 실행됩니다. 두 번째 파서는 모드가 (MODE_RUN | MODE_EDIT | MODE_CHECK) 그리고 (MODE_SHELL|MODE_LOGIN_SHELL)일 때 실행됩니다.
- 첫 번째 파서는 실행하지 않고 두 번째 파서만 실행하는 방법이 있습니다: sudoedit -s
- set_cmnd는 인수의 크기를 계산하고 힙에 청크를 만들어 인수를 연결합니다. 그런 다음 각 인수를 널 바이트를 만날 때까지 복사하고 다음 인수로 계속합니다. 입력 인수가 '\'로 끝나면 set_cmnd는 이스케이프 문자를 건너뛰고 널 바이트를 복사하지만 다음 인수를 같은 인수인 것처럼 계속 복사합니다. 이로 인해 힙에 버퍼 오버플로가 발생합니다.
- 따라서 마지막 인수가 '\'로 끝나면 set_cmnd는 첫 번째 환경 변수의 내용으로 버퍼를 오버플로합니다. 이 환경 변수가 '\'로 끝나면 다음 변수도 계속 복사합니다.
익스플로잇 생성기
- (https://packetstormsecurity.com/files/161160/Sudo-Heap-Based-Buffer-Overflow.html)에 설명된 방법 2를 사용합니다.
- sudo는 오버플로 후 glibc nsswitch를 사용하여 group 데이터베이스를 읽습니다.
- nsswitch는 nsswitch.conf의 모든 데이터베이스와 서비스에 대해 힙에 service_table 데이터 구조를 생성합니다.
- group의 서비스는 struct service_user 유형입니다.
- 이 구조에는 서비스 이름을 가진 name 필드가 있습니다.
- nsswitch는 /etc/nsswitch.conf의 설정에 따라 group 데이터베이스의 각 서비스에 대해 libnss_[서비스이름]_.so.2 라이브러리를 동적으로 로드합니다.
- library 포인터는 NULL로 덮어써서 충돌을 방지해야 합니다. 또한 이로 인해 라이브러리가 메모리에 다시 로드되며, 이번에는 쉘코드가 포함된 로컬 라이브러리가 로드됩니다.
- 힙 그루밍(heap grooming) 또는 풍수(feng shui) 기술을 사용합니다. 힙에 레이아웃을 생성하기 위해 크기가 다양한 "로컬" 환경 변수를 만들어 set_cmnd가 인수를 복사하는 청크를 대상인 group의 service_user 구조에 가깝게 만듭니다.
- set_cmnd의 malloc이 실행되기 직전에 힙의 tcache를 확인하여 재활용될 첫 번째 청크와 그 크기를 확인합니다.
- nsswitch의 데이터베이스와 서비스를 구성하는 청크들을 정렬합니다. 정렬된 순서로 순회합니다. 대상 청크의 메모리 주소와 이전 청크의 주소를 저장합니다.
- tcache bin에서 해당 범위 내의 청크를 찾습니다.
- 청크의 위치와 크기를 알게 되면 익스플로잇을 생성합니다.
- 해당 청크가 재활용되도록 정확한 크기의 '\'로 끝나는 인수와 널 바이트를 사용합니다.
- 적절한 레이아웃을 만들기 위해 필요한 정확한 '로컬' 환경 변수를 생성합니다.
- 이러한 '로컬' 변수들 앞에 대상 구조에 도달하기 위해 필요한 크기의 환경 변수를 생성합니다.
- 이스케이프 문자 '\'가 포함된 문자열 배열을 사용하여 대상 구조를 널 바이트로 덮어씁니다. 이렇게 하면 set_cmnd에서 널 바이트가 기록됩니다.
- service_user의 name을 'x/x'로 덮어써서 이후 'libnss_x/x.so.2' 라이브러리가 로드되도록 합니다.
- 라이브러리에서 쉘코드를 실행하는 생성자(constructor)를 만듭니다.
- sudo가 setuid 비트로 실행되므로 쉘은 root에 속합니다.
라이선스
이 프로젝트는 GNU GPL 라이선스 하에 있습니다.