본문 바로가기

Webhacking/WebGoat

[WebGoat] Broken Access Control-Hijack a session 풀이

728x90
반응형

Broken Access control. 쉽게 말해 접근 제어에 실패한 경우를 의미한다. 
한국에서는 주로 '부적절한 인가' 라는 항목으로 불리운다.

첫 문제는 세션이 완전히 난수가 아닌 추측 가능한 경우 hijacking당할 수 있음을 알려주는 문제로 보인다.
이제 풀어보자.

문제를 보면 hijack_cookie라는 쿠키값을 예측해서, 로그인에 성공한 타 사용자의 세션값을 맞추라는 문제이다. 
우선 어떻게 하는지 모르겠으니 아무거나 입력을 하고 Access를 눌러보자

 Access 버튼을 누르면 login api로 request가 전송된다.
request header나 body에 크게 일반적인 request와의 차이점이 보이지는 않는다.

response를 보니 Set-Cookie를 이용하여 hijack_cookie라는 쿠키값을 세팅한다는 것을 확인할 수 있다.
하나만 가지고는 어떤 변화가 있는지 예측이 어려우니, 여러번 요청을 보내보자

연속해서 여러번 요청을 보내다보면, 표시된 부분이 1씩 늘어나고 있음을 확인할 수 있다. 
또 그 옆은 아마 요청한 시간이 아닐까 추측할 수 있다. 그런데 마지막 request를 보면 1씩늘어나야하는데 혼자 2가 늘어나있다.

이 뜻은 중간에 타 사용자가 로그인을 시도했다(성공 유무는 모르지만...)는 것을 알 수 있는 대목이다. 
아마 타 사용자의 hijack_cookie의 앞부분은 41로 끝날것이며... 뒤에있는 시간관련한 값은, 40과 42번 request에 할당된 시간 그 사이 어딘가일 것이다. 
Burpsuite의 intruder를 이용하여 그 사람의 세션을 한번 예측해보도록 하자. 

주황색으로 표시된 부분은 아까 예상한 41번을 넣고, 뒤에 파랗게 표시된 부분은 Burpsuite intruder에서 자동으로 변조를 수행해줄 값이다.
40번 request는 파란색으로 표시된 부분이 2944이며, 42번 request는 파란색으로 표시된 부분이 3137이다. 
즉 41번 request는 그 사이일 것이기 때문에, 저 4자리만 바꿔주면서 맞는 값을 찾아주면 된다.

payload 는 숫자만 넣으면 되기때문에 Numbers로 세팅하면 되며, 그 값은 위에서 설명했듯 2944부터 3137 사이가 되도록 범위를 설정하면 된다.
자동으로 1씩 더해가며 테스트하도록 세팅했으며, 4자리가 고정되어야하기에 최소 길이도 4자, 최대길이도 4자로 세팅했고 소수점은 없도록 설정했다.

이제 Start Attack 을 눌러 공격을 진행하면 된다.
(community edition이기때문에 속도가 느리나, 느린 속도를 크게 체감할정도로 많은 양의 변조가 아니기에 이정도면 충분하다)

엥? 예상치 못한결과다.
일단 풀긴풀었는데 2944 가 정답이었다. 2944는 40번 request와 동일한 값인데...
아마도 문제 설정상, 특정 횟수 이후에 request가 들어오면 거의 동시에 세션을 발급해서, 시간차이가 없었을수도 있겠다 싶다.
(자세한 건 코드를 봐야 알겠지만...)

코드를 github에서 다운로드 받아 HijackSessionAssignment.java를 열어보면, /HijackSession/login API에 대한 controller를 확인할 수 있다.

코드를 보면 if 문을 통해 cookie값이 있을때와 없을때의 로직을 분기하고 있다.
cookie값이 존재할 경우,  그 값을 Authentication DTO의 id에 넣은 뒤 authenticate 작업을 바로 진행하게 되며 존재하지 않을 경우, 입력받은 id/pw값을 Authentication DTO의 id, credentials 각각의 값으로 설정한 뒤 그 값을 기반으로 authenticate 작업을 진행한 뒤, Set-cookie 한다는것을 알 수 있다.

여기서 authenticate 작업은 HijackSessionAuthenticationProvider.java 파일에서 확인할 수 있다.

