본문 바로가기

Webhacking/WebGoat

[WebGoat] Injection-Path traversal 풀이

728x90
반응형

Path traversal 취약점에 대한 챕터이다. 한글로는 경로 탐색 또는 디렉터리 탐색 취약점이라고 한다.
쉽게 설명하면 개발자가 의도한 경로에서 임의로 벗어나 어떠한 행위를 하거나 정보를 조회하는 등의 행위를 할 수 있는 취약점을 의미한다. 문제를 한번 보자.

첫번째 문제를 보니... 임의의 경로에 파일을 업로드하면 풀리는 문제로 보인다.
우선 파일을 업로드 할 수 있는 부분은 프로필 사진밖에 없으니, 프로필 사진을 업로드 한 뒤 Update를 시도해보자

파일명 testtesttest.png를 이용해 업로드했는데, 응답을 보니 /root/.webgoat-2023.3/PathTraversal/testtest/asdf 경로에 저장된다고 한다. 보아하니 testtest까지는 고정이고 그 뒤 경로는 Full Name에 적은 값을 기반으로 정해지는것 같다. 
이 문제의 목적은 PathGraversal 하위에 파일을 업로드하는것이 목표이니... Full Name에 ../asdf 이런식으로 값을 대입하면, testtest라는 usual upload location을 벗어나 '/root/.webgoat-2023.3/PathTraversal/asdf' 이런 경로에 image업로드가 가능해질 것이다.

문제가 해결되었다. 다음문제로 넘어가자.

두번째 문제는 첫번째 문제에서 ../ 만 추가로 필터링한 문제라고 한다. 우선 첫번째 문제를 풀었던 답을 그대로 입력해보자.

음 보아하니 ../를 아예 삭제하는 로직을 추가한듯 하다. 그런데... SQL Injection mitigation때도 보았다. 저렇게 특정 문자열을 없애버리는 형태의 조치를 취하면 우회가 쉽다는 것을 말이다.
../ 가 사라지니까, ....// 이런식으로 공격을 하면 가운데 있는 ../가 사라지면서 앞뒤 문자가 붙어 자연스레 ../가 될 것이다. ....//asdf로 공격을 수행해보자.

깔끔하게 해결되었다. 다음 문제로 넘어가자.

이번문제는 개발자가 full name을 이용해서 파일을 저장하는것을 수정했다고 한다. 
그렇다면 일단 어떤것을 기준으로 파일을 저장하는지 알 수 없으니, 임의의 값으로 Update를 해보자. Full Name은 asdf로 Email은 asdf@asdf.com으로 Password는 aaaa로, 마지막으로 파일은 testtesttest.png를 업로드 해보았다.

이번엔 파일명을 이용한다는걸 확인했다. 그럼 첫번째 문제와 같이 파일명 앞에 ../만 붙여주면 되는데, 기본적으로 /는 파일명에 쓸수없는 예약어이다. 그렇기에 우리는 프록시툴을 이용하여 패킷 내 파일명을 수정함으로써 공격을 할 수 있다.

filename 부분을 드래그 된 부분과 같이 변조 후 공격을 이어가보자.

문제가 해결되었음을 알 수 있다. 다음문제로 넘어가자.

드디어 다른 타입의 문제다 . Path Traversal 취약점을 이용해서 path-traversal-secret.jpg를 찾아내라는 문제이다.
우선 무엇이 힌트일지 모르니 Show random cat picture를 클릭해보자.

보아하니, random-picture를 파라미터없이 호출하면, 임의의 값을 id 파라미터에 넣어 이미지를 띄워주는 형식인것 같다. 그렇다면 id파라미터를 이용하여 공격하면 되지 않을까 싶으니, id파라미터를 이용하여 공격을 시도해보자.

뭔가 이상하다. Location과 동일하게 했는데 10.jpg의 정보가 아닌 디렉토리 내 정보가 나온다. 여기에 취약점이 있는건 알겠는데... 왜 안나오지..? 라는 생각이 들어, 확장자를 지우고 요청해보았다.

 

