본문 바로가기

Hack The Box CBBH

CBBH - Web Attacks[IDOR Prevention]

IDOR 취약점이 주로 백엔드 서버의 접근 제어 시스템 설정이 부족하다는 것을 이해할 수 있다. 이러한 취약점을 방지하려면 Object-Level Access Control(객체 수준의 접근 제어) 시스템을 구축하고, 객체를 저장하고 호출할 때 보안 참조 방식을 사용해야 한다.

Object-Level Access Control

접근 제어 시스템은 웹 애플리케이션의 핵심 요소이며, 웹 애플리케이션의 전체 설계와 구조에 영향을 미칠 수 있다. 따라서, 웹 애플리케이션의 각 영역을 적절하게 제어하려면 역할(Role)과 권한(Permission)을 중앙에서 관리하는 구조를 지원해야 한다.

접근 제어는 매우 광범위한 개념이지만, IDOR 취약점과 관련된 객체 수준의 접근 제어(Object-Level Access Control) 메커니즘에 초점을 맞춰 살펴보려 한다.

Role-Based Access Control

사용자 역할 및 권한은 모든 접근 제어 시스템의 핵심 요소로, 이러한 개념을 제대로 구현한 시스템이 RBAC(Role-Based Access Control)이다. IDOR 취약점을 방지하려면 RBAC 시스템을 모든 객체 및 리소스와 매핑해야 한다.

즉, 백엔드 서버는 요청자가 해당 객체에 접근할 권한이 있는지 확인한 후 요청을 허용 또는 차단해야 한다.

RBAC가 구현되면, 각 사용자는 특정 역할과 권한을 할당받게 된다. 사용자가 요청을 보낼 때 마다 RBAC 시스템이 해당 역할과 권한을 검증해 객체 접근을 허용할지 결정한다.

RBAC 시스템을 웹 애플리케이션의 객체 및 리소스와 매핑하는 방법에는 여러 가지가 있으며, 웹 애플리케이션의 구조에 깊이 통합하는 것이 중요하다. 다음은 웹 애플리케이션에서 사용자 역할을 객체와 비교해 접근을 허용하거나 차단하는 예제 코드이다.

match /api/profile/{userId} {
    allow read, write: if user.isAuth == true
    && (user.uid == userId || user.roles == 'admin');
}

위의 예는 사용자의 다양한 역할과 권한을 검색하기 위해 RBAC에 대한 HTTP 요청에서 매핑할 수 있는 사용자 토큰을 사용한다. 그런 다음 RBAC 시스템에서 사용자의 UID가 요청하는 API 엔드포인트에서 UID와 일치하는 경우에만 읽기/쓰기 접근만 허용한다. 또한 사용자가 백엔드 RBAC에서 관리자 역할을 하는 경우 읽기/쓰기 접근이 허용된다.

이전의 공격에서 우리는 사용자의 세부 사항이나 쿠키에 사용자의 역할이 저장되는 예시를 보았다. 둘 다 사용자의 제어가 가능했고, 접근 권한을 확대하도록 조작할 수 있었다. 위에 코드는 사용자 권한이 HTTP 요청을 통해 전달되지 않았지만 사용자의 로그인 세션 토큰을 인증 메커니즘으로 사용해 백엔드의 RBAC가 직접 매핑되었으므로 사용자 역할을 매핑하는 데 더 안전한 접근 방식을 보여준다.

제어 시스템과 RBAC에 접근하는 데 더 많은 것이 있다. 디자인하기 가장 어려운 시스템이 될 수 있다. 그러나 웹 응용 프로그램의 객체 및 리소스를 통해 사용자 접근을 어떻게 제어 해야하는지에 대한 아이디어를 떠올려야 한다.

Object Referencing

IDOR의 핵심 문제는 부적절한 접근 제어(Broken Access Control)이다. 그러나 객체를 직접 참조할 수 있다면 접근 제어 취약점을 열거하고 악용할 수 있는 가능성이 높아진다.

직접 객체 참조를 사용할 수 있지만, 반드시 강력한 접근 제어 시스템이 구축된 경우에만 안전하게 활용할 수 있다. 강력한 접근 제어 시스템을 구축한 후에도, 객체 참조를 평문이나 단순한 패턴으로 사용해서는 안된다. 항상 Salted Hashes 또는 uuid와 같은 고유한 참조 방식을 사용해야 한다.

예를 들어, UUID V4를 사용해 강력한 난수 기반의 ID를 생성할 수 있으며, 그 예시는 다음과 같다. 89c9b29b-d19f-4515-b2dd-abb6e693eb20 이 uuid를 백엔드 데이터베이스에서 특정 객체와 매핑하면, 이 uuid가 호출될 때 백엔드 데이터베이스가 올바른 객체를 반환하도록 설정할 수 있다.

다음 코드는 PHP로 이것이 어떻게 작동하는지 볼 수 있는 코드이다.

$uid = intval($_REQUEST['uid']);
$query = "SELECT url FROM documents where uid=" . $uid;
$result = mysqli_query($conn, $query);
$row = mysqli_fetch_array($result));
echo "<a href='" . $row['url'] . "' target='_blank'></a>";

또한, 해시를 프론트엔드에서 계산하면 안된다. 객체가 생성될 때 해시를 백엔드에서 생성하고 데이터베이스에 저장해야 한다. 이후, 데이터베이스에서 객체 참조 맵을 구축해 빠르게 참조할 수 있도록 설계해야 한다.

마지막으로, uuid를 사용하면 IDOR 취약점 테스트가 더욱 어려워질 수 있다. uuid를 사용하면 공격자가 참조 ID를 무작위로 추측하는 것이 어렵기 때문에, IDOR 취약점이 숨겨질 가능성이 있다. 그래서 객체 참조는 접근 제어 시스템을 탄탄하게 만들고 나서, 이루어져야 한다.