corCTF2022

Web

jsonquiz.be.ax

js看源码找接口,修改传入的参数,默认不管你做题得了多少分都是传入0分,所以可以在最后一个选项的时候用burp抓包,然后修改score的值就能拿到flag

image-20220809232729977

image-20220809233306343

msfrog-generator

image-20220809234101500

尝试目录穿越得到报错回显,试了一下,不让读非图片的文件

image-20220809234205987

image-20220809234745397

可以看到使用反引号包裹的,尝试执行多条命令

image-20220809235004844

拿到flag

simplewaf

这是一道nodejs的题,源代码特别简单,就是一个简单的关键词过滤

const express = require("express");
const fs = require("fs");

const app = express();

const PORT = process.env.PORT || 3456;

app.use((req, res, next) => {
    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");
        res.send(fs.readFileSync(req.query.file || "index.html").toString());  
        // 传入一个参数file,通过fs.readFileSync函数读取文件,并返回渲染
    }
    catch(err) {
        console.log(err);
        res.status(500).send("Internal server error");
    }
});

app.listen(PORT, () => console.log(`web/simplewaf listening on port ${PORT}`));

image-20220810003301243

允许传入URL对象,URL对象会自动对参数进行url解码,再进行一次浏览器解码,所以可以通过传入url对象进行绕过,但是这里传入的参数是通过req.query.file参数进行解析的,只能允许传入字符串。但是可以通过数组的方式传入对象

分析一下fs.readFileSync函数

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);
    // 创建一个文件句柄,如果不是文件描述符就调用fs.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;
}

很显然会调用到fs.openSync(path, options.flag, 0o666)这里去,继续跟进

function openSync(path, flags, mode) {
  path = getValidatedPath(path);
    //检测url,跟进看看
  const flagsNumber = stringToFlags(flags);
  mode = parseFileMode(mode, 'mode', 0o666);

  const ctx = { path };
  const result = binding.open(pathModule.toNamespacedPath(path),
                              flagsNumber, mode,
                              undefined, ctx);
  handleErrorFromBinding(ctx);
  return result;
}

getValidatedPath

function getValidatedPath (fileURLOrPath) {
  const path = fileURLOrPath != null && fileURLOrPath.href && fileURLOrPath.origin
    ? fileURLToPath(fileURLOrPath)
    : fileURLOrPath
  return path
}

这里有个函数fileurltopath,看名字估计是将url对象转换为时间文件地址的功能,继续跟进

const fileURLToPath = (path) => {
  if (typeof path === 'string') {
    path = new URL(path)
  } else if (!isURLInstance(path)) {
    throw new ERR_INVALID_ARG_TYPE('path', ['string', 'URL'], path)
  }

  if (path.protocol !== 'file:') {
    throw new ERR_INVALID_URL_SCHEME('file')
  }
    //令path.protocol属性为file:

  return isWindows
    ? getPathFromURLWin32(path)
    : getPathFromURLPosix(path)
    //进入getPathFromURLPosix
}
const isURLInstance = (input) => {
  return input != null && input.href && input.origin
    //存在href和origin属性就返回真
}

跟进getPathFromURLPosix

const getPathFromURLPosix = (url) => {
  if (url.hostname !== '') {
    throw new ERR_INVALID_FILE_URL_HOST(process.platform)
  }
//令path.hostname为空
  const pathname = url.pathname

  for (let n = 0; n < pathname.length; n++) {
    if (pathname[n] === '%') {
      const third = pathname.codePointAt(n + 2) | 0x20
      if (pathname[n + 1] === '2' && third === 102) {
        throw new ERR_INVALID_FILE_URL_PATH('must not include encoded / characters')
      }
    }
      //这里对pathname进行解码
  }

到这里就结束了,总结一下

protocol属性为file:

存在href和origin属性

hostname填url编码后的值

image-20220810010600914

本地复现成功

image-20220810010816892

数组传参,转对象,成功拿到flag

参考文章

https://nanimokangaeteinai.hateblo.jp/entry/2022/08/09/022238

最后修改:2022 年 08 月 12 日
如果觉得我的文章对你有用,请随意赞赏