[하루한줄] Stack-based buffer overflow RCE in WD NAS

URL

Pwn2Own: A Tale of a Bug Found and Lost Again

Target

My Cloud Pro Series PR4100 2.31.204 (2019-12-16), 2.40.155 (2020-07-28), 2.40.157 (2020-10-20)

Explain

하드디스크 생산 업체 Western Digital(WD)의 대용량 네트워크 스토리지 My Cloud Pro 제품에서 스택 버퍼 오버플로우를 통한 root 권한 원격 코드 실행 취약점의 세부 정보가 공개되었습니다.

CGI 바이너리 login_mgr.cgi는 로그인 프로세스와 관련된 여러 루틴을 수행합니다.

login_mar.cgiwd_login() 함수는 ID와 패스워드를 각각 usernamepwd 매개 변수로 받아 인증 시도를 하는데, 이때 pwd는 Base64 인코딩 된 값이어야 합니다. 이후 cgiFormString() 함수에서 usernamepwd를 스택 버퍼 인 usernamepwd_b64에 복사합니다. base64decode() 함수는 pwd_b64를 Base64 디코딩해 pwd_decoded에 저장하며 디코딩은 내부적으로 glibc의 b64_pton()에서 이루어집니다.

char pwd_decoded[64]; // [rsp+90h] [rbp-1178h] BYREF
char pwd_b64[256];    // [rsp+D0h] [rbp-1138h] BYREF

디코딩 과정에서 디코딩 전 패스워드인 pwd_b64의 크기는 256 bytes이지만 디코딩된 패스워드가 복사되는 pwd_decoded의 크기는 64 bytes입니다. 256 bytes 길이의 base64 인코딩 값의 최대 디코딩 문자열 길이는 192 bytes로 64 bytes 크기인 pwd_decoded에 128 bytes 만큼 데이터를 더 쓸 수 있습니다.

char password_copy_shadow[80]; // [rsp+ 0h] [rbp-C8h] BYREF
char password_copy_input[88];  // [rsp+50h] [rbp-78h] BYREF

이후 check_login() 함수에서 /etc/shadow의 패스워드 해시와 비교하기 위해 strcpy()pwd_decoded 에서 password_copy_input으로 복사합니다. 이때 이전 오버플로 취약점으로 인해 192 bytes 크기의 데이터가 있는 pwd_decoded에서 복사하고 이는 check_login()의 반환 주소를 덮어 원격 코드 실행으로 이어질 수 있습니다.