회원가입 Form
form.php
(...)
<form name="member" action="insert.php" method="post">
<h2>회원 가입</h2>
<ul class="join_form">
<li class="list-group-item">
<span class="col1">아이디</span>
<span class="col2"><input type="text" name="id"></span>
<span class="col3"><button type="button" onclick="check_id()">중복체크</button></span>
</li>
<li class="list-group-item">
<span class="col1">비밀번호</span>insert.php
<span class="col2"><input type="password" name="pass"></span>
</li>
<li class="list-group-item">
<span class="col1">비밀번호 확인</span>
<span class="col2"><input type="password" name="pass_confirm"></span>
</li>
<li class="list-group-item">
<select name="role">
<option value="personal">개인</option>
<option value="business">사업자</option>
</select>
</li>
</ul>
<ul class="buttons">
<li><button type="button" onclick="check_input()">저장하기</button></li>
<li><button type="button" onclick="reset_form()">취소하기</button></li>
</ul>
</form>
(...)
회원가입 백엔드 처리
insert.php
<?php
$id = $_POST["id"];
$id = htmlspecialchars($id, ENT_QUOTES);
$pass = $_POST["pass"];
$role = $_POST["role"]; // 취약한 구현
$role = htmlspecialchars($role, ENT_QUOTES);
include "../include/db_connect.php";
$hash_pw = password_hash($pass, PASSWORD_DEFAULT);
$stmt = $con->prepare("INSERT INTO _mem (id, pass, role) values(?, ?, ?)");
$stmt->bind_param('sss', $id, $hash_pw, $role);
$stmt->execute();
mysqli_close($con);
echo "<script>
location.href = './login_form.php';
</script>";
?>
회원정보 수정 Form
modify_form.php
<form name="member" action="modify.php?id=<?=$userid?>" method="post" enctype="multipart/form-data">
<div class="board_form">
<h2>회원 정보 수정</h2>
<ul class="list-group">
<li class="list-group-item">
<span class="badge bg-secondary">아이디</span>
<span><?=$userid?></span>
</li>
<li class="list-group-item">
<span class="badge bg-secondary">비밀번호</span>
<span><input type="password" name="pass"></span>
</li>
<li class="list-group-item">
<span class="badge bg-secondary">비밀번호 확인</span>
<span><input type="password" name="pass_confirm"></span>
</li>
<li class="list-group-item">
<select name="role">
<option value="personal">개인</option>
<option value="business">사업자</option>
</select>
</li>
</ul>
<ul class="buttons">
<li><button class="btn btn-primary" type="button" onclick="check_input()">저장하기</button></li>
<li><button class="btn btn-secondary" type="button" onclick="reset_form()">취소하기</button></li>
</ul>
</div>
</form>
회원정보 수정 백엔드 처리
modify.php
<?php
include "../include/session.php";
include "../include/db_connect.php";
$expected_domain = 'localhost';
$pass = $_POST["pass"];
$hash_pw = "";
$role = $_POST["role"]; // 취약한 구현
$stmt = $con->prepare("SELECT pass FROM _mem WHERE id=?");
$stmt->bind_param('s', $userid);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
$hash_pw = password_hash($pass, PASSWORD_DEFAULT);
$stmt = $con->prepare("UPDATE _mem SET pass = ?, role = ? WHERE id=?");
$stmt->bind_param('sss', $hash_pw, $role, $userid);
$stmt->execute();
$_SESSION['role'] = $role;
mysqli_close($con);
// 목록 페이지로 이동
echo "<script>
location.href = '../index.php';
</script>"
?>
취약점 : role데이터를 공격자가 조작할 수 있다.
insert.php
$role = $_POST["role"]; // 취약한 구현
modify.php
$role = $_POST["role"]; // 취약한 구현
- POST 메시지로 받은 role 파라미터에 대한 어떠한 검증도 없이 그냥 신뢰해 버린다.
- 이 role값을 공격자가 admin으로 조작하면 공격자가 소유한 계정을 admin으로 조작할 수 있다.
공격 시나리오

- 회원 정보 수정에서 비밀번호 변경 요청

- Brup에서 회원정보 수정 POST 메시지 캡쳐, Repeater로 전송

- Burp Repeater에서 role

원래 관리자 페이지 ./admin은 관리자 세션이 아니면 들어올 수 없다.

그러나, 공격을 통해 sample계정의 role 데이터를 admin으로 조작해버렸기 때문에, sample계정으로 로그인하고 ./admin으로 접속을 시도하면 관리자 페이지에 접속할 수 있다.
- sample계정으로 로그인

./admin(관리자 페이지) 접속 시도

role조작을 통한 sample계정으로 관리자 페이지 접속 성공
대응 방안
- 회원가입 백엔드 로직에서 $role 값에 대해 화이트리스트를 기반으로 검증한다.
insert.php
<?php
$id = $_POST["id"];
$id = htmlspecialchars($id, ENT_QUOTES);
$pass = $_POST["pass"];
$pass = htmlspecialchars($pass, ENT_QUOTES);
$role = $_POST["role"] ?? "personal"; // Defualt값은 personal(개인)
if($role !== 'personal' && $role !== 'business') { // 화이트리스트로 검증
echo "<script>
alert('유효하지 않은 role 입력입니다.');
history.go(-1);
</script>";
exit;
}
$role = htmlspecialchars($role, ENT_QUOTES);
(...)
- 사용자의 입력값(role)을 서버가 그대로 신뢰하는 것이 취약점 발생 핵심 원인이다.
- POST 요청에 role데이터가 포함되어 있지 않으면 기본값을 personal로 둔다.
- personal, business가 아닌 role 값이 들어오면 필터링한다.
'Normaltic 취업반 > 인증 및 인가 취약점' 카테고리의 다른 글
| CTF 풀이 - Authorization Bypass (0) | 2026.03.20 |
|---|---|
| CTF 풀이 - Authentication Bypass (0) | 2026.03.20 |
| JWT 서명 검증 우회를 통한 관리자 권한 탈취 분석 (0) | 2026.03.20 |
| 비밀번호 변경 토큰 취약점 (0) | 2026.03.20 |
| 인증/인가 취약점 (0) | 2026.03.20 |