web ctf中的计算器
web ctf中的计算器
1 简介
某ctf的web题是计算器,主要就是考脚本编写能力,自动化提交,整体还算简单。 服务端的php文件。
2 实现数学表达式计算
使用instaparse这个库,写parser非常方便,虽然有点杀鸡用牛刀,代码如下:
1: (require '[instaparse.core :as insta :refer [defparser]]) 2: 3: (defparser ques-exp 4: "expr = add-sub '=' 5: <add-sub> = mul-div | add | sub 6: add = add-sub <'+'> mul-div 7: sub = add-sub <'-'> mul-div 8: <mul-div> = term | mul | div 9: mul = mul-div <'*'> term 10: div = mul-div <'/'> term 11: <term> = number 12: number = #'[0-9]+' 13: " 14: :auto-whitespace :standard ;; 忽略空格 15: ) 16: 17: ;;; 测试解析器 18: (ques-exp "234 + 55 * 28 = ") 19: ;; => [:expr [:add [:number "234"] [:mul [:number "55"] [:number "28"]]] "="] 20: 21: (defn eval-exp 22: "执行表达式" 23: [exp-str] 24: (->> (ques-exp exp-str) 25: (insta/transform {:add + 26: :sub - 27: :mul * 28: :div / 29: :number clojure.edn/read-string 30: :expr (fn [a _] a) ;; 忽略右边的= 31: :sign nil}))) 32: 33: (eval-exp "234 + 55 * 28 = ") 34: ;; => 1774 35: 36: (eval-exp "2 * 3 / 2 - 8 + 6 / 3 = ") 37: ;; => -3
3 请求表达式和提交计算结果
接下来就是请求页面,解析出表达式,并提交结果:
1: (require '[reaver :as html]) ;; 使用Jsoup解析html 2: (require '[clj-http.client :as http]) ;; http请求 3: (require '[clj-http.cookies :as cookies]) 4: 5: ;; 使用统一的cookie,用于保存session 6: (def cs (cookies/cookie-store)) 7: 8: (def default-header 9: {:headers {"User-Agent" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.50 Safari/537.36" 10: "Accept-Charset" "utf-8"} 11: ;; 使用本地代理,方便查找问题 12: :proxy-host "127.0.0.1" 13: :proxy-port 8080 14: :cookie-policy :standard 15: :insecure? true 16: }) 17: 18: (def ctf-u "http://192.168.47.129/web/html/index.php") 19: 20: (defn extract-exp 21: "从html中提取出表达式字符串" 22: [html] 23: (some-> (html/parse html) 24: (html/extract [] "form > div" html/text) 25: (->> (apply str)))) 26: 27: (defn get-question 28: "获取问题" 29: [] 30: (-> (http/get ctf-u (merge default-header {:cookie-store cs})) 31: :body 32: extract-exp)) 33: 34: (defn post-ans 35: "提交答案" 36: [ans] 37: (-> (http/post ctf-u (merge default-header 38: {:cookie-store cs 39: :form-params ans})) 40: :body)) 41: 42: 43: ;; 提交一次测试 44: (get-question) 45: ;; => "+3572241+1162006+6727538*7357031-2782980-5908042+5687297*3471162+9574274=" 46: ;; => "+3688345*6914278-7003441+7001140+2403633*2570857+7772803-1273673*2507819=" 47: 48: ;; 这次的题目需要前面再加一个数字 49: (eval-exp (str "123456" *1)) 50: ;; => 69236240589747 51: 52: ;; 提交一次请求,返回"must input some big number ~" 53: (post-ans {:ans *1 54: :input 123456})
看后面的提示<!–the big number is the fist prime after 1000000 –>,需要计算素数:
1: (def certainty 10) 2: 3: (defn prime? [n] 4: "n是否为一个素数" 5: (.isProbablePrime (BigInteger/valueOf n) certainty)) 6: 7: (defn gen-prime 8: "从start开始产生一个素数" 9: [start] 10: (first 11: (filter prime? 12: (range start Integer/MAX_VALUE)))) 13: 14: (gen-prime 20) 15: ;; => 23 16: 17: (def i1 (gen-prime 1000000)) 18: 19: ;; 再请求一次 20: (->> (get-question) 21: (str i1) 22: eval-exp 23: (hash-map :input i1 :ans) 24: post-ans) 25: 26: ;; 返回结果还是不对,当然看源码可以知道,生成的素数位数不对,少个0。 27: ;; 再来一次 28: (def i1 (gen-prime 10000000)) 29: (->> (get-question) 30: (str i1) 31: eval-exp 32: (hash-map :input i1 :ans) 33: post-ans) 34: ;;; 这一次只有slow down的消息,证明提交的input数字是对的
4 循环提交
单次提交测试没有问题,就可以循环提交计算结果,直到返回的页面中没有提问,就算计算完成:
1: ;; 添加了better-cond依赖,为了使用cond let子句 2: (refer-clojure :exclude '[cond]) 3: (require '[better-cond.core :refer [cond]]) 4: 5: (defn process-answer 6: " 不断请求答案,直到返回的页面中没有提问 7: `format-fn` 格式化请求返回的表达式 8: `answer-fn` 格式化提交的form内容" 9: [{:keys [format-fn answer-fn] 10: :or {format-fn identity 11: answer-fn #(hash-map :ans %)}}] 12: (loop [ques (get-question) 13: result ""] 14: (cond 15: (empty? ques) result 16: 17: ;; 计算表达式 18: :let [ans (-> (format-fn ques) 19: (doto println) 20: (eval-exp))] 21: 22: ;; 表达式计算错误 23: (insta/failure? ans) (println :process "no eval result for" ques) 24: 25: ;; 提交结果 26: :let [r (-> (answer-fn ans) 27: (post-ans))] 28: 29: (do 30: (println :process "question:" ques "anser:" ans) 31: (recur (extract-exp r) 32: r))))) 33: 34: (process-answer {:format-fn #(str i1 %) 35: :answer-fn #(hash-map :input i1 :ans %)}) 36:
可以看到post提交答案,返回的都是slow down,现在加入延时,延时数字要自己调整,当然看了代码才知道是2-3秒:
1: (process-answer {:format-fn #(str i1 %) 2: :answer-fn (fn [e] 3: (Thread/sleep 2000.) 4: {:ans e 5: :input i1})}) 6: ;; => "\n\t\t<script language=\"javascript\"> \n\t\talert(\"right answer\"); \n\t </script> got flag....................."
5 总结
考察的就是基本的脚本编程能力,http GET/POST请求,表达式解析计算。
Created: 2019-06-10 周一 16:48