这比赛的 WP 我一定要写,复现我也一定要写,北邮天璇排面

真的感谢北邮天璇师傅们的精彩奉献,办赛确实不易,平台很好,质量比去年的题目高了不少 level,经典《学到很多.jpg》


一开始说是新生赛,参考了一下去年的,问题应该不是很大,恰好忙着搞毕设,没打,别的小伙伴用我之前注册的账号去打了一下,结果告诉我打傻了,签到都不会,👴寻思着不至于吧,进去看了一眼

妙啊

题目质量真的高嗷,这就是新生赛🐎?I了I了,签到题差点给👴整不会了,菜鸡 Web 手最后的挣扎

等官方 Web 的 wp 等了一周多了……

来了来了:MRCTF2021 Web方向Wp


1.签到:

群里 @bot 回复一段码,一开始没看明白是啥东西,后来群里有老哥提示零宽隐写,放到 linux 下,vim 打开一看确实嗷

CSDN 搜了一下,放到那几个网站下发现都不行,offdev 那个也打不开,没办法上那个老哥的 github 上找了一段源码,本地下了一个 node.js 然后 install 个包,直接本地执行

const ZwspSteg = require('zwsp-steg');

let encoded = '@LND_Web 叮咚!你的好大儿zbr向你发送了签到码,请注意查收:​​​​‍‎‏​​​​‍‎‍​​​​‍‎‌​​​​‌‌‍​​​​‍‎​​​​​‍‎‏​​​​‎‌‎​​​​‌‌‍​​​​‎‌‌​​​​‎‌‌​​​​‍‏‎​​​​‍‎‎​​​​‌‌‍​​​​‌‏‎​​​​‎‌‍​​​​‎‌​​​​​‍‎​​​​​‍‎‎​​​​‍‎‎​​​​‍‏‎​​​​‎​‎​​​​‍‏‌jTQZp99TMYVPwJRjyKkDG/JJULeAwc0ujgDFYZUZNr6pGAEsrOCysay7PEhVLPK32+HWuJjyP4RIkHeZ9RqLYOJwS/IA4c/EbVAmVW2MDhSQ/ftKbrHQ+4DUxH2XnAnZ';
let decoded = ZwspSteg.decode(encoded);

console.log(decoded);

看这意思,AES 在 ECB 模式下的 Zero 填充,密钥是 QQID,但是我 QQ 号是 10 位的,密钥至少要 16 位,直接用 0 填充,然后信心满满的到在线加解密去解,结果前几个全 gg,👴心态都崩了,最后终于找到一个可以解的

微信公众号发过去就可以

妙啊


2.wwwafed_app:

赛后出的,比赛的时候一直在搞那个签到注入,真的是一言难尽……

这个 SSTI 还好,没弹 shell,直接打的,但是根目录下没找到 flag,可能要匹配吧,懒得匹配了,反正知道怎么做了

图是后来的

检测域名存活,以为是个 SSRF,然后看了一眼 /waf

import re,sys
import timeout_decorator

@timeout_decorator.timeout(5)
def waf(url):
	# only xxx.yy-yy.zzz.mrctf.fun allow
	pat = r'^(([0-9a-z]|-)+|[0-9a-z]\.)+(mrctf\.fun)$'
	if re.match(pat,url) is None:
		print("BLOCK",end='') # 拦截
	else:
		print("PASS",end='') # 不拦截

if __name__ == "__main__":
	try:
		waf(sys.argv[1])
	except:
		print("PASS",end='')

有个正则匹配判断是否,本来没什么的,但是有个 timeout_decorator 模块的引入,以前没见过,搜了一下发现是个超时处理模块

这就有意思了,正则匹配我们知道有匹配失效的情况,如果输入的内容使得正则匹配时间过长,那么应该就会从 try 进入 except 处理

一开始这样的:

但是过长就是这样的:

因为是 python,所以随手测了个 {{7*7}}

妙啊,成了

没过滤,直接出 {{().__class__.__bases__[0].__subclasses__()}}

有很多可利用的类,但是想着快点,直接 {{lipsum.__globals__['os'].popen('ls').read()}} 上去了,结果提示超时,想着单引号可能被过滤了,直接切双引号,打过去就行

{{lipsum.__globals__["os"].popen("ls /").read()}}

但是木得 flag,有个 zbr,里面是 “hao da er” 迷茫中……,好像被某个小可爱🤭删掉了


另:有个 /source 里面有源码,可以看出来 SSTI,我是 SB

