ClickCease 취약점 뒤에 숨겨진 버그 3부

콘텐츠 표

인기 뉴스레터 구독하기

4,500명 이상의 Linux 및 오픈소스 전문가와 함께하세요!

한 달에 두 번. 스팸이 없습니다.

취약점 뒤에 숨겨진 버그 3부

조아오 코레이아

2023년 1월 3일 - 기술 에반젤리스트

이번 글은 5부로 구성된 블로그 시리즈 중 3부로서, 매일 나타나는 취약점을 유발하는 코드 버그를 살펴봅니다. 이번 파트에서는 다음과 같은 버그 중 15번부터 11번까지를 다룹니다. Mitre CWE 상위 25개 목록의 15번부터 11번까지의 버그를 다루며, 여기에는 모두가 가장 좋아하는 11번 버그인 "NULL 포인터 역참조"가 포함됩니다.

여기에서 찾을 수 있습니다. 파트 1파트 2는 여기에서.

15 - 하드 코딩된 자격 증명 사용

이 취약성 등급으로 분류된 문제는 컴파일된 패키지에 자격 증명을 포함하거나 최종 사용자에게 도달하는 배포 미디어 또는 저장 장치에 자격 증명을 포함할 때 발생합니다. 즉, 이러한 자격 증명이 발견되면 모든 동일한 장치에서 작동할 수 있습니다. 이는 코딩 자체의 버그라기보다는 패키징 및 릴리스와 관련된 문제입니다.

이러한 문제가 발생할 수 있는 방법은 여러 가지가 있습니다. CI/CD 중에 하드코딩된 테스트 자격 증명 사용 동안 하드코딩된 테스트 자격 증명을 사용하거나(예를 들어 전용 인증 토큰을 사용하는 것과는 대조적으로), 프로덕션 디바이스에 어떤 형태의 프로덕션 디바이스에 대한 백도어 포함하지 않습니다.

이러한 유형의 문제는 일단 발견되면 해결하기가 매우 어려울 수 있습니다. 하드웨어 장치에서 이러한 문제가 발생하는 경우 하드코딩된 자격 증명을 제거하는 유일한 방법은 펌웨어를 업데이트하는 것입니다. 이 작업의 난이도는 특정 장치에 따라 다릅니다.

그러나 이러한 문제는 하드웨어뿐만 아니라 소프트웨어 패키지에서도 발생할 수 있으며 실제로 발생하고 있습니다. 이 경우 자격 증명이 실제 소프트웨어 패키지에 구워져 있으므로 일반적으로 문제를 해결하려면 업데이트된 버전이 필요합니다.

또한 소프트웨어나 하드웨어를 리버스 엔지니어링할 때 이러한 유형의 크리덴셜을 비교적 쉽게 발견할 수 있기 때문에 악의적인 공격자들은 이러한 크리덴셜을 발견하는 것을 매우 좋아합니다.

하드코딩된 자격 증명의 진짜 문제는 해당 시스템에서 설정할 수 있는 모든 액세스 제한을 무효화한다는 것입니다. 하드코딩된 관리자 자격 증명이 존재하면 다른 관리자 계정을 만들지 않아도 하드코딩된 계정으로 로그인할 수 있습니다.

아키텍처 수준에서 하드코딩된 자격 증명은 시스템 간의 연결을 구성할 때에도 나타나며, 연결 문자열 또는 비밀번호는 연결을 설정하기 위해 읽는 구성 파일에 저장됩니다. 해당 파일을 발견한 공격자는 연결을 가장할 수 있습니다.

14 - 부적절한 인증

부적절한 인증이란 애플리케이션이 일련의 자격 증명, 토큰 또는 기타 인증 메커니즘을 검증하는 대신 특정 신원을 가지고 있다고 주장하는 클라이언트를 신뢰하는 상황을 말합니다.

이는 사이트에 대한 인증을 검증하는 것이 아니라 쿠키의 존재를 검증하는 웹사이트 연결을 처리할 때 매우 일반적입니다. 쿠키를 위조하여 이미 인증되었다고 주장할 수 있으며, 웹사이트는 이를 신뢰하게 됩니다.

