SSRF에서 애플리케이션 관리자까지

시리즈 · CJ Ticket Shop 2편
  1. 01SSRF에서 애플리케이션 관리자까지
  2. 02403의 함정에서 컨테이너 root까지
🛡️ 강사 제공 인가된 실습 환경에서 수행

SSRF를 이용한 AWS 클라우드 침투

이번에는 현직자 강사님이 준비해주신 웹 사이트를 대상으로, 최종적으로 시스템의 root 권한을 획득하는 것을 목표로 모의해킹을 진행했다.

본 글은 허가된 실습 환경에서 진행한 내용을 정리한 것이다.

그전에 지금까지 배운 웹 해킹 기법을 큰 틀에서 정리하면 다음과 같다.

  1. Command Injection
  2. SQL Injection
  3. XSS
  4. 파일 관련 취약점

이번 모의해킹에서는 아직 배우지 않은 웹 해킹 기법과 AWS 클라우드에 대한 지식이 필요했다. 따라서 먼저 새로 사용한 기법을 정리하고, 해당 기법을 이용해 진행한 모의해킹 과정을 작성해보려고 한다.

SSRF(Server-Side Request Forgery)

SSRF는 공격자가 서버로 하여금 의도하지 않은 대상에 요청을 보내게 만드는 취약점이다.

보통 서버가 사용자가 입력한 URL로 직접 요청을 보내주는 기능에서 발생한다. 예를 들면 외부 이미지 불러오기, URL 미리보기, 웹훅, 프록시 같은 기능이 있다.

그전에 웹 해킹 기법이 어떤 원리로 성립하는지 간단히 이해할 필요가 있다.

클라이언트가 웹에서 어떠한 행동을 하면 웹 서버로 요청이 전송된다. 서버는 요청을 처리한 뒤 그 결과를 다시 클라이언트에게 반환한다.

공격자가 쿼리스트링, 요청 바디, 헤더, 쿠키 등의 입력 지점에 페이로드를 넣었을 때, 서버가 이를 공격자가 의도한 지점에서 해석하거나 실행한다면 취약점 공격이 성립한다. 결국 사용자의 입력값이 서버 내부에서 어떤 방식으로 처리되는지가 중요하다.

예를 들어 파일 업로드 취약점을 이용하기 위해 웹셸 PHP 파일을 업로드했다고 가정해보자. 업로드한 파일의 코드가 실행되지 않고 화면에 텍스트로 출력되기만 한다면, 서버가 해당 파일을 PHP 코드로 실행하지 않은 것이다. 이 경우 공격은 성공하지 않는다.

단, 공격에 성공했다고 해서 반드시 결과가 화면에 출력되는 것은 아니다. Blind SQL Injection이나 Blind SSRF처럼 결과가 보이지 않더라도 응답 차이, 처리 시간 또는 외부 서버에 남은 요청 기록 등을 통해 성공 여부를 확인할 수 있다.

다시 SSRF로 돌아가 보자.

서버에 사용자가 입력한 URL로 대신 요청을 보내주는 기능이 있고, 요청 대상을 제대로 제한하지 않는다면 SSRF가 발생할 수 있다.

공격자가 특정 URL을 입력하면 중간 웹 서버가 해당 URL로 요청을 보낸다. 최종 대상의 입장에서 요청을 보낸 주체는 공격자의 PC가 아니라 중간 웹 서버가 된다.

이러한 특징을 이용하면 일반 사용자가 직접 접근할 수 없는 내부 서버나 클라우드 메타데이터 서비스에 중간 서버의 권한으로 접근할 가능성이 생긴다.

이제 실제 모의해킹 과정을 정리해보겠다.

0. 정찰

대상은 티켓 예매 사이트다.

각 이벤트 페이지에서 좌석을 선택하고 티켓을 예매할 수 있으며, 로그인과 회원가입 기능도 존재한다.

회원가입과 로그인을 마친 뒤 티켓을 예매해보았지만, 화면에서 바로 확인할 수 있는 특별한 입력 지점이나 취약점은 찾지 못했다.

