Post

[논문 리뷰] VOAPI²: RESTful API의 취약점을 똑똑하게 찾아내는 방법

[논문 리뷰] VOAPI²: RESTful API의 취약점을 똑똑하게 찾아내는 방법

논문 정보

제목: Vulnerability-oriented Testing for RESTful APIs
학회: USENIX Security 2024 (33rd USENIX Security Symposium)
저자: Wenlong Du, Jian Li (Shanghai Jiao Tong University) 외
키워드: RESTful API, Vulnerability Testing, Fuzzing, Security Testing


TL;DR

  • 🎯 핵심: API 기능성을 활용해 취약점 탐지를 목표 지향적으로 수행
  • 🔑 혁신: 키워드 기반 후보 API 추출 → 상태 기반 테스트 시퀀스 생성 → 피드백 기반 검증
  • 성과: 7개 실제 API에서 26개 취약점 발견 (zero-day 7개 포함, CVE 23개 할당)
  • 🚀 효율성: 기존 도구 대비 테스트 시간 10배 단축, 취약점 발견률 5배 향상

1. 연구 배경

RESTful API란?

REST (REpresentational State Transfer): HTTP 기반 웹 아키텍처 스타일

RESTful API는 현대 웹/모바일/클라우드/IoT 시스템에서 데이터 교환의 핵심입니다:

도메인사용 사례
클라우드AWS, Azure API
CMSJellyfin, Appwrite, GitLab
IoT디바이스 제어 및 데이터 수집

HTTP 메서드: GET, POST, PUT, DELETE


문제: 취약점이 숨어있다

Motivation 슬라이드 3: 단순한 이미지 가져오기 API에 숨겨진 SSRF 취약점

실제 사례 (CVE-2021-29490):

1
2
3
4
5
6
/Images/Remote:
  get:
    parameters:
      - name: imageUrl
        type: string
        description: The image url

imageUrl 파라미터 검증 미흡 → SSRF (Server-Side Request Forgery) 발생

문제의 핵심:

겉으로는 평범한 이미지 API지만, 내부 네트워크 접근이 가능한 치명적 취약점을 숨기고 있었습니다.


2. 기존 방법의 한계

블랙박스 테스팅 도구들의 문제점

1. 비효율적인 시간 소모:

  • RESTler는 GitLab API 테스트에 5시간 소요
  • 대부분 모든 인터페이스를 무차별적으로 탐색

2. 낮은 탐지 정확도:

  • HTTP 500 에러에만 의존 → 논리적 취약점 (SSRF, XSS 등) 놓침
  • RESTler가 GitLab에서 발견한 28개 버그 중 대부분이 서비스 장애, 보안 취약점 소수

3. 목표 지향성 부재:

기존 도구문제점
RESTler모든 API에 동일한 payload 적용
ZAP/Astra상태 의존성 처리 불가
RestTestGen500 에러 중심, 보안 취약점 비율 낮음

3. VOAPI²의 핵심 아이디어

관찰: API 기능성 ↔ 취약점 유형 강한 연관성

CVE 데이터베이스 분석 결과 (544개 API 취약점):

CWE ID취약점 유형기능성 비율키워드 예시
CWE-434Unrestricted Upload83%upload, submit, import
CWE-918SSRF81%remote, proxy, url
CWE-22Path Traversal52%path, dir, file
CWE-78Command Injection40%cmd, command, system
CWE-89SQL Injection53%sql, database, select
CWE-79XSS35%display, content, view

핵심 통찰: 평균 57%의 취약한 API가 동일한 기능성을 가짐!


VOAPI²의 전략: 똑똑한 보안 검사원처럼 행동하기

비유:

1
2
3
4
5
건물 검사원이 "서버실"과 "업로드 전용"이라는 표지를 보면 
"회의실"보다 먼저 확인하는 것처럼,

VOAPI²는 API 경로와 파라미터의 키워드를 보고 
가장 의심스러운 곳부터 집중 검사합니다.

