웹 애플리케이션 내 API는 중요한 공격 표면 중 하나입니다. 프론트엔드가 견고해도 백엔드 API에 취약점이 존재하면 시스템 장악이 가능하기 때문입니다.

1. API 정찰(Recon)

API 테스팅의 첫 단계는 대상 API에 대한 정보를 최대한 많이 수집하는 것입니다. 가장 먼저 해야 할 일은 API 엔드포인트 식별입니다. 엔드포인트란 API가 서버의 특정 리소스에 대한 요청을 수신하는 위치를 말합니다.

간단한 예시를 보겠습니다.

GET /api/books HTTP/1.1
Host: example.com

여기서 API 엔드포인트는 /api/books이며, 책 목록을 검색하기 위해 API와 상호작용합니다. 다른 API 엔드포인트로 미스터리 책 목록을 검색하는 요청도 생각해볼 수 있습니다.

GET /api/books/mystery
Host: example.com

엔드포인트를 식별한 다음에는 상호작용 방법을 파악해야 합니다. 이를 통해 API를 테스트하기 위한 유효한 HTTP 요청을 구성할 수 있습니다. 구체적으로 파악해야 할 정보는 다음과 같습니다.

  • API가 처리하는 입력 데이터 (필수 및 선택적 파라미터 모두 포함)
  • API가 허용하는 요청 유형 (지원하는 HTTP 메서드 및 미디어 형식 포함)
  • 속도 제한 및 인증 메커니즘

2. API 문서 활용하기

API는 일반적으로 문서화되어 있으며, 문서는 크게 두 가지 형식으로 제공됩니다.

사람이 읽을 수 있는 문서는 개발자가 API 사용 방법을 이해할 수 있도록 설계되었습니다. 여기에는 자세한 설명, 예제 및 사용 시나리오가 포함될 수 있습니다. 기계가 읽을 수 있는 문서는 API 통합 및 검증과 같은 작업을 자동화하기 위해 소프트웨어에서 처리하도록 설계되었으며, JSON 또는 XML과 같은 구조화된 형식으로 작성됩니다.

API 문서가 외부 개발자가 사용하도록 의도적으로 공개된 경우가 많으므로, 항상 문서를 검토하는 것으로 정찰을 시작하는 것이 좋습니다.

API 문서 발견하기

공개적으로 API 문서가 제공되지 않더라도 API를 사용하는 애플리케이션을 탐색하여 접근할 수 있는 경우가 많습니다. API 문서를 참조할 수 있는 대표적인 엔드포인트는 다음과 같습니다.

  • /api
  • /swagger/index.html
  • /openapi.json

리소스에 대한 엔드포인트를 식별한 경우 기본 경로를 조사해야 합니다. 예를 들어, 리소스 엔드포인트 /api/swagger/v1/users/123을 식별했다면 다음과 같은 경로를 차례로 조사해볼 수 있습니다.

  • /api/swagger/v1
  • /api/swagger
  • /api

BurpSuite의 Intruder를 사용하여 문서를 자동으로 찾을 수도 있습니다.

기계가 읽을 수 있는 문서 활용

기계가 읽을 수 있는 API 문서를 발견했다면, 분석을 위해 자동화 도구를 활용할 수 있습니다. Burp Scanner를 사용하여 OpenAPI 문서 또는 JSON이나 XML 형식의 기타 문서를 크롤링하고 감사할 수 있으며, OpenAPI Parser BApp을 사용하여 OpenAPI 문서를 구문 분석할 수도 있습니다. Postman 또는 SoapUI와 같은 전문 도구를 사용하여 문서화된 엔드포인트를 직접 테스트하는 것도 효과적입니다.

3. 실습: 문서를 사용하여 API 엔드포인트 악용하기

목표: 노출된 API 문서를 찾아 carlos 계정을 삭제하기

제공 계정: wiener:peter

이 랩을 해결하려면 API 문서가 무엇인지, API 문서가 공격자에게 어떻게 유용할 수 있는지, 그리고 API 문서를 발견하는 방법을 알아야 합니다.

/api/ 엔드포인트로 접근하니 사용자가 접근할 수 있는 REST API 문서가 노출되었습니다. 문서를 확인해보면 DELETE 메서드를 사용하고 엔드포인트로 /api/user/[username]을 전송하면 실제로 해당 사용자가 삭제될 것으로 보입니다.

실제로 DELETE /api/user/carlos 요청을 전송하니 계정이 삭제되었습니다. API 문서가 인증 없이 외부에 노출되면, 공격자가 모든 기능을 파악하고 악용할 수 있다는 점을 보여주는 사례입니다.

