이번 LINE CTF의
웹 분야 4번 문제 입니다.
1번 문제인
Baby Simple GoCurl 문제를
변형시킨 문제입니다.
# 개요
이번에도
/flag/ 페이지에 접속하여
FLAG를 획득하는
SSRF 문제입니다.
# 환경 구성
문제 파일을 다운받으면,
Docker Image의 내용이 명시된
Dockerfile과
docker-compose.yaml 파일이
있습니다.
아래 명령어로
도커 컨테이너를 설치 및 실행시킬 수 있습니다.
# DockerFile과 docker-compose.yaml 파일이 있는
# 디렉토리로 이동
> cd dist
# Docker 컨테이너 설치&실행
> docker-compose up
설치된 후 실행 중인 컨테이너의 CMD 창은
종료해도 상관 없습니다.
아래 명령어를 통해
실행 중인 컨테이너의 PORT를
확인할 수 있습니다.
# 실행 중인 Docker Container 확인
> docker ps
도커가 실행 중인 서버(호스트)의
IP와 PORT로 접속하면
문제 사이트를 확인할 수 있습니다.
# 분석
문제로 제공된 파일들 중
중요한 파일은
main.pl
파일입니다.
+ main.pl
main.pl 파일의 내용입니다.
Web 1번 Baby Simple GoCurl 문제에서
ClientIP가 127.0.0.1 인지 확인하는 코드가
사라졌습니다.
필터링이 간단해진 것 처럼 보이지만...
사실,
ClientIP를 127.0.0.1로 속이면
뒤의 문자열 필터링을 우회할 수 있었던
취약점이 사라지면서
무조건, url 파라미터에
flag, curl, % 문자열이
존재하는지 확인하게 됩니다.
package main
...
func main() {
flag := os.Getenv("FLAG")
...
r.GET("/curl/", func(c *gin.Context) {
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return redirectChecker(req, via)
},
}
reqUrl := strings.ToLower(c.Query("url"))
...
/**
* c.ClientIP() != "127.0.0.1" &&
* 코드가 사라지면서,
* ClientIP를 127.0.0.1로 속여도
* 문자열 필터링을 우회할 수 없게 됐다.
**/
if strings.Contains(reqUrl, "flag") || strings.Contains(reqUrl, "curl") || strings.Contains(reqUrl, "%") {
c.JSON(http.StatusBadRequest, gin.H{"message": "Something wrong"})
return
}
req, err := http.NewRequest("GET", reqUrl, nil)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"message": "Something wrong"})
return
}
if reqHeaderKey != "" || reqHeaderValue != "" {
req.Header.Set(reqHeaderKey, reqHeaderValue)
}
resp, err := client.Do(req)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"message": "Something wrong"})
return
}
...
})
...
r.Run()
}
# 풀이
/flag/ 페이지의 코드를 보면
결국, 서버가
/flag/ 페이지에 접속해야 합니다.
+ X-Forwarded-Prefix Header
HTTP Request가 프록시(예: 로드밸런서)를 통과할 때
호스트, 포트 및 체계가 변경될 수 있으므로
클라이언트 관점에서
올바른 호스트, 포트 및 체계를 가리키는 링크를 만드는 것이 어렵습니다.
RFC 7239는
프록시가 원래 요청에 대한 정보를 제공하는 데 사용할 수 있는
Forwarded HTTP 헤더를 정의합니다.
표준 Forwarded Header:
- X-Forwarded-For, X-Forwarded-Host, X-Forwarded-Proto
비표준 Forwarded Header:
- X-Forwarded-Ssl, X-Forwarded-Prefix 등
여기서, X-Forwarded-Prefix header는
프록시에 의해 변경되기 전,
Client가 요청했던 Origin Target Path를
명시하는 Header로 보이며,
이번 문제에서 사용된
Gin library는
불완전한(?) URL로 접속했을 때
올바른 URL로
Redirection을 발생시키며,
이때 X-Forwarded-Prefix header의 값을
참조합니다.
이번 문제에 사용된 문제점에 대한 글 또한 찾아 볼 수 있습니다.
/(슬래시) 만으로 리다이렉션하면
올바르지 않게 동작한다는 내용 입니다.
+ Solve!
url 파라미터가
http://127.0.0.1:8080// 로
/(슬래시)가 붙은 이유는
이래야 Redirection이 발생하기 때문