首页
社区
课程
招聘
[corctf]simplewaf
发表于: 2022-8-21 00:28 8415

[corctf]simplewaf

2022-8-21 00:28
8415

题目提供了源码,是一个node.js
先从main.js入手

漏洞点存在于readFileSync()存在文件读取,而且传入参数file可控,但是本题提供了一个看似简单,实则恶心的waf中间件

用于检测请求中是否包含flag字样,但是node基于javascript,所以并不像php那样有很多的bypass方式,所以我们需要看看readFileSync()有没有什么比较特殊的用法,来绕过"flag"

readFileSync()的一个调用堆栈情况:

从内部一步步的审计readFileSync()函数:

调用了openSync()函数,跟进一下:

继续跟进getValidatePath()函数:

这里发现当fileURLOrPath的href属性和origin属性不为空,就会执行fileURLToPath函数

控制path.protocol属性为file: 可以继续执行getPathFromURLPosix()

当url.hostname为空时,这个函数对pathname进行了一个url的解码,因此我们发现,当传入的值在满足上述的情况下,readFileSync()支持传入urlencode的形式,于是可以通过url解码来避免对字符串flag的使用

当然flag需要经过双url编码,因为 Express 已经将 URL 解码一次

payload:

分析到这里,可以说这道题雀氏精彩,剖析了整个函数的调用,最终发现了可利用的点,实现了完美的bypass

const express = require("express");
const fs = require("fs");  //引入fs模块
 
const app = express();  //基于nodejs平台的web框架
 
const PORT = process.env.PORT || 3456;
 
app.use((req, res, next) => {   //一个waf中间件,检测检查请求正文、标头或查询字符串的 JSON 表示是否包含字符串“flag”
    if([req.body, req.headers, req.query].some(
        (item) => item && JSON.stringify(item).includes("flag")
    )) {
        return res.send("bad hacker!");
    }
    next();
});
 
app.get("/", (req, res) => {
    try {
        res.setHeader("Content-Type", "text/html");  //设置一个header
        console.log(req.query.file);
        res.send(fs.readFileSync(req.query.file || "index.html").toString());        //同步读取文件
    }
    catch(err) {
        console.log(err);  //发生错误则返回500
        res.status(500).send("Internal server error");
    }
});
 
app.listen(PORT, () => console.log(`web/simplewaf listening on port ${PORT}`));
const express = require("express");
const fs = require("fs");  //引入fs模块
 
const app = express();  //基于nodejs平台的web框架
 
const PORT = process.env.PORT || 3456;
 
app.use((req, res, next) => {   //一个waf中间件,检测检查请求正文、标头或查询字符串的 JSON 表示是否包含字符串“flag”
    if([req.body, req.headers, req.query].some(
        (item) => item && JSON.stringify(item).includes("flag")
    )) {
        return res.send("bad hacker!");
    }
    next();
});
 
app.get("/", (req, res) => {
    try {
        res.setHeader("Content-Type", "text/html");  //设置一个header
        console.log(req.query.file);
        res.send(fs.readFileSync(req.query.file || "index.html").toString());        //同步读取文件
    }
    catch(err) {
        console.log(err);  //发生错误则返回500
        res.status(500).send("Internal server error");
    }
});
 
app.listen(PORT, () => console.log(`web/simplewaf listening on port ${PORT}`));
app.use((req, res, next) => {   //一个waf中间件,检测检查请求正文、标头或查询字符串的 JSON 表示是否包含字符串“flag”
    if([req.body, req.headers, req.query].some(
        (item) => item && JSON.stringify(item).includes("flag")
    )) {
        return res.send("bad hacker!");
    }
    next();
});
app.use((req, res, next) => {   //一个waf中间件,检测检查请求正文、标头或查询字符串的 JSON 表示是否包含字符串“flag”
    if([req.body, req.headers, req.query].some(
        (item) => item && JSON.stringify(item).includes("flag")
    )) {
        return res.send("bad hacker!");
    }
    next();
});
 
readFileSync -> openSync -> getValidatedPath
-> toPathIfFileURL -> fileURLToPath -> getPathFromURLPosix
readFileSync -> openSync -> getValidatedPath
-> toPathIfFileURL -> fileURLToPath -> getPathFromURLPosix
function readFileSync(path, options) {
  options = getOptions(options, { flag: 'r' });
  const isUserFd = isFd(path); // File descriptor ownership
  const fd = isUserFd ? path : fs.openSync(path, options.flag, 0o666); //调用了openSync()
 
  const stats = tryStatSync(fd, isUserFd);
  const size = isFileType(stats, S_IFREG) ? stats[8] : 0;
  let pos = 0;
  let buffer; // Single buffer with file data
  let buffers; // List for when size is unknown
 
  if (size === 0) {
    buffers = [];
  } else {
    buffer = tryCreateBuffer(size, fd, isUserFd);
  }
 
  let bytesRead;
 
  if (size !== 0) {
    do {
      bytesRead = tryReadSync(fd, isUserFd, buffer, pos, size - pos);
      pos += bytesRead;
    } while (bytesRead !== 0 && pos < size);
  } else {
    do {
      // The kernel lies about many files.
      // Go ahead and try to read some bytes.
      buffer = Buffer.allocUnsafe(8192);
      bytesRead = tryReadSync(fd, isUserFd, buffer, 0, 8192);
      if (bytesRead !== 0) {
        ArrayPrototypePush(buffers, buffer.slice(0, bytesRead));
      }
      pos += bytesRead;
    } while (bytesRead !== 0);
  }
 
  if (!isUserFd)
    fs.closeSync(fd);
 
  if (size === 0) {
    // Data was collected into the buffers list.
    buffer = Buffer.concat(buffers, pos);
  } else if (pos < size) {
    buffer = buffer.slice(0, pos);
  }
 
  if (options.encoding) buffer = buffer.toString(options.encoding);
  return buffer;
}
function readFileSync(path, options) {
  options = getOptions(options, { flag: 'r' });
  const isUserFd = isFd(path); // File descriptor ownership
  const fd = isUserFd ? path : fs.openSync(path, options.flag, 0o666); //调用了openSync()
 
  const stats = tryStatSync(fd, isUserFd);
  const size = isFileType(stats, S_IFREG) ? stats[8] : 0;
  let pos = 0;
  let buffer; // Single buffer with file data
  let buffers; // List for when size is unknown
 
  if (size === 0) {
    buffers = [];
  } else {
    buffer = tryCreateBuffer(size, fd, isUserFd);
  }
 
  let bytesRead;
 
  if (size !== 0) {
    do {
      bytesRead = tryReadSync(fd, isUserFd, buffer, pos, size - pos);
      pos += bytesRead;
    } while (bytesRead !== 0 && pos < size);
  } else {
    do {
      // The kernel lies about many files.
      // Go ahead and try to read some bytes.
      buffer = Buffer.allocUnsafe(8192);
      bytesRead = tryReadSync(fd, isUserFd, buffer, 0, 8192);
      if (bytesRead !== 0) {
        ArrayPrototypePush(buffers, buffer.slice(0, bytesRead));
      }
      pos += bytesRead;

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 3
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//