4. API 엔드포인트 식별과 상호작용

API를 사용하는 애플리케이션을 탐색하면 많은 정보를 수집할 수 있습니다. API 문서에 액세스할 수 있는 경우에도 이 과정은 종종 가치가 있습니다. 문서가 부정확하거나 오래된 경우가 있기 때문입니다.

Burp Scanner를 사용하여 애플리케이션을 크롤링한 다음, Burp의 브라우저를 사용하여 흥미로운 공격 표면을 수동으로 조사할 수 있습니다. 애플리케이션을 탐색하는 동안 URL 구조에서 /api/와 같이 API 엔드포인트를 나타내는 패턴을 찾아야 합니다.

JavaScript 파일도 주의 깊게 살펴봐야 합니다. 이러한 파일에는 웹 브라우저를 통해 직접 트리거하지 않은 API 엔드포인트에 대한 참조가 포함될 수 있습니다. Burp Scanner는 크롤링 중에 일부 엔드포인트를 자동으로 추출하지만, 보다 강력한 추출을 위해서는 JS Link Finder BApp을 사용하는 것이 좋습니다. Burp에서 JavaScript 파일을 수동으로 검토할 수도 있습니다.

API 엔드포인트와 상호작용하기

API 엔드포인트를 식별한 후에는 Burp Repeater 및 Burp Intruder를 사용하여 상호작용합니다. 이를 통해 API의 동작을 관찰하고 추가 공격 표면을 발견할 수 있습니다. 예를 들어, HTTP 메서드와 미디어 타입을 변경할 때 API가 어떻게 응답하는지 조사할 수 있습니다.

API 엔드포인트와 상호작용하면서 오류 메시지 및 기타 응답을 면밀히 검토해야 합니다. 때때로 이러한 정보에는 유효한 HTTP 요청을 구성하는 데 사용할 수 있는 정보가 포함되어 있습니다.

지원되는 HTTP 메서드 식별하기

HTTP 메서드는 리소스에 대해 수행할 작업을 지정합니다. 대표적인 메서드는 다음과 같습니다.

  • GET - 리소스에서 데이터를 검색합니다.
  • PATCH - 리소스에 부분적인 변경 사항을 적용합니다.
  • OPTIONS - 리소스에 사용할 수 있는 요청 메서드 유형에 대한 정보를 검색합니다.

API 엔드포인트는 서로 다른 HTTP 메서드를 지원할 수 있으므로, 엔드포인트를 조사할 때 모든 잠재적인 메서드를 테스트하는 것이 중요합니다. 이를 통해 추가 엔드포인트 기능을 식별하여 더 많은 공격 표면을 열 수 있습니다.

예를 들어, 엔드포인트 /api/tasks는 다음 메서드를 지원할 수 있습니다.

  • GET /api/tasks - 작업 목록을 검색합니다.
  • POST /api/tasks - 새 작업을 생성합니다.
  • DELETE /api/tasks/1 - 작업을 삭제합니다.

Burp Intruder의 내장 HTTP 동사 목록을 사용하여 다양한 메서드를 자동으로 순환할 수 있습니다.

참고: 다양한 HTTP 메서드를 테스트할 때는 우선순위가 낮은 객체를 대상으로 하세요. 이는 중요한 항목을 변경하거나 과도한 레코드를 생성하는 등의 의도하지 않은 결과를 피하는 데 도움이 됩니다.

지원되는 콘텐츠 유형 식별하기

API 엔드포인트는 종종 특정 형식의 데이터를 예상합니다. 따라서 요청에 제공된 데이터의 콘텐츠 유형에 따라 다르게 동작할 수 있습니다. 콘텐츠 유형을 변경하면 다음과 같은 것들이 가능합니다.

  • 유용한 정보를 공개하는 오류를 트리거할 수 있습니다.
  • 결함이 있는 방어를 우회할 수 있습니다.
  • 처리 로직의 차이를 활용할 수 있습니다. 예를 들어, API가 JSON 데이터를 처리할 때는 안전하지만 XML을 처리할 때는 인젝션 공격에 취약할 수 있습니다.

콘텐츠 유형을 변경하려면 Content-Type 헤더를 수정한 다음 그에 따라 요청 본문을 다시 포맷하면 됩니다. Content Type Converter BApp을 사용하면 요청 내에서 제출된 데이터를 XML과 JSON 간에 자동으로 변환할 수 있습니다.

5. 실습: 사용되지 않는 API 엔드포인트 찾기 및 악용하기

목표: 숨겨진 API 엔드포인트를 악용하여 Lightweight l33t Leather Jacket 구매하기

제공 계정: wiener:peter

이 랩을 해결하려면 오류 메시지를 사용하여 유효한 요청을 구성하는 방법, RESTful API에서 HTTP 메서드가 사용되는 방법, 그리고 HTTP 메서드를 변경하여 추가 기능을 공개하는 방법을 알아야 합니다.

먼저 상품 상세보기로 시작합니다.

/api 경로를 가진 엔드포인트가 존재하며, 해당 요청의 메서드를 OPTIONS로 변경 후 전송하면 GET, PATCH 메서드를 허용하는 것을 볼 수 있습니다.

PATCH로 변경하여 전송하니 응답 값의 오류로 어떤 값이 필요한지 알려줍니다. 이번에는 Content-Type 값을 application/json으로 설정합니다.

이번에는 Body에 price 파라미터가 존재하지 않다는 오류가 발생합니다. 오류 메시지가 공격자에게 유효한 요청 구성 방법을 단계적으로 알려주고 있는 것입니다.

해당 파라미터 값에 원하는 임의의 가격으로 설정한 뒤 전송하면 가격이 설정된 것을 볼 수 있습니다. 웹 사이트에서 다시 확인해보겠습니다.

1번 상품의 가격이 이전 요청과 같이 0으로 설정된 것을 볼 수 있습니다.

변경된 값으로 실제 결제까지 가능한 것을 볼 수 있습니다. OPTIONS 메서드를 통해 숨겨진 PATCH 기능을 발견하고, 오류 메시지를 단서로 유효한 요청을 단계적으로 구성한 것이 핵심입니다.

6. Intruder를 사용한 숨겨진 엔드포인트 탐색

초기 API 엔드포인트를 식별한 후에는 Intruder를 사용하여 숨겨진 엔드포인트를 발견할 수 있습니다. 예를 들어, 사용자 정보를 업데이트하기 위한 다음 API 엔드포인트를 식별한 시나리오를 고려해보겠습니다.

PUT /api/user/update

숨겨진 엔드포인트를 식별하려면 Burp Intruder를 사용하여 동일한 구조를 가진 다른 리소스를 찾을 수 있습니다. 예를 들어, 경로의 /update 위치에 deleteadd와 같은 다른 일반적인 기능 목록을 페이로드로 추가할 수 있습니다.

숨겨진 엔드포인트를 찾을 때는 일반적인 API 명명 규칙 및 업계 용어를 기반으로 한 워드리스트를 사용하는 것이 좋습니다. 또한 초기 정찰을 기반으로 애플리케이션과 관련된 용어도 포함해야 합니다.

7. Mass Assignment 취약점

Mass Assignment(대량 할당, auto-binding이라고도 함)은 의도치 않게 숨겨진 파라미터를 생성할 수 있습니다. 이는 소프트웨어 프레임워크가 요청 파라미터를 내부 객체의 필드에 자동으로 바인딩할 때 발생합니다. 따라서 Mass Assignment는 개발자가 처리할 의도가 없었던 파라미터를 애플리케이션이 지원하게 만들 수 있습니다.

숨겨진 파라미터 식별

Mass Assignment는 객체 필드로부터 파라미터를 생성하므로, API가 반환하는 객체를 수동으로 검토하여 이러한 숨겨진 파라미터를 식별할 수 있는 경우가 많습니다.

예를 들어, 사용자가 사용자명과 이메일을 업데이트할 수 있는 PATCH /api/users/ 요청을 살펴보겠습니다. 이 요청에는 다음과 같은 JSON이 포함됩니다.

{
    "username": "wiener",
    "email": "wiener@example.com"
}

동시에 GET /api/users/123 요청은 다음과 같은 JSON을 반환합니다.

{
    "id": 123,
    "name": "John Doe",
    "email": "john@example.com",
    "isAdmin": "false"
}

이는 숨겨진 idisAdmin 파라미터가 업데이트되는 usernameemail 파라미터와 함께 내부 사용자 객체에 바인딩되어 있음을 나타낼 수 있습니다.

Mass Assignment 취약점 테스트

열거된 isAdmin 파라미터 값을 수정할 수 있는지 테스트하려면, PATCH 요청에 해당 파라미터를 추가합니다.

{
    "username": "wiener",
    "email": "wiener@example.com",
    "isAdmin": false
}

추가로, 유효하지 않은 isAdmin 파라미터 값을 포함한 PATCH 요청도 전송합니다.

{
    "username": "wiener",
    "email": "wiener@example.com",
    "isAdmin": "foo"
}

