D3CTF_24wp学习

Kap0k的老大哥们豪🐂,加油啊

d3pythonhttp

I love using various Python web frameworks to create my projects~
nc 106.14.121.29:31084
文档里有压缩包

  1. 阅读源码

    1. frontend

      1. jwt的kid绕过,目录遍历(或者可读都可以)或者使用sql注入
      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
      104
      105
      106
      107
      108
      109
      110
      111
      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):
      # options是指定jwt.decode的选项,verify_signature为False表示不验证签名,但是verify_token中会验证
      return "Hello " + jwt.decode(token, algorithms=["HS256"], options={"verify_signature": False})["username"]
      else:
      return redirect("/login", code=302)

      # 可以用来设置token
      @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") # 我们要获取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()

      # 需要isadmin,有转发到后端 --> 具体还要看后端
      @app.route('/admin', methods=['GET', 'POST'])
      def admin():
      token = request.cookies.get('token')
      if token and verify_token(token):
      if request.method == 'POST':
      # 不对签名验证,但是需要设置isadmin为true
      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()) # 会在data发送时加上长度并构造http的post请求
      if "BackdoorPasswordOnlyForAdmin" not in data: # 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

      # jwt --> kid绕过
      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)

    2. backend

      1. backend和frontend一个需要BackdoorPasswordOnlyForAdmin一个不需要,了解一下Transfer-Encoding,可以拿来做东西
      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
      import web  # web模块的,有点意思
      import pickle
      import base64

      urls = (
      '/', 'index',
      '/backdoor', 'backdoor'
      )
      web.config.debug = False # 非debug模式
      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:
      # 打pickle
      data = base64.b64decode(data)
      pickle.loads(data)
      return "Done!"


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

  2. 看一下web模块的内容