티스토리 뷰

Backend

Apache Commons IO 취약점 파헤치기

지마켓 홍민지 2023. 6. 7. 14:25

안녕하세요! Platform Technology 팀 홍민지 입니다.

이번 포스팅에서는 현재 진행하고 있는 스프링부트 프로젝트에 적용되어 있던 라이브러리 'Apache Commons IO'에서 발견된 보안 취약점에 대한 이야기를 해보려고 합니다.

 

 

1. Open source 와 CVE

다수의 프로그램이 모여있는 Open source, Open source 에서 많이 발견되는 CVE

Open source 는 현대 소프트웨어 개발의 핵심 구성 요소로 자리 잡았다고 볼 수 있습니다.

그러나 소스코드가 공개되어 있는 만큼 취약점이 많이 발견되기도 합니다.

 

 

2022 OSSRA 보고서에 따르면, 오픈소스는 전 세계적 산업 분야의 프로젝트 97%에 사용되고 있으며,

이 중 81%는 알려진 오픈소스 취약점을 하나 이상 가지고 있었다고 합니다.

Maven 을 사용하시는 분이라면 익숙하실 mvn repository 에 라이브러리를 검색해 보면 다음과 같이 해당 버전에 대한 취약점이 명시되어 있는데요.

 

이런 취약점은 'CVE' 식별자를 통해 추적되고 관리되고 있습니다.

Direct vulnerabilities: CVE-2021-29425 링크를 누르면 CVE 페이지로 이동됩니다.

 

 

▪️ CVE 란?

CVE는 "Common Vulnerabilities and Exposures"의 약어로, 비영리 연구 개발 기관인 MITRE가 소프트웨어와 펌웨어의 취약점들을 파악하고 분류해 기업과 기관이 보안 강화에 사용할 수 있는 무료 '코드(Dictionary)'를 만들기 위해 시작한 프로그램입니다.

한마디로, 정보보안 취약점에 대한 일관된 식별자 체계입니다. 이 CVE를 통해 보안 취약점에 대한 정보 공유와 대응을 편리하게 할 수 있습니다. 유명한 오픈소스 프로그램의 취약점 CVE 사례로는 openSSL(2014), Bash(2014), Log4j(2021) 등이 있습니다.

CVE 가 관리되고 있는 사이트를 보도록 하겠습니다.

 

 

각각의 CVE는 취약점을 고유하게 식별하는 일련번호가 있으며 해당 이미지의 CVE 코드(CVE-2021-29425) 의미는 'Cve - 년도 - 그해에 발견된 취약점 번호, 즉 개수'입니다. 이러한 일련번호는 CVE 넘버링 기관, 즉 CNA(CVE Numbering Authorities) 에서 할당하며 CNA는 보안 기업 및 리서치 조직과 Red Hat, IBM, Cisco, Oracle, Microsoft와 같은 주요 IT 벤더를 대표하는 CNA가 약 100개 정도 있습니다.

 

 

2. Apache Commons IO

저희 팀에서 운영 중인 서비스의 경우 Lombok, Commons io 등의 오픈소스 라이브러리를 사용하는데요.

Apache Commons IO 에 대한 보안 패치를 적용한 내용에 대해 설명해 보려고 합니다.

Apache Commons IO는 자바 언어를 위한 오픈소스 라이브러리로, 파일 및 디렉터리 조작, 입출력, 파일 필터링 등과 같은 기능을 제공합니다. 이 라이브러리는 다양한 자바 프로젝트에서 사용되며, 이를 통해 파일 및 디렉토리 관리 작업을 보다 편리하게 사용하고 있습니다.

이 Commons Io 에 대한 취약점은 CVE-2021-29425 로, CVE 공식 사이트에 대한 내용은 다음과 같습니다.

 

In Apache Commons IO before 2.7, When invoking the method FileNameUtils.normalize with an improper input string, like "//../foo", or "\\\\..\\foo", the result would be the same value, thus possibly providing access to files in the parent directory, but not further above (thus "limited" path traversal), if the calling code would use the result to construct a path value.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
2.7 버전 이전의 Apache Commons IO 에서는 "//../foo", or "\\\\..\\foo" 와 같은 부적절한 문자열을 string FileNameUtils.normalize 함수의 인자로 실행하면 결과는 input 으로 넣어주었을 때와 동일하지만, 이 코드를 바탕으로 경로값을 구성하면 부모 디렉터리에 접근할 수 있습니다. 하지만 더욱 상위로 접근할 순 없습니다. (그래서 해당 취약점으로 경로를 순회하는 건 어느 정도 제한적입니다.)

정리하자면 CVE-2021-29425는 Apache Commons IO 2.5 버전에서 발견된 취약점으로, 파일 경로를 처리하는 과정에서 발생하는 보안 결함입니다. 이 취약점을 악용하면 공격자가 악의적인 파일 경로를 사용하여 원격에서 임의의 코드를 실행할 수 있습니다.

 

 

▪️ 취약점 동작 방식

Apache Commons IO 2.5 버전부터 2.7 버전에서는 파일 경로 처리를 위해 FilenameUtils 클래스의 normalize 메서드가 사용되는데, 이 메서드가 바로 입력된 파일 경로를 정규화할 때 취약점을 가지고 있었는데요.

 

취약한 버전에서는 파일 경로에 ../과 같은 특정한 패턴을 사용하면, 파일 경로 문자열을 해석할 때,

"//../foo" 또는 "\\\\..\\foo"와 같은 패턴의 문자열을 이용해 FileNameUtils.normalize 메서드를 호출하면 허용하지 않은 상위 디렉터리의 접근을 허용하는 문제가 있습니다.

간단한 예제 코드를 통해 직접 문제를 확인해 봅시다.

 

 

3. FilenameUtils.class

취약한 버전인 commons-io-2.4와 보안 패치가 적용된 버전인 commons-io-2.7 을 비교하기 위해 새 Java 프로젝트를 2개 생성하고 두 프로젝트에 각각 다음 라이브러리를 추가해 줍니다. 그리고 취약한 버전에서 확인할 접근되어서는 안 되는 파일을 생성해 줍니다.

$ mkdir -p /Users/minjimarket/a/b
$ cat test.txt
Happy

 

Test 코드는 다음과 같습니다.

import org.apache.commons.io.FilenameUtils;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class Test {
    public static void main(String[] args) throws IOException {
        String path = "//../test.txt";
        String fileName = FilenameUtils.normalize(path); // args[0] == "/../<malicious file>"

        File file = new File("/Users/mingmarket/a/b" + fileName);
        // New file created at /inaccessible/<fileName>
        FileReader filereader = new FileReader(file);
        int singleCh = 0;
        while ((singleCh = filereader.read()) != -1) {
            System.out.print((char) singleCh);
        }
        filereader.close();
    }
}

 

그리고 두 프로젝트를 실행해 보면 다음과 같이 상이한 결과를 확인할 수 있습니다.

 

보안 패치 이전 버전:

/Users/mingmarket/a/b/test.txt 파일에 접근하여 그 내용을 읽어왔습니다.

 

 

보안 패치 이후 버전:

 

어떤 차이 때문에 이런 다른 결과가 나오는지, 보안 패치 이전 버전인 Commons IO 2.4 버전의 FilenameUtils.class 를 확인해 봅시다.

 

FilenameUtils 는 일반 파일 이름 및 파일 경로 조작 유틸리티로, .normalize(String fileName) 메서드를 사용해 접근할 경로를 정규화하는 클래스 입니다. (Class FilenameUtils) Test class 에서 호출한 FilenameUtils.normalize() 를 따라가 보겠습니다.

 

.normalize() 메서드는 doNormalize 메서드를 호출하고,

 

doNormalize 는 fileName 이 정상적으로 입력되었을 경우 getPrefixLength 메서드를 호출합니다.

 

ASCII 58 은 ":" 입니다.

getPrefixLength 메서드는 입력받은 fileName 의 첫 번째 문자와 fileName 길이를 확인하는데요.

