본문 바로가기

Normaltic 취업반/인증 및 인가 취약점

권한 상승 취약점 구현 시나리오

회원가입 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 값이 들어오면 필터링한다.