애플리케이션이 다르게 동작한다면, 이는 유효하지 않은 값이 쿼리 로직에 영향을 미치지만 유효한 값은 영향을 미치지 않음을 시사할 수 있습니다. 이는 해당 파라미터가 사용자에 의해 성공적으로 업데이트될 수 있음을 나타낼 수 있습니다.

그런 다음 isAdmin 파라미터 값을 true로 설정한 PATCH 요청을 전송하여 취약점을 악용해 볼 수 있습니다.

{
    "username": "wiener",
    "email": "wiener@example.com",
    "isAdmin": true
}

요청의 isAdmin 값이 적절한 검증 및 새니타이징 없이 사용자 객체에 바인딩된다면, 사용자 wiener는 부적절하게 관리자 권한을 부여받을 수 있습니다. 이를 확인하려면 wiener로 애플리케이션을 탐색하여 관리자 기능에 접근할 수 있는지 확인합니다.

8. 실습: Mass Assignment 취약점 악용

목표: Mass Assignment 취약점을 찾아 악용하여 Lightweight l33t Leather Jacket 구매하기

제공 계정: wiener:peter

구매를 원하는 상품을 카트에 이동시킵니다. 그런 뒤 카트로 이동하면 /api/checkout 경로로 GET 요청을 전송하게 됩니다.

이때 응답 값에 chosen_discount라는 파라미터와 함께 상품에 대한 정보가 전송되고 있습니다. 이 정보는 실제로 카트 내에서 결제 시 필요한 정보입니다.

카트에서 실제로 제품 구매를 시도해봅니다.

이때 기존에 존재하지 않았던 chosen_discount 변수와 함께 값으로 100을 입력한 뒤 전송하면 별도의 오류가 발생하지 않는 것을 볼 수 있습니다.

이 랩의 핵심은 GET 응답에서 노출된 chosen_discount 파라미터를 발견하고, 이를 POST 요청에 삽입하여 100% 할인을 적용시키는 것입니다. 서버 측에서 해당 파라미터에 대한 검증이 없기 때문에, 공격자가 임의로 할인율을 조작하여 무료로 상품을 구매할 수 있는 전형적인 Mass Assignment 취약점입니다.

9. API 취약점 예방

API를 설계할 때, 처음부터 보안을 고려 사항에 포함시켜야 합니다. 특히 다음 사항을 반드시 확인해야 합니다.

  • API를 공개적으로 접근 가능하게 할 의도가 없다면 문서를 보호해야 합니다.
  • 정당한 테스터가 API의 공격 표면을 완전히 파악할 수 있도록 문서를 최신 상태로 유지해야 합니다.
  • 허용된 HTTP 메서드의 허용 목록(allowlist)을 적용해야 합니다.
  • 각 요청 또는 응답에 대해 콘텐츠 타입이 예상된 것인지 검증해야 합니다.
  • 공격자에게 유용할 수 있는 정보를 노출하지 않도록 일반적인 오류 메시지를 사용해야 합니다.
  • 현재 프로덕션 버전뿐만 아니라 모든 버전의 API에 보호 조치를 적용해야 합니다.

Mass Assignment 취약점을 예방하려면, 사용자가 업데이트할 수 있는 속성을 허용 목록(allowlist)으로 관리하고, 사용자가 업데이트해서는 안 되는 민감한 속성을 차단 목록(blocklist)으로 관리해야 합니다.

10. 서버 측 파라미터 오염 (Server-Side Parameter Pollution)

일부 시스템에는 인터넷에서 직접 접근할 수 없는 내부 API가 포함되어 있습니다. 서버 측 파라미터 오염은 웹사이트가 적절한 인코딩 없이 사용자 입력을 내부 API에 대한 서버 측 요청에 포함시킬 때 발생합니다. 이는 공격자가 파라미터를 조작하거나 주입할 수 있게 되어, 예를 들어 다음과 같은 행위가 가능해질 수 있음을 의미합니다.

  • 기존 파라미터를 덮어쓰기
  • 애플리케이션 동작을 수정
  • 비인가 데이터에 접근

모든 종류의 파라미터 오염에 대해 모든 사용자 입력을 테스트할 수 있습니다. 예를 들어, 쿼리 파라미터, 폼 필드, 헤더, URL 경로 파라미터 모두 취약할 수 있습니다.

출처: https://portswigger.net/web-security/api-testing/images/api-testing-2.jpg

참고: 이 취약점은 때때로 HTTP 파라미터 오염이라고도 불립니다. 그러나 이 용어는 웹 애플리케이션 방화벽(WAF) 우회 기법을 지칭하는 데에도 사용됩니다. 혼동을 피하기 위해, 이 글에서는 서버 측 파라미터 오염이라고만 지칭하겠습니다. 또한, 이름이 유사함에도 불구하고, 이 취약점 클래스는 서버 측 프로토타입 오염과 공통점이 거의 없습니다.

