
Sudo의 힙 기반 버퍼 오버플로(CVE-2021-3156)에 대한 기술 분석 및 악용 개발로, 주요 Linux 배포판에서 로컬 권한 상승을 위한 세 가지 작동 악용을 포함합니다.
Qualys Security Advisory
Baron Samedit: Sudo의 힙 기반 버퍼 오버플로 (CVE-2021-3156)
요약 분석 익스플로잇 감사의 말 타임라인
Sudo(https://www.sudo.ws/)에서 힙 기반 버퍼 오버플로를 발견했습니다. 이 취약점:
모든 로컬 사용자(일반 사용자 및 시스템 사용자, sudoers 및 비sudoers 모두)가 인증 없이(즉, 공격자는 사용자 비밀번호를 알 필요 없음) 악용할 수 있습니다.
2011년 7월(커밋 8255ed69)에 도입되었으며, 기본 구성에서 1.8.2부터 1.8.31p2까지의 모든 레거시 버전과 1.9.0부터 1.9.5p1까지의 모든 안정 버전에 영향을 미칩니다.
이 취약점에 대해 세 가지 다른 익스플로잇을 개발하여 Ubuntu 20.04(Sudo 1.8.31), Debian 10(Sudo 1.8.27), Fedora 33(Sudo 1.9.2)에서 전체 루트 권한을 획득했습니다. 다른 운영 체제 및 배포판도 악용 가능할 가능성이 높습니다.
Sudo가 "shell" 모드(shell -c command)로 명령을 실행하기 위해 실행되는 경우:
그런 다음 Sudo의 main() 시작 시 parse_args()는 모든 명령줄 인수를 연결하고(587-595행) 모든 메타 문자를 백슬래시로 이스케이프하여(590-591행) argv를 다시 작성합니다(609-617행):
나중에 sudoers_policy_main()에서 set_cmnd()는 명령줄 인수를 힙 기반 버퍼 "user_args"에 연결하고(864-871행) 메타 문자를 이스케이프 해제합니다(866-867행). "sudoers 매칭 및 로깅 목적":
불행히도 명령줄 인수가 단일 백슬래시 문자로 끝나는 경우:
866행에서 "from[0]"은 백슬래시 문자이고 "from[1]"은 인수의 널 종결자입니다(즉, 공백 문자가 아님).
867행에서 "from"이 증가되고 널 종결자를 가리킵니다.
868행에서 널 종결자가 "user_args" 버퍼로 복사되고 "from"이 다시 증가되어 널 종결자 다음 첫 번째 문자를 가리킵니다(즉, 인수의 범위를 벗어남).
865-869행의 "while" 루프는 범위를 벗어난 문자를 읽고 "user_args" 버퍼로 복사합니다.
즉, set_cmnd()는 힙 기반 버퍼 오버플로에 취약합니다. "user_args" 버퍼로 복사되는 범위 밖의 문자들이 해당 버퍼의 크기(852-853행에서 계산됨)에 포함되지 않았기 때문입니다.
그러나 이론적으로는 어떤 명령줄 인수도 단일 백슬래시 문자로 끝날 수 없습니다. MODE_SHELL 또는 MODE_LOGIN_SHELL이 설정된 경우(858행, 취약한 코드에 도달하기 위한 필수 조건) MODE_SHELL이 설정되고(571행) parse_args()가 이미 모든 메타 문자(백슬래시 포함)를 이스케이프했기 때문입니다(즉, 모든 단일 백슬래시를 두 번째 백슬래시로 이스케이프).
그러나 실제로 set_cmnd()의 취약한 코드와 parse_args()의 이스케이프 코드는 약간 다른 조건으로 둘러싸여 있습니다:
대비:
따라서 질문은 다음과 같습니다. MODE_SHELL을 설정하고 MODE_EDIT 또는 MODE_CHECK를 설정하여(취약한 코드에 도달하기 위해) 기본 MODE_RUN을 설정하지 않음으로써(이스케이프 코드를 피하기 위해) 가능할까요?
대답은 아니오인 것 같습니다. MODE_EDIT(-e 옵션, 361행) 또는 MODE_CHECK(-l 옵션, 423 및 519행)를 설정하면 parse_args()가 "valid_flags"에서 MODE_SHELL을 제거하고(363 및 424행) MODE_SHELL과 같은 잘못된 플래그를 지정하면 오류와 함께 종료됩니다(532-533행):
하지만 허점을 찾았습니다. "sudo" 대신 "sudoedit"으로 Sudo를 실행하면 parse_args()가 자동으로 MODE_EDIT을 설정하지만(270행) "valid_flags"를 재설정하지 않으며, "valid_flags"에는 기본적으로 MODE_SHELL이 포함됩니다(127 및 249행):
결과적으로 "sudoedit -s"를 실행하면 MODE_EDIT 및 MODE_SHELL(둘 다 설정되지만 MODE_RUN은 아님)을 설정하여 이스케이프 코드를 피하고 취약한 코드에 도달하며 단일 백슬래시 문자로 끝나는 명령줄 인수를 통해 힙 기반 버퍼 "user_args"를 오버플로시킬 수 있습니다:
perl -e 'print "A" x 65536'
malloc(): corrupted top size
Aborted (core dumped)공격자 관점에서 이 버퍼 오버플로는 이상적입니다:
오버플로하는 "user_args" 버퍼의 크기를 제어할 수 있습니다(연결된 명령줄 인수의 크기, 852-854행).
오버플로 자체의 크기와 내용을 독립적으로 제어할 수 있습니다(마지막 명령줄 인수 뒤에는 크기 계산(852-853행)에 포함되지 않은 첫 번째 환경 변수들이 편리하게 이어집니다).
오버플로하는 버퍼에 널 바이트를 쓸 수도 있습니다(단일 백슬래시로 끝나는 모든 명령줄 인수 또는 환경 변수는 "user_args"에 널 바이트를 씁니다, 866-868행).
예를 들어 amd64 Linux에서 다음 명령은 24바이트 "user_args" 버퍼(32바이트 힙 청크)를 할당하고 다음 청크의 크기 필드를 "A=a\0B=b\0"(0x00623d4200613d41), fd 필드를 "C=c\0D=d\0"(0x00643d4400633d43), bk 필드를 "E=e\0F=f\0"(0x00663d4600653d45)로 덮어씁니다:
--|--------+--------+--------+--------|--------+--------+--------+--------+-- | | |12345678|90123456|789012.A|A=a.B=b.|C=c.D=d.|E=e.F=f.| --|--------+--------+--------+--------|--------+--------+--------+--------+-- size <---- user_args buffer ----> size fd bk