테스트 사이트 - 개발 중인 베타 버전입니다

해킹당했습니다. 채택완료

라젠더 10개월 전 조회 2,018

1. 그누보드 5.3.3.3 사용중입니다. 

2. 아미나 사용중입니다.

 

서버는 안털린 거 같은데, 관리자 계정이 털렸습니다. 

집 말고 관리자 로그인 한 적 없구요. 

 

<?php
if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가

// 세션이 시작되지 않았다면 세션 시작
if (session_status() == PHP_SESSION_NONE) {
    session_start();
}

function admin_access() {
    global $is_admin;

    // 허용된 IP 주소 목록 (IPv4 및 IPv6 포함)
    $allowed_ips = [
        '12.34.56.78', 
    ];

    // 로그 파일 경로
    $log_file = '/var/www/html/admin.log';

    // 현재 사용자 IP
    $user_ip = $_SERVER['REMOTE_ADDR'];

    // 브라우저 정보
    $user_browser = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'Unknown';

    // 로그 기록 함수
    function write_log($message, $log_file) {
        $date = date('Y-m-d H:i:s');
        $log_entry = "[$date] $message\n";
        file_put_contents($log_file, $log_entry, FILE_APPEND | LOCK_EX);
    }

    // 세션 변수 이름 정의
    $session_success_log = 'admin_login_success_logged';
    $session_failure_log = 'admin_login_failure_logged';

    if ($is_admin) {
        // 로그인 성공 로그가 아직 기록되지 않았다면
        if (!isset($_SESSION[$session_success_log])) {
            if (in_array($user_ip, $allowed_ips)) {
                // 어드민 접속 성공 로그
                $message = "LOGIN_SUCCESS - IP: $user_ip - Browser: $user_browser";
                write_log($message, $log_file);
                // 세션에 로그 기록 완료 표시
                $_SESSION[$session_success_log] = true;
            } else {
                // 어드민 접속 실패 로그
                $message = "LOGIN_FAILURE - IP: $user_ip - Browser: $user_browser";
                write_log($message, $log_file);
                // 실패 로그 기록 완료 표시 (필요 시)
                $_SESSION[$session_failure_log] = true;
                exit('비밀번호가 맞지 않습니다.');
            }
        }
    } else {
        // 관리자 권한이 없을 때 (로그인 시도 중일 가능성이 있음)
        // 여기서는 로그인 시도 페이지에서만 로그를 기록하도록 설정할 수 있음
        // 예시: 로그인 페이지일 경우에만 실패 로그 기록
        // 로그인 페이지를 식별하는 조건을 추가해야 함 (예: 특정 GET/POST 파라미터 확인)
        // 아래는 단순 예시입니다.

        // 예를 들어, 로그인 시도 시 'login_attempt' 파라미터가 있을 때만 기록
        if (isset($_POST['login_attempt']) && $_POST['login_attempt'] === '1') {
            // 로그인 실패 로그가 아직 기록되지 않았다면
            if (!isset($_SESSION[$session_failure_log])) {
                $message = "LOGIN_FAILURE - IP: $user_ip - Browser: $user_browser";
                write_log($message, $log_file);
                // 세션에 실패 로그 기록 완료 표시
                $_SESSION[$session_failure_log] = true;
            }
        }
    }
}

// admin_access 함수 호출
admin_access();
?>
 

 

해당 소스 사용해서 등록된 아이피가 아니면 로그인이 안되게도 세팅해뒀습니다. 

 

이유를 모르겠는데, 혹시 경험 많으신분, 짐작가는 이유가 있으신지 궁금합니다.

 

추가로 어디를 봐야하는지 조언 주시면 감사하겠습니다...

 

 

댓글을 작성하려면 로그인이 필요합니다.

답변 5개

채택된 답변
+20 포인트
glitter0gim
10개월 전

"""짐작가는 이유가 있으신지 궁금합니다 """

IP 기반 접근 제한을 구현하기 전에 또는 코드가 작동하지 않았을 때,

공격자가 관리자 계정에 접근했을 수 있으며,

또한 권한 설정이 잘못되어 관리자 계정 정보가 포함된 설정 파일이나

세션 정보가 노출 되었을 수 있으며,

그누보드의 구조는 익히 알려져 있기에,

'IP 스푸핑'이나 'brute force' 공격을 당했을 가능성도 있습니다.

 