쿼리 스트링에서 서버 측 파라미터 오염 테스트

쿼리 스트링에서 서버 측 파라미터 오염을 테스트하려면, 입력에 #, &, =와 같은 쿼리 구문 문자를 삽입하고 애플리케이션이 어떻게 응답하는지 관찰합니다.

사용자명을 기반으로 다른 사용자를 검색할 수 있는 취약한 애플리케이션을 예로 들어보겠습니다. 사용자를 검색하면, 브라우저는 다음과 같은 요청을 보냅니다.

GET /userSearch?name=peter&back=/home

사용자 정보를 가져오기 위해, 서버는 다음과 같은 요청으로 내부 API를 쿼리합니다.

GET /users/search?name=peter&publicProfile=true

쿼리 스트링 잘라내기 (Truncating)

URL 인코딩된 # 문자를 사용하여 서버 측 요청을 잘라내기를 시도할 수 있습니다. 응답을 해석하는 데 도움이 되도록, # 문자 뒤에 문자열을 추가할 수도 있습니다.

예를 들어, 쿼리 스트링을 다음과 같이 수정할 수 있습니다.

GET /userSearch?name=peter%23foo&back=/home

프론트엔드는 다음 URL에 접근하려고 시도합니다.

GET /users/search?name=peter#foo&publicProfile=true

참고: # 문자를 반드시 URL 인코딩해야 합니다. 그렇지 않으면 프론트엔드 애플리케이션이 이를 프래그먼트 식별자로 해석하여 내부 API로 전달되지 않습니다.

응답에서 쿼리가 잘렸는지에 대한 단서를 확인합니다. 예를 들어, 응답이 사용자 peter를 반환한다면 서버 측 쿼리가 잘렸을 수 있습니다. Invalid name 오류 메시지가 반환된다면, 애플리케이션이 foo를 사용자명의 일부로 처리했을 수 있습니다. 이는 서버 측 요청이 잘리지 않았을 수 있음을 시사합니다.

서버 측 요청을 잘라낼 수 있다면, publicProfile 필드가 true로 설정되어야 하는 요구 사항이 제거됩니다. 이를 악용하여 비공개 사용자 프로필을 반환받을 수 있을 가능성이 있습니다.

유효하지 않은 파라미터 주입

URL 인코딩된 & 문자를 사용하여 서버 측 요청에 두 번째 파라미터를 추가하려고 시도할 수 있습니다.

예를 들어, 쿼리 스트링을 다음과 같이 수정할 수 있습니다.

GET /userSearch?name=peter%26foo=xyz&back=/home

이는 내부 API에 대한 다음과 같은 서버 측 요청을 생성합니다.

GET /users/search?name=peter&foo=xyz&publicProfile=true

추가된 파라미터가 어떻게 파싱되는지에 대한 단서를 응답에서 확인합니다. 예를 들어, 응답이 변경되지 않았다면 파라미터가 성공적으로 주입되었지만 애플리케이션에 의해 무시되었음을 나타낼 수 있습니다. 보다 완전한 그림을 구축하려면 추가 테스트가 필요합니다.

유효한 파라미터 주입

쿼리 스트링을 수정할 수 있다면, 서버 측 요청에 두 번째 유효한 파라미터를 추가하려고 시도할 수 있습니다. 예를 들어, email 파라미터를 식별했다면 다음과 같이 쿼리 스트링에 추가할 수 있습니다.

GET /userSearch?name=peter%26email=foo&back=/home

이는 내부 API에 대한 다음과 같은 서버 측 요청을 생성합니다.

GET /users/search?name=peter&email=foo&publicProfile=true

추가된 파라미터가 어떻게 파싱되는지에 대한 단서를 응답에서 확인합니다.

기존 파라미터 덮어쓰기

애플리케이션이 서버 측 파라미터 오염에 취약한지 확인하려면, 원래 파라미터를 덮어쓰기를 시도할 수 있습니다. 동일한 이름의 두 번째 파라미터를 주입하여 이를 수행합니다.

예를 들어, 쿼리 스트링을 다음과 같이 수정할 수 있습니다.

GET /userSearch?name=peter%26name=carlos&back=/home

이는 내부 API에 대한 다음과 같은 서버 측 요청을 생성합니다.

GET /users/search?name=peter&name=carlos&publicProfile=true

