一、题目
点进去看到登录界面
可以考虑抓包,看能否可以利用里面的条件
抓到一个302之后的get包,访问对应的页面
当然这里也可以用admin + 万能密码绕过
"or "a"="a
二、解题
进到网页之后发现提示信息
需要我们构造含有 /tmp/hint.php的参数
在path里直接传参试试
发现进不了,编码也不行,后面我又试了php://filter伪协议也不行
瞎试了一下file://
发现行了,属于是运气比较好
点开源代码
<?php
error_reporting(0);
/*
* flag in /home/flaaag.txt and go to flag.php
*
*/
class A extends service {
public $event;
function __construct($event) {
$this->event = $event;
}
function __destruct() {
$flag = $this->event["name"];
return $this->$flag();
}
}
class read {
public $filename;
function __construct($filename) {
$this->filename = $filename;
}
function __toString() {
if (isset($this->filename)) {
$res = file_get_contents($this->filename);
} else {
$res = "nonononono";
}
return $res;
}
}
class test {
public $file;
function __construct($f) {
$this->file = $f;
}
function __get($txey) {
echo $this->file;
}
}
class service {
public $server;
public $str;
function __call($method, $args) {
if (is_string($this->server->str)) {
echo "hello" . $method . $this->server->str;
} else {
die("");
}
}
}
if (isset($_GET["cmd"])) {
unserialize($_GET["cmd"]);
} else {
echo "now get flag!";
}
提取信息:
1. flag的路径是 /home/flaaag.txt
2. 这里有多个魔法函数,需要构造pop链一个一个触发
POP链的构造:
观察到存在文件输出函数的在 class read{}里面,为file_get_contents()
class read {
public $filename;
function __construct($filename) {
$this->filename = $filename;
}
function __toString() {
if (isset($this->filename)) {
$res = file_get_contents($this->filename);
} else {
$res = "nonononono";
}
return $res;
}
这里有一个魔术方法__toString()
当我们调试程序时,需要知道是否得出正确的数据。比如打印一个对象时,看看这个对象都有哪些属性,其值是什么,如果类定义了toString方法,就能在测试时,echo打印对象体,对象就会自动调用它所属类定义的toString方法,格式化输出这个对象所包含的数据。__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串(或者执行语句),否则将发出一条
E_RECOVERABLE_ERROR
级别的致命错误。
可以确定这是POP链的尾巴,执行 echo 语句时可能会触发__toString()方法,继续往下看
发现有两处都执行了echo
那就找链头吧
class A是class service的继承类,而只有class A里面有自定义的析构函数,析构函数只需要在对象销毁的时候就能触发
function __destruct() {
$flag = $this->event["name"];
return $this->$flag();
}
分析,这里flag获得的值是传进来的数组参数event里键名为name的键值
return 的是一个以flag值为名的方法,即name的键值
而__call()方法的调用条件是,当对象里一个不可访问(包括父类,自身)的方法被调用时被调用
找到__call()方法的位置
function __call($method, $args) {
if (is_string($this->server->str)) {
echo "hello" . $method . $this->server->str;
} else {
die("");
}
}
其中$method是自动收集的无法访问的方法名,$args是对应的参数
而__get()方法的调用条件如下:
在 php 面向对象编程中,类的成员属性被设定为 private 后,如果我们试图在外面调用它则会出现“不能访问某个私有属性”的错误。那么为了解决这个问题,我们可以使用魔术方法 __get()。或者对象中有不存在的属性被调用也能触发__get()。
在这里,所有对象都没有私有属性,让$this->server=另一个对象
经过上面的排除加之“另一个对象”不能含有$str属性,只能是test了
所以POP链的逻辑如下:
A类的__destruct()触发父类service的__call(),__call()来触发test类的__get(),最后用__get()触发read类的__toString(),由__toString()回显flag的值
构造exp:
在脚本之家工具里面运行php代码
将序列化的结果赋值给cmd,拿到flag