본문 바로가기

Webhacking/WebGoat

[WebGoat] Broken Access Control-Insecure Direct Object References 풀이

728x90
반응형

이번 주제는 Insecure Direct Object References, 즉 IDOR 취약점에 대한 것이다.
이것저것 예시들이 나와있는데, 파라미터 값이나, URI 만을 가지고 토큰이나 세션에 대한 검증없이 객체에 접근시킬 경우, 수직적 또는 수평적 권한상승이 일어날 수 있는 가장 보편적으로 나오는 취약점이다. 

그럼 바로 문제 풀러 가보자

이건 문제라기보다는, IDOR 취약점이 주로 인증된 사용자이지만 허가되지 않은 객체에 접근할때 발생한다는 점을 알려주려는 내용으로 보인다. 가볍게 본문내용에서 알려주는 tom과 cat으로 로그인하면 된다.

완료되었다면 다음스텝으로 넘어가면 된다.

이것 역시 문제라기 보다는 Application Security에서 공격자들이 주로 화면에만 보이는 정보가 아닌 응답값을 통해 보이지 않는 정보까지를 확인하여, 공격한다는 내용을 알려주려는 학습적인 내용으로 보인다.

View Profile을 물어보자.

화면상에는 name, color, size만을 서버에서 응답하는것으로 보인다. 그렇다면 실제 response는 어떨까?

Response 패킷을 보니 role과 userId 까지 응답되고 있음을 확인할 수 있다.
공격자 입장에서는 눈에 보이는 name, color, size보다 숨겨져있는 role과 userId가 더 유의미하게 사용될 수 있겠다는 생각이 들면, 이번 문제는 끝이다.

숨겨져있던 2개의 값을 입력하고 다음 문제로 넘어가자.

이번문제는, REST API에서 특정 리소스를 선택하고자 할때 어떤식으로 선택하는지를 안다면 쉬운문제이다.
우선 이전 문제에서 View Profile 버튼을 눌렀을 때의 request 패킷을 확인해보자.

URI를 보면 /WebGoat/IDOR/profile 이다.
저렇게 request를 보내면 Tom의 사용자 정보가 나왔었다. 하지만 이 요청에는 Tom을 의미하는 정보가 하나도 포함되어있지 않다. 그렇다면 다른사람의 정보를 보고싶다면 어떻게 해야할까?
내가 만약 관리자이고, 일반 계정들의 정보를 모두 볼수 있는 권한을 가졌다면 어떤식으로 특정 리소스를 서버로부터 조회할 수 있을까?

REST API는 URI를 통해 특정 리소스를 선택하게 된다. 우리는 이전문제에서 눈에보이지 않는 숨겨진 필드 2개를 찾아냈었다. 
하나는 role, 하나는 userId. 이 두개의 필드 중 특정 사용자를 지칭할 수 있는 값은 무엇일까? 
답은 바로 userId이다.

그렇다면 /WebGoat/IDOR/profile/[userId값] 이런형태로 request를 보낸다면? 
권한이 있는 사용자에 한해서 해당 사용자에 대한 profile 정보를 응답해 줄 것이다. 우선은 우리가 아는건 이전 문제를 통해 Tom 계정의 userId만을 알고 있으니, 이를통해 문제를 풀어보도록 하자.

이렇게 예상되는 REST API URI를 넣고 Submit을 누르면 된다.

문제가 해소되면서 원하던 사용자의 profile이 response를 통해 화면에 노출됨을 확인할 수 있다.
REST API를 이해하고 있다면 어렵지 않은 문제이다.

이제 마지막 문제이다.

지금까지는 Direct Object Reference 자체가 무엇인지에 대한 설명이었다면, 이번엔 Insecure Direct Object Reference 취약점에 대한 내용이다. 이전 문제에서 설명했듯 주로 IDOR 취약점이 발생하는 이유는 인증된 사용자이나, 허가되지 않은 리소스에 접근할 때, 접근 제어를 제대로 서버에서 수행하지 않아 발생한다.

지금까지 학습한 내용을 토대로 첫번째 문제부터 풀어보자.
첫번째 문제는 View Another Profile. 즉 타 사용자의 정보를 보라는 것이다. 바로 이전 문제를 통해 특정 사용자의 profile을 보는 방법은 알아냈다. 그렇다면 userId값만을 바꾸어가며 타 사용자의 정보를 얻을 수 있는지 확인해보면 된다.

Burpsuite repeater를 이용하여 테스트해보자.