세션 로그아웃 이나 비밀번호 변경은 하셨을 것같고,

현재 IP 제한 코드의 정상 동작 여부를 확인하셨나요?

- 관리자 계정이 아닌 상태에서, 허용되지 않은 IP에서 관리자 페이지에 접속을 시도,

- 서버가 파일 캐시를 사용하는 경우, IP 제한 코드가 반영되지 않았을 가능성이 있으니,

  서버 캐시를 삭제하거나 재시작,

- IPv4와 IPv6 환경이 샤용되는 경우,

  코드가 IPv6 주소를 제대로 처리하지 못할 수 있으니, IPv6 주소도 테스트 등을 하여 보세요.

 

 

♠ 해결 방안

 

*관리자 경로를 예측하기 어려운 이름으로 변경(예: /admin-asdgshrtjkk345678wrg).

*.htaccess(apache) 또는 서버 설정에서 특정 IP만 관리자 페이지에 접근할 수 있도록 제한.

</p>

<p><Directory "/path/to/admin">

    Require ip 12.34.56.78

</Directory></p>

<p>

 

★ 아래는 관리자 페이지 접근, brute force 공격 등에 대응하여 

  제시하신 소스를 수정한 예시입니다.

</p>

<p><?php

if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가</p>

<p>if (session_status() == PHP_SESSION_NONE) {

    session_start();

}</p>

<p>define('ADMIN_LOG_FILE', '/var/log/admin_access.log');

define('FAILED_ATTEMPTS_FILE', '/var/log/failed_attempts.json');

define('MAX_LOGIN_ATTEMPTS', 5);

define('BLOCK_DURATION', 3600); // 차단 시간 (1시간)</p>

<p>// IP 검증 함수 (CIDR 지원)

function ip_in_allowed_range($ip, $allowed_ips) {

    foreach ($allowed_ips as $allowed_ip) {

        if (strpos($allowed_ip, '/') === false) {

            // 단일 IP 비교

            if ($ip === $allowed_ip) return true;

        } else {

            // CIDR 대역 비교

            [$subnet, $bits] = explode('/', $allowed_ip);

            $subnet_binary = ip2long($subnet);

            $ip_binary = ip2long($ip);

            $mask = -1 << (32 - $bits);

            if (($ip_binary & $mask) === ($subnet_binary & $mask)) return true;

        }

    }

    return false;

}</p>

<p>// 로그 기록 함수

function write_log($message) {

    $date = date('Y-m-d H:i:s');

    $log_entry = "[$date] $message\n";

    file_put_contents(ADMIN_LOG_FILE, $log_entry, FILE_APPEND | LOCK_EX);

}</p>

<p>// 실패 시도 기록 및 차단 처리

function block_ip($ip) {

    $failed_attempts = is_file(FAILED_ATTEMPTS_FILE) ? json_decode(file_get_contents(FAILED_ATTEMPTS_FILE), true) : [];

    $current_time = time();</p>

<p>    if (!isset($failed_attempts[$ip])) {

        $failed_attempts[$ip] = ['count' => 0, 'last_attempt' => $current_time];

    }</p>

<p>    $failed_attempts[$ip]['count']++;

    $failed_attempts[$ip]['last_attempt'] = $current_time;</p>

<p>    // 차단 처리

    if ($failed_attempts[$ip]['count'] > MAX_LOGIN_ATTEMPTS && ($current_time - $failed_attempts[$ip]['last_attempt'] < BLOCK_DURATION)) {

        exit('Your IP has been temporarily blocked.');

    }</p>

<p>    file_put_contents(FAILED_ATTEMPTS_FILE, json_encode($failed_attempts));

}</p>

<p>// 관리자 접근 제한 함수

