Client Side Template Injection (CSTI)

Client Side Template Injection (CSTI)

in

๐Ÿ”Ž What is Client Side Template Injection?

ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด DOM ๋‚ด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๊ธฐ์— ๊ฐ„ํŽธํ•˜๊ณ , ์›น ํŽ˜์ด์ง€๋ฅผ ์ปดํฌ๋„ŒํŠธํ™” ํ•˜์—ฌ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ๋ฐœํ•˜๋Š”๋ฐ์— ์žˆ์–ด์„œ ๊ต‰์žฅํžˆ ํŽธ๋ฆฌํ•ด์ง€๊ธฐ ๋–„๋ฌธ์— ํ”„๋ก ํŠธ์—”๋“œ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ž์ฃผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ณดํŽธ์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ์ข…๋ฅ˜๋กœ๋Š” Vue, React, AngularJS ๋“ฑ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
CSTI๋Š” ์ด์šฉ์ž์˜ ์ž…๋ ฅ๊ฐ’์ด client side template framework์— ์˜ํ•ด ํ…œํ”Œ๋ฆฟ์œผ๋กœ ํ•ด์„๋˜์–ด ๋ Œํ„ฐ๋ง๋  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
๊ฐœ๋ฐœ์˜ ํŽธ์˜์„ฑ์„ ๋†’์—ฌ์ฃผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์ง€๋งŒ ์ž˜๋ชป๋œ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉ๋  ๊ฒฝ์šฐ, ์ด๋ฅผ ํ†ตํ•ด XSS ์ทจ์•ฝ์ ๊นŒ์ง€ ์—ฐ๊ณ„ํ•˜๋Š”๊ฒƒ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

