新手入门级 | nodejs调试

admin 2024年5月29日16:30:53评论10 views字数 5311阅读17分42秒阅读模式

本文由掌控安全学院 - 我是大白 投稿

nodejs调试记录

问题 & 环境搭建

环境搭建

1,安装nodejs,这个直接网上找教程就行

2,安装vscode

3,我是直接在nodejs安装路径下,创建了一个app文件,然后在里面写js代码的。

新手入门级 | nodejs调试

然后接下来,就是写代码,进行调试,以下代码是让node监听33456端口,然后,获取file参数,返回指定文件内容的一个功能。


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

const app = express();

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

app.get("/", (req, res) => { try { res.setHeader("Content-Type", "text/html"); console.log(req.query.file); res.send(fs.readFileSync(req.query.file || "index.html").toString()); } catch(err) { console.log(err); res.status(500).send("Internal server error"); }});

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


问题

我在vscode中点击运行和调试后,报错:windows Cannot find module 'express',提示说找不到express这个模块

问题解决:https://juejin.cn/post/7159201118457692174

在写代码的目录下打开cmd,然后运行一下npm install express进行模块安装即可。

新手入门级 | nodejs调试

调试过程

现在当前目录创建一个launch.json配置文件,一般在vscode左上角有创建提示,点击即可创建,然后将node_internals那一行注释掉,在将js文件改成实际的js文件名。

新手入门级 | nodejs调试


//launch.json:

{ // 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。 // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "启动程序", "skipFiles": [ // "<node_internals>/**" ], "program": "${workspaceFolder}\main.js" } ]}

对于下面这句话不是很理解,因此尝试自己手动调试看结果会更理解一些。


express 使用 qs npm 模块来提供 req.query.file (file 为查询字符串参数名) ,这意味着它可以与字符串以外的其他类型一起使用。

如:?file[]=1&file[]=2 或者 ?file=1&file=2 ,这样最后 req.query.file 获取到的就是一个数组 ['1', '2'] ; 还有 ?file[a]=b&file[c]=d , req.query.file 获取到的是一个对象 {'a': 'b', 'c': 'd'}

首先,我们得先理解:req.query.file的作用是,req.query是获取URL中传递的参数,req.query.file就是获取URL中传递的file参数,同时呢,它除了字符串类型的传参file=abcd,还可以传参数组file=1&file=2以及对象file[a]=b

代码就是上面的代码,我们直接访问本地的33456传参查看结果:

可以看到,当我们进行重复传参file=b&file=areq.query.file

新手入门级 | nodejs调试

新手入门级 | nodejs调试

当我们传参数组,那么得到的就是一个对象,file[a]=b&file[c]=d,其中含有a属性,值为b,c属性,值为d

新手入门级 | nodejs调试

新手入门级 | nodejs调试

readFileSync调试

有以上前置知识,接下来我们进入readFileSync调试,来看看如何拿到/app/flag.txt中的flag.

在以上传参过程中,我们在vscode中,可以很明显的看到readFileSync的报错:The "path" argument must be of type string or an instance of Buffer or URL. Received an instance of Object,这个报错提示我们,readFileSync方法接收的路径必须是 字符串 、Buffer实例化对象以及URL实例化对象

新手入门级 | nodejs调试

而我们的传参file=b&file=a 以及file[a]=b&file[c]=d,一个是数组,一个是普通对象,显然readFileSync方法是不接受的。

接下来我们另写一段代码,并且在当前目录创建一个flag.txt,进行验证:


const fs = require('fs');// 注意:路径需要绝对路径let file1 = new URL("file:///G:/Software/nodejs/app/fl%61g.txt"); // URL类是内置的类,可以直接new URL对象let file2 = Buffer.from("G:/Software/nodejs/app/flag.txt"); // Bufferlet file3 = "G:/Software/nodejs/app/flag.txt"; // 字符串

console.log(fs.readFileSync(file1).toString());

console.log(fs.readFileSync(file2).toString());

console.log(fs.readFileSync(file3).toString());

正常读取flag,那么我们在做simplewaf这一题时,首先就是要想办法传入一个能够读取到flag的URL类,或者字符串,至于Buffer貌似是没法构造的【毕竟我不是这方面的master】,并且在路径中存在URL编码也同样可以正常读取文件。

新手入门级 | nodejs调试

接下来,我们来进行下断点调试,看看当readFileSync方法接收到参数之后,在其内部是如何进行处理的,看看是否能通过其工作原理,来达到我们绕过的目的。

我们将其他无关紧要的代码全部注释掉,只留下核心代码进行DEBUG。


const fs = require('fs');let file1 = new URL("file:///G:/Software/nodejs/app/fl%61g.txt"); console.log(fs.readFileSync(file1).toString());

在第三行下断点,代码运行后,我们跟进,进入到readFileSync方法内部:

第一行代码,根据英文释义,这应该是设置对文件如何进行操作的标志位,默认应该是r读取操作。这一行可以直接步过

第二行是类似于文件描述符样的东西,这一行也可以直接步过

第三行,是一个三目运算符,由于前面一行代码运行完之后,isUserFd为false,所以会进入到表达式fs.openSync当中。

新手入门级 | nodejs调试

此时path会传入到openSync方法当中进行一些处理,可以看到openSync的代码,我们在步过方法当中的第一行代码之后,传入的path从"file:///G:/Software/nodejs/app/fl%61g.txt"变成了"file:///G:/Software/nodejs/app/flag.txt",这说明在方法getValidatedPath中对我们传入的path进行了解码操作。

新手入门级 | nodejs调试

我们可以进入到getValidatedPath内部,看看它对我们传的URL对象是如何处理的:可以看到,第一行代码就调用了toPathIfFileURL,那么进入这个方法看看

新手入门级 | nodejs调试

进来之后,可以看到由调用了isURLInstance方法,继续跟进,看看该方法如何处理

新手入门级 | nodejs调试

跟进之后可以看到,首先判断是否为空,然后判断是否有href属性以及 origin属性,最终的结果返回一个布尔值,我们可以判断一下,因为我们传入的是一个URL对象,因此 不等于null,并且是具有href和origin属性的,因此三个都为true(这个地方如果有不懂的,可以停止程序,出去将这三者都输出一下就知道结果了),最终返回结果true。

新手入门级 | nodejs调试

代码继续往下运行:因为isURLInstance方法得出结果为true,所以!true即为false,代码运行到1515行,此时又调用了一个fileURLToPath方法对URL对象path进行处理,进入该函数看看:

新手入门级 | nodejs调试

第一个if判断,判断是不是string类型,显然不是,是一个URL对象

第二判断,判断是不是URL对象,是

第三个判断,判断protocol是不是file:,是file:,来到return这一行,不是的话就抛出异常;

新手入门级 | nodejs调试

来到return 三目运算,isWindows是判断当前是linux系统还是windows系统,因为我这里调试使用的是windows系统,所以isWindows是有值的,所以会进入到 getPathFromURLWin32 方法

新手入门级 | nodejs调试

进入该方法看看:大致阅读代码,会从传入的url中检测是否包含%,并且不能包含有编码后的\/,否则会抛出异常,然后会对字符串进行替换,将/全部替换成\,然后使用decodeURIComponent方法进行URL解码。

新手入门级 | nodejs调试

解码之后还会检测是否为绝对路径,如果不是绝对路径会抛出异常,返回URL解码后的路径。

再往下看,判断hostname属性是否为空,不为空则会返回一个UNC路径,为空往下继续(在对应的linux方法中只判断hostname是否为空,如果为空则继续,不为空则抛出异常,毕竟linux下默认是没有SMB服务的):

新手入门级 | nodejs调试

接下来获取盘符,判断盘符是否合法a-Z,接着获取冒号,判断路径是否为绝对路径(这个判断在windows对应的方法下有,linux下没有,毕竟linux下没有盘符的说法):

新手入门级 | nodejs调试

最终,将路径前面多余的\去掉并返回:

新手入门级 | nodejs调试

出来之后便进入到validatePath函数,

新手入门级 | nodejs调试

validatePath函数主要是对路径是否为字符串以及是否为空的检测,如果未通过则抛出异常,显然是通过的,直接步过这个函数。

新手入门级 | nodejs调试

之后就是对flag.txt的读取操作了。

因此,这里我们可以画个草图方便理解:

新手入门级 | nodejs调试

因此,我们要获取flag需要满足以下条件:

1,传参要为URL对象(避免引发readFileSync报错)

2,存在href属性

3,存在origin属性(只要具有href、origin属性且不为null,isURLInstance方法就返回true)

4,protocol的属性值必须为file:

5,hostname`属性值必须为空

6,进行双重url编码(因为express模块会进行一次编码),绕过对flag关键字的检测(并且在getPathFromURLWin32或getPathFromURLPosix方法中会进行一次URL解码,因此可以进行URL编码绕过)

7,pathname设置为要读取的文件路径,注意包含flag字符就需要进行url编码绕过


linux:

?file[href]=1&file[origin]=2&file[protocol]=file:&file[hostname]=&file[pathname]=/etc/passwd

?file[href]=1&file[origin]=2&file[protocol]=file:&file[hostname]=&file[pathname]=/app/fl%2561g.txt

windows:

?file[href]=1&file[origin]=2&file[protocol]=file:&file[hostname]=&file[pathname]=/G:/Software/nodejs/app/fl%2561g.txt

新手入门级 | nodejs调试

新手入门级 | nodejs调试

最后解释一下为什么windows下,路径长这样/G:/Software/nodejs/app/fl%2561g.txt,前面多了一个斜杠

因为在getPathFromURLWin32方法中,letter会获取盘符,取的是字符串中的下标为1的字符,因此需要多加一个斜杠,并且seq取冒号,取得是字符串中的下标为2的字符。在使用file协议的时候,通常也会是用file:///路径的形式。

新手入门级 | nodejs调试

新手入门级 | nodejs调试

参考资料

https://cloud.tencent.com/developer/article/2123023

https://brycec.me/posts/corctf_2022_challenges#simplewaf

感谢大佬的分享,【膜膜膜】

申明:本公众号所分享内容仅用于网络安全技术讨论,切勿用于违法途径, 所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法.

原文始发于微信公众号(掌控安全EDU):新手入门级 | nodejs调试

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年5月29日16:30:53
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   新手入门级 | nodejs调试https://cn-sec.com/archives/2791373.html

发表评论

匿名网友 填写信息