from flask import Flask, request,render_template,url_for
from jinja2 import Template
import requests,base64,shlex,os

app = Flask(__name__)

@app.route("/")
def index():
	return render_template('index.html')

@app.route("/waf")
def wafsource():
	return open("waf.py").read()

@app.route("/source")
def appsource():
	return open(__file__).read()

@app.route("/api/spider/<url>")
def spider(url):
	url = base64.b64decode(url).decode('utf-8')
	safeurl = shlex.quote(url)
	block = os.popen("python3 waf.py " + safeurl).read()
	if block == "PASS":
		try:
			req = requests.get("http://"+url,timeout=5)
			return Template("访问成功!网页返回了{}字节数据".format(len(req.text))).render()
		except:
			return Template("访问{}失败!".format(safeurl)).render()
	else:
		return Template("WAF已拦截,请不要乱输入参数!").render()

if __name__ == "__main__":
	app.run(host="0.0.0.0",port=5000,debug=True)

3.easy_larave1:

这个应该是签到的类型,基本上可以用五空的直接打

经典反序列化代码审计,直接手撸源码 www.zip

在 laravel.log 里可以看到是 5.7 的版本,TaskController.php 中存在反序列化的点

老链子,先找 run(),果然被处理了

再找一下,发现在 run() 方法里有个变量 $key 需要找

直接去 public 下的 txt 文件

没显示,全局搜索,在 session 下看到

接下来就是找利用点的事了,因为底部的入口被注释掉了,所以可以参考去年的第五空间那道题,代码没变动,直接打就行

为什么在 /hello 下,是因为 routes 里写了

啊,对了,exp 用的 zeroyu 师傅博客里的


4.web_check_in:

这题真绝了,提示给了要盲注,没想出来怎么搞,看着 Troy3e 师傅的 wp 先复现一下:

md 复现不出来,注入那部分跑死了也出不来,等官方

官方来了,带个 PWN,麻了,真麻了,做非预期解吧,预期太痛苦了

笛卡尔积盲注可以注出文件的内容,先是 shell.php

<?php
if($_POST['M2cTf']){
    mrctf($_POST['M2cTf']);
}

传一下试试看,有两种回显

第一个回显里提示了另一个文件夹 /Kanto,里面有东西,但是直接访问的话是那个啥,我忘了那个神奇宝贝叫啥了

然后读一下 hint.php

<?php
require "sum.php";
$check = $_POST['check'];
$command = $_POST['command'];

if ($check == enc("MRCTF2021")){
    if (strlen($command < 5)){
        $path = "/proc/self/".$command;
        $myfile = fopen($path,"r") or die("Unable to open file!");
        echo fread($myfile,400000);
        fclose($myfile);
    }

}
if (strlen($command < 5))

非预期在这里,出题人可能手抖,括号打歪了,$command < 5是 true,strlen(true) 是 1,所以这里可以直接穿越目录,往回打到根目录下拿 flag

这也是 ITroyeSivan 师傅的解决思路

还有个 sum.php,本地搭个环境跑一下就可以,最后写个脚本直接打 /hint.php


5.Half-Nosqli:

给的提示其实不少,就是之前没怎么接触过这些东西,上次看到 MongoDb 的注入都两年前的事了😓


因为是 Swagger,所以直接访问 /docs

在 auth 下有两个接口,/login 会返回一个 JWT,看一下格式是 JSON

根据提示,存在注入,利用 $ne 发包过去,可以用 1 也可以用 true,也可以是空字符串 “”

得到 token 后,利用 /home 打 SSRF

因为 JWT 附带时间戳,所以写个 Python 脚本方便点,试个网址打过去可以得到网站内容

后面就是考察 CRLF 的注入,打 FTP 服务器

具体原理可以参考:通过拆分攻击实现的SSRF攻击

\u010D\u010A 代替 \r\n,同时空格也要处理 \u0120

FTP 部分的命令参考:FTP协议完全详解

Eki 师傅的出题笔记里写的很清楚了:在不知道账号密码的情况下使用匿名模式登录,USER 为 anonymous 密码随意,切换到 files 文件夹后打开主动传输模式,使用 PORTRETR 向 vps 发送 flag

发送请求,这里根据 docker-compose.yml 给出的内容知道 ftp 的 hostname 为 ftp,同时 ftp:// 不能直接用,用 http:// 代替

payload 直接参考就可以


0 条评论

发表评论

邮箱地址不会被公开。 必填项已用*标注