메뉴를 눌러도 URL이 바뀌지 않는 것을 보아 이 사이트는 SPA(Single Page Application) 구조라고 판단했다.

SPA는 JavaScript를 이용해 화면을 동적으로 변경한다. 이 과정에서 사용하는 API 주소나 일부 설정값이 JavaScript 번들 또는 전역 설정 객체에 포함되는 경우가 있다.

따라서 JavaScript 번들과 전역 설정 객체를 중심으로 정찰을 진행했다.

JavaScript 번들 확인

SPA처럼 화면에서 공격 지점이 바로 보이지 않는 사이트에서는 JavaScript 번들을 분석하는 것이 중요하다.

분석 결과, 전역 설정 객체에서 여러 엔드포인트와 메모를 발견했다.

window.__APP_CONFIG__

개발자 도구의 Console에서 해당 전역 객체를 확인했다.

메모 중 다음과 같은 내용을 발견했다.

asset-service: external image fetcher (allow-list pending review)

외부 이미지를 서버가 대신 가져오는 기능이 있으며, 허용 목록에 대한 검토가 끝나지 않았다는 의미로 보였다.

이 메모를 통해 외부 URL 요청 기능에 SSRF 취약점이 존재할 가능성을 생각할 수 있었다.

1. SSRF를 통한 AWS 임시 자격 증명 노출

SSRF 가능성을 확인하기 위해 Burp Suite에서 API 요청을 잡은 뒤 URL 파라미터를 변경해 전송했다.

요청은 다음과 같이 변조했다.

GET /api/v1/proxy/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/redteam-lab-role HTTP/1.1
Host: <LAB_SERVER_IP>:3000

응답을 확인한 결과 AWS IAM Role의 임시 자격 증명이 그대로 반환되었다.

/proxy/fetch?url=의 역할

/proxy/fetch?url=은 URL을 입력받아 서버가 해당 주소로 대신 요청을 보내는 기능이다.

하지만 요청 대상에 대한 적절한 허용 목록이나 내부 IP 차단이 적용되어 있지 않았기 때문에, 외부 주소뿐만 아니라 AWS 메타데이터 서비스에도 요청을 보낼 수 있었다.

169.254.169.254를 사용한 이유

169.254.169.254는 AWS를 비롯한 여러 클라우드 환경에서 인스턴스 메타데이터 서비스를 제공할 때 사용하는 링크 로컬 주소다.

이 주소는 일반적으로 외부 인터넷에서 직접 접근할 수 없다. 하지만 SSRF가 존재한다면 클라우드 인스턴스 내부의 서버가 대신 요청을 보내도록 만들 수 있다.

그 결과 다음과 같은 임시 자격 증명을 얻을 수 있었다.

  • AccessKeyId
  • SecretAccessKey
  • Token

이 값들은 영구적인 계정 비밀번호가 아니라 IAM Role에 발급된 임시 자격 증명이다. 하지만 유효시간 동안에는 해당 Role에 부여된 AWS 권한을 사용할 수 있다.

2. AWS CLI를 이용한 클라우드 권한 분석

AWS를 공부해본 적이 없었기 때문에 먼저 AWS 환경이 어떤 구조로 되어 있고, IAM Role과 각 서비스가 어떤 역할을 하는지 간단히 알아보았다.

Burp Suite를 통해 얻은 다음 값들을 AWS CLI 환경 변수로 설정했다.

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • AWS_SESSION_TOKEN

그다음 아래 순서로 권한을 확인했다.

  1. 현재 자격 증명의 사용자 또는 Role 확인
  2. 해당 Role에 연결된 정책 확인
  3. 정책 내용을 확인해 사용할 수 있는 권한 파악
  4. SSM Parameter Store에서 접근 가능한 파라미터 조회

IAM 정책 JSON을 해석하는 방법은 이전 AWS Cloud Security 글에 정리해두었다.

정책을 확인한 결과 현재 redteam-lab-role로 수행할 수 있는 작업들이 출력되었다.

S3 리소스는 다음과 같이 버킷 이름의 접미사가 와일드카드로 지정되어 있었다.

stix-ticket-assets-*

따라서 정확한 버킷 이름은 바로 알 수 없었고, 추후 추가 정찰이 필요했다.

