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) }
funcSearch(w http.ResponseWriter, r *http.Request) { reqBody, _ := ioutil.ReadAll(r.Body)
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) }
funcFlag(w http.ResponseWriter, r *http.Request ) { action:= r.URL.Query().Get("action") if action == "" { fail(w, "Error getting action") 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") }
路由中的/flag里面get两个参数,?action=readFlag&token=xxxxx如果token对了的话就会拿到flag,怎么拿token也是很容易发现的,query := fmt.Sprintf("SELECT * FROM languages WHERE votes >= %d OR name LIKE '%s';", votes, name)存在sql注入漏洞,所以这题思路很明确,sql注入拿到token,然后直接readflag。
@app.route("/<path:path>")这种路由定义的是一种叫path的类型的path,查阅官方文档,path like the default but also accepts slashes,是一种允许斜杠的路径,也就是说任何路由都会匹配进这个handle函数里,这不禁让我们想到了rctf2021的那个easyphp,利用nginx转发给php-fpm这个中间过程能够进行一些编码绕过,于是在这里也尝试对?进行url编码,payload为/flag%3faction=readFlag&token=,这样在python层面,%3f会经过一层编码,会理解成url而不是get标志,整个path的内容也是/flag?action=readFlag&token而action = request.args.get('action')就无法获取到readFlag的内容,这个请求被转发到了GO,GO的处理器就会认识?了,就会把action=readFlag解析到了,于是我们绕过了第一层,开始看第二层的sql注入。
最终的payload:(我也记得不太清了){"vote":0,"name":"java","name":"java' union select 1,secret,1 from token where '1'='1"},就能拿到token了,之后带着token访问/flag%3faction=readFlag&token=就好了。