또 다른 일반적인 시나리오는 인증이 클라이언트 측에서만 유효성을 검사하는 경우로, 공격자가 이미 인증되었다고 주장하여 클라이언트 소프트웨어를 수정하거나 간섭하고 서버 측 리소스에 액세스할 수 있습니다.

13 - 정수 오버플로 또는 래퍼라운드

이것은 소프트웨어 버그의 가장 초창기 형태 중 하나입니다. 주어진 컴퓨터 아키텍처(32/64비트, ARM, 밉스 등)에서 모든 변수는 주어진 양의 메모리를 차지합니다. 메모리의 양이 많을수록 해당 변수에 저장할 수 있는 값도 커집니다. 그러나 산술 연산을 수행할 때 이러한 연산 결과가 변수의 메모리 크기에 맞는지 확인하는 것을 놓치기 쉽습니다. 예를 들어 두 정수를 곱하고 그 결과를 저장하면 값이 너무 커져 더 이상 최대 변수 크기에 맞지 않을 수 있습니다. 이런 일이 발생하면 정수가 '오버플로'됩니다. 

컴퓨터 아키텍처에 따라 이 이벤트를 처리하는 방식이 다릅니다. 어떤 아키텍처는 여분의 값을 삭제하여 잘못된 값을 저장하고, 어떤 아키텍처는 사용 가능한 다음 메모리 위치에 값을 기록하여 이미 저장된 다른 값을 덮어쓸 수 있으며, 어떤 아키텍처는 최대 값에 도달하면 0으로 돌아가는 자동차의 주행 거리 카운터처럼 값을 랩어라운드하여 계속 주행합니다.

변수가 루프의 반복 횟수를 제어한다고 가정하면 무한 루프로 이어질 수 있습니다. 다른 시나리오에서는 충돌, 응답하지 않는 애플리케이션 또는 응답하지 않는 서비스를 유발할 수 있으며, 사용 가능한 메모리를 소진하거나 전반적인 시스템 불안정성을 초래할 수도 있습니다. 또한 버퍼 오버플로 시나리오로 직접 이어질 수도 있습니다(이 목록의 다른 버그에서 다루고 있음).

많은 컴퓨터 언어에는 기본적으로 바운드 검사가 포함되어 있지 않으며 컴파일러 플래그를 통해 또는 특정 라이브러리를 사용하여 명시적으로 활성화해야 하므로, 계획 중인 애플리케이션에 사용할 언어를 신중하게 결정하는 것이 중요합니다. 

이미 개발된 애플리케이션에서 이러한 현상이 발견되면 적절한 바운드 검사를 추가하여 계산이 사용 가능한 가변 메모리 크기를 벗어나지 않는지 검증해야 하며, 애플리케이션이 크로스 플랫폼인 경우 플랫폼/아키텍처마다 허용되는 크기가 다르다는 점을 항상 기억해야 합니다.

퍼저와 같은 도구는 일반적으로 이러한 기능을 트리거하므로 개발자에게 큰 도움이 될 수 있습니다.

애플리케이션의 흐름에서 이러한 유형의 버그에 취약한 상황을 찾는 것은 공격자가 수행하는 가장 기본적인 작업 중 하나입니다. 

12 - 신뢰할 수 없는 데이터의 역직렬화

직렬화(또는 마샬링)는 메모리에 저장된 데이터를 다른 시스템/애플리케이션/컴포넌트에 저장하거나 네트워크 연결을 통해 전송할 수 있는 형식으로 변환하는 프로세스입니다. 역직렬화(또는 언마샬링)는 데이터 스트림을 애플리케이션의 내부 객체('객체'는 변수 또는 변수 그룹을 의미)의 메모리 표현으로 변환하는 역방향 프로세스입니다.