전략:

  1. 키워드로 기능 추론upload → 파일 업로드 취약점 가능성
  2. 적절한 payload 선택 → 악성 파일 업로드 시도
  3. 상태 기반 시퀀스 생성 → 의존성 있는 API 호출 순서 구성
  4. 피드백 검증 → 실제 취약점인지 확인

4. VOAPI² 시스템 아키텍처

System Architecture 슬라이드 7: VOAPI² 전체 시스템 구조

4단계 파이프라인:

1
2
3
4
5
6
7
8
9
Step 1: Specification Analysis
   ↓ (키워드 추출)
Step 2: Candidate Interface Extraction  
   ↓ (취약 API 식별)
Step 3: Test Sequence Generation
   ↓ (상태 기반 시퀀스)
Step 4: Feedback-based Test
   ↓ (취약점 검증)
PoC 생성

Step 1: Semantic Keyword Collection

목표: 취약점 유형 ↔ API 기능 ↔ 키워드 매핑 구축

방법:

  1. CVE 데이터 수집: 544개 API 취약점 분석
  2. CWE ID 클러스터링: 가장 빈번한 6가지 유형 선택
  3. 단어 빈도 분석: API 경로/파라미터에서 고빈도 단어 추출
  4. 전문가 검증: 도메인 지식으로 키워드 정제

결과 (Table 2):

취약점 유형Path 키워드 수Param 키워드 수
SSRF1022
Unrestricted Upload128
Path Traversal123
Command Injection127
SQL Injection114
XSS1512

Step 2: Candidate Interface Extraction

목표: 키워드로 취약 가능성 높은 API 추출

프로세스:

1
2
3
4
5
6
7
8
9
10
11
# 1. RESTler Compile Module로 Grammar 파일 생성
grammar_file = parse_openapi_spec(api_spec)

# 2. Grammar 파일에서 정보 추출
api_path = extract_endpoint(grammar_file)      # e.g., /Images/Remote
method = extract_method(grammar_file)           # e.g., GET
params = extract_parameters(grammar_file)       # e.g., imageUrl

# 3. 키워드 매칭
if "url" in params:
    tag_as_candidate(api_path, vulnerability_type="SSRF")

추출 전략:

  1. Path 키워드만 있는 경우 → 모든 파라미터를 테스트 대상으로
  2. Parameter 키워드 있는 경우 → 해당 파라미터만 테스트

예시:

1
2
3
4
5
GET /Images/Remote?imageUrl=fuzzstring

Path: /Images/Remote → "remote" keyword 발견
Parameter: imageUrl → "url" keyword 발견
→ Candidate API로 선택, SSRF 취약점 가능성 태깅

Step 3: Test Sequence Generation

도전 과제: API는 상태 기반이므로 단일 요청으로는 불충분

해결책: 2가지 서브 프로세스

3-1. Reverse Sequence Construction

목표: 의존성 있는 API 호출 순서 구성

알고리즘 (Algorithm 1):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def GENERATE_SEQUENCE(candidate_api):
    S = [candidate_api]  # 시퀀스 초기화
    i = -1
    
    while True:
        producers = FIND_PRODUCERS(S[i])
        
        for producer in producers:
            if IS_VALID_PRODUCER(producer, S) and not IS_DUPLICATE(producer, S):
                S.INSERT(0, producer)  # 앞에 삽입
        
        if S[i] == S[0]:
            break
        else:
            i -= 1
    
    return S

예시:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Candidate API: GET /database/collections/{collectionId}/documents/{documentId}

Step 1: Producer 탐색
→ POST /database/collections
→ POST /database/collections/{collectionId}/documents

Step 2: 유효성 검증
- Producer-Consumer 관계 확인
- CRUD Semantics 검증 (생성 전 접근 불가)
- Resource Hierarchy 검증 (계층 구조 일치)

Step 3: 최종 시퀀스
POST /database/collections 
→ POST /database/collections/{id}/documents 
→ GET /database/collections/{id}/documents/{docId}

