WebSocket 기반 회전 각도 조작을 통한 플래그 획득

문제 개요

이 문제는 WebSocket을 통해 클라이언트의 마우스 움직임을 서버에 전송하고, 특정 조건을 만족할 때 플래그를 반환하는 방식으로 구성되어 있다. 사용자가 중심점을 기준으로 마우스를 일정 각도 이상 회전하면 누적 각도가 계산되고, 전체 회전 수가 9999번 이상일 경우 플래그가 노출된다.

백엔드 로직 분석

서버는 Node.js 기반으로 ws 라이브러리를 사용해 WebSocket 연결을 처리한다. 주요 동작은 다음과 같다:
  • 클라이언트 연결 시 사용자별 데이터 (spins, cumulativeAngle, touchedPoints 등)를 저장.
  • 수신된 마우스 좌표(x, y)와 중심점(centerX, centerY)을 기반으로 현재 각도를 계산.
  • 이전 각도 대비 변화량(delta)를 누적하여 360도 이상 회전할 때마다 spins 카운터 증가.
  • 동일한 좌표는 중복 처리되지 않도록 touchedPoints 배열에 의해 필터링됨.
  • spins가 9999 이상이면 환경 변수에서 플래그를 읽어 전송.
핵심 제약 조건은 **중복된 좌표는 무시**된다는 점이다. 따라서 단순 반복 입력은 효과가 없다.

프론트엔드 동작 구조

클라이언트 측 코드는 다음과 같은 역할을 수행:
  • 화면 중심 좌표를 기준으로 중심점을 렌더링.
  • WebSocket 연결을 생성하고, mousemove 이벤트 발생 시 현재 좌표를 서버로 전송.
  • 서버로부터 수신한 spins 값을 화면에 업데이트.
  • 플래그 메시지 수신 시 알림 창으로 출력.

공격 전략: 자동 회전 시뮬레이션

수동으로 9999회 이상 회전하는 것은 비현실적이므로, 자동화 스크립트를 작성해야 한다. 핵심 아이디어는:
  1. 각도 계산을 기반으로 원주 상의 점들을 생성.
  2. 매번 다른 좌표를 전달하기 위해 반지름을 점진적으로 변화시킴.
다음과 같은 유틸리티 함수를 정의:
function createPoint(angleDegrees, radius) {
    const radians = (angleDegrees * Math.PI) / 180;
    return {
        x: centerX + Math.cos(radians) * radius,
        y: centerY + Math.sin(radians) * radius,
        centerX: centerX,
        centerY: centerY
    };
}
이 함수는 주어진 각도와 반지름에 따라 중심점 주위의 좌표를 생성한다.

자동 전송 루프 구현

반지름을 점차 증가시키며 매 반복에서 새로운 경로를 따라 회전하도록 구성:
for (let r = 1; r <= 10000; r += 0.5) {
    for (let deg of [0, 90, 180, 270]) {
        socket.send(JSON.stringify(createPoint(deg, r)));
    }
}
이 루프는:
  • 각 반복에서 반지름 r을 0.5씩 증가시켜 고유한 궤도를 생성.
  • 4개의 주요 각도(0°, 90°, 180°, 270°)를 사용해 한 바퀴를 완성.
  • 매번 다른 좌표가 전송되므로 touchedPoints 필터를 우회 가능.

최종 실행 스크립트

브라우저 콘솔에서 아래 코드를 실행하여 플래그 획득:
function createPoint(angleDegrees, radius) {
    const radians = (angleDegrees * Math.PI) / 180;
    return {
        x: centerX + Math.cos(radians) * radius,
        y: centerY + Math.sin(radians) * radius,
        centerX: centerX,
        centerY: centerY
    };
}

for (let r = 1; r <= 10000; r += 0.5) {
    for (let deg of [0, 90, 180, 270]) {
        socket.send(JSON.stringify(createPoint(deg, r)));
    }
}
이 스크립트는 충분히 많은 회전을 시뮬레이션하여 spins 카운터가 9999를 초과하게 하고, 곧바로 플래그를 수신하게 된다.

획득한 플래그

vsctf{i_ran_out_of_flag_ideas_so_have_this_random_string_2CSJzbfeWqVBnwU5q8}

태그: websocket Node.js CTF Web Security Angle Manipulation

6월 1일 18:24에 게시됨