查看页面源码,得到提示:/source
访问后自动下载源码:
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
from flask import Flask , request , redirect , g , send_from_directory
import sqlite3
import os
import uuid
app = Flask ( __name__ )
SCHEMA = """CREATE TABLE files (
id text primary key,
path text
);
"""
def db ():
g_db = getattr ( g , '_database' , None )
if g_db is None :
g_db = g . _database = sqlite3 . connect ( "database.db" )
return g_db
@app.before_first_request
def setup ():
os . remove ( "database.db" )
cur = db () . cursor ()
cur . executescript ( SCHEMA )
@app.route ( '/' )
def hello_world ():
return """<!DOCTYPE html>
<html>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
Select image to upload:
<input type="file" name="file">
<input type="submit" value="Upload File" name="submit">
</form>
<!-- /source -->
</body>
</html>"""
@app.route ( '/source' )
def source ():
return send_from_directory ( directory = "/var/www/html/" , path = "www.zip" , as_attachment = True )
@app.route ( '/upload' , methods = [ 'POST' ])
def upload ():
if 'file' not in request . files :
return redirect ( '/' )
file = request . files [ 'file' ]
if "." in file . filename :
return "Bad filename!" , 403
conn = db ()
cur = conn . cursor ()
uid = uuid . uuid4 () . hex
try :
cur . execute ( "insert into files (id, path) values (?, ?)" , ( uid , file . filename ,))
except sqlite3 . IntegrityError :
return "Duplicate file"
conn . commit ()
file . save ( 'uploads/' + file . filename )
return redirect ( '/file/' + uid )
@app.route ( '/file/<id>' )
def file ( id ):
conn = db ()
cur = conn . cursor ()
cur . execute ( "select path from files where id=?" , ( id ,))
res = cur . fetchone ()
if res is None :
return "File not found" , 404
# print(res[0])
with open ( os . path . join ( "uploads/" , res [ 0 ]), "r" ) as f :
return f . read ()
if __name__ == '__main__' :
app . run ( host = '0.0.0.0' , port = 80 )
第一部分代码的注释:
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
36
37
38
# 路由定义:处理文件上传
@app.route ( '/upload' , methods = [ 'POST' ])
def upload ():
# 检查请求中是否包含文件
if 'file' not in request . files :
# 如果没有文件,重定向回主页
return redirect ( '/' )
# 从请求中获取上传的文件对象
file = request . files [ 'file' ]
# 简单验证文件名,避免恶意文件上传
if "." in file . filename :
# 如果文件名中包含".",可能是恶意文件,返回错误响应
return "Bad filename!" , 403
# 获取数据库连接
conn = db ()
# 创建数据库游标
cur = conn . cursor ()
# 生成唯一的文件ID
uid = uuid . uuid4 () . hex
try :
# 将文件信息插入数据库
cur . execute ( "insert into files (id, path) values (?, ?)" , ( uid , file . filename ,))
except sqlite3 . IntegrityError :
# 处理数据库完整性错误,即文件ID重复
return "Duplicate file"
# 提交对数据库的更改
conn . commit ()
# 将文件保存到服务器的uploads目录
file . save ( 'uploads/' + file . filename )
# 重定向到文件查看页面,页面URL包含新上传文件的唯一ID
return redirect ( '/file/' + uid )
上传的文件名不能包含 .
,没法利用文件上传来 getshell,看下一段代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 路由定义:处理文件查看请求
@app.route ( '/file/<id>' )
def file ( id ):
# 获取数据库连接
conn = db ()
# 创建数据库游标
cur = conn . cursor ()
# 查询文件路径
cur . execute ( "select path from files where id=?" , ( id ,))
res = cur . fetchone ()
# 检查文件是否存在
if res is None :
# 如果文件不存在,返回 "File not found" 并设置状态码为404
return "File not found" , 404
# 获取文件内容
with open ( os . path . join ( "uploads/" , res [ 0 ]), "r" ) as f :
# 读取文件内容并返回给客户端
return f . read ()
os.path.join("uploads/", res[0])
用于拼接文件路径,可以传入多个路径。代码中的意思是把 uploads/
与文件名拼接为一个路径,通过 with open() as f
打开这个路径,读取内容并显示
os.path.join()
有一个特性:当某个路径以 /
开头,会丢弃之前的路径
相关实验
可以利用这个这个特性来进行路径遍历
上传一个名为 /flag
的文件, with open(os.path.join("uploads/", res[0]), "r") as f
执行的结果就变成:with open("/flag", "r") as f
得到 flag :NSSCTF{c1c5c881-6f4b-41e3-b43d-b031717171fe}