Vue (https://vuejs.org/)

image

์˜คํ”ˆ์†Œ์Šค ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ์ด์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค๋‚˜ Single Page Application์„ ๋นŒ๋“œํ• ๋•Œ ์‚ฌ์šฉ๋˜๋ฉฐ Vue ํ˜น์€ Vue.js๋ผ๊ณ  ๋ถˆ๋ฆฝ๋‹ˆ๋‹ค.
`` ๋กœ ๊ฐ์‹ธ์ง„ ๋ถ€๋ถ„์ด Vue ํ…œํ”Œ๋ฆฟ ๋ถ€๋ถ„์ด๋ฉฐ ํ•ด๋‹น ํ…œํ”Œ๋ฆฟ ๋‚ด์—์„œ ๋ฌธ์ž์—ด์„ ํ‘œ์‹œํ•˜๊ฑฐ๋‚˜, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ‘œํ˜„์‹์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<script src="https://unpkg.com/vue@3"></script>
<div id="app">{{ message }}</div>
<script>
  Vue.createApp({
    data() {
      return {
        message: 'Hello Vue!'
      }
    }
  }).mount('#app')
</script>

์•„๋ž˜์˜ ์ฝ”๋“œ๋Š” ์ด์šฉ์ž์˜ ์ž…๋ ฅ์„ ๊ทธ๋Œ€๋กœ ํŽ˜์ด์ง€์— ์ถœ๋ ฅํ•˜๋Š” Vue Template Injection์— ์ทจ์•ฝํ•œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.
์ด์šฉ์ž๋กœ๋ถ€ํ„ฐ ์ž…๋ ฅ๋ฐ›์€ GET ๋ฉ”์†Œ๋“œ์˜ msg ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ทธ๋Œ€๋กœ ์ถœ๋ ฅํ•˜์ง€๋งŒ, htmlspecialchars ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๊บฝ์‡  ๋ฌธ์ž๊ฐ€ ๋ชจ๋‘ ์ธ์ฝ”๋”ฉ ๋˜๊ธฐ ๋–„๋ฌธ์— ์ผ๋ฐ˜์ ์ธ XSS ๊ณต๊ฒฉ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ์ถœ๋ ฅ๋˜๋Š” ๊ฐ’์ด Vue ํ…œํ”Œ๋ฆฟ์œผ๋กœ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๊ธฐ ๋–„๋ฌธ์— Template Injection์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

<script src="https://unpkg.com/vue@3"></script>
<div id="app">
	<?php echo htmlspecialchars($_GET['msg']); ?>
</div>
<script>
  Vue.createApp({
    data() {
      return {
        message: 'Hello Vue!'
      }
    }
  }).mount('#app')
</script>

Template Injection์ด ๋ฐœ์ƒํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์€ `` ๊ณผ ๊ฐ™์€ ์‚ฐ์ˆ  ์—ฐ์‚ฐํ˜•ํƒœ์˜ ํ…œํ”Œ๋ฆฟ์„ ์ž…๋ ฅํ–ˆ์„ ๋•Œ, ๊ฐ’์ด 2 ๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค๋ฉด Template Injection์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

image

Template Injection์ด ๋ฐœ์ƒํ•  ๋•Œ ์ด๋ฅผ ์ž„์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ ์‹คํ–‰์œผ๋กœ ์—ฐ๊ณ„ํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ๋ณดํ†ต ์ƒ์„ฑ์ž(constructor)๋ฅผ ์ด์šฉํ•˜๋นˆ๋‹ค. Vue ํ…œํ”Œ๋ฆฟ ์ปจํ…์ŠคํŠธ ๋‚ด์—์„œ ์ƒ์„ฑ์ž์— ์ ‘๊ทผํ•˜์—ฌ ์ž„์˜ ์ฝ”๋“œ์— ํ•ด๋‹นํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ์ด๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ XSS ๊ณต๊ฒฉ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
Vue ํ…œํ”Œ๋ฆฟ ์ปจํ…์ŠคํŠธ ๋‚ด์—์„œ ์ƒ์„ฑ์ž์— ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์กด์žฌํ•˜์ง€๋งŒ ๋Œ€ํ‘œ์ ์œผ๋กœ `` ๋ฅผ ์ด์šฉํ•ด ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

{{_Vue.h.constructor("alert(1)")()}}



AngularJS (https://angular.io/docs)

image

์•ต๊ทค๋ŸฌJS๋Š” Google์—์„œ ๊ฐœ๋ฐœํ•˜์—ฌ 2010๋…„ ์ถœ์‹œ๋œ ํ”„๋ก ํŠธ์—”๋“œ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.
AngularJS๋Š” ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ๊ธฐ๋ฐ˜์˜ ์˜คํ”ˆ์†Œ์Šค ํ”„๋ ˆ์ž„์›Œํฌ์ด๋ฉฐ CLI ๋„๊ตฌ์—์„œ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ๋ฐœ์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.
Vue ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ `` ๋กœ ๊ฐ์‹ธ์ง„ ๋ถ€๋ถ„์ด AngularJS ํ…œํ”Œ๋ฆฟ ๋ถ€๋ถ„์ด๋ฉฐ, ํ•ด๋‹น ํ…œํ”Œ๋ฆฟ ๋‚ด์—์„œ ๋ฌธ์ž์—ด์„ ํ‘œ์‹œํ•˜๊ฑฐ๋‚˜, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ‘œํ˜„์‹์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<!doctype html>
<html ng-app>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
  </head>
  <body>
    <div>
      <label>Name:</label>
      <input type="text" ng-model="yourName" placeholder="Enter a name here">
      <hr>
      <h1>Hello {{yourName}}!</h1>
    </div>
  </body>
</html>

AngularJS Template Injection์— ์ทจ์•ฝํ•œ ์˜ˆ์ œ ์†Œ์Šค์ฝ”๋“œ๋กœ ์ž…๋ ฅ๋ฐ›์€ GET๋ฉ”์†Œ๋“œ์˜ msgํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ทธ๋Œ€๋กœ ์ถœ๋ ฅํ•˜์ง€๋งŒ, ๊บฝ์‡ ๊ฐ€ ๋ชจ๋‘ ์ธ์ฝ”๋”ฉ ๋˜๊ธฐ ๋–„๋ฌธ์— XSS ๊ณต๊ฒฉ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์ถœ๋ ฅ๋˜๋Š” ๊ฐ’์ด AngularJS์˜ ํ…œํ”Œ๋ฆฟ์œผ๋กœ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๊ธฐ ๋–„๋ฌธ์— Template Injection์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<!doctype html>
<html ng-app>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
  </head>
  <body>
      <?php echo htmlspecialchars($_GET['msg']); ?>
  </body>
</html>

AngularJS ํ…œํ”Œ๋ฆฟ์—์„œ ์ƒ์„ฑ์ž์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” `` ๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ด๋ฅผ ์ด์šฉํ•˜์—ฌ ์ž„์˜ ์ฝ”๋“œ์— ํ•ด๋‹นํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ํ˜ธ์ถœํ•˜์—ฌ ๊ณต๊ฒฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

{{ constructor.constructor("alert(1)")() }}

๐Ÿ Cheat sheet


๐Ÿ‘€ How to Prevent ?


๐Ÿ“ƒ References