현재 입력받은 fileName 의 길이가 13이고, fileName 의 첫번째 문자는 //../test.txt 에서 / 이기 때문에

isSeparator 메소드를 호출합니다.

 

여기서는 입력받은 첫 번째 문자열과 두 번째 문자열이 / 이기 때문에 다음 로직이 수행됩니다.

 

ASCII 47: / , ASCII 92: \

입력받은 Filename 문자열에서 문자열의 시작이 // 또는 \\\\ 로 시작하면,

즉 UNC path 컴퓨터 내의 공유 파일을 내부 네트워크가 아닌 외부 네트워크에서도 확인이 가능하게 명시하여 확인하는 방법으로 시작하는 경우, server name 을 제외한 파일의 경로의 시작 index 번호 +1 인 5를 반환합니다.

 

이때 파일 이름 앞에 있는 hostname 을 검증하지 않기 때문에 hostname 부분의 .. 과 같은 문자를 그대로 실행하게 됩니다.

결국 array 에 "//../test.txt" 문자 그대로를 반환해서 접근되어서는 안 되는 경로인 /Users/mingmarket/a/b/../test.txt 의 내용이 보이게 됩니다. 이를 통해 악성 공격자는 임의의 파일을 덮어쓰거나, 권한이 없는 디렉터리에 악성 파일을 생성할 수 있을 것입니다.

 

그렇다면 Commons IO 2.7 버전은 어떻게 패치되었을까요?

getPrefixLength 메서드에 대한 보안 패치 이전 버전의 코드와 이후 버전의 코드를 비교해 보겠습니다.

 

먼저 공통적인 로직으로 ch0, ch1 은 입력된 filename 의 첫 번째, 두 번째 시작 문자입니다.

보안 패치 이후 버전인 Commons IO 2.7 에서는 문제가 되었던 hostname 을 검증하는 과정이 다음과 같이 추가되었습니다.

 

Commons IO 2.4 에서 바로 return 했던, fileName 의 2번째부터 pos 값인 5번째까지의 문자열을 자른 값인 ".." 을 isValidHostName 메서드를 통해 정상적인 hostName 인지 확인 합니다.

 

isValidHostName 메서드에서는 isIPv6Address 메서드를 통해 정상적인 IPv6, IPv4 인지 확인하고,

isRFC3986HostName 메서드를 통해 정상적인 URI 형식임을 확인합니다. (RFC 3986 문서. RFC 3986은 "Uniform Resource Identifier (URI): Generic Syntax"라는 이름의 표준 문서로, URI의 일반적인 구문과 표준을 정의하여 인터넷에서 일관된 식별 방식을 제공하는 문서입니다.)

 

즉, isValidHostName 메서드에 상위 경로를 볼 수 없도록 방어하는 로직인 "//" 이후 '..' 이 있을 경우에 호스트명을 체크하는 코드가 추가되어서 ".." 과 같은 경로 문자일 경우 -1 을 반환해 예외처리를 해주기 때문에, 이제는 filename 에는 null 이 저장되어 이제 접근되어서는 경로의 파일 내용을 볼 수 없습니다.

 

 

4. 마무리

결국 프로젝트의 취약점은 Apache Commons IO 라이브러리 버전을 2.7 로 변경해 줌으로써 보완될 수 있었습니다.

이번 이슈로 버전 업그레이드의 중요성을 다시금 깨달았고, 라이브러리 사용자는 항시 사용 중인 라이브러리의 보안 패치 내용을 확인하여 보안 패치를 적용해야 함을 느낄 수 있었습니다. 긴 글 읽어주셔서 감사합니다!☺️

이 참에 모두 한 번씩 관리하시는 프로젝트 확인 해보심은 어떤가요~?

 

 

 

 

 


참고:

CVE 에 대한 더 자세한 설명: https://www.cve.org/ResourcesSupport/FAQs 

참고 기사 : https://www.itworld.co.kr/tags/55539/CVE/108107 

UNC

댓글