경로 탐색(Path Traversal)

경로 탐색(Path Traversal)은 디렉터리 탐색(Directory Traversal)이라고도 불리며, 공격자가 웹 애플리케이션을 통해 서버 내부의 임의 파일을 읽거나 쓸 수 있게 만드는 취약점입니다.

경로 탐색 취약점이 존재하면 공격자는 서버의 파일 시스템에서 의도하지 않은 파일에 접근할 수 있습니다. 접근 가능한 대상에는 애플리케이션 소스 코드 및 설정 파일, 데이터베이스 자격 증명과 같은 백엔드 시스템 정보, 그리고 /etc/passwd와 같은 민감한 운영 체제 파일이 포함됩니다.

더 나아가 일부 환경에서는 서버의 임의 파일에 쓰기까지 가능한 경우도 있는데, 이를 통해 애플리케이션 데이터나 동작을 변조하고, 최종적으로 서버 전체를 장악할 수도 있습니다.

기본 원리: 임의 파일 읽기

쇼핑 애플리케이션이 상품 이미지를 다음과 같은 HTML로 로드한다고 가정해 봅시다.

<img src="/loadImage?filename=281.png">

서버는 filename 파라미터의 값을 받아 기본 디렉터리인 /var/www/images/에 결합한 뒤 해당 파일을 읽어 반환합니다. 즉, 위 요청이 처리되면 서버는 아래 경로의 파일을 읽게 됩니다.

/var/www/images/218.png

여기서 문제는 경로 탐색에 대한 아무런 검증이 없다는 것입니다. 공격자가 filename 파라미터에 ../ 시퀀스를 삽입하면 디렉터리 구조를 상위로 이동할 수 있습니다.

https://insecure-website.com/loadImage?filename=../../../etc/passwd

이 요청이 서버에서 처리되면 다음과 같은 경로가 완성됩니다.

/var/www/images/../../../etc/passwd

../는 파일 시스템에서 한 단계 상위 디렉터리로 이동하는 것을 의미합니다. /var/www/images/에서 세 번 상위로 이동하면 파일 시스템 루트(/)에 도달하므로, 최종적으로 서버는 /etc/passwd 파일의 내용을 반환하게 됩니다.

참고로 Windows 환경에서는 ../..\ 모두 유효한 디렉터리 탐색 시퀀스입니다. Windows 서버를 대상으로 하는 동등한 공격은 다음과 같은 형태가 됩니다.

https://insecure-website.com/loadImage?filename=..\..\..\windows\win.ini

실습: 파일 경로 탐색

이 랩에는 제품 이미지 표시 부분에 경로 탐색 취약점이 포함되어 있습니다. 랩을 해결하려면 /etc/passwd 파일의 내용을 가져오세요.

메인 페이지로 이동한 뒤 임의의 상품 이미지를 클릭하면, filename 파라미터로 이미지를 불러오고 있음을 확인할 수 있습니다.

filename 파라미터의 값을 ../../../etc/passwd로 설정한 뒤 전송하면, 서버 내부의 /etc/passwd 파일 내용이 응답으로 반환됩니다. 아무런 방어 기제가 없기 때문에 가장 기본적인 형태의 공격이 바로 성공한 것입니다.

접근 제어 (Access Control)

접근 제어(Access Control)는 누가 또는 무엇이 작업을 수행하거나 리소스에 접근할 수 있는지에 대한 제약을 적용하는 것입니다. 웹 애플리케이션의 맥락에서 접근 제어는 다음 세 가지 요소에 의존합니다.

  • 인증(Authentication): 사용자가 본인이 주장하는 사람이 맞는지 확인
  • 세션 관리(Session Management): 이후의 HTTP 요청이 동일한 사용자에 의해 이루어지고 있는지 식별
  • 접근 제어(Access Control): 사용자가 수행하려는 작업을 실행할 수 있는 권한이 있는지 결정

취약한 접근 제어는 흔하게 발견되며, 종종 심각한 보안 취약점을 나타냅니다. 접근 제어의 설계와 관리는 비즈니스, 조직, 법적 제약 조건을 기술적 구현에 적용해야 하는 복잡하고 동적인 문제이며, 설계 결정이 사람에 의해 이루어지므로 오류 가능성이 높습니다.