먼저 SSM Parameter Store를 확인했다.

SSM Parameter Store

SSM은 AWS Systems Manager의 약자다. Systems Manager에는 설정값이나 비밀정보를 저장할 수 있는 Parameter Store 기능이 있다.

Parameter Store에는 다음과 같은 값들이 저장될 수 있다.

  • 애플리케이션 설정값
  • 비밀번호
  • API Key
  • 인증서
  • 개인키

획득한 redteam-lab-role에는 SSM 파라미터를 조회할 수 있는 권한이 있었다. 해당 권한을 이용해 접근 가능한 파라미터를 복호화하여 조회했다.

확인된 주요 파라미터는 총 3개였다.

  1. jwt-private-key
  2. admin-api-key
  3. db-master-password

가장 먼저 눈에 들어온 것은 jwt-private-key였다.

현재 웹 애플리케이션에서는 일반 USER 권한으로 로그인한 상태였다. 만약 서버가 JWT 서명에 사용하는 개인키를 알고 있다면, 서버가 정상적인 토큰으로 인식하는 ADMIN 권한의 JWT를 새로 생성할 가능성이 있다.

3. JWT 위조를 통한 애플리케이션 관리자 권한 획득

먼저 기존 USER JWT를 디코딩하여 헤더와 페이로드 구조를 확인했다.

그다음 사용자 식별 정보와 권한을 ADMIN에 맞게 구성하고, SSM Parameter Store에서 얻은 jwt-private-key를 사용해 새로운 서명을 생성했다.

단순히 JWT의 페이로드만 수정한 것은 아니다. 페이로드를 수정하면 기존 서명이 무효화되기 때문에 반드시 서버가 사용하는 알고리즘과 개인키로 다시 서명해야 한다.

생성한 JWT를 검증 API에 전송한 결과 서버가 다음과 같이 인식했다.

{
  "valid": true,
  "sub": "admin",
  "role": "ADMIN"
}

보호된 API에서도 정상 응답이 반환되는 것을 확인했다. 이를 통해 웹 애플리케이션 수준의 ADMIN 권한을 획득한 것을 확인할 수 있었다.

여기서 주의할 점은 웹 애플리케이션의 ADMIN 권한과 서버 운영체제의 root 권한은 서로 다르다는 것이다.

이번 단계에서 획득한 것은 애플리케이션 관리자 권한이며, 아직 시스템의 root 권한을 획득한 것은 아니다.

마무리

오늘은 여기까지 진행했다. 여기까지 도달하는 데 약 4시간이 걸렸다 그것도 Claude의 도움을 받아서..

이번 모의해킹에는 SSRF와 AWS 클라우드 보안이 함께 등장했는데, 두 내용 모두 이번에 처음 접했다.

처음에는 S3 버킷이나 IAM Role이 무엇인지도 정확히 모르는 상태였다. 그러다 보니 중간에 막혔을 때 어떤 서비스를 확인해야 하고, 다음 정찰 지점을 어디로 잡아야 하는지 판단하기가 어려웠다.

그래도 이번 실습을 통해 다음과 같은 공격 흐름을 직접 확인할 수 있었다.

SSRF
→ EC2 메타데이터 접근
→ IAM Role 임시 자격 증명 획득
→ IAM 권한 분석
→ SSM Parameter Store 비밀정보 조회
→ JWT 서명키 노출
→ 애플리케이션 ADMIN 권한 획득

근데 아직도 IAM 이 뭔지 EC2가 뭔지 개념은 알지만 이것들이 어떻게 연결되어있는지 , 어떤상관관계인지는 이해를 못했다.

이번 주에 배우는 내용이 AWS Cloud 보안이기 때문에 관련 내용을 더 학습한다면, 다음에는 클라우드 환경에서 확인해야 할 지점을 조금 더 구체적으로 생각할 수 있을 것 같다.

다음 실습에서는 정확한 S3 버킷 이름을 찾고, 애플리케이션 관리자 권한 이후에 시스템 권한 상승으로 이어질 수 있는 경로를 계속 확인해볼 예정이다.

원본 · velog.io