제약 조건:

  1. Producer-Consumer Relationship:
    • Request B의 응답이 Request A의 입력 파라미터 제공
    • 우선순위: POST > PUT > GET > PATCH
  2. CRUD Semantics:
    • 리소스 생성 전 접근 불가
    • 삭제 후 접근 불가
  3. Resource Hierarchy:
    • /user/{id}/user의 하위 리소스
    • /user/team은 관계 없음

3-2. Parameter Value Generation

목표: 각 파라미터에 현실적인 값 할당

5가지 전략 (우선순위 순):

전략설명예시
CONSUMERProducer 응답에서 값 추출collectionId ← POST 응답의 id
SPECIFICATIONOpenAPI spec의 example/enum/defaultstatus: "active"
FORMAT타입별 사전 정의 값email: "test@example.com"
SUCCESS이전 성공 요청의 값 재사용200 OK였던 파라미터 값
RANDOM랜덤 생성 (다양성 확보)name: "fuzz_12345"

실제 예시:

1
2
3
4
5
6
7
8
9
10
1. POST /collections
   → Response: {"id": 42, "name": "TestCollection"}

2. POST /collections/42/documents
   → collectionId = 42 (CONSUMER 전략)
   → Response: {"docId": 99}

3. GET /collections/42/documents/99
   → collectionId = 42 (CONSUMER)
   → docId = 99 (CONSUMER)

Step 4: Feedback-based Testing & Verification

목표: 취약점 존재 여부 확인

구성 요소:

  1. Test Case Generator: 타겟 payload 삽입
  2. Test Case Sender: API 애플리케이션에 전송
  3. Validation Server: 취약점 검증

Testing Corpus (Table 3):

API 유형Payload 예시
Resource Requesthttp://IP:PORT/ssrf/{0}
File Uploadevil.jsp, evil.php, evil.asp
Path Processing/etc/passwd, C://Windows//win.ini
System Configurationcurl http://IP:PORT/command/{0}
Database Operation1" or "1"="1, SQLMap 통합
Text Display<img src='http://IP:PORT/xss/{0}'>

검증 프로세스:

A. SSRF/XSS/Command Injection:

1
2
3
1. Test Case Sender → API Application (payload 전송)
2. API Application → Validation Server (취약 동작 시 요청 발생)
3. Validation Server → 요청 수신 여부로 취약점 판단

B. Unrestricted Upload:

1
2
3
4
5
6
1. 악성 파일 업로드
2. 응답 분석:
   - "success" 포함 or 2xx 상태 코드 → 업로드 성공
3. 파일 경로 확인:
   - 응답에 경로 포함 → 파일 접근 및 실행 테스트
   - 경로 없음 → 수동 검증 필요 (Limitation)

C. Triggering Request (CVE-2023-27161 사례):

일부 취약점은 추가 트리거 요청 필요:

1
2
3
4
5
6
7
POST /Repositories (url 파라미터에 payload 삽입)
→ 취약점 발생하지 않음

GET /Packages (트리거 요청)
→ 백도어 동작 활성화
→ Validation Server로 검증 요청 전송
→ 취약점 확인!

Trigger 후보:

  1. 동일 경로 + GET 메서드
  2. 파라미터 불필요 + GET 메서드

5. 구현

VOAPI² Prototype:

  • 기반: RESTler 확장
  • 코드 규모: 2,700 lines of Python
  • 라이브러리: Scrapy, Beautiful Soup, socket
  • 지원 형식: OpenAPI v2/v3
  • 탐지 유형: 6가지 취약점 (SSRF, Upload, Path Traversal, Command Injection, SQL Injection, XSS)

주요 확장:

  • SQLMap 통합 (SQL Injection 고도화)
  • multipart_formdata primitive 추가 (파일 업로드 지원)

GitHub: https://github.com/NSSL-SJTU/VoAPI2


6. 평가

실험 설정

비교 도구 (5개):

카테고리도구설명
취약점 스캐너ZAPOWASP 블랙박스 웹 스캐너
 AstraRESTful API 전용 스캐너