수직적 권한 상승(Vertical Privilege Escalation)

사용자가 접근이 허용되지 않은 상위 권한의 기능에 접근할 수 있다면, 이는 수직적 권한 상승입니다. 예를 들어, 비관리자 사용자가 사용자 계정을 삭제할 수 있는 관리자 페이지에 접근할 수 있다면, 이는 수직적 권한 상승에 해당합니다.

보호되지 않는 기능(Unprotected Functionality)

가장 기본적인 수준에서, 수직적 권한 상승은 애플리케이션이 민감한 기능에 대해 어떠한 보호도 적용하지 않을 때 발생합니다. 관리 기능이 관리자의 환영 페이지에서는 링크되어 있지만 일반 사용자의 환영 페이지에서는 링크되어 있지 않을 수 있습니다. 그러나 사용자는 관련 관리자 URL로 직접 접속하여 관리 기능에 접근할 수 있을 수도 있습니다.

https://insecure-website.com/admin

이 URL은 관리자뿐만 아니라 모든 사용자가 접근할 수 있을 수도 있습니다. 경우에 따라 관리자 URL은 robots.txt 파일과 같은 다른 위치에서 노출되기도 합니다. URL이 어디에도 노출되지 않더라도, 공격자는 워드리스트를 사용하여 민감한 기능의 위치를 무차별 대입(브루트포스)할 수 있습니다.

실습: 보호되지 않는 관리자 기능

이 랩에는 보호되지 않는 관리자 패널이 있습니다. carlos 사용자를 삭제하여 랩을 해결하세요.

웹사이트에서 /robots.txt 파일에 접근하면 Disallow: /administrator-panel 항목이 노출되어 있습니다. 검색 엔진 크롤러를 차단하기 위한 설정이 오히려 공격자에게 관리자 경로를 알려주고 있는 것입니다.

/administrator-panel로 직접 접근하면 별도의 인증 없이 관리자 페이지가 표시되며, carlos 사용자를 삭제할 수 있습니다.

모호성을 통한 보안 (Security by Obscurity)

경우에 따라 민감한 기능은 예측하기 어려운 URL을 부여하여 은닉됩니다. 이는 소위 "모호성을 통한 보안(Security by Obscurity)"의 한 예입니다. 그러나 민감한 기능을 숨기는 것만으로는 효과적인 접근 제어를 제공하지 못합니다. 사용자가 여러 가지 방법으로 난독화된 URL을 발견할 수 있기 때문입니다.

예를 들어 사용자의 역할에 따라 UI를 구성하는 JavaScript에서 URL이 노출될 수 있습니다.

<script>
    var isAdmin = false;
    if (isAdmin) {
        ...
        var adminPanelTag = document.createElement('a');
        adminPanelTag.setAttribute('href', 'https://insecure-website.com/administrator-panel-yb556');
        adminPanelTag.innerText = 'Admin panel';
        ...
    }
</script>

isAdminfalse여서 링크가 렌더링되지 않더라도, URL이 포함된 스크립트 자체는 역할에 관계없이 모든 사용자에게 전달됩니다.

실습: 예측 불가능한 URL을 가진 보호되지 않는 관리자 기능

이 랩에는 보호되지 않는 관리자 패널이 예측 불가능한 위치에 있지만, 해당 위치는 애플리케이션 어딘가에 노출되어 있습니다. 관리자 패널에 접근하고 이를 사용하여 carlos 사용자를 삭제하여 랩을 해결하세요.

웹사이트 접속 시 서버 측 응답 값 내 JavaScript 코드에서 관리자 페이지에 대한 경로 정보가 나타나고 있습니다.

해당 페이지에 직접 접근하면 별도의 권한 체크 없이 관리자 페이지로 접근이 가능하며, carlos 사용자를 삭제할 수 있습니다.

매개변수 기반 접근 제어 방법(Parameter-based Access Control Methods)