이 파일은 Authentication.java 파일로  위에서 설명한 Authentication DTO를 설정해둔 곳이다.

위 내용은 HijackSessionAuthenticationProvider.java의 authentication 메서드의 내용이다.
보면 if문을 통해 authentication.getId에 할당된 값이 있으며, sessions에 해당 ID가 등록이 되어 있어야만, 사용자를 인가시켜준다는 것을 알 수 있다. 아까 본 내용대로 authentiation.getID는 쿠키값 또는 id가 들어가있기때문에, 이해가 되는데 sessions는 어떻게 더해지는 것인지 알 수가 없다.

authorizedUserAutoLogin 메서드를 보면 뭔가 맨 마지막줄에 addSession이라는 말그대로 세션에 더하는 행동을 취하고 있음을 확인할 수 있다. 이어서 addSession을 보면, sessions에 sessionId를 추가하는 작업을 함을 알 수 있다. sessions가 선언된 부분을 따라가 올라가 보면 Linked List라는 것까지도 확인할 수 있다.

그렇다면 authorizedUserAutoLogin 메서드는 언제 호출되는것일까? 바로 authenticate 메서드이다. 
authenticate 메서드의 맨 마지막을 보면 authorizedUserAutoLogin 메서드를 호출한다는것을 알 수 있다. 
그런데 authorizedUserAutoLogin 메서드를 호출 하기 이전에 2개의 조건문에 작성되어있는 return문에 걸리면 안되는데, 그 조건은 아예 authentication 메서드가 인자없이 호출되었거나, 실제로 쿠키를 맞춰서 로그인에 성공한 경우이다.  또 하나 있다. 바로 authorizedUserAutoLogin 메서드 자체에 있는 조건문인데, 이건 아래 스크린샷과 연관이 있다.

위에서 설명한 authorizedUserAutoLogin 메서드 자체에 있는 조건문의 조건을 보면 ThreadLocalRandom.current().nextDouble() 메서드의 호출 결과를 인자로 PROBABILITY_DOUBLE_PREDICATE라는 생성자의 test메서드를 실행했을 때, False가 응답되어야 !연산으로 인해 True가 되어, 조건문이 실행되는 것이다. 조건 자체를 분석할 필요는 없어, 자세히 보진않겠으나, 아마도 현재 시간과 연관이 있는것으로 보인다.

즉 정리하자면 authorizeduserAutoLogin은 ID/PW로 로그인을 성공했거나, ID/PW로 로그인에 실패한 경우 두 경우에 진행된다. 
우리는 아까 연속적으로 실패하면서 생성된 Session값들을 보았고, 그 중 비어있는 값에 무작위 대입을 통해 타 사용자의 권한을 얻어냈었다. 즉 그 비어있는 값은 조건 2개중 성공 조건에 해당하여 생성된 값이라는 것인데... 우리는 성공한 적이 없으니, 어디서 자동으로 로그인을 성공하도록 만든것일까?

GENERATE_SESSION_ID 는 사용될 때 마다 ++id를 통해 id값을 증가시킨다.
그리고 AUTHENTICATION_SUPPLIER는 사용될때마다 GENERATE_SESSION_ID 값을 참고한다.
이걸 정리하면 AUTHENTICATION_SUPPLIER를 한번 호출하면, id값이 1 증가한다는 것이다.

다시 authorizedUserAutoLogin 메서드를 보면, 일정 확률로 조건문을 통과하면, AUTHENTICATION_SUPPLIER 값을 참고하여 session에 값을 추가한다는 것을 알 수 있다. 

즉 우리가 로그인을 시도하던 중, 일정 확률로 id값이 1 더해진 GENERATE_SESSION_ID값이 session에 더해지기때문에, 간혹 우리가 요청을 하다보면 1씩 올라가야하는데 2씩 올라가는 상황이 벌어졌던 것이다. 실제로 로그인이 이루어진 세션이라기보다는 문제를 내기위해 일정확률로 마치 로그인된 사용자인양 임의로 session에 값을 추가하는 로직이었던 것이다.

문제를 푸는 과정에는 크게 중요치않으나, 이렇게 해석하면서 보면 더 학습이 잘된다.


728x90
반응형