SCTF2021__loginme

SCTF2021__loginme


赛后复现

docker创建容器,打开

在这里插入图片描述

提示我们需要localhost,但是我们使用XFF不可以,同时题目也给了附件,是go语言的,不是很会,这两天学了一下,来看下这个题目,复现下

题目给的附件:

在这里插入图片描述

打开看了main.go,这个源码是缺了一部分的,但是不影响做题(题目附件中缺了templates,赛后出题人将所有的源码放在了github,复现题目没问题)

之前没碰过go,边学边做吧

本题目用了一个gin框架

Gin是一个使用Go开发的web框架,简洁,性能好,开箱即用,方便扩展。

package main

import (
	"html/template"
	"loginme/middleware"
	"loginme/route"
	"loginme/templates"

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

//Gin是一个使用Go开发的web框架
func main() {
	gin.SetMode(gin.ReleaseMode)	//选择release模式
	r := gin.Default()	//创建实例
	templ := template.Must(template.New("").ParseFS(templates.Templates, "*.tmpl"))	//模板解析
	r.SetHTMLTemplate(templ)	//该方法会gin实实例绑定一个模板引擎(内部其实是设置了engine的HTMLRender属性),也就是模板渲染
	// 通过use设置全局中间件
	// 设置日志中间件,主要用于打印请求日志
	r.Use(gin.Logger())
	// 设置Recovery中间件,主要用于拦截paic错误,不至于导致进程崩掉
	r.Use(gin.Recovery())
	//一个新的路由
	authorized := r.Group("/admin")
	//调用函数middleware.LocalRequired(),其实是个waf
	authorized.Use(middleware.LocalRequired())
	{
		authorized.GET("/index", route.Login)
	}

	r.GET("/", route.Index)
	r.Run(":9999")	//运行服务器,监听9999端口
}

查看请求头

首先是创建了一个gin实例,然后是在/admin路由下,调用middleware.LocalRequired(),跟进,发现这个是一个简单的防护

func LocalRequired() gin.HandlerFunc {
	return func(c *gin.Context) {
		//获取数据报头部,并且判断
		if c.GetHeader("x-forwarded-for") != "" || c.GetHeader("x-client-ip") != "" {
			c.AbortWithStatus(403)
			return
		}
		ip := c.ClientIP()
		if ip == "127.0.0.1" {
			c.Next()
		} else {
			c.AbortWithStatus(401)
		}
	}
}

请求头中不能有x-forwarded-for或者x-client-ip,不然返回403

同时还使用ClientIP(),用来判断ip是否等于127.0.0.1,这个可以通过X-Real-IP:127.0.0.1进行绕过

在这里插入图片描述

绕过这个之后,继续分析

在这里插入图片描述

接下来跳到route.Login,跟进

func Login(c *gin.Context) {
	idString, flag := c.GetQuery("id")	//gin的获取url query参数
	if !flag {
		idString = "1"
	}
	id, err := strconv.Atoi(idString)	//用于将字符串类型转换为int类型,整了之后,id就是int类型的数据了
	if err != nil {		//当出现不等于nil的时候,说明出现某些错误了,这个地方是调用方法出错的时候应该怎么做
		id = 1
	}
	TargetUser := structs.Admin	//一个结构体
	for _, user := range structs.Users {		//循环,看id等于几,然后将TargetUser赋值
		if user.Id == id {
			TargetUser = user
		}
	}

	age := TargetUser.Age
	if age == "" {
		age, flag = c.GetQuery("age")
		if !flag {
			age = "forever 18 (Tell me the age)"
		}
	}

	if err != nil {
		c.AbortWithError(500, err)
	}

	html := fmt.Sprintf(templates.AdminIndexTemplateHtml, age)	//格式化字符串并赋值给新串
	if err != nil {
		c.AbortWithError(500, err)
	}
	//Parse()方法用来解析、评估模板中需要执行的action,其中需要评估的部分都使用{{}}包围,并将评估后(解析后)的结果赋值给tmpl。
	tmpl, err := template.New("admin_index").Parse(html)
	if err != nil {
		c.AbortWithError(500, err)
	}
	//将对象实例应用到已经解析的tmpl模板
	tmpl.Execute(c.Writer, TargetUser)
}

这一段,会获取url中的参数id,进行参数类型转换

同时还定义了TargetUser,为一个结构体,注意这个结构体,跟进

var Users = []UserInfo{
	{
		Id:       1,
		Username: "Grandpa Lu",
		Age:      "22",
		Password: "hack you!",
	},
	{
		Id:       2,
		Username: "Longlone",
		Age:      "??",
		Password: "i don't know",
	},
	{
		Id:       3,
		Username: "Teacher Ma",
		Age:      "20",
		Password: "guess",
	},
}

var Admin = UserInfo{
	Id:       0,
	Username: "Admin",
	Age:      "",
	Password: "flag{}",
}

在之后,会将传入的id和users中的id进行对比,id=0时,还是Username=“Admin”

接下来传参age,因为Admin中的Age默认是空的,所以会执行age传参

if age == "" {
		age, flag = c.GetQuery("age")
		if !flag {
			age = "forever 18 (Tell me the age)"
		}
	}

格式化字符串

html := fmt.Sprintf(templates.AdminIndexTemplateHtml, age)	//格式化字符串并赋值给新串

模板渲染

在这里插入图片描述

接着往下走,有个渲染

tmpl, err := template.New("admin_index").Parse(html)

go语言模板渲染支持传入一个结构体的实例来渲染它的字段,就有可能造成信息泄露

而在go语言中使用的是{{.name}}代表要应用的对象,所以可以让age={{.Password}}

在这里插入图片描述

抓包修改

在这里插入图片描述

参考链接

  1. [2021SCTF]web-Loginme wp-CTF对抗-看雪论坛-安全社区|安全招聘|bbs.pediy.com
  2. [(1条消息) CTF]SCTF2021 WEB复现(详细版)_Sapphire037的博客-CSDN博客
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值