일부 애플리케이션은 로그인 시 사용자의 접근 권한이나 역할을 결정한 후, 이 정보를 사용자가 제어할 수 있는 위치에 저장합니다. 숨겨진 필드(Hidden field), 쿠키(Cookie), 사전 설정된 쿼리 스트링 매개변수(Preset query string parameter) 등이 이에 해당합니다.

https://insecure-website.com/login/home.jsp?admin=true
https://insecure-website.com/login/home.jsp?role=1

애플리케이션이 제출된 값을 기반으로 접근 제어 결정을 내리므로, 사용자가 값을 수정하여 권한이 없는 기능에 접근할 수 있습니다.

실습: 요청 매개변수로 제어되는 사용자 역할

이 랩에는 /admin에 관리자 패널이 있으며, 위조 가능한 쿠키를 사용하여 관리자를 식별합니다. 관리자 패널에 접근하고 이를 사용하여 carlos 사용자를 삭제하여 랩을 해결하세요. 제공 계정: wiener:peter

wiener 계정으로 로그인을 시도합니다.

로그인 이후 요청을 전송하는 헤더 내 Cookie 값에 Admin=false라고 설정된 것을 볼 수 있습니다. 이 값이 관리자를 판별하는 값이라면 공격자가 쉽게 변조할 수 있습니다.

Admin=true로 쿠키 값을 변경한 뒤 요청을 전송하면, 관리자 페이지에 접근이 가능해집니다.

관리자 기능을 통해 carlos 사용자를 삭제할 수 있습니다.

수평적 권한 상승(Horizontal Privilege Escalation)

수평적 권한 상승은 사용자가 자신의 리소스가 아닌 동일한 권한 수준의 다른 사용자에게 속한 리소스에 접근할 수 있을 때 발생합니다. 예를 들어, 직원이 자신의 기록뿐만 아니라 다른 직원의 기록에도 접근할 수 있다면, 이는 수평적 권한 상승에 해당합니다.

수평적 권한 상승 공격은 수직적 권한 상승과 유사한 유형의 익스플로잇 방법을 사용할 수 있습니다. 예를 들어, 사용자는 다음 URL을 사용하여 자신의 계정 페이지에 접근합니다.

https://insecure-website.com/myaccount?id=123

공격자가 id 매개변수 값을 다른 사용자의 값으로 수정하면, 다른 사용자의 계정 페이지와 관련 데이터 및 기능에 접근할 수 있습니다. 이는 안전하지 않은 직접 객체 참조(IDOR, Insecure Direct Object Reference) 취약점의 한 예입니다.

일부 애플리케이션에서는 증가하는 숫자 대신 GUID(전역 고유 식별자)를 사용하여 사용자를 식별합니다. 직접 추측은 어렵지만, 다른 사용자에게 속한 GUID는 게시글이나 리뷰 등 애플리케이션의 다른 곳에서 노출될 수 있습니다.

실습: 예측 불가능한 사용자 ID(GUID)를 이용한 수평적 권한 상승

이 랩에는 사용자 계정 페이지에 수평적 권한 상승 취약점이 있지만, GUID로 사용자를 식별합니다. carlos의 GUID를 찾아 API 키를 탈취하세요. 제공 계정: wiener:peter

wiener 계정으로 로그인 후 My Account 페이지에서 개인 API Key를 확인할 수 있습니다. 목표는 타 사용자의 API Key 추출입니다.

블로그 게시글을 확인하다보면 carlos가 작성한 글을 볼 수 있습니다. 이 때 carlos 이름을 클릭합니다.

carlos 계정이 작성한 게시글 내에서 계정 링크를 클릭하면, 요청 내에 GUID로 설정된 userId 값을 확인할 수 있습니다. 이 값을 통해 carlos 계정을 특정할 수 있습니다.

/my-account 엔드포인트의 파라미터 id에 확인한 GUID 값을 넣고 요청을 전송하면, carlosMy Account 페이지로 이동하게 되고 API Key까지 확인이 가능합니다.

수평적에서 수직적 권한 상승으로의 전환(Horizontal to Vertical Privilege Escalation)