내부 API는 두 개의 name 파라미터를 해석합니다. 이것의 영향은 애플리케이션이 두 번째 파라미터를 어떻게 처리하는지에 따라 달라지며, 이는 웹 기술마다 다릅니다.

  • PHP마지막 파라미터만 파싱합니다. 이 경우 carlos에 대한 사용자 검색이 수행됩니다.
  • ASP.NET두 파라미터를 결합합니다. 이 경우 peter,carlos에 대한 사용자 검색이 수행되어, Invalid username 오류 메시지가 발생할 수 있습니다.
  • Node.js / express첫 번째 파라미터만 파싱합니다. 이 경우 peter에 대한 사용자 검색이 수행되어, 변경되지 않은 결과가 반환됩니다.

원래 파라미터를 덮어쓸 수 있다면, 공격을 수행할 수 있을 가능성이 있습니다. 예를 들어, 요청에 name=administrator를 추가하면 관리자 사용자로 로그인할 수 있게 될 수 있습니다.


11. 실습: 쿼리 스트링에서 서버 측 파라미터 오염 악용

목표: administrator로 로그인하여 carlos 삭제하기

이 랩을 풀기 위해서는 URL 쿼리 구문을 사용하여 서버 측 요청을 변경하려고 시도하는 방법과, 오류 메시지를 사용하여 서버 측 API가 사용자 입력을 어떻게 처리하는지에 대한 이해를 구축하는 방법을 알아야 합니다.

1단계: 비밀번호 재설정 요청

로그인 창 내 비밀번호 찾기 버튼을 클릭합니다.

비밀번호 찾기를 원하는 계정명 administrator를 입력한 뒤 제출합니다.

이메일로 비밀번호 변경에 관한 내용이 전송된 것 같습니다.

2단계: 파라미터 오염 여부 확인

이전에 비밀번호 재설정 요청을 전송할 때 username 파라미터에 계정명을 입력한 뒤 전송했습니다. 이번에는 존재하지 않는 임의의 계정명을 입력한 뒤 전송하면 Invalid username 오류가 발생하고, 계정명을 서버에서 확인하고 있다는 것을 알 수 있습니다.

이번에는 & 문자와 함께 파라미터를 전송해보겠습니다. 서버 측 응답 값으로 Parameter is not supported 오류가 발생하는데, 이것은 내부 API가 &x=y를 별도의 파라미터로 해석했기 때문입니다. 이것으로 파라미터 오염이 가능하다는 증거를 발견했습니다.

이제 # 문자를 통해 쿼리 스트링을 잘라내도록 시도해봤습니다. 그러면 Field not specified라는 오류가 발생하는데, Field 파라미터가 추가적으로 존재했는데 #으로 인해 잘려나갔다는 의미입니다. 여기서 핵심은 내부 API에 우리가 모르고 있던 field라는 파라미터가 존재했다는 것을 알 수 있다는 점입니다.

이제 field 파라미터를 추가한 뒤 임의의 x 문자를 값으로 전송하면 Invalid field라는 오류가 발생합니다. field 파라미터에 들어갈 수 있는 값이 따로 존재할 것 같습니다.

비밀번호 재설정 요청 페이지에서 forgotPassword.js 파일 내부에 재설정 URL 내에서 reset_token이라는 파라미터를 추가로 전송하는 것을 알 수 있습니다. 이 값을 field 파라미터의 값으로 전송해보겠습니다.

서버 측 응답 값으로 administrator 계정의 비밀번호 재설정 토큰 값이 확인되는 것을 볼 수 있습니다. 이 값을 통해 재설정 페이지로 직접 이동해보겠습니다.

핵심은 오류 메시지를 단서로 내부 API 구조를 점진적으로 파악하고, 최종적으로 민감한 데이터(reset_token)를 유출시킬 수 있다는 점입니다.

12. REST 경로에서의 서버 측 파라미터 오염

RESTful API는 쿼리 스트링이 아닌 URL 경로에 파라미터 이름과 값을 배치할 수 있습니다. 예를 들어, 다음 경로를 살펴보겠습니다.

/api/users/123

URL 경로는 다음과 같이 분류될 수 있습니다.

  • /api는 루트 API 엔드포인트입니다.
  • /users는 리소스를 나타내며, 여기서는 users입니다.
  • /123은 파라미터를 나타내며, 여기서는 특정 사용자의 식별자입니다.

사용자명을 기반으로 사용자 프로필을 편집할 수 있는 애플리케이션을 예로 들어보겠습니다. 요청은 다음 엔드포인트로 전송됩니다.

GET /edit_profile.php?name=peter

이는 다음과 같은 서버 측 요청을 생성합니다.

GET /api/private/users/peter

