리눅스 커널의 백도어: wait4() 버그가 루트 액세스를 거의 부여할 뻔한 사건
2003년, 리눅스 개발자들이 CVS 저장소에서 무단 변경 사항을 발견했습니다. wait4() 함수에 수상한 코드 조각이 삽입되어 있어 임의의 사용자에게 루트 권한을 부여할 수 있게 되었습니다. 이 변경은 BitKeeper 메인 저장소에 아무런 흔적 없이 나타났으며, CVS 저장소 침입을 직시로 가리켰습니다.
처음 봤을 때는 무해해 보이는 코드였습니다:
+ if ((options == (__WCLONE|__WALL)) && (current->uid = 0))
+ retval = -EINVAL;
문제는 = 연산자에 숨어 있었습니다. == 비교 대신 __WCLONE과 __WALL 플래그가 있는 모든 wait4() 호출에서 current->uid를 0(루트)으로 설정하는 대입 연산이었습니다. 이로 인해 추가 검사 없이 권한 상승 경로가 열리게 되었습니다.
저장소 역사와 취약점 침투 경로
당시 리눅스는 두 저장소를 병행 사용했습니다:
- BitKeeper — 커널의 주요 최신 버전 제어 시스템.
- CVS — 구 도구와 구식 개발자를 위한 레거시 저장소.
동기화는 수동으로 이뤄졌습니다: BitKeeper의 변경 사항과 태그를 CVS로 복사하는 식이었습니다. 공격자는 CVS를 노려 패치를 몰래 삽입했습니다. 자동 검사 부재와 저장소별 다른 사용자층 덕에 침입이 간과되었습니다.
개발자들은 일상적인 검토 중 이상을 포착했습니다: CVS에 커밋은 있는데 BitKeeper에 대응하는 것이 없었습니다. 버전 비교로 악성 코드가 드러났습니다.
취약점 기술적 분석
wait4() 함수는 자식 프로세스 종료 대기를 처리하며 확장 옵션을 지원합니다. __WCLONE과 __WALL 플래그는 클론과 모든 프로세스를 추적합니다.
공격 로직은 간단했습니다:
- 사용자가
wait4(options = __WCLONE | __WALL)을 호출합니다. - 조건
current->uid = 0이 UID 0(루트)을 대입합니다. -EINVAL을 반환하며 성공을 감춥니다.- 이후 시스템 호출이 루트로 실행됩니다.
이것은 제로데이 백도어였습니다: 별도 익스플로잇 없이 표준 호출로 활성화됩니다. C에서 흔한 실수(= 대신 ==)가 취약점 원인이 됩니다.
현대 개발을 위한 교훈
이 사건은 레거시 시스템의 위험을 부각시켰습니다. 오늘날 관행은 이런 위협을 최소화합니다:
- 단일 저장소: 브랜칭과 PR을 지원하는 Git.
- 검사 포함 CI/CD: 자동 diff, 정적 분석(Coverity, Clang).
- 커밋 서명: 인증을 위한 GPG/PGP.
- 서명된 태그: 릴리스 보호.
- 퍼징과 감사: 시스템 콜 지속 테스트.
리눅스 커널은 이제 이런 패턴에 대한 코드 리뷰와 자동 테스트를 의무화합니다.
주요 교훈
wait4()의=대신==버그가 모든 사용자에게 루트 백도어를 만들었습니다.- 해킹은 CVS를 통해 이뤄졌고 BitKeeper는 깨끗했습니다.
- 2003년 수동 저장소 동기화가 약점이었죠.
- 이 사건이 커널 개발의 Git 전환을 가속화했습니다.
- if 문 조건은 이중 확인하세요: 컴파일러가 대입을 항상 잡지 못합니다.
— Editorial Team
아직 댓글이 없습니다.