예상이 맞았다. id 파라미터에 입력한 값에 서버에서 .jpg 확장자를 붙인 뒤 해당 파일을 응답해주는것으로 보인다. 우리가 추후 찾아야 하는 파일의 확장자도 .jpg이니 추후 공격할때 확장자는 빼고 공격하면 될것 같다.

다시 공격으로 돌아와서, id에 이상한값을 넣어보자.

asdf라는 값을 보냈고, 이는 서버에서 asdf.jpg가 되어 파일을 찾을텐데, 그런 파일이 없으니 그 파일을 찾고자 했던 디렉터리 내 다른 파일들을 리스트로 응답하는것을 볼 수 있다. 다만 우리가 원하는 파일은 보이지 않는다. 그렇다면 상위로 올라가면서 찾아보면 어떨까?

엥..? 막힌다. 테스트해보니 ..과 / 모두 막히는것을 확인할 수 있었다. 어떻게 해야하지...?
자 여기서 한가지. 이번 문제를 푸는 과정은 조금은 전제조건이 특이하다. 이 문제이기에 가능한 우회패턴임을 미리 말한다. 우회 방법은 바로 URL Encoding이다.
../를 URL Encoding한 값인 %2e%2e%2f를 넣으면 필터링이 우회되는것을 확인할 수 있을것이다.

우회에 성공하여, 상위 디렉토리 내 파일들을 보았지만, 우리가 원하는 파일은 보이지가 않는다.
그런데 기본적으로 백엔드에서 파라미터를 처리할때는, URL Encoding된 값은 처리중 자동으로 Decoding되어 처리되기 때문에 우회가 안되는 것이 보통이다. 그렇다면 왜 이 문제는 이런 우회방법이 가능했던 것일까?

ProfileUploadRetrieval.java 파일을 보면 random-picture을 담당하는 controller를 확인할 수 있다.
여기에 보면 request.getQueryString() 을 이용해 가져온 데이터들이 ..이나 /를 포함하고 있는지를 검증하고 있음을 알 수 있다. QueryString은 쉽게말하자면 URI의 맨 뒷부분 ?로 시작하는 부분부터 끝까지를 의미한다. 예를 들어 'http://www.test.com/1/2/test?asdf=asdf&1234=1234' 라는 URI가 있다면 request.getQueryString()을 이용하면 'asdf=asdf&1234=1234' 이 값이 추출된다고 생각하면 된다. 여기서 특이한점이 getQueryString 메서드는 URL Decoding을 수행해주지 않는다는 것이다. 그래서 우리가 URL Encoding된 값으로 우회를 할 수 있었던 것이다.

그렇다면 각 파라미터별로 제대로된 데이터를 URL Decoding까지 해서 받아오고 싶다면 어떻게 하면될까? 바로 파란색으로 드래그된 getParameter() 메서드를 사용하고, 받아오고자 하는 파라미터 명을 인자로 넘겨주면 자동으로 URL Decoding까지 해서 데이터를 넘겨준다. 이 코드를 조치하는건 맨 마지막에 해보도록 하고, 우선 우회에 성공했으니 공격을 이어가보자.

아까 한번 상위로 갔을때는 우리가 찾고자하는 파일이 없어, 한번 더 상위로 올라가기 위해 %2e%2e%2f를 두번 넣어준 뒤, 없는 파일명인 a를 넣어 request를 보내자, 드디어 찾는 파일이 나타났다. 마지막에 .jpg는 자동으로 붙으니, 파일이름만 request에 추가해보자.

드디어 답인가 했더니, 허들이 하나 더 있네..? 내가 로그인한 사용자의 username을 sha-512 hash화 해서 넣으면 된다고 한다.
지금 로그인한 사용자의 username은 testtest이니 바로 해보자.

python으로 해도되고, 온라인사이트로 해도된다. 
나는 온라인사이트에서 했으며, 그 결과를 answer에 넣어주면 된다.

문제가 해결됨을 알 수 있다. 조치방안은 가장 아래에서 다루도록 하고 우선 넘어가자.