수평적 권한 상승 공격은 더 높은 권한을 가진 사용자를 침해함으로써 수직적 권한 상승으로 전환될 수 있습니다. 예를 들어, 수평적 상승을 통해 공격자가 관리자 사용자의 비밀번호를 재설정하거나 탈취할 수 있습니다. 공격자가 관리자 계정을 침해하면, 관리자 접근 권한을 얻어 수직적 권한 상승을 수행할 수 있습니다.

https://insecure-website.com/myaccount?id=456

대상 사용자가 애플리케이션 관리자인 경우, 공격자는 관리자 계정 페이지에 접근하게 됩니다. 이 페이지는 관리자의 비밀번호를 노출하거나, 비밀번호를 변경할 수 있는 수단을 제공하거나, 권한이 있는 기능에 직접 접근할 수 있게 할 수 있습니다.

실습: 비밀번호 노출이 있는 요청 매개변수 기반 사용자 ID

이 랩에는 현재 사용자의 기존 비밀번호가 마스킹된 입력 필드에 미리 채워져 있는 사용자 계정 페이지가 있습니다. 관리자의 비밀번호를 탈취한 다음, 이를 사용하여 carlos 사용자를 삭제하세요. 제공 계정: wiener:peter

wiener 계정으로 로그인 후 My Account 페이지로 이동하면, 하단의 Password 칸에 미리 작성된 비밀번호가 마스킹 처리되어 존재합니다.

/my-account 엔드포인트의 id 파라미터 값을 administrator로 입력한 뒤 접속하면, 동일하게 비밀번호 탭에 음영 처리된 비밀번호가 존재합니다. 개발자 도구(F12)를 통해 해당 <input> 요소를 확인하면 value 속성에 평문 비밀번호가 그대로 노출되어 있습니다.

탈취한 관리자 계정 정보를 통해 로그인하면 정상적으로 접속이 가능하며, carlos 사용자를 삭제할 수 있습니다.

인증 취약점(Authentication Vulnerabilities)

인증 취약점은 개념적으로 이해하기 쉽지만, 인증과 보안 사이의 직접적인 관계 때문에 일반적으로 심각한 취약점에 해당합니다. 공격자가 민감한 데이터와 기능에 접근할 수 있게 하며, 추가적인 익스플로잇을 위한 공격 표면을 노출시킵니다.

인증(Authentication)과 인가(Authorization)의 차이

인증(Authentication)은 사용자가 자신이 주장하는 사람이 맞는지 확인하는 과정입니다. 인가(Authorization)는 인증된 사용자가 특정 작업을 수행할 수 있는 권한이 있는지 확인하는 것을 포함합니다.

예를 들어, 인증은 Carlos123이라는 사용자명으로 접근하려는 사람이 실제로 해당 계정을 생성한 동일 인물인지 확인합니다. Carlos123이 인증되면, 인가가 그의 권한 범위를 결정합니다. 다른 사용자의 개인 정보 접근, 계정 삭제 등의 작업 수행 여부가 이에 해당합니다.

무차별 대입 공격 (Brute-force Attacks)

무차별 대입 공격은 공격자가 시행착오 방식을 사용하여 유효한 사용자 자격 증명을 추측하는 것입니다. 이러한 공격은 일반적으로 사용자명과 비밀번호의 워드리스트를 사용하여 자동화됩니다. 이 과정을 자동화하면, 특히 전용 도구를 사용할 경우, 공격자가 고속으로 방대한 수의 로그인 시도를 할 수 있게 됩니다.

무차별 대입은 항상 사용자명과 비밀번호를 완전히 무작위로 추측하는 것만은 아닙니다. 기본적인 논리나 공개적으로 이용 가능한 지식을 활용하여, 공격자는 무차별 대입 공격을 미세 조정하여 훨씬 더 정확한 추측을 할 수 있습니다. 이는 이러한 공격의 효율성을 상당히 높입니다. 사용자 인증의 유일한 방법으로 비밀번호 기반 로그인에 의존하는 웹사이트는 충분한 무차별 대입 방어를 구현하지 않으면 매우 취약할 수 있습니다.

사용자명 무차별 대입 (Brute-forcing Usernames)