우선 기존에 알고있던 Tom의 userId를 이용해서 정보조회를 시도해보았다.
시도해본 결과, 다른 사용자의 정보를 조회하라고 피드백이 왔다는것을 확인할 수 있다. userId를 조금씩 바꿔서 테스트 해보자.

조금씩 값을 더하고 빼다보니 타 사용자의 정보를 얻어낼수 있었다.
그렇다면 이어서 찾아낸 타 사용자의 정보를 토대로 다음 문제를 풀어보자.

다음 문제는 Edit Another Profile. 즉 타 사용자의 profile을 수정하라는 것이다.
이 문제에서는 Buffalo Bill의 profile 중 role은 좀더 낮은걸로, color는 red로 바꾸라는 미션을 준다.

REST API에서 주로 수정의 역할을 맡는 http request method는 PUT아니면 PATCH이다.
그렇다면 우리가 고민해야할 것은, 수정하고자 하는 정보를 서버에서 받을때, 일반적인 파라미터 형태로 받는지, json형태인지 어떤 형태인지를 알아야 한다.

그런데 , 직전 문제인 View Another Profile에서 사용된 request 패킷을 자세히보니 Content-Type이 application/json 인것을 알 수 있다. 그렇다면 Edit Another Profile 문제 역시 동일 API URI를 사용하기에 json 타입을 쓸 확률이 매우 높다. 

알아낸 정보를 토대로 공격을 수행해보자.

문제에서 View Profile 버튼을 누른 뒤, 해당 패킷을 위 스크린샷 처럼 수정하였다.
수정한 내용은 아래와 같다.

  1. HTTP Request Method PUT으로 변경
  2. URI에 있는 userId 아까 찾아낸 타 사용자의 값으로 변경
  3. Content-Type application/json으로 변경
  4. Request body에 아까 타 사용자 정보조회 했을때 확인한 output 데이터를 json 형태에 맞게 수정하여 추가
  5. role을 1로 color를 red로 수정함

이제 Request를 보내보자

깔끔하게 문제가 해소됨을 확인할 수 있다.

그러면 코드를 보면서 실제 내가 로그인한 Tom 계정이 아닌 다른 계정은 조회나 수정이 불가하도록 조치를 취해보자.

IDORViewOtherProfile.java 파일을 보면 위 스샷에 드래그된 것과 같은 조건문이 있다.
userId 변수는 URI에 입력되어있는 userId 값을 가져온 값이고, authUserId는 userSessonData에서 특정 값을 가져온 것이며, 이는 실제 내가 로그인을 한 계정인 'tom'의 userId를 가져온다는 것을 알 수 있다.
그런데 내용을 보니... userId가 null이 아니고, 현재 내가 로그인한 tom의 userId와 같이 않은경우, 아래 과정을 쭉 진행한다...? 뭔가 이상하다. 이부분은 일부러 IDOR 취약점을 만들어 내기 위해, tom이라는 사용자가 다른 사용자의 정보를 조회하는 경우에만 문제가 풀리도록 만들어 둔 것이다.

안전하게 하기 위해서는 반대로 하면 된다. 
사용자로부터 입력받은 userId와 현재 로그인한 사용자의 authUserId가 동일할 경우에만 조회를 할 수 있도록 조건문에서 ! 하나만 떼주면 된다.

이번에는 타 사용자 정보를 수정하는 부분을 조치할 것이다. IDOREditOtherProfile.java를 수정하면 된다.
이 역시 조건문이 하나 있는데 이때 userSubmittedProfile은 json 형태로 request body에 넣어 전달한 값들을 의미한다.
전달한 저보들 중 getUserId를 통해 userId를 가져오는데 이 값이 비어있거나, 실제 로그인한 사용자의 userId값인 authUserId와 같이 않다면...? 수정과정이 진행되고 문제가 해결된다.

이 역시 일부러 문제를 내기위해 authUserId와 다른 경우에만 이어서 진행이 되도록 구현되어있다. 여기도 ! 하나만 떼주면 안전한 서비스가 될 수 있다.

이렇게 코드를 수정하면 된다.

지금 2개의 파일을 수정했는데, 저장하고 바로 재 빌드 할 경우, 테스트 결과가 실패가 나오기 때문에, './mvnw clean install -DskipTests' 명령을 통해 테스트를 스킵하고 빌드하도록 하자.

확인해보면, 문제를 풀었을때와 동일한 request를 전송하지만, 결과는 실패됨을 알 수 있다.


728x90
반응형