Oh! JUN

[WebGoat] Cross-Site Request Forgeries - 1 본문

웹 해킹/Request Forgeries(CSRF)

[WebGoat] Cross-Site Request Forgeries - 1

Kwon Oh! JUN 2023. 12. 23. 00:19

1. 문제설명


기본 CSRF 가져오기 연습
로그인한 동안 외부 소스에서 아래 양식을 트리거합니다. 응답에는 '플래그'(숫자 값)가 포함됩니다.

 

플래그 확인
아래 이전 페이지에서 획득했어야 하는 플래그를 확인하세요.


2. 풀이

그림 - 1

제출 버튼을 클릭하면 [그림 - 1]와 같은 응답값을 확인할 수 있습니다.

메시지를 살펴보면, "요청이 원래 호스트로 부터 온것으로 나타난다" 문구를 확인할 수 있고

결과적으로 실패한것을 확인할 수 있습니다.

 

그렇다면, 해당 문제를 해결하기 위해서는 요청이 원래의 호스트가 아닌 타 호스트로 부터 오도록 설정해줘야 합니다.


그림 - 2
그림 - 3

제출 버튼을 누르면 요청을 보내는 주소와 요청방식을 동일하게

"http://127.0.0.1:5555/WebGoat/csrf/basic-get-flag"와 POST로 설정해줍니다.

 

csrf와 submit 파라미터도 동일하게 설정을 해주는데 요청 보내는 호스트의 주소만 확인하기 때문에

value값은 어떤 값을 넣든 상관없습니다.


그림 - 4

코드 완성하고 실행시키면 [그림 - 4]와 같이 페이지가 뜹니다.


그림 - 5

제출 버튼을 클릭하면 성공한것을 확인할 수 있습니다.


3. 코드 분석

CSRFGetFlag.java

'csrf' 파라미터 찾으면서 코드 확인해보니까 CSRFGetFlag.java 파일에서 문제의 코드를 확인할 수 있습니다.


String host = (req.getHeader("host") == null) ? "NULL" : req.getHeader("host");
String referer = (req.getHeader("referer") == null) ? "NULL" : req.getHeader("referer");
String[] refererArr = referer.split("/");

요청헤더에서 host, referer 값이 있다면 그대로 변수에 담아주고 없으면 NULL 처리합니다.

referer값은 '/' 기준으로 문자를 잘라서 refererArr 배열에 저장합니다.


        if (referer.equals("NULL")) {
            if ("true".equals(req.getParameter("csrf"))) {
                Random random = new Random();
                userSessionData.setValue("csrf-get-success", random.nextInt(65536));
                response.put("success", true);
                response.put("message", pluginMessages.getMessage("csrf-get-null-referer.success"));
                response.put("flag", userSessionData.getValue("csrf-get-success"));
            } else {
                Random random = new Random();
                userSessionData.setValue("csrf-get-success", random.nextInt(65536));
                response.put("success", true);
                response.put("message", pluginMessages.getMessage("csrf-get-other-referer.success"));
                response.put("flag", userSessionData.getValue("csrf-get-success"));
            }
        } else if (refererArr[2].equals(host)) {
            response.put("success", false);
            response.put("message", "Appears the request came from the original host");
            response.put("flag", null);
        } else {
            Random random = new Random();
            userSessionData.setValue("csrf-get-success", random.nextInt(65536));
            response.put("success", true);
            response.put("message", pluginMessages.getMessage("csrf-get-other-referer.success"));
            response.put("flag", userSessionData.getValue("csrf-get-success"));
        }

        return response;

위의 코드에서 referer가 NULL이면 내부 조건문과 상관없이 'success'가 뜨게됩니다. referer는 제출 버튼을 클릭하기 이전 URL인데 referer가 없으면 원래의 호스트가 아니라고 판단해서 그렇습니다.

 

referer가 있으면 http://127.0.0.1:5555/WebGoat/start.mvc을 '/'을 기준으로 나누어서 refererArr[] 배열에 저장을 하게됩니다. 그리고 refererArr[2]는 [http:/공백/127.0.0.1:5555]에서 3번째인 ' 127.0.0.1:5555 '을 요청 헤더의 host와 비교를 해서 일치하면 원래의 호스트라고 판단해 'false'가 뜨고 일치하지 않으면 타 호스트라 판단해 'success'가 뜨게됩니다. 

좌: 원래의 호스트의 요청 값 / 우: 타 호스트의 요청 값