공격자는 서버 측 URL 경로 파라미터를 조작하여 API를 악용할 수 있습니다. 이 취약점을 테스트하려면, 경로 순회(path traversal) 시퀀스를 추가하여 파라미터를 수정하고 애플리케이션이 어떻게 응답하는지 관찰합니다.

name 파라미터의 값으로 URL 인코딩된 peter/../admin을 제출할 수 있습니다.

GET /edit_profile.php?name=peter%2f..%2fadmin

이는 다음과 같은 서버 측 요청을 생성할 수 있습니다.

GET /api/private/users/peter/../admin

서버 측 클라이언트 또는 백엔드 API가 이 경로를 정규화하면, /api/private/users/admin으로 해석될 수 있습니다.

공격자는 JSON이나 XML과 같은 다른 구조화된 데이터 형식의 서버 처리 과정에서 취약점을 악용하기 위해 파라미터를 조작할 수 있습니다. 이를 테스트하려면, 사용자 입력에 예상치 못한 구조화된 데이터를 주입하고 서버가 어떻게 응답하는지 확인합니다.

폼 데이터를 사용하는 경우

사용자가 프로필을 편집한 다음, 서버 측 API에 요청을 보내 변경 사항을 적용하는 애플리케이션을 예로 들어보겠습니다. 이름을 편집하면, 브라우저는 다음과 같은 요청을 보냅니다.

POST /myaccount
name=peter

이는 다음과 같은 서버 측 요청을 생성합니다.

PATCH /users/7312/update
{"name":"peter"}

다음과 같이 요청에 access_level 파라미터를 추가하려고 시도할 수 있습니다.

POST /myaccount
name=peter","access_level":"administrator

사용자 입력이 적절한 검증이나 새니타이징 없이 서버 측 JSON 데이터에 추가되면, 다음과 같은 서버 측 요청이 생성됩니다.

PATCH /users/7312/update
{name="peter","access_level":"administrator"}

이로 인해 사용자 peter에게 관리자 접근 권한이 부여될 수 있습니다.

클라이언트 측 JSON 데이터를 사용하는 경우

유사한 예시이지만, 클라이언트 측 사용자 입력이 JSON 데이터인 경우를 살펴보겠습니다. 이름을 편집하면, 브라우저는 다음과 같은 요청을 보냅니다.

POST /myaccount
{"name": "peter"}

이는 다음과 같은 서버 측 요청을 생성합니다.

PATCH /users/7312/update
{"name":"peter"}

다음과 같이 요청에 access_level 파라미터를 추가하려고 시도할 수 있습니다.

POST /myaccount
{"name": "peter\",\"access_level\":\"administrator"}

사용자 입력이 디코딩된 후 적절한 인코딩 없이 서버 측 JSON 데이터에 추가되면, 다음과 같은 서버 측 요청이 생성됩니다.

PATCH /users/7312/update
{"name":"peter","access_level":"administrator"}

마찬가지로, 이로 인해 사용자 peter에게 관리자 접근 권한이 부여될 수 있습니다.

구조화된 형식 주입은 응답에서도 발생할 수 있습니다. 예를 들어, 사용자 입력이 데이터베이스에 안전하게 저장된 후, 적절한 인코딩 없이 백엔드 API의 JSON 응답에 포함될 때 이런 현상이 발생할 수 있습니다. 일반적으로 요청에서와 동일한 방식으로 응답에서의 구조화된 형식 주입을 탐지하고 악용할 수 있습니다.

참고: 위 예시는 JSON으로 되어 있지만, 서버 측 파라미터 오염은 모든 구조화된 데이터 형식에서 발생할 수 있습니다. XML에서의 예시는 XML 외부 엔터티(XXE) 주입 주제의 XInclude 공격 섹션을 참고하면 됩니다.

13. 실습: REST URL에서 서버 측 파라미터 오염 악용

목표: administrator로 로그인하여 carlos 삭제하기

이 랩을 풀기 위해서는 사용자 입력이 서버 측 URL 경로에 포함되는지 또는 쿼리 스트링에 포함되는지 식별하는 방법, 경로 순회(path traversal) 시퀀스를 사용하여 서버 측 요청을 변경하려고 시도하는 방법, 그리고 API 문서를 발견하는 방법을 알아야 합니다.

administrator 계정의 비밀번호 찾기부터 시작하겠습니다.

계정에 저장된 이메일로 비밀번호 찾기에 대한 내용이 전송된 것 같습니다.

username 파라미터에 임의의 값을 전송하면 계정명을 서버에서 확인하는 것 같습니다.