API 테스팅 도구RESTlerMicrosoft, 상태 기반 퍼징
 RestTestGen데이터 의존성 기반
 MINER신경망 기반 파라미터 예측

평가 대상 (7개 실제 API):

ApplicationEndpoints규모다운로드
GitLab358대규모100M+
Jellyfin405대규모100M+
Appwrite95중규모5M+
Microcks44소규모600K+
Casdoor121중규모20K+
Gitea325대규모20K+
Rbaskets22소규모10K+

실험 환경:

  • CPU: 4 cores
  • RAM: 8 GB
  • OS: Ubuntu 20.04 LTS
  • 최대 시간: 5시간

RQ1: Vulnerability Detection

핵심 결과:

VOAPI² 발견:

  • 26개 취약점
  • Zero-day 7개
  • 알려진 취약점 19개
  • CVE 23개 할당

Vulnerabilities by Type 슬라이드 24: 도구별 발견 취약점 비교

도구별 비교:

도구발견 취약점 수취약점 유형 수비고
VOAPI²264 types모든 앱에서 발견
ZAP52 typesGitea/Rbaskets/GitLab 실패
Astra52 typesZAP과 유사
RESTler31 type500 에러 중심
RestTestGen21 type500 에러 중심
MINER31 type500 에러 중심

독점 발견 (14개):

VOAPI²만 발견한 취약점 14개
→ 다른 도구들은 놓침

이유:

  1. RESTful API 테스팅 도구: 포괄적인 테스팅 corpus 부족
  2. 스캐너: 상태 전이 및 데이터 의존성 처리 불가

HTTP 500 에러 ≠ 취약점

HTTP 500 Comparison 슬라이드 23: HTTP 500 에러 vs 실제 취약점

비교 (Table 5):

도구총 #500 에러총 #Packet취약점 비율
RESTler111747,1160.015%
MINER99433,4850.023%
RestTestGen122222,0400.055%
RestTestGen+V103145,4500.071%
VOAPI²2934,1270.085%

VOAPI² 장점:

  • 전송 패킷 10배 적음
  • 취약점 발견 비율 5배 높음

정확도 (False Positive Rate)

ApplicationVOAPI²ZAPAstra
Appwrite4/11 (36%)6/8 (75%)5/8 (63%)
Jellyfin5/13 (38%)9/11 (82%)4/6 (67%)
평균 FDR42.22%85.71%69.57%

VOAPI² FP가 낮은 이유:

  1. XSS: 디스플레이 기능 있는 API만 테스트
  2. Path Traversal: 응답 내용 분석 (e.g., root:x:0:0 확인)

RQ2: Efficiency

1. Operation Coverage

Coverage Comparison 슬라이드 26: Endpoint Coverage 비교

결과:

도구평균 Coverage
RESTler37.3%
MINER44.0%
RestTestGen58.8%
VOAPI² (TSG)62.7%

이유: VOAPI²의 우수한 의존성 구성 능력


2. 테스트 시간

도구총 테스트 시간 (7개 앱)
Astra235분 19초
ZAP372분 54초
ZAP+V63분 46초
VOAPI²31분 05초

VOAPI² 효율성:

  • Astra 대비 7.6배 빠름
  • ZAP 대비 12배 빠름

이유:

  • Vulnerability-oriented 전략: 후보 API만 집중 테스트
  • Targeted payloads: 관련 payload만 사용

대조적으로 ZAP/Astra는:

  • 모든 API에 모든 payload 무차별 적용
  • 엄청난 시간 낭비

RQ3: Ablation Study

VOAPI²-V: Candidate Interface Extraction 제거 버전

결과 (Table 9):

ApplicationBug-IDPathVOAPI²VOAPI²-V
Jellyfin1 unassigned/Startup/User10m 30s495m 28s
AppwriteCVE-2022-2925/teams53s26m 30s
AppwriteCVE-2022-2925…/memberships1m 03s34m 36s
GitLabCVE-2022-1190…/milestone2m 36s145m 41s

VOAPI²-V의 문제:

  • 모든 API를 순차적으로 검사 → 시간 낭비
  • 모든 파라미터에 모든 payload 적용 → 비효율

Keyword Shortlisting의 효과:

  • RestTestGen+V: 패킷 감소, 시간 단축
  • ZAP+V: 테스트 시간 5.8배 단축

그러나:

  • 새로운 취약점 발견 못함 (corpus 부족)
  • 의존성 처리 불가

결론: Keyword selection은 효율성은 높이지만,
전체 VOAPI² 파이프라인 없이는 효과성 개선 어려움


Case Study: XSS in Appwrite (CVE-2022-2925)

취약점 위치: 5개 endpoint의 name 파라미터

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/teams:
  POST:
    RequestBody:
      name  # ← XSS 취약

/teams/{teamId}/memberships:
  POST:
    parameters:
      - name: teamId  # path parameter
    RequestBody:
      name  # ← XSS 취약

/database/collections, /users, /functions:
  POST:
    RequestBody:
      name  # ← XSS 취약

왜 ZAP/Astra는 실패했나?

/teams/{teamId}/memberships 공격 불가:

1
2
3
4
5
1. POST /teams (name에 XSS payload)
   → Response: {"$id": "abc123", ...}

2. POST /teams/abc123/memberships (name에 XSS payload)
   ↑ teamId를 응답에서 파싱해야 함

ZAP/Astra:

  • 데이터 의존성 추론 불가
  • 단일 요청만 전송

VOAPI²:

  • Reverse Sequence Construction으로 의존성 파악
  • /teams/teams/{teamId}/memberships 시퀀스 생성
  • XSS 취약점 성공적으로 발견! ✅

7. Limitations & Future Work

한계점

1. Bug Verification 제한:

1
2
Stored XSS: 어느 페이지에 저장되는지 자동 판단 불가
Unrestricted Upload: 파일 접근 경로 자동 추출 불가

예시:

1
2
3
4
5
6
POST /storage/files
  → Response: {"id": "651506441776a", "status": "success"}
  
# 문제: 파일 접근 경로가 응답에 없음
# VOAPI²는 /files/651506441776a 자동 생성 불가
# → 수동 확인 필요

2. Manual Effort 필요:

  • Stored XSS: 관련 페이지 수동 브라우징
  • Upload: 파일 접근 인터페이스 수동 탐색

3. OpenAPI만 지원:

  • HTML 기반 문서 처리 불가
  • 비구조화 API 문서 처리 불가

개선 방향

1. 더 스마트한 검증:

1
2
3
4
5
6
7
8
# Extensible interface 설계
class VulnerabilityVerifier:
    def verify_stored_xss(self, payload, api):
        # 휴리스틱 기반 페이지 탐색
        potential_pages = self.find_display_pages(api)
        for page in potential_pages:
            if self.check_xss_triggered(page, payload):
                return True

2. AI 활용:

  • LLM으로 비구조화 문서에서 API 정보 추출
  • NLP로 기능성 자동 분류

3. 더 넓은 지원:

  • GraphQL API
  • gRPC
  • WebSocket

8. 의의 및 시사점

주요 기여

1. 새로운 패러다임:

기능성 기반 취약점 탐지
API가 무엇을 하는지를 이해하고, 그에 맞는 취약점을 찾는다

2. 실용적 성과:

  • 7개 실제 API에서 26개 취약점 발견
  • Zero-day 7개, CVE 23개

3. 효율성 증명:

  • 테스트 시간 10배 단축
  • 취약점 발견률 5배 향상

4. 오픈소스:

  • GitHub 공개로 커뮤니티 기여

한계 및 향후 연구

한계:

  • 키워드 기반 분류의 정확도 한계
  • OpenAPI 의존성
  • 일부 검증의 수동 개입 필요

향후 연구 방향:

  1. Machine Learning 통합:
    • LLM 기반 API 문서 분석
    • 자동 키워드 추출 및 분류
  2. 검증 자동화:
    • 휴리스틱 기반 업로드 경로 추론
    • 동적 페이지 탐색
  3. 확장성:
    • GraphQL, gRPC 지원
    • Adaptive attack generation

9. 개인적인 생각 💭

인상 깊었던 점

1. 문제 정의의 명확성:

“API 기능성과 취약점 유형의 연관성”이라는 단순하지만 강력한 통찰

데이터 기반 검증 (57% correlation)으로 아이디어를 뒷받침한 점이 훌륭합니다.

2. 실용주의적 접근:

  • RESTler 확장: 처음부터 새로 만들지 않고 기존 도구 활용
  • SQLMap 통합: 검증된 도구를 적재적소에 사용
  • Extensible interface: 미래 확장성 고려

3. 포괄적 평가:

  • 5개 도구, 7개 실제 API
  • Operation coverage, efficiency, ablation study까지
  • False positive rate 분석

아쉬운 점

1. Keyword 의존성:

현재는 전문가가 수동으로 추출한 키워드에 의존:

1
2
"upload", "submit", "import" → File Upload
"remote", "proxy", "url" → SSRF

한계:

  • 새로운 취약점 유형에 대응 어려움
  • 언어/도메인 특화 키워드 놓칠 수 있음

개선 방향:

1
2
3
4
5
6
# LLM 기반 자동 키워드 추출
def extract_keywords_with_llm(api_spec):
    prompt = f"Analyze this API: {api_spec}. What functionality does it provide?"
    functionality = llm.generate(prompt)
    vulnerability_types = map_functionality_to_vulnerability(functionality)
    return vulnerability_types

2. False Positive 여전히 높음 (42%):

Stored XSS와 Unrestricted Upload의 불완전한 검증:

1
2
Upload 성공 ≠ 실행 가능
XSS payload 저장 ≠ 실제 렌더링

필요:

  • 동적 페이지 크롤링
  • 파일 실행 환경 테스트

3. OpenAPI 의존성:

많은 레거시 시스템은 OpenAPI spec 없음:

1
HTML 문서, Markdown, 심지어 코드 주석에만 API 설명

미래 작업:

  • HTML parser로 API endpoint 추출
  • LLM으로 비구조화 문서 분석

연구의 영향

1. 패러다임 전환:

BeforeAfter (VOAPI²)
모든 API 무차별 테스트기능성 기반 선택적 테스트
HTTP 500 에러 중심취약점 유형별 타겟팅
정적 payload기능 맞춤형 payload

2. 실무 적용 가능성:

  • CI/CD 통합: API 배포 전 자동 보안 검증
  • Bug Bounty: 효율적인 취약점 탐색
  • Security Audit: 대규모 API 프로젝트 검증

3. 학계 기여:

  • Functionality-aware testing 새로운 연구 방향
  • Stateful fuzzing 개선 아이디어
  • LLM + Security testing 융합 연구

10. 결론

VOAPI²의 핵심 메시지:

API를 무작정 때리지 말고,
무엇을 하는지 이해하고,
그에 맞는 공격을 시도하라.

성과 요약:

26개 취약점 (zero-day 7개)
CVE 23개 할당
✅ 테스트 시간 10배 단축
✅ 취약점 발견률 5배 향상

시사점:

현대 API 보안 테스팅은 지능이 필요합니다:

  • 단순 퍼징 → 의미론적 이해
  • 무차별 탐색 → 목표 지향적 검증
  • 500 에러 → 논리적 취약점

VOAPI²는 이러한 방향으로 나아가는 중요한 첫걸음입니다.


참고 자료


함께 읽으면 좋은 논문

  • RESTler: Stateful REST API Fuzzing (Microsoft Research)
  • RestTestGen: Automated Black-box Testing of RESTful APIs
  • NAUTILUS: Automated RESTful API Vulnerability Detection

태그: #논문리뷰 #보안 #API테스팅 #취약점탐지 #USENIX

This post is licensed under CC BY 4.0 by the author.