Post

[VAPT Report] 2차 SQL Injection을 통한 전체 계정 정보 유출 및 변조

1. Executive Summary

본 보고서는 PHP 기반 회원 시스템에서 발견된 2차 SQL Injection(Second-order SQLi) 취약점을 정리합니다. 회원가입 단계에서 악의적인 SQL 구문이 포함된 username이 저장되고, 이후 mypage.php와 mypage_update.php에서 해당 값이 Prepared Statement 없이 쿼리에 직접 삽입 및 실행되어 전체 계정 정보가 유출 및 변경되는 취약점이 확인되었습니다.

주요 발견사항

  • 취약점 유형: 2차 SQL Injection
  • 심각도: HIGH
  • 영향 대상: 마이페이지, 회원정보 수정 기능
  • 주요 파라미터: username

2. 취약점 상세 분석

2.1 취약점 개요

회원가입 시 입력된 username에 SQL 구문이 포함된 값을 그대로 DB에 저장합니다. 이후 저장된 값이 세션을 통해 불러와서 mypage.php와 mypage_update.php에서 SQL 쿼리에 직접 삽입되면, 2차 SQL Injection이 발생합니다.

이 취약점의 핵심은 다음과 같습니다.

  • 첫 번째 단계: 악의적인 문자열을 회원가입에서 저장
  • 두 번째 단계: 저장된 문자열이 SQL 구문의 일부로 재사용됨
  • 결과: 쿼리가 항상 참(True)이 되어 전체 레코드를 조회 또는 수정

2.2 취약 위치

위치파라미터영향비고
mypage.php세션 username사용자 정보 조회 시 SQL InjectionPrepared Statement 미사용
mypage_update.php세션 username회원정보 수정 시 SQL InjectionWHERE 절이 항상 참이 됨

2.3 취약 쿼리 예시

  • 회원 정보 조회
    1
    
    SELECT * FROM logindata WHERE username=''admin' or '1' = '1'
    
  • 회원 정보 수정
    1
    
    UPDATE logindata SET password_hash='$password_hash', address='$address' WHERE username=''admin' or '1' = '1'
    

이 쿼리들은 username 필드에 삽입된 악의적 문자열 때문에 조건절이 항상 참이 되며, 결과적으로 전체 테이블이 대상이 됩니다.


3. Proof of Concept (PoC)

3.1 Step 1: 악의적인 username 계정 생성

회원가입 페이지에서 아래와 같이 SQL 구문이 포함된 username으로 계정을 생성합니다.

  • 입력값: 'admin' or '1' = '1

회원가입 페이지 회원가입 페이지. 입력값 이스케이프만 하고 있으므로 특수문자 있는 상태 그대로 DB에 저장됩니다.

이 값은 DB에 문자열로 저장되어, 이후 조회 및 수정 시 재사용됩니다.

3.2 Step 2: 마이페이지 접속

생성한 계정으로 로그인 후 mypage.php에 접속하면, 서버는 세션에 저장된 username 값을 이용하여 사용자 정보를 조회합니다. 이때 다음과 같은 쿼리가 실행됩니다.

마이페이지 ‘admin’ or ‘1’ = ‘1 계정의 마이페이지

  • 실행 쿼리: SELECT * FROM logindata WHERE username=''admin' or '1' = '1'

이 쿼리는 조건절이 항상 참이므로, 테이블의 첫 번째 레코드를 반환하게 됩니다. 그 결과 실제 로그인 계정과 다른 정보가 화면에 표시되며, SQL Injection이 확인됩니다.

3.3 Step 3: 전체 계정 정보 일괄 변경

mypage_update.php에서 비밀번호 또는 주소를 변경하면 아래와 같은 취약 쿼리가 실행됩니다.

공격 결과 모든 계정의 패스워드와 주소가 변경된 결과

  • 실행 쿼리: UPDATE logindata SET password_hash='$password_hash', address='$address' WHERE username=''admin' or '1' = '1'

이 쿼리는 WHERE 조건이 항상 참이 되어 logindata 테이블의 모든 계정이 업데이트됩니다. 공격자는 모든 계정의 비밀번호와 주소를 일괄 변경할 수 있습니다.


4. 영향도 분석

4.1 심각도 평가

  • Confidentiality: High
  • Integrity: High
  • Availability: Medium

이번 취약점은 전체 회원 데이터베이스에 영향을 주며, 모든 계정 정보의 무단 변경이 가능하므로 심각도가 높습니다.

4.2 예상 피해 시나리오

  1. 공격자가 악의적인 username으로 계정 생성
  2. mypage.php에서 전체 사용자 정보 조회 또는 다른 계정 정보 노출
  3. mypage_update.php를 통해 모든 계정의 비밀번호 및 주소 일괄 변경
  4. 결과적으로 서비스 인증 기능이 무력화되어 전체 계정 접근이 가능해짐

4.3 영향 범위

영향 영역설명
전체 회원 정보logindata 테이블의 모든 레코드가 대상
계정 무력화전체 계정 비밀번호 및 주소 일괄 수정 가능
서비스 신뢰도인증 기능 붕괴로 인해 서비스 신뢰도 저하

5. 보안 대응 (Remediation)

5.1 즉시 조치

  • mypage.php, mypage_update.php에 Prepared Statement 적용
  • register_process.php에도 Prepared Statement 적용을 권고
  • username 입력값에 대한 서버 사이드 검증 추가
  • 세션에서 가져온 값도 쿼리에 직접 삽입하지 않도록 수정
  • WHERE username = ? LIMIT 1 형태로 단일 레코드만 조회/수정

5.2 권장 개선사항

  • SQL 메타문자를 제거하는 대신 파라미터 바인딩 사용
  • 회원정보 수정 시 세션 기반 사용자 검증 강화
  • 쿼리 오류 발생 시 민감 정보 노출 방지를 위한 에러 처리 개선
  • DB 연결 시 mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) 및 mysqli_set_charset($conn, 'utf8mb4') 적용

5.3 우선순위

우선순위조치난이도
CriticalPrepared Statement 적용낮음
High입력값 검증 강화중간
High단일 레코드 조회/수정 제한낮음
Medium에러 처리 및 문자셋 설정낮음

6. 결론

이번 분석에서는 회원가입 시 악의적인 username이 저장되어 후속 쿼리에서 재사용될 때 2차 SQL Injection이 발생하는 위험을 확인했습니다. 외부 입력값뿐 아니라 DB에서 불러오는 값도 모두 Prepared Statement로 안전하게 처리해야 하며, 입력값 검증과 단일 레코드 제한을 통해 동일한 취약점이 재발하지 않도록 해야 합니다.

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