administrator%23을 전송하면 API 명세에 대해 확인해보라는 응답이 나옵니다.

administrator%3F도 마찬가지의 응답이 발생합니다.

./administrator의 경우 정상 응답이 발생합니다.

../administrator의 경우 이전과 동일하게 오류가 발생하는 것을 볼 수 있습니다. 이를 통해 사용자가 입력한 username의 값이 서버 측 REST URL 경로에 직접 포함되며, 경로 순회가 가능하다는 것을 확인했습니다.

이제 ../를 반복해서 API 루트 바깥으로 이동합니다.

../../../%23 입력 시 404 Not Found 오류가 발생했고, API 루트 밖으로 나온 것입니다. 이 위치에서 일반적인 API 문서 파일명을 시도합니다.

../../../../openapi.json%23 값으로 전송 시 오류 메시지 내 API 명세가 보이게 됩니다.

/api/internal/v1/users/{username}/field/{field}

이 경로에서 숨겨진 파라미터인 field를 확인할 수 있습니다.

임의의 값을 전송하면 현재 API 버전에서는 지원하지 않는다는 오류가 발생합니다. 이전 응답 값에 존재하는 API 명세는 v1에서 지원되므로 현재는 v1이 아닐 가능성이 높습니다.

비밀번호 재설정 페이지의 forgotPassword.js 파일 내 passwordResetToken 값을 가지고 URL로 접근하는 것을 볼 수 있습니다.

해당 API 명세를 v1 버전으로 변경한 뒤 fieldpasswordResetToken으로 설정한 뒤 전송하면 토큰이 발행되는 것을 볼 수 있습니다.

이전에 확인한 URL로 토큰 값을 통해 접속하면 비밀번호 재설정 페이지로 이동하게 됩니다.

변경한 비밀번호로 administrator 계정 접근 시 정상적으로 로그인이 가능함을 확인했습니다.

이 실습은 경로 순회를 통해 API 문서를 발견하고, 버전 차이를 활용하여 민감한 토큰을 유출시키는 복합적인 공격 시나리오입니다.

14. 자동화 도구를 활용한 테스트

Burp에는 서버 측 파라미터 오염 취약점을 탐지하는 데 도움이 되는 자동화 도구가 포함되어 있습니다.

Burp Scanner는 감사(audit) 수행 시 의심스러운 입력 변환을 자동으로 탐지합니다. 이는 애플리케이션이 사용자 입력을 받아 어떤 방식으로 변환한 다음, 그 결과에 대해 추가 처리를 수행할 때 발생합니다. 이 동작이 반드시 취약점을 구성하는 것은 아니므로, 앞서 설명한 수동 기법을 사용하여 추가 테스트를 수행해야 합니다.

또한 Backslash Powered Scanner BApp을 사용하여 서버 측 주입 취약점을 식별할 수 있습니다. 이 스캐너는 입력을 지루함(boring), 흥미로움(interesting), 또는 취약함(vulnerable)으로 분류합니다. 흥미로운 입력에 대해서는 앞서 설명한 수동 기법을 사용하여 조사해야 합니다.

15. 서버 측 파라미터 오염 예방

서버 측 파라미터 오염을 예방하려면, 인코딩이 필요 없는 문자를 정의하는 허용 목록(allowlist)을 사용하고, 다른 모든 사용자 입력이 서버 측 요청에 포함되기 전에 인코딩되도록 해야 합니다. 또한 모든 입력이 예상된 형식과 구조를 준수하는지 확인해야 합니다.

16. 마무리

API 테스팅은 단순히 엔드포인트를 찾는 것에서 끝나지 않습니다. 문서 탐색부터 시작해서, HTTP 메서드와 콘텐츠 타입 변경, Mass Assignment 테스트, 서버 측 파라미터 오염까지 체계적으로 접근해야 합니다. 특히 오류 메시지는 공격자에게 내부 API 구조를 하나씩 드러내주는 강력한 단서가 된다는 점을 이번 실습들을 통해 확인할 수 있었습니다.

정리하면 다음과 같습니다.

  • API 문서 노출은 전체 공격 표면을 드러낸다.
  • OPTIONS 메서드와 오류 메시지는 숨겨진 기능을 발견하는 열쇠다.
  • GET 응답과 POST 요청의 파라미터 차이에서 Mass Assignment 취약점을 찾을 수 있다.
  • 쿼리 구문 문자(#, &, =)와 경로 순회를 활용한 서버 측 파라미터 오염은 내부 API까지 도달할 수 있게 한다.
  • 모든 사용자 입력은 서버 측 요청에 포함되기 전에 반드시 검증과 인코딩을 거쳐야 한다.