Season 1/기술 보안

Server-side JavaScript Injection

작성자 - LRTK

Objective

Python과 Perl처럼 자체적인 엔진를 사용하여 Java Script를 사용할 수 있게 해준 것이 Node.js이다.

이를 통해 Node.js로 구성된 백엔드에서 Headless Browser 등을 통해 처리하는 로직이 있는 경우, 공격자가 Node.js가 해석하여 처리할 수 있는 값을 전달함으로써 서버사이드에서 원하는 Java Sctipt를 실행하도록 하는 공격이다.

 

간단하게 생각하면 SSTI와 비슷한 개념으로 이해하면 된다. 서버 사이드의 템플릿 엔진에 악성 템플릿 구문을 삽입함으로써 원하는 행위를 실행하도록 하는 것이 템플릿 엔진이 아닌 Node.js를 해석하는 엔진인 V8로 타겟으로 공격을 시도한 것으로 난 이해했다.

 

HAHWUL님의 블로그에서 해당 취약점을 알게되었고, 어떻게 서버 사이드에서 어떻게 처리하길래 사용자가 입력한 Java Script가 실행되는지 궁금하여 정리한다.

Introduction

 const express = require('express')
 const app = express()
 ​
 ​
 app.get('/', function (req, res) {
     console.log("[INPUT LOG] Calc Parameter - " + req.query.calc)
     
     // eval 함수에 입력 값 삽입
     res.send(
       "<h1>This is SSJI Test Page<h1><br>\
        <h3>Calc Result : " + eval(req.query.calc) + "<h3>"
     )
 })
 ​
 ​
 var server = app.listen(3000, function () {
     var host = server.address().address
     var port = server.address().port
 ​
     console.log('Server is working : PORT - ', port)
 })

SSJI 취약점을 설명하기 위해 위와 같은 간단하게 Node.js를 이용하여 웹서버를 제작하였다. / 경로에서 calc 파라미터에 값을 넣으면, eval 함수에 넣어지면서 페이지에 출력된다.

 

위 예시가 정말 말이 안되지만 복잡한 서비스를 구축할 때, eval 함수의 강력한 기능을 이용하여 간단하게 구현하는 경우도 있다고 한다.

 

calc 파라미터에 "1%2b1"이라는 부분은 Get 메소드로 전달하기 때문에 +이 URL 인코딩된 모습이다. 1+1를 전달해주면 서버에선 eval 함수에 넣어서 결과값을 사진과 같이 출력한다.

 

하지만 공격자가 require("child_process").execSync('ls') 와 같은 값을 삽입하면 eval 함수에 그래로 들어가서 디렉터리 내부의 파일들을 출력할 것이다.

 

위의 예시처럼 사용자의 입력값을 신뢰하여 eval 함수에 넣으면 SSJI 취약점으로 이어질 수 있다.

해당 취약점을 보안하는 방법은 사용자의 입력값을 신뢰하지 않으며, 서버 단에서는 클라이언트에서 온 값을 eval 함수에 넣지 않도록 한다.

Reference

Contents

이 글이 도움이 되었다면, 응원의 댓글 부탁드립니다.