티스토리 뷰

728x90
반응형

문제개요

AES Decrypt with known key

 

코드분석

우선 Flag의 위치를 탐색해보았습니다. 아래는 app.conf 파일의 내용인데요. flag는 해당 설정파일의 속성에 존재하고 있었습니다.

app_name = superbee
auth_key = [----------REDEACTED------------]
id = admin
password = [----------REDEACTED------------]
flag = [----------REDEACTED------------]

그리고 해당 flag의 내용은 다시 index.html 에서 확인할 수 있었습니다.

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

그리고 아래 내용에서 보면 알 수 있듯이 admin 계정으로 로그인에 성공하면 index.html 로 이동하고 flag 내용을 확인할 수 있다고 합니다.

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>")
}

근데 여기서 id 와 password 를 모르더라도 session 값이 관리자와 일치할 경우에도 관리자로 인식될 수 있겠다 싶었습니다.

 

문제에서 주어진 정보 중에 우리가 아는 내용은 admin_id 입니다. 그리고 auth_key 는 app.conf 에서도 확인했다시피 알 수 없었습니다. 그래서 auth_key부터 알아낼 방법을 찾아보았습니다.

 

그러다가 auth_key 를 암호화한 값을 출력해주는 타겟을 찾을 수 있었는데,

func (this *AdminController) AuthKey() {
	encrypted_auth_key, _ := AesEncrypt([]byte(auth_key), []byte(auth_crypt_key))
	this.Ctx.WriteString(hex.EncodeToString(encrypted_auth_key))
}

바로 관리자페이지의 AuthKey Controller였습니다. 그렇기 때문에 AutoRouter 에 따르면 /admin/authkey 경로일 것으로 예상되어 해당 경로로 요청을 보내보았지만,

localhost가 아니라고 나옵니다. 그리고 다시 BaseController의 Prepare 함수 부분에서 localhost 임을 확인하는 코드에서 Domain 값을 사용자입력을 받는듯 해보였습니다.

else if controllerName == "AdminController" {
    domain := this.Ctx.Input.Domain()

    if domain != "localhost" {
        this.Abort("Not Local")
        return
    }
}

그래서 경로로 요청을 보낼 때 HTTP Header에서 Host를 localhost 로 수정하여 보내보았더니 아래와 같이 encrypted_auth_key를 획득할 수 있었습니다.

encrypted_auth_key 값은 다시 AesEncrpyt 함수로 만들어지는데, app.conf 에서도 확인했지만 auth_crypt_key는 존재하지 않는 변수이기 때문에 null 값이 되게 됩니다.

func (this *AdminController) AuthKey() {
	encrypted_auth_key, _ := AesEncrypt([]byte(auth_key), []byte(auth_crypt_key))
	this.Ctx.WriteString(hex.EncodeToString(encrypted_auth_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
}

null 값인 key값은 전부 padding 값으로 채워지게 되고 그 상태로 CBC 모드로 AES 암호화가 되어 버립니다. key값을 알고 있는 상태로라면 위 encrypt 과정을 반대로 수행해서 그대로 복호화가 가능하다는 말과 동일하기 때문에 encrypted_auth_key를 그대로 복호화를 할 수 있습니다.

 

이제 auth_key도 구했고, admin_id도 이미 알고 있기 때문에 아까 보았던 세션 값을 조작할 수 있게 됩니다.

 

문제풀이

우선 AesDecrpyt 함수를 작성합니다.

func AesDecrypt(cipherText string, key []byte) ([]byte, error) {
	padded_key := Padding(key, 16)
	block, err := aes.NewCipher(padded_key)
	if err != nil {
		return nil, err
	}
	ct, _ := hex.DecodeString(cipherText)
	blockSize := block.BlockSize()
	blockMode := cipher.NewCBCDecrypter(block, padded_key[:blockSize])
	plainText := make([]byte, len(ct))
	blockMode.CryptBlocks(plainText, ct)
	return plainText, nil
}

그리고 AesDecrypt 함수에 localhost bypass 해서 구했던 encrypted_auth_key 값을 파라미터로 넣어주고 결과값을 확인합니다.

decrypted_auth_key, _ := AesDecrypt("00fb3dcf5ecaad607aeb0c91e9b194d9f9f9e263cebd55cdf1ec2a327d033be657c2582de2ef1ba6d77fd22784011607", []byte(auth_crypt_key))
fmt.Println(string(decrypted_auth_key))

결과값으로는 아래와 같이 나옵니다.

Th15_sup3r_s3cr3t_K3y_N3v3r_B3_L34k3d

정확히는 아래와 같이 패딩값이 같이 추가되어져서 보입니다.

그리고 문제의 요구사항대로 아래 코드에서 나타난 형태로 맞춰줍니다.

this.Ctx.SetCookie(Md5("sess"), Md5(admin_id + auth_key), 300)

그러면 아래와 같이 됩니다.

f5b338d6bca36d47ee04d93d08c57861 e52f118374179d24fa20ebcceb95c2af

그리고 이 둘을 각각 쿠키값으로 해주기 위해서 브라우저에서 아래 명령을 수행해줍니다.

document.cookie="f5b338d6bca36d47ee04d93d08c57861=e52f118374179d24fa20ebcceb95c2af";

그리고 새로고침하면 아래와 같이 Flag값을 획득할 수 있게 됩니다.

 

- 끝 -

728x90
반응형
댓글