[문제]
To solve the lab, log in as the administrator and delete carlos.
Required knowledge
To solve this lab, you'll need to know:
-How to use URL query syntax to attempt to change a server-side request.
-How to use error messages to build an understanding of how a server-side API processes user input.
These points are covered in our API Testing Academy topic.
[개념 설명]
이번 랩의 핵심 개념은 API엔드포인트 탐색과 파라미터 조작이다
먼저 API부터 간단히 설명하자면 API(Application Programming Interface)는 클라이언트(브라우저)와 서버가 통신하는 방식이다
흔히 REST API의 형태를 많이 사용하며 예시는 아래와 같다
| 메서드 | 의미 | 예시 |
| GET | 조회 | GET /api/users/carlos |
| POST | 생성 | POST /api/users |
| PUT/PATCH | 수정 | PATCH /api/users/carlos |
| DELETE | 삭제 | DELETE /api/users/carlos |
REST API에서 요청을 보낼 때 URL에 추가적인 정보를 담을 수 있는데, 이를 쿼리 파라미터라고 한다
아래와 같이 URL에서 "?" 이후에 오는 부분이 쿼리 파라미터이며 &로 여러 개를 연결할 수 있다
예시:
https://example.com/api/users?role=user&id=123
서버는 이 파라미터 값을 기반으로 어떤 데이터 값을 반환할지 결정하는데 만약 서버가 클라이언트에서 넘어온 값을 아무런 검증 없이 신뢰하면 공격자가 임의로 바꿔 의도치 않은 동장을 유발할 수 있다
예시:
(서버 코드)
role = request.get("role") # URL에서 role 값을 가져옴
if role == "admin":
show_admin_panel()
----------------------------------------------------------------
(결과)
원래 요청: /api/users?role=user
조작 요청: /api/users?role=admin ← 그냥 바꿔치기
이를 이용할 경우 API 문서에 공개되지 않은 숨겨진 파라미터를 이용한 권한 상승 또한 가능해진다
예시:
원래 요청: GET /api/users/carlos
추가 시도: GET /api/users/carlos?admin=true
추가 시도: GET /api/users/carlos?debug=true
추가 시도: GET /api/users/carlos?role=administrator
여기서 어떤 파라미터가 존재하는지, 어떤 값을 받는지는 서버의 오류 메시지를 통해 파악할 수 있다
서버가 잘못된 요청을 받으면 오류 메시지를 반환하는데 이 메시지 안에 API 내부 구조에 대한 힌트가 담겨 있는 경우가 종종 있다
예시:
(잘못된 요청)
GET /api/users?role=12345
----------------------------------------------------------------
(서버 에러 응답)
{
"error": "Invalid value for 'role': expected one of ['user', 'moderator', 'administrator']"
}
----------------------------------------------------------------
(에러 유형별 정보들)
400 Bad Request
→ 파라미터 형식이 틀렸다는 뜻
→ 해당 파라미터가 존재한다는 증거
401 Unauthorized
→ 인증이 필요하다는 뜻
→ 엔드포인트가 존재하긴 함
403 Forbidden
→ 권한이 없다는 뜻
→ 엔드포인트 존재 + 권한 체크가 있음
404 Not Found
→ 없는 경로
→ 다른 경로 시도해야 함
422 Unprocessable Entity
→ 파라미터 타입/값이 틀렸다는 뜻
→ 파라미터 이름은 맞는데 값이 잘못됨
→ 가장 정보가 많이 나오는 에러
----------------------------------------------------------------
(실전에서 오류 유도하기)
값을 이상하게 넣기: ?role=AAAAAAA
타입 깨기: ?id=abc (숫자 타입인데 문자 넣기)
빈 값 넣기: ?role=
없는 필드 넣기: ?nonexistent=test
지금까지 설명한 파라미터 조작은 우리가 서버에 직접 보내는 요청을 조작하는 것이었다
그러나 서버가 우리의 입력값을 내부 백엔드 API에 그대로 전달할 경우 심각한 문제가 발생할 수 있는데, 이것이 바로 Server-Side Parameter Pollution(SSPP)이다
일반적인 웹사이트는 아래와 같은 구조이다
예시:
[사용자 브라우저] → [프론트 서버] → [백엔드 API 서버]
이와 같이 브라우저는 프론트 서버하고만 대화하며 프론트 서버는 우리 요청을 처리하기 위해 내부적으로 백엔드 API를 호출한다
여기서 문제가 발생하는 것은 프론트 서버가 우리의 입력값을 검증 없이 백엔드 API에 그대로 전달할 때이다
URL에서 &는 파라미터 구분자 역할을 하지만 &를 URL 인코딩하면 %26이 된다
입력값에 %26을 포함시키면 프론트 서버는 이를 단순한 문자열로 처리하지만, 백엔드 API에 전달될 때 &로 디코딩되면서 파라미터가 분리된다
예시:
(우리가 보내는 요청)
username=administrator%26field=email
(백엔드로 전달될 때)
username=administrator&field=email
→ 파라미터가 두 개로 분리됨
즉 %26을 의도적으로 삽입함으로써 프론트 서버가 제어하던 백엔드 파라미터를 공격자가 직접 조작할 수 있게 된다
[풀이 과정]
먼저 Burp suite를 이용해 비밀번호 재설정을 트리거 해주었다


그 결과 Burp HTTP History에서 POST /forgot-password 요청 발견하였으며 Repeater로 전송 후 username을 administrator로 변경해주었다


그결과 200 OK와 함께 이메일이 반환되어 administrator 계정이 존재함을 확인하였다
다음으로 forgotPassword.js 파일을 분석하여 비밀번호 재설정 시 사용되는 토큰 파라미터 이름이 reset_token임을 확인하였다

토큰 파라미터의 이름을 확인하였으니 Repeater에서 SSPP 기법을 이용해 username=administrator%26field=reset_token%23 을 삽입하였다
%26은 &의 URL 인코딩으로 백엔드가 이를 파라미터 구분자로 해석하게 만들고, %23은 #의 URL 인코딩으로 백엔드가 원래 뒤에 붙이던 파라미터를 제거한다
그 결과 백엔드는 field=reset_token을 독립적인 파라미터로 인식하여 reset_token 값을 응답으로 반환하였다

기존 reset_token 값은 이메일로 전송해 값을 알지 못하면 재설정 페이지에 접근이 불가능하다
하지만 SSPP 기법을 이용해 토큰을 탈취 했으니 토큰을 URL에 직접 붙여 비밀번호 재설정이 가능해진다

비밀번호 재설정후 administrator 계정 로그인에 성공하였다
이후 carlos의 계정을 삭제하여 랩을 성공적으로 완료해주었다


[대응 방안]
- 입력값 검증 사용자 입력값에 이번 랩에서 사용된 %26, %23과 같은 특수문자를 필터링하거나 이스케이프 처리해야한다
- 프론트 서버에서 백엔드로 파라미터를 넘길 때 사용자 입력값을 그대로 보내지 말고 명시적으로 허용된 것들만 전달한다
- reset_token은 API 응답으로 반환하면 안 되고 이메일로만 전송하도록 구현해야 한다
- JS 파일 내부 정보 노출 금지하고 forgotPassword.js처럼 클라이언트에 노출되는 JS 파일에 내부 API 파라미터 이름이나 엔드포인트를 노출해선 안된다
- 토큰 유효시간 단축 및 일회성 처리하고 reset_token은 짧은 유효시간과 일회성으로 설계해야한다
'PortSwigger_API testing' 카테고리의 다른 글
| Lab: Exploiting an API endpoint using documentation (0) | 2026.03.08 |
|---|