난 역시 정리하면서 문제푸는게 편하다…

<?php
    $a = array();
    foreach ($_POST as $k => $v) {
        $a[$k] = $v;
    }

    $j = json_encode($a);
    echo '<input type="hidden" name="cred" value="' . base64_encode($j) . '">';
    ?>

먼저 여기서 로그인, 패스워드, otp 값을 입력받고 검증을 한다.

일단 입력값이 들어오면 json encoding → base64 encoding을 해서 저장하는 것을 확인할 수 있다.

 <?php

function generatePassword($length) {
    $characters = '0123456789abcdef';
    $charactersLength = strlen($characters); //16?
    $pw = '';
    for ($i = 0; $i < $length; $i++) {   //전달받은 param 값 = length
        $pw .= $characters[random_int(0, $charactersLength - 1)];   #0 ~ 15까지 randomint를 생성해서 하나씩 붙이는건
    }
    return $pw;
}

function generateOTP() {
    return 'P' . str_pad(strval(random_int(0, 999999)), 6, "0", STR_PAD_LEFT);
}

$admin_pw = generatePassword(32);
$otp = generateOTP();

function login() {
    if (!isset($_POST['cred'])) {
        echo "Please login...";
        return;
    }   // default 설

    if (!($cred = base64_decode($_POST['cred']))) {
        echo "Cred error";
        return;
    }

    if (!($cred = json_decode($cred, true))) {
        echo "Cred error";
        return;
    }

    if (!(isset($cred['id']) && isset($cred['pw']) && isset($cred['otp']))) {
        echo "Cred error";
        return;
    }

    if ($cred['id'] != 'admin') {
        echo "Hello," . $cred['id'];
        return;
    }
    
    if ($cred['otp'] != $GLOBALS['otp']) {
        echo "OTP fail";
        return;
    }        // 느슨한 비교

    if (!strcmp($cred['pw'], $GLOBALS['admin_pw'])) {
        require_once('flag.php');
        echo "Hello, admin! get the flag: " . $flag;
        return;
    }

    echo "Password fail";
    return;
}

?>

json parse

입력으로 JSON 문자열을 받아 JSON 문자열의 구문을 분석하고, 리턴값으로 그에 대한 JavaScript 값이나 객체를 생성

JSON.parse() - JavaScript | MDN

str_pad

return 'P' . str_pad(strval(random_int(0, 999999)), 6, "0", STR_PAD_LEFT);

문자열을 지정한 길이가 되도록 다른 문자열로 채우는 함수

OTP의 값은 P로 시작하는 7자리

strcmp

취약점이 나타나는 함수 부분

만약에 배열과 문자를 비교할려고 할 때→ strcmp() 함수는 null을 반환한다.

입력 값으로 배열을 넣을 경우 → strcmp() 함수는 리턴값으로 0을 출력함…ㅋ

[PHP] strcmp 취약점을 이용한 인증 우회

훌륭한 예시가 있다.

이러한 문제가 발생하는 건 php 5.3 ver이라고 한다.

php 5.3 ver에서, null값은 0으로 취급된다.