애플리케이션이 파일에서 읽거나 웹사이트의 양식 제출에서 수락하거나 네트워크 연결을 통해 수신하는 데이터가 본질적으로 유효하다고 맹목적으로 신뢰한 후 특정 메모리 위치로 역직렬화하려고 시도하면 애플리케이션이 위험에 노출될 수 있습니다. 특수하게 조작된 데이터 스트림은 애플리케이션을 속여 의도한 위치가 아닌 기존 메모리 위치를 덮어쓰거나 모든 변수를 채우기에 충분한 데이터를 제공하지 않아 초기화되지 않은 변수가 발생하거나 예상 범위를 벗어난 데이터를 전송할 수 있으며, 이는 모두 예기치 않은 이벤트로 이어질 수 있습니다.

개발 경험상 어떤 입력도 예외 없이 신뢰해서는 안 됩니다. 개발자가 클라이언트와 서버 애플리케이션을 직접 작성하더라도 연결을 파괴하거나, 둘 사이의 통신을 변조하거나, 대화의 한 쪽을 가장하는 시도가 항상 가능하며, 이로 인해 수신되는 데이터를 더 이상 신뢰할 수 없으므로 역직렬화해서는 안 되는 시나리오가 발생할 수 있습니다.

다음 Java 코드는 충분히 정상적으로 보입니다:

try {
File file = new File("object.obj");
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
javax.swing.JButton button = (javax.swing.JButton) in.readObject();
in.close();
}


[source: https://cwe.mitre.org/data/definitions/502.html]

 

그러나 자세히 살펴보면 "object.obj" 파일은 전혀 유효성이 검사되지 않았으며 애플리케이션이 예상한 대로 버튼 정의가 아닌 다른 것을 포함할 수 있습니다.

11 - NULL 포인터 역참조

이는 C와 같이 포인터 친화적인 언어로 작성된 애플리케이션에서 발견되는 가장 일반적인 취약점 중 하나로, 이전에 무효화되었거나 아직 초기화되지 않은 포인터가 참조하는 값을 가져오려고 시도하는 것으로 구성됩니다.

아키텍처상의 차이를 제외하면 일반적인 결과는 운영 체제가 해당 애플리케이션의 허용된 메모리 공간을 벗어난 메모리 위치를 읽으려는 시도를 감지하여 애플리케이션이 충돌하는 것입니다.

다음 C# 샘플은 이 문제를 설명합니다:

 

string name;
int k=0;
if(k==1)
{
  name=Console.ReadLine();
}
Console.Writeline(name.Length);

실행이 마지막 호출에 도달하면 "Length" 메서드가 널 문자열에 대해 호출되어 애플리케이션이 충돌합니다.

많은 언어가 개발 프로세스 초기에 이러한 유형의 오류를 포착하기 위한 변경 사항(널 가능 유형, 컴파일러 플래그, 새로운 경고 유형)을 도입했다는 점은 흥미롭습니다. 다른 언어에서는 이를 방지하기 위해 포인터를 명시적으로 사용하지 않는 경우도 있지만, 신중한 프로그래밍 관행 외에는 NULL 포인터 역참조를 찾아내고 방지하는 데 100% 효과적인 만병통치약은 존재하지 않습니다.

버그 #12에 설명된 것과 같은 상황에서는 애플리케이션의 흐름에 예기치 않은 값이 도입되어 NULL 포인터 역참조가 트리거될 수 있습니다.

이 버그에 대한 비교적 간단한 수정 방법은 적절한 변수 사용 시 항상 NULL이 아닌지 확인하는 검사를 추가하는 것이지만, 동시/병렬 프로그래밍에서는 이 방법이 최적의 해결책이 아닐 수 있습니다.

 

요약
취약점 뒤에 숨겨진 버그 3부
기사 이름
취약점 뒤에 숨겨진 버그 3부
설명
5부작으로 구성된 블로그 시리즈 중 3부에서는 매일 나타나는 취약점을 유발하는 코드 버그에 대해 살펴봅니다.
작성자
게시자 이름
TuxCare
게시자 로고

Kernel 재부팅, 시스템 다운타임 또는 예정된 유지 보수 기간 없이 취약성 패치를 자동화하고 싶으신가요?

TuxCare로 라이브 패치에 대해 알아보기

TuxCare 게스트 작가 되기

시작하기

메일

가입

4,500

Linux & 오픈 소스
전문가!


뉴스레터 구독하기