ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Clear] CODEGATE2022 superbee Writeup
    CTF/CODEGATE2022 2022. 3. 1. 14:21

    주어진 페이지에 접근해보자.

    404 Error page가 나와 다소 당황스럽지만, beego 2.0.0 을 사용한다는 점을 확인 하고 주어진 소스코드를 분석해보자.

     

    우선 app.conf 파일에 글로벌 파라미터처럼 보이는 값들이 존재하지만, auth_key, password, flag 값은 숨겨져있다.

    ### app.conf
    
    app_name = superbee
    auth_key = [----------REDEACTED------------]
    id = admin
    password = [----------REDEACTED------------]
    flag = [----------REDEACTED------------]

    아래와 같이 index.html과 login.html 페이지가 존재함을 알 수 있다.

     

    index.html 페이지에 접근하면 위 글로벌 설정 값에서 flag를 읽어서 바로 출력해준다.

    ### index.html
    <html>
        <head>
            <title>{{.app_name}}</title>
        </head>
        <body>
            <h3>Index</h3>
            {{.flag}}
        </body>
    </html>

    login.html 페이지가 별도로 존재하는것으로 보아, admin 계정으로 로그인하면 위 index.html 페이지에서 바로 플래그를 출력해주는 듯 하다.

    ### login.html
    <html>
        <head>
            <title>{{.app_name}}</title>
        </head>
        <body>
            <h3>Login</h3>
            <form action="./auth" method="post">
                ID : <input type="text" name="id"><br>
                PW : <input type="password" name="password"><br>
                <input type="submit" value="login">
            </form>
        </body>
    </html>

     

    가장 핵심적인 main.go 페이지를 분석해보자.

    package main
    
    import (
    	"crypto/aes"
    	"crypto/cipher"
    	"crypto/md5"
    	"encoding/hex"
    	"bytes"
    	"github.com/beego/beego/v2/server/web"
    )
    
    type BaseController struct {
    	web.Controller
    	controllerName string
    	actionName string
    }
    
    type MainController struct {
    	BaseController
    }
    
    type LoginController struct {
    	BaseController
    }
    
    type AdminController struct {
    	BaseController
    }
    
    var admin_id string
    var admin_pw string
    var app_name string
    var auth_key string
    var auth_crypt_key string
    var flag string
    
    func AesEncrypt(origData, key []byte) ([]byte, error) {
    	padded_key := Padding(key, 16)
    	block, err := aes.NewCipher(padded_key)
    	if err != nil {
    		return nil, err
    	}
    	blockSize := block.BlockSize()
    	origData = Padding(origData, blockSize)
    	blockMode := cipher.NewCBCEncrypter(block, padded_key[:blockSize])
    	crypted := make([]byte, len(origData))
    	blockMode.CryptBlocks(crypted, origData)
    	return crypted, nil
    }
    
    func Padding(ciphertext []byte, blockSize int) []byte {
    	padding := blockSize - len(ciphertext)%blockSize
    	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
    	return append(ciphertext, padtext...)
    }
    
    func Md5(s string) string {
    	h := md5.New()
    	h.Write([]byte(s))
    	return hex.EncodeToString(h.Sum(nil))
    }
    
    func (this *BaseController) Prepare() { ##### ......................................... (1)
    	controllerName, _ := this.GetControllerAndAction()
    	session := this.Ctx.GetCookie(Md5("sess")) ##### .......................... (1)-1
    
    	if controllerName == "MainController" {
    		if session == "" || session != Md5(admin_id + auth_key) { ##### ....... (1)-2
    			this.Redirect("/login/login", 403)
    			return
    		}
    	} else if controllerName == "LoginController" {
    		if session != "" {
    			this.Ctx.SetCookie(Md5("sess"), "")
    		}
    	} else if controllerName == "AdminController" {
    		domain := this.Ctx.Input.Domain()
    
    		if domain != "localhost" {
    			this.Abort("Not Local")
    			return
    		}
    	}
    }
    
    func (this *MainController) Index() { ##### ...................................... (2)
    	this.TplName = "index.html"
    	this.Data["app_name"] = app_name
    	this.Data["flag"] = flag
    	this.Render()
    }
    
    func (this *LoginController) Login() { ##### ..................................... (3)
    	this.TplName = "login.html"
    	this.Data["app_name"] = app_name
    	this.Render()
    }
    
    func (this *LoginController) Auth() { ##### ...................................... (4)
    	id := this.GetString("id")
    	password := this.GetString("password")
    
    	if id == admin_id && password == admin_pw {
    		this.Ctx.SetCookie(Md5("sess"), Md5(admin_id + auth_key), 300)
    
    		this.Ctx.WriteString("<script>alert('Login Success');location.href='/main/index';</script>")
    		return
    	}
    	this.Ctx.WriteString("<script>alert('Login Fail');location.href='/login/login';</script>")
    }
    
    func (this *AdminController) AuthKey() { ##### ................................... (5)
    	encrypted_auth_key, _ := AesEncrypt([]byte(auth_key), []byte(auth_crypt_key))
        ##### .................................................................... (5)-1
    	this.Ctx.WriteString(hex.EncodeToString(encrypted_auth_key)) ##### ....... (5)-2
    }
    
    func main() { ##### .............................................................. (6)
    	app_name, _ = web.AppConfig.String("app_name")
    	auth_key, _ = web.AppConfig.String("auth_key")
    	auth_crypt_key, _ = web.AppConfig.String("auth_crypt_key") ##### ......... (6)-1
    	admin_id, _ = web.AppConfig.String("id")
    	admin_pw, _ = web.AppConfig.String("password")
    	flag, _ = web.AppConfig.String("flag")
    
    	web.AutoRouter(&MainController{})
    	web.AutoRouter(&LoginController{})
    	web.AutoRouter(&AdminController{})
    	web.Run()
    }

    (1)의 prepare() 부분을 보면, 이 서비스는 크게 3가지 controller로 구성되어있음을 확인 할 수 있다.

    MainController : 특정 세션 관련 조건이 맞지 않으면(권한이 없으면) /login/login으로 튕겨낸다.

    (1)-1, (1)-2 를 참고하면 쿠키의 파라미터명이 "sess" 문자열을 MD5 한 값이고 value가 "admin"+auth_key 값을 MD5 한 값이면 플래그를 획득 할 수 있음을 알 수 있다. 

    LoginController : 세션값이 비어있으면 특정 조건의 세션을 세팅한다.

    AdminController : domain이 localhost가 아니면 Not local 을 출력해주며 Abort 해버린다.

     

    그리고 (2)~(5) 의 선언부를 보면 각 기능이 특정 Controller로 선언되어있음을 알 수 있다.

    MainController : Index()

    LoginController : Login(), Auth()

    AdminController : Authkey()

     

    (6)의 main을 보면 각각 파라미터에 config 파일의 값을 불러옴을 알 수 있다.

    그런데 이상한점이 있다. 

    (6)-1 을 보면 auth_crypt_key 를 app.conf파일에서 불러오는데 다시 살펴보면 해당 값은 선언되어있지 않다.

    ### app.conf
    
    app_name = superbee
    auth_key = [----------REDEACTED------------]
    id = admin
    password = [----------REDEACTED------------]
    flag = [----------REDEACTED------------]

     

    그 후 각 컨트롤러에 대해 AutoRouter가 실행되는데 document를 살펴보면 아래와 같이 작동함을 알 수 있다.

    https://github.com/beego/beedoc/blob/master/en-US/mvc/controller/router.md#auto-matching

     

    GitHub - beego/beedoc: An open source project for beego documentation.

    An open source project for beego documentation. Contribute to beego/beedoc development by creating an account on GitHub.

    github.com

    실제로 (5)-2를 통해 encrypted_auth_key 를 획득하기 위해서는 /admin/authkey 에 접근해야 한다.

    (1)에서 AdminController의 조건을 맞춰주기 위해 Burp Suite를 활용하여 조건을 맞춰주면 정상적으로 encrypted_auth_key를 획득 할 수 있다.

    (5)-1을 보면 아래 추출 된 값은 auth_key와 auth_crypt_key 로 aes 암호화가 된 값임을 알 수 있다.

    00fb3dcf5ecaad607aeb0c91e9b194d9f9f9e263cebd55cdf1ec2a327d033be657c2582de2ef1ba6d77fd22784011607

    따라서, 이를 decrypt하면 auth_key를 획득 할 수 있다. 

    aes.py
    
    from Crypto.Cipher import AES
    from Crypto.Util.Padding import pad, unpad
    
    key = b"\x10"*16
    iv = b"\x10"*16
    #raw = pad(input_value)
    encrypted = bytes.fromhex("00fb3dcf5ecaad607aeb0c91e9b194d9f9f9e263cebd55cdf1ec2a327d033be657c2582de2ef1ba6d77fd22784011607")
    cipher = AES.new(key, AES.MODE_CBC, iv)
    #output_value = cipher.encrypt(raw).hex()
    decrypted_value = unpad(cipher.decrypt(encrypted),16)
    print(decrypted_value)

    필요한 조건을 모두 갖추었으므로 admin의 cookie key/value를 구해보자.

     

    "sess" 를 md5화 하면 

    f5b338d6bca36d47ee04d93d08c57861

    "adminTh15_sup3r_s3cr3t_K3y_N3v3r_B3_L34k3d" 를 md5화 하면

    e52f118374179d24fa20ebcceb95c2af

    위 두 값을 활용하여 /main/index.html에 접근하면 플래그를 획득 할 수 있다.

     

    FLAG = codegate2022{d9adbe86f4ecc93944e77183e1dc6342}

    반응형

    'CTF > CODEGATE2022' 카테고리의 다른 글

    [Study] CODEGATE2022 myblog Writeup  (0) 2022.03.01
    [Study] CODEGATE2022 babyfirst Writeup  (0) 2022.03.01
    [Clear] CODEGATE2022 CAFE Writeup  (0) 2022.03.01

    댓글

Designed by Tistory.