다음문제는 zip slip 취약점 관련한것이다. zip slip 취약점은 쉽게말해 압축파일을 업로드했을때, 서버에서 압축된 파일을 풀다가 압축파일에 포함된 파일의 이름으로 인해 파일이 덮어써지는 등의 공격을 당하는 상황을 의미한다.
이번 문제는 Location으로 주어진 파일을 덮어쓰는것이나, 일단 아무것도 모르니 zip파일 아무거나 업로드를 해보자.

음... 답과 다른 zip파일을 업로드하면 어떠한 정보도 주지않는것으로 보인다.
지금까지는 업로드되는 위치를 파악해서 상대경로로 공격을 했었는데, 이번에는 조금 다르게 해야할것같다. Zip Slip 자체가 압축파일 내 존재하는 파일의 파일이름때문에 발생하는것이니, 공격은 파일이름에 하면되는데, 그 파일이름은 이렇게 할 것이다. "../../../../../../../../../../root/.webgoat-2023.3/PathTraversal/testtest/testtest.jpg" 왜냐하면, 업로드 되는 위치를 모르는 이상 우선 최상위 디렉토리로 나가야했기에 ../를 10개정도 넣으면 최상단이 되지 않을까(?)라고 추측하여 10개를 넣었으며, 그 뒤에는 문제에서 제공한 파일 경로를 넣어주었다.

자 이제 저런이름을 가진 파일을 만들어주면 되는데... 아까도 말했듯 /는 파일이름에 들어갈수가 없다.
어떻게 해야할까? 바로 Linux zip 커맨드를 이용하면 된다.

우선 압축을 하기전에, 문제에서 제공하는 경로와 동일한 이름을 가진 디렉터리를 생성해준뒤, 아무 이미지파일이나 해당 디렉토리에 넣어 이름을 testtest.jpg로 바꾸어준다.

그리고 10번 상위로 이동하기때문에, 그 10번을 도와줄 temporary directory를 만들어준다.
그리고 zip명령어를 실행하기 위해 만든 temp10 디렉터리로 이동한다.

zip 명령어를 이용하여 zipslip.zip을 만드는데, 이때 인자로 아까 준비한 payload를 넣어주면, 아까 만들어둔 파일을 찾아 추가해줄 것이다. 이때 파일이름은 payload와 동일한 값으로 압축된다.

자 이제 업로드해보자.

마지막 문제까지 해결되었다. (testtest.jpg를 빈파일로 만들경우 에러가 발생하니 참고하자.)

자 그렇다면 Path traversal, 어떻게 막을 수 있을까?
업로드 디렉터리만 권한을 주고, 다른 디렉터리엔 권한을 빼서 경조조작이 되더라도 조회나, 수정 등이 불가하게 막거나, AWS와 같은 클라우드에서 제공하는 S3와 같은 Key-value 형태의 Storage service를 이용하면 막을 수 있다.
하지만 가장 간단한 방법은 사용자로부터 저장할 경로를 전달받지 않고, 또 파일명에 들어갈 수 없는 특수문자에 대하여 필터링하는것이다. 
아까 랜덤하게 고양이를 보여주는 문제에서 사용된 코드를 안전하게 수정해봄으로써 취약점 조치를 실습해보도록 하자.

이 부분이 필터링을 수행하는 부분이었다.
우리는 어짜피 id 파라미터에 대해서만 필터링을 진행하면 되기때문에, getQueryString이 아닌 getParameter를 이용할수 있도록 코드를 변경해줄 것이다. 그러면 아래 if문에서 ..과 /를 입력하지 못하도록 필터링 기능을 수행해줄 것이다.

맨 윗줄만 이렇게 드래그한 부분처럼 변경해주면 된다. 이제 빌드하고 재실행해보자.

아까 문제 풀었을때 사용한 paylaod그대로 사용했지만. 차단되는것을 볼 수 있다.
이렇게 문자열 필터링을 잘만 해준다면 쉽게 막을수 있다.


 

 

728x90
반응형