function admin_access() {

    global $is_admin;</p>

<p>    $allowed_ips = [

        '12.34.56.78',       // 단일 IP

        '192.168.1.0/24',    // CIDR 대역

    ];</p>

<p>    $user_ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'];

    $user_browser = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown';

    $session_success_log = 'admin_login_success_logged_' . $user_ip;</p>

<p>    // 실패 시도 감지 및 차단

    block_ip($user_ip);</p>

<p>    if ($is_admin && ip_in_allowed_range($user_ip, $allowed_ips)) {

        // 세션 IP 바인딩

        if (!isset($_SESSION['user_ip'])) {

            $_SESSION['user_ip'] = $user_ip;

        } elseif ($_SESSION['user_ip'] !== $user_ip) {

            session_destroy();

            exit('Session invalid due to IP mismatch.');

        }</p>

<p>        // 성공 로그

        if (!isset($_SESSION[$session_success_log])) {

            $message = "LOGIN_SUCCESS - IP: $user_ip - Browser: $user_browser";

            write_log($message);

            $_SESSION[$session_success_log] = true;

        }

    } else {

        // 실패 로그

        $request_method = $_SERVER['REQUEST_METHOD'];

        $request_uri = $_SERVER['REQUEST_URI'];

        $message = "LOGIN_FAILURE - IP: $user_ip - Browser: $user_browser - Method: $request_method - URI: $request_uri";

        write_log($message);</p>

<p>        // 차단 처리

        block_ip($user_ip);</p>

<p>        exit('Unauthorized access.');

    }

}</p>

<p>// admin_access 함수 호출

admin_access();

?></p>

<p>

 

 

로그인 후 평가할 수 있습니다

댓글을 작성하려면 로그인이 필요합니다.

10개월 전

관리자 계정이 털렸다는게

타 아이디로 관리자 권한 탈취 인가요.

아니면 순수 관리자 계정이 털렸다는건가요?

명확하게 현재 상황을 적어주셔야 합니다.

 

후자라면 

$is_admin 변수로 하지 마시고 $member['mb_level'] 로 채크 하세요.

패스워드는 가급적 길게 영문 숫자 특수문자 조합으로 재설정 하시구요

 

그리고 해당 PC 가 털린건 아닌지도 채크해보시구요

로그인 후 평가할 수 있습니다

댓글을 작성하려면 로그인이 필요합니다.

R
10개월 전

그누보드로는 해킹방어에 구조상 한계가 있습니다.

특히나, 플러그인 및 공개되어 있는 소스들의 짜집기라면...

그중 하나는 비집고 해킹할 부분이 존재한다고 봐야겠죠.

전문가와 상의하세요.

로그인 후 평가할 수 있습니다

댓글을 작성하려면 로그인이 필요합니다.

10개월 전

1. 관리자 계정이 털렸다는 건 어떻게 아신건지요 ?
 $log_file = '/var/www/html/admin.log'; 에서 확인하셨나요?

아니면, 저기에는 아무런 내용이 없는 건가요 ?

 

만약 저기에 내용이 기록이 되어있다면, 아이피 + 2차 인증 같은걸 고민해보셔야 합니다.

그래야 조금 더 보안적으로 좋을 듯 합니다. 

 

그렇지만, 저기에 기록이 없다면... ? 무슨 내용을 확인하신 건지 조금 궁금하네요

 

 

2. 최신 버전이 아니네요 항상 최신 버전을 유지하시는 게 좋습니다.

로그인 후 평가할 수 있습니다

답변에 대한 댓글 3개

라젠더
10개월 전
1. 관리자 계정으로 글을 작성했더라구요. 그래서 알게되었습니다.
2. 아미나를 사용중이라 버전업이 어렵네요 ㅠㅠ
미니님a
10개월 전
1. 관리자 계정 자체로 글 작성은 되었지만, 저기 로그 기록에는 없다면, 여러가지를 고려해야 할 것 같습니다

그 단적인 예로 애초에 로그인이 막혔는데 글이 등록되었다면,
글 등록 관련을 조사(?)해야 할 것 같구요

mysql 같은 디비 비밀번호가 열린 건 아닌지 확인해보셔야 합니다.
혹시라도 서버에 비정상적인 파일이 없는지 새롭게 추가된 파일은 없는지도 한번 확인해보세요

2. 아미나 사용자가 아니라 정확한 답변은 아니지만, 애초에 "그누보드 기반"이라서, 그누보드 업데이트 부분만 패치 하면 될텐데요 ?
미니님a
10개월 전
이메일 2차 인증 혹은 OTP 를 구현하시는게 가장 좋을 듯 합니다.

댓글을 작성하려면 로그인이 필요합니다.

웅푸
10개월 전
로그인 후 평가할 수 있습니다

답변에 대한 댓글 1개

라젠더
10개월 전
감사합니다. 이미 적용해두고 있었어요...

댓글을 작성하려면 로그인이 필요합니다.

답변을 작성하려면 로그인이 필요합니다.

로그인