사용자명은 이메일 주소와 같이 인식 가능한 패턴을 따르는 경우 특히 추측하기 쉽습니다. 예를 들어, firstname.lastname@somecompany.com 형식의 비즈니스 로그인을 보는 것은 매우 흔합니다. 그러나 명확한 패턴이 없더라도, 때때로 높은 권한의 계정이 admin 이나 administrator와 같은 예측 가능한 사용자명으로 생성되기도 합니다.

감사(auditing) 중에 웹사이트가 잠재적인 사용자명을 공개적으로 노출하는지 확인하세요. 예를 들어, 로그인하지 않고도 사용자 프로필에 접근할 수 있는지 확인합니다. 프로필의 실제 내용이 숨겨져 있더라도, 프로필에 사용된 이름이 때때로 로그인 사용자명과 동일할 수 있습니다. 또한 HTTP 응답을 확인하여 이메일 주소가 노출되는지 점검해야 합니다. 가끔 응답에 관리자나 IT 지원과 같은 높은 권한을 가진 사용자의 이메일 주소가 포함되어 있을 수 있습니다.

비밀번호 무차별 대입 (Brute-forcing Passwords)

비밀번호도 마찬가지로 무차별 대입이 가능하며, 난이도는 비밀번호의 강도에 따라 달라집니다. 많은 웹사이트는 사용자가 높은 엔트로피의 비밀번호를 생성하도록 강제하는 비밀번호 정책을 채택하고 있으며, 이론적으로는 무차별 대입만으로는 크래킹하기 더 어렵게 만듭니다. 이는 일반적으로 다음을 요구하는 비밀번호를 강제하는 것을 포함합니다:

  • 최소 문자 수
  • 소문자와 대문자의 혼합
  • 최소 하나의 특수 문자

그러나 높은 엔트로피의 비밀번호는 컴퓨터만으로는 크래킹하기 어렵지만, 인간 행동에 대한 기본적인 지식을 활용하면 사용자가 무의식적으로 이 시스템에 도입하는 취약점을 익스플로잇할 수 있습니다. 무작위 문자 조합으로 강력한 비밀번호를 만드는 대신, 사용자는 종종 기억할 수 있는 비밀번호를 가져와 비밀번호 정책에 억지로 맞추려 합니다.

예를 들어, mypassword가 허용되지 않으면, 사용자는 Mypassword1! 이나 Myp4$$w0rd와 같은 것을 시도할 수 있습니다.

비밀번호를 정기적으로 변경하도록 요구하는 정책의 경우, 사용자가 선호하는 비밀번호에 사소하고 예측 가능한 변경만 가하는 것도 흔합니다. 예를 들어, Mypassword1!Mypassword1? 또는 Mypassword2!로 변경됩니다.

가능성 있는 자격 증명과 예측 가능한 패턴에 대한 이러한 지식은 무차별 대입 공격이 모든 가능한 문자 조합을 단순히 반복하는 것보다 훨씬 더 정교하고, 따라서 효과적일 수 있음을 의미합니다.

사용자명 열거 (Username Enumeration)

사용자명 열거는 공격자가 웹사이트 동작의 변화를 관찰하여 주어진 사용자명이 유효한지 식별할 수 있는 것입니다. 일반적으로 로그인 페이지(유효한 사용자명 + 잘못된 비밀번호)나 이미 사용 중인 사용자명을 입력하는 등록 양식에서 발생합니다. 유효한 사용자명의 축소된 목록을 빠르게 생성할 수 있어, 후속 무차별 대입 공격의 효율성을 크게 높여줍니다.

실습: 서로 다른 응답을 통한 사용자명 열거

이 랩은 사용자명 열거 및 비밀번호 무차별 대입 공격에 취약합니다. 유효한 사용자명을 열거하고, 해당 사용자의 비밀번호를 무차별 대입한 다음, 계정 페이지에 접근하세요.

usernamepassword 파라미터 값을 대상으로 워드리스트 기반 브루트포싱을 수행합니다.

존재하지 않는 아이디를 입력하면 Invalid username 오류가 발생하고, 존재하는 계정이지만 비밀번호가 틀린 경우에는 Incorrect password 오류가 발생합니다. 이 응답 차이를 이용하면 먼저 유효한 사용자명을 식별할 수 있습니다.

유효한 사용자명을 확보한 뒤 비밀번호 워드리스트로 브루트포싱하면, 올바른 자격 증명 입력 시 my-account 엔드포인트로 이동하는 302 상태 코드가 발생합니다.

확인된 자격 증명으로 정상 로그인에 성공합니다.

2단계 인증 우회 (Bypassing Two-Factor Authentication)

때때로 2단계 인증의 구현이 완전히 우회될 수 있을 정도로 결함이 있는 경우가 있습니다. 사용자가 먼저 비밀번호를 입력하고 그 다음 별도의 페이지에서 인증 코드를 입력하는 구조에서, 사용자는 인증 코드를 입력하기 전에 사실상 "로그인된" 상태에 있게 됩니다. 이 경우, 첫 번째 인증 단계를 완료한 후 로그인 전용 페이지로 직접 건너뛸 수 있는지 테스트해 볼 가치가 있습니다.

실습: 2FA 단순 우회

이 랩의 2단계 인증은 우회될 수 있습니다. 유효한 사용자명과 비밀번호는 이미 획득했지만, 사용자의 2FA 인증 코드에는 접근할 수 없습니다. Carlos의 계정 페이지에 접근하세요. 본인 계정: wiener:peter / 피해자 계정: carlos:montoya

wiener 계정으로 로그인하면 4자리의 2FA 코드를 요구합니다.

이메일을 통해 Your security code is 0421.과 같은 인증 코드를 수신할 수 있습니다.

정상적으로 2차 인증을 완료하면 이동하는 페이지의 엔드포인트는 /my-account?id=wiener입니다.

여기서 id 파라미터의 값을 타 사용자인 carlos로 변경하면, 2FA를 완료하지 않은 해당 사용자의 My Account 페이지로 이동할 수 있습니다. 서버가 2단계 인증 완료 여부를 페이지 로드 시 검증하지 않기 때문에 발생하는 취약점입니다.

접근 제어 취약점 유형 요약

유형 설명 공격 방식
보호되지 않는 기능 민감한 기능에 접근 제어 미적용 관리자 URL 직접 접근, robots.txt 노출 활용
모호성을 통한 보안 예측 어려운 URL로 기능 은닉 클라이언트 측 JavaScript에서 URL 추출
매개변수 기반 접근 제어 쿠키/쿼리 파라미터로 권한 판별 쿠키 값 변조 (예: Admin=true)
수평적 권한 상승 (IDOR) 사용자 ID로 타 사용자 리소스 접근 id 파라미터 값 변경, GUID 수집
수평→수직 권한 상승 관리자 계정 침해를 통한 권한 획득 관리자 계정 페이지 접근 후 비밀번호 탈취

인증 취약점 유형 요약

유형 설명 공격 방식
무차별 대입 공격 워드리스트 기반 자격 증명 추측 자동화 도구를 이용한 대량 로그인 시도
사용자명 열거 응답 차이를 통한 유효 사용자명 식별 오류 메시지 차이 관찰 (Invalid username vs Incorrect password)
2FA 우회 2단계 인증 구현 결함 악용 1차 인증 후 로그인 전용 페이지로 직접 이동

마무리

이번 글에서는 웹 보안의 기초가 되는 세 가지 취약점 유형을 살펴보았습니다.

  • 경로 탐색: ../ 시퀀스를 이용한 서버 파일 시스템의 임의 파일 접근
  • 접근 제어: 수직적·수평적 권한 상승을 통한 인가되지 않은 리소스 접근
  • 인증: 무차별 대입, 사용자명 열거, 2FA 우회를 통한 인증 메커니즘 공격

정리하면 다음과 같습니다.

  • robots.txt, JavaScript 소스 코드 등 의도치 않은 정보 노출이 공격의 시작점이 된다.
  • 쿠키, 쿼리 파라미터 등 클라이언트 측에서 제어 가능한 값으로 접근 제어를 결정하면 안 된다.
  • 서로 다른 오류 메시지는 공격자에게 유효한 자격 증명을 추론할 수 있는 단서를 제공한다.
  • 2FA를 포함한 인증 흐름은 각 단계의 완료 여부를 서버 측에서 반드시 검증해야 한다.