-
-
[corctf]simplewaf
-
发表于: 2022-8-21 00:28 8399
-
题目提供了源码,是一个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;
赞赏记录
参与人
雪币
留言
时间
伟叔叔
为你点赞~
2023-3-18 01:29
一笑人间万事
为你点赞~
2023-1-11 18:29
ArT1_
为你点赞~
2022-8-21 00:28
赞赏
他的文章
- [原创]kalmarCTF 部分WP 9428
- [原创]【2022 羊城杯】 WP(部分) 11999
- [corctf]simplewaf 8400
- [原创]【HWS】两道好玩的misc 9925
看原图
赞赏
雪币:
留言: