Zip Slip

Introduction

ZIP Slip은 Path traversal(directory traversal) 구문이 포함된 Archive 파일(zip, tar, jar, etc..)을 extract할 떄 이를 이용하여 공격자가 의도한 경로로 원하는 파일을 이동시키는 공격 방법입니다.

일반적으로 Unarchive 관련해서 안전한 라이브러리가 있다면 크게 영향받지 않지만, 일부 언어는 이러한 라이브러리의 부재로 개발자들이 작성한 Code snippet 으로 extract 하는 경우가 많습니다. 그래서 이러한 공격에 대한 대응이 코드인 경우 공격자가 쉽게 상위 디렉토리 등으로 원하는 파일을 이동시킬 수 있습니다.

e.g

├── dir1
│   ├── file1.txt
│   ├── file2.txt
├── ../../../../../../evil.sh

어떻게 보면 OOXML XXE와 유사한 부분도 있네요.

Offensive techniques

Detect

Archive를 처리하는 모든 구간에서는 테스트해볼 가치가 있습니다. 특정 요청 등을 이용해 서비스에 압축 파일 형태로 업로드하여 처리되거나, 다른 파라미터 정보들이 조합되서 ZIP과 같이 처리되는 경우 유효할 수 있으며, 이를 쉽게 식별하기 이해서는 Path traversal과 같이 단순한 경로를 우선 지정하여 extract 되는지 체크해야합니다.

ZIP

./normal.jpg
../inject.jpg
../ZIP_NAME/inject2.jpg

Check

/normal.jpg (200)
/inject.jpg (404)
/inject2.jpg (200) => 취약

Exploitation

실제 공격은 단순합니다. 이를 통해서 시스템의 중요 파일을 덮어쓰거나, 실행 가능한 경로로 파일을 이동시켜 RCE를 유도할 수 있습니다.

../../../../../../../../tmp/evil.sh

물론 이 경우 실행 권한이 있는 디렉토리르 정확하게 알아야하고, 어떤 조건으로 인해 실행이 되어야 합니다. 이외에도 다른 취약점으로 연결될 수 있는 브릿지 역할을 할 수 있습니다.

Bypass protection

접근 경로에 대해 권한 기반으로 Limit이 있는 경우는 우회하기 어렵습니다. 다만 Path traversal과 동일하게 문자열 기반으로 체크하는 로직인 경우 Path traversal의 bypass 패턴을 동일하게 사용할 수 있습니다. 자세한 내용은 아래 링크를 참고해주세요.

https://www.hahwul.com/cullinan/path-traversal/#bypass-protection

Defensive techniques

Extract 시 path를 검증하는 형태로 대응이 가능합니다. 또한 extract 하는 프로세스의 권한을 이용해서 최대 write할 수 있는 범위를 명시하는 방법으로도 공격을 예방할 수 있습니다.

Example

golang 코드로 예시를 들어보겠습니다.

Vuln code

func (rarFormat) Read(input io.Reader, dest string) {
  rr := rardecode.NewReader(input, "")
  for {
    header := rr.Next()
    writeNewFile(filepath.Join(dest, header.Name), rr, header.Mode())
  }
}

Validation code

func sanitizeExtractPath(filePath string, destination string) error {
 destpath := filepath.Join(destination, filePath)
 if !strings.HasPrefix(destpath, filepath.Clean(destination) + string(os.PathSeparator)) {
   return fmt.Errorf("%s: illegal file path", filePath)
  }
  return nil
}

Tools

References