海南之旅
万宁-石梅湾
三亚-西岛


SNMP是一种用于监控诸如,路由器,交换机,打印机,服务器,防火墙等设备的管理协议。它运行在UDP协议之上,常用端口是161,162。其中161端口用于管理请求,162端口用于trap告警通知。
作为IETF定义的一种标准协议(RFC:3411-3418),存在很多实现版本(每种实现针对不同场景和需求进行了优化或扩展),比如最常用的开源版本net-snmp,使用纯JAVA语言实现的SNMP4J,PYTHON编写的PySNMP,运用在嵌入式设备、高性能网络设备Agent的SNMP++版本,甚至很多厂商都有自己的私有实现,下文的所有操作都基于net-snmp版本
sudo apt install snmp snmpd snmptrapd snmp-mibs-downloader
sudo download-mibs // 下载mibs数据库
SNMP的全称简单网络管理协议,简单的含义是仅包含少数核心操作,但其实现可能相当复杂。下表列出协议规定需要实现的基本操作
| 操作 | 解释 |
|---|---|
| GET | 获取某个值,例如设备名、系统运行时间 |
| GETNEXT | 获取下一个OID的值(用于遍历) |
| SET | 修改某个值,例如设置位置、启用端口 |
| WALK | 连续GETNEXT操作,相当于扫描整个MIB树 |
| TRAP | 设备主动发送事件通知(如链路断开) |
| INFORM | 主动发送事件通知的同时,需接收方ACK确认,可靠的消息投递 |
| 术语 | 解释 |
|---|---|
| Manager(管理者) | 发起查询、控制请求的设备(比如 NMS 网络管理系统) |
| Agent(代理) | 被管理设备上运行的进程,响应请求并发送trap |
| MIB(管理信息库) | 存储管理数据的数据库,结构是树形 |
| OID(对象标识符) | 唯一标识一个MIB对象,例如系统名、CPU 使用率等 |
| Community String | 类似密码,控制访问(public只读,private可写) |
用于唯一标识网络设备中可管理对象的标识符。它们在MIB(管理信息库)中定义,并遵循树状的结构。OID主要有三种表示形式,便于人类阅读的模块::名字形式(SNMPv2-MIB::sysDescr.0),适合机器解析的纯数字形式(.1.3.6.1.2.1.1.1.0),混合形式(iso.3.6.1.2.1.1.1.0),详细定义可移步至参考文献1
示例:1.3.6.1.2.1.1.1.0
| 数字部分 | 名称 | 含义 |
|---|---|---|
| 1 | iso | 国际标准化组织 |
| 3 | org | 组织(Organization) |
| 6 | dod | 国防部(Department of Defense) |
| 1 | internet | Internet相关 |
| 2 | mgmt | 管理 |
| 1 | mib-2 | 管理信息库版本2 |
| 1 | system | 系统信息 |
| 1 | sysDescr | 系统描述 |
| 0 | 实例编号 | 表示这是一个具体实例 |
在SNMP中,“资源视图”(View)是SNMP中的重要概念,属于访问控制模型(VACM View-based Access Control Model)的一部分。它用于定义一个用户可以访问或不能访问哪些OID子树
Read more...YARA is a tool aimed at (but not limited to) helping malware researchers to identify and classify malware samples. With YARA you can create descriptions of malware families (or whatever you want to describe) based on textual or binary patterns.
yara(Yet Another Recursive Acronym)是一款强大的模式匹配工具(卧槽,老外的幽默是骨子里的),主要用于恶意软件分析、威胁检测和文件分类。它允许研究人员和安全分析师基于文本或二进制模式(如字符串、正则表达式、字节码等)创建规则,从而识别和分类文件或进程。
包管理工具安装
sudo apt-get install yara yara-doc libyara8 libyara-dev python3-yara python3-yaramod libyaramod0 libyaramod-dev
源码安装
git clone https://github.com/VirusTotal/yara.git
./bootstrap.sh && ./configure --enable-cuckoo --enable-magic --enable-dotnet
make && make install
yara语法规则十分简单,下面是入门文档中介绍的一则简单的样例。
Read more...大型工程,如果必需清楚每一个源文件都包含了哪些头文件,并且在加入或删除某些头文件时,也需要一并修改Makefile,这将是一个繁重琐碎维护性极差的工作。
gcc -M -MF main.d main.c
main.c文件内容:
#include <stdio.h>
int main(int argc,char** argv) {
printf("hello,world\r\n");
return 0;
}
main.d文件内容:
main.o: main.c /usr/include/stdc-predef.h /usr/include/stdio.h \
/usr/include/x86_64-linux-gnu/bits/libc-header-start.h \
/usr/include/features.h /usr/include/x86_64-linux-gnu/sys/cdefs.h \
/usr/include/x86_64-linux-gnu/bits/wordsize.h \
/usr/include/x86_64-linux-gnu/bits/long-double.h \
/usr/include/x86_64-linux-gnu/gnu/stubs.h \
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
/usr/lib/gcc/x86_64-linux-gnu/9/include/stddef.h \
/usr/lib/gcc/x86_64-linux-gnu/9/include/stdarg.h \
/usr/include/x86_64-linux-gnu/bits/types.h \
/usr/include/x86_64-linux-gnu/bits/timesize.h \
/usr/include/x86_64-linux-gnu/bits/typesizes.h \
/usr/include/x86_64-linux-gnu/bits/time64.h \
/usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \
/usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \
/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \
/usr/include/x86_64-linux-gnu/bits/types/__FILE.h \
/usr/include/x86_64-linux-gnu/bits/types/FILE.h \
/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \
/usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
/usr/include/x86_64-linux-gnu/bits/sys_errlist.h
gcc -c -MM -MG main.c -MP -MF main.d
main.c文件内容:
#include <stdio.h>
#include "defs.h" // defs.h文件不存在
int main(int argc,char** argv) {
printf("hello,world\r\n");
return 0;
}
main.d文件内容:
main.o: main.c defs.h
defs.h:
gcc -MF main.d -MG -MM -MP -MT main.d -MT main.o main.c
main.d文件内容:
main.d main.o: main.c defs.h
defs.h:
以上介绍gcc中的-M的大部分选项,下面结合makefile,深入理解-M选项的作用。首次编译时生成依赖文件,后续编译过程中,当任何头文件发生更改,make系统通过*.d文件可反推,依赖该.h的源文件,仅重新编译目变文件。通过这种方式,-include $(DEPS)使得Makefile能够智能地管理文件依赖,简化构建过程,并避免手动管理依赖关系的麻烦。
Read more...Breakpad是一个由Google开发的开源跨平台崩溃报告收集系统,主要作用是在程序崩溃时生成崩溃转储minidump文件,供开发者进行后续分析和调试。

上图摘自官方文档,可以看出在开发者在构建系统中将调试信息从可执行文件中分离,分发给用户运行的是一个包含breakpad客户端的可执行文件,crash收集平台存储原始版本的调试信息及用户自动上传的minidumps文件。
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH=/path/to/depot_tools:$PATH
fetch breakpad && cd src
./configure && make && make check && make install
使用Breakpad捕获崩溃时,有两种常见的集成模式,进程内(崩溃处理逻辑运行在发生崩溃的同一进程中),进程外(崩溃捕获由另一个监控进程完成,发生崩溃的进程将信号或信息传递给守护进程,由后者写minidump)。下面通过两份简短的代码片段介绍,breakpad的使用方法和两种集成模式。
示例函数crash对空地址进行赋值导致进程coredump,在回调函数dump_callback中会自动将minidump文件上传至预设服务器对应的版本目录,供开发者分析崩溃原因
#include "client/linux/handler/exception_handler.h"
#include "common/linux/http_upload.h"
using google_breakpad::ExceptionHandler;
using google_breakpad::HTTPUpload;
using google_breakpad::MinidumpDescriptor;
static bool upload_minidump(const MinidumpDescriptor& descriptor) {
std::map<string, string> parameters;
std::map<string, string> files = {{"upload_file_minidump", descriptor.path()}};
std::string url = "http://192.168.5.170:1127/post";
parameters["prod"] = "test";
parameters["ver"] = "0.1.3";
std::string response, error;
bool success = HTTPUpload::SendRequest(url, parameters, files, "", "", "", &response, NULL, &error);
return success;
}
static bool dump_callback(const MinidumpDescriptor& descriptor, void* context, bool succeeded) {
printf("Dump path: %s\n", descriptor.path());
if (succeeded) upload_minidump(descriptor);
return succeeded;
}
void crash() {
volatile int *a = (int *)(NULL);
*a = 1;
}
int main(int argc, char *argv[]) {
MinidumpDescriptor descriptor("/tmp/test");
ExceptionHandler eh(descriptor, NULL, dump_callback, NULL, true, -1);
crash();
return 0;
}
注意: dump_callback的运行上下文在信号处理函数中,可调用函数需满足async-signal-safe要求,printf不属于其中,同时upload_minidump调用网络请求可能不安全,推荐的做法是异步通知,在单独线程或进程中上传.(man 7 signal-safety)
Read more...工作中总是难免遇到没有使用compose管理的容器服务,复杂的容器服务导出命令参数可能需要使用inspect子命令,分析json文件。runlike工具简化相关过程,直接使用runlike+容器名可以导出容器构建命令
pip install runlike
runlike posteio
docker run --name=posteio --hostname=mail.diyao.me --volume /data --env=TZ=Asia/Shanghai --env=DISABLE_CLAMAV=TRUE --network=host --restart=always --runtime=runc --detach=true analogic/poste.io
buildkit是docker新一代镜像构建引擎,用来替代旧版docker build所使用的legacy builder,作为构建引擎的后端其前端使用docker的buildx插件进行调用与配置,实现现代化、多平台、安全可复现的容器镜像构建流程,同时buildx也支持分布式构建
sudo apt-get install docker-buildx-plugin
多平台方面,buildx支持构建以下类型镜像:
linux/arm64
linux/amd64
linux/amd64/v2
linux/riscv64
linux/ppc64le
linux/s390x
linux/386
linux/mips64le
linux/mips64
linux/arm/v7
linux/arm/v6
使用buildx构建docker镜像,构建工作都在moby/buildkit镜像实例中进行,在容器中实际执行构建任务,包括拉取基础镜像、执行指令(如 RUN、COPY)、构建缓存、输出镜像等
# 创建buildx构建实例
# buildx-create命令的bootstrap参数,立即运行buildx实例,否则在构建时惰性运行实例
# buildx-create命令的driver参数,可以生产不同引擎的buildx实例
# 在后续的构建流程中,使用test实例
docker buildx create --name test --bootstrap
docker buildx use test
docker buildx build --sbom=true --provenance=true --output=type=local,dest=build --platform=linux/amd64 -f Dockerfile .
# 多节点分布式构建
docker buildx create --name test --node test0 --driver docker-container ssh://root@192.168.5.46
docker buildx create --name test --node test1 --append --driver docker-container ssh://root@192.168.5.47
buildx支持多种构建引擎,下表列出引擎的相应使用场景
Read more...OpenCTI is an open source platform allowing organizations to manage their cyber threat intelligence knowledge and observables. It has been created in order to structure, store, organize and visualize technical and non-technical information about cyber threats.
OpenCTI(Open Cyber Threat Intelligence Platform)是一个开源的网络威胁情报平台,旨在帮助组织集中化地收集、存储、组织、共享和可视化网络威胁情报数据。它基于图数据库模型,支持STIX 2(结构化威胁信息表达格式),并与多个情报源无缝集成。
opencti的安装方式分为手动安装和docker安装,其安装方法在官方网站已介绍十分全面,本文只记录相关踩坑部分。
mkdir -p /opt/opencti
cd /opt/opencti && git clone https://github.com/OpenCTI-Platform/docker.git platform
cd /opt/opencti && git clone https://github.com/OpenCTI-Platform/connectors.git connectors
注意事项:
Observable(可观察对象): 网络或系统中可以直接观测到的技术性数据元素
例子:
Read more...neurobin-shc作为一款通用的SHELL脚本编译器,将换用户输入的SHELL脚本转换为C语言代码,编译链接形成一份剥离符号的可执行程序。shc并不是类似与cc的编译器工具,它只是简单的编码和加密SHELL脚本,生成C语言代码,同时增加比如脚本过期检测,等其他特性。其使用系统的编译器和相关工具生成剥离符号的可执行程序,程序与脚本执行效果相同。
需要注意的是其编译的二进制文件取决于SHELL解释器,shc产生的并非完全独立与系统的二进制程序,其处理后的可执行程序只能在编译主机相同的环境中运行
shc支持很多选项,开启特定功能,比如用户可以使用-e指定脚本的有效执行时间,-m定制过期后的技术支持联系信息,-D选择在执行时显示调试信息,-v展示编译过程的参数信息,-S将root执行的脚本设置为setuid,-U使得程序防止strace,ptrace追踪,-r允许生成的二进制文件,能够在相同版本的操作系统运行
下面假设用户需要加密的脚本文件test.sh,使用shc -rf test.sh加密脚本,默认生成可执行文件test.sh.x和C语言文件test.sh.x.c
cat test.sh
#!/bin/bash
echo "hello,world
test.sh test.sh.x test.sh.x.c
test.sh.x: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=044499892c9c33d7c91f45feb1413e41dd1e2a27, for GNU/Linux 3.2.0, stripped
test.sh.x.c文件内容如下所示
#if 0
shc Version 4.0.3, Generic Shell Script Compiler
GNU GPL Version 3 Md Jahidul Hamid <jahidulhamid@yahoo.com>
shc -rf test.sh
#endif
static char data [] =
#define tst1_z 22
#define tst1 ((&data[1]))
"\030\115\076\307\352\251\122\334\257\345\123\205\106\331\310\126"
"\306\365\222\136\257\375\074\131\151\322\311\234"
#define tst2_z 19
#define tst2 ((&data[29]))
"\373\001\053\034\243\243\327\252\343\047\144\311\002\062\041\060"
"\005\045\255\050\026\056"
#define shll_z 10
#define shll ((&data[50]))
"\026\324\303\131\264\245\233\155\211\041\365"
#define chk2_z 19
#define chk2 ((&data[63]))
"\343\134\027\267\010\164\350\146\006\173\011\157\012\014\065\350"
"\070\374\246\344\244\152"
#define lsto_z 1
#define lsto ((&data[83]))
"\241"
#define chk1_z 22
#define chk1 ((&data[84]))
"\263\160\137\140\034\005\137\221\361\364\205\373\062\214\141\366"
"\004\177\354\243\302\204\026"
#define date_z 1
#define date ((&data[107]))
"\212"
#define msg2_z 19
#define msg2 ((&data[111]))
"\124\074\340\040\252\304\020\035\177\177\014\140\114\351\344\073"
"\344\317\367\045\356\177"
#define rlax_z 1
#define rlax ((&data[130]))
"\255"
#define opts_z 1
#define opts ((&data[131]))
"\234"
#define pswd_z 256
#define pswd ((&data[152]))
"\357\272\107\322\026\261\100\334\314\320\337\023\346\020\111\025"
"\233\351\027\357\262\242\314\056\113\200\220\263\311\116\231\164"
"\001\342\261\062\254\213\257\162\322\374\160\203\243\005\063\236"
"\235\220\174\120\063\111\176\177\311\017\062\223\135\314\010\137"
"\256\271\221\132\105\101\315\030\076\076\233\341\103\317\177\341"
"\137\374\061\223\105\257\023\017\276\106\243\034\022\253\173\301"
"\145\015\034\252\117\352\302\215\050\136\157\153\055\356\114\215"
"\353\175\040\061\055\064\100\354\172\344\010\215\217\204\116\364"
"\222\153\237\341\125\141\157\175\277\336\351\355\315\066\172\270"
"\264\233\351\341\317\052\316\112\016\327\327\236\133\046\223\355"
"\221\062\317\347\224\076\144\124\034\116\101\352\204\273\242\071"
"\126\214\032\046\267\351\160\306\300\110\144\034\157\367\011\001"
"\052\330\350\276\027\115\023\063\233\124\036\040\020\300\131\147"
"\115\164\215\004\135\376\312\036\107\057\072\266\047\103\267\121"
"\034\240\020\064\355\044\150\210\170\206\250\210\106\001\357\224"
"\165\175\230\323\174\143\361\303\222\053\172\272\157\062\013\214"
"\322\034\300\277\100\050\110\271\256\361\102\365\363\062\211\151"
"\257\042\074\054\046\370\232"
#define text_z 34
#define text ((&data[418]))
"\252\356\135\373\375\114\265\121\361\372\222\156\226\243\055\231"
"\035\244\362\272\374\214\031\277\114\012\305\343\323\073\203\042"
"\047\167\354\114\031\202\220\354\201\105\037\314"
#define msg1_z 65
#define msg1 ((&data[461]))
"\303\057\210\326\026\230\210\113\110\226\277\361\215\017\204\015"
"\040\353\156\317\067\270\136\021\057\307\176\277\035\021\052\261"
"\320\264\037\020\256\021\054\242\230\360\304\367\224\310\357\365"
"\010\152\041\053\367\163\225\221\162\211\202\264\264\313\370\056"
"\022\052\234\065\246\133\025\037\054\063\011\104\043\057\074\276"
"\000\022"
#define inlo_z 3
#define inlo ((&data[537]))
"\002\114\021"
#define xecc_z 15
#define xecc ((&data[542]))
"\304\135\155\330\141\311\253\016\333\076\154\063\324\263\057\111"
"\254\103\344\051"/* End of data[] */;
#define hide_z 4096
#define SETUID 0 /* Define as 1 to call setuid(0) at start of script */
#define DEBUGEXEC 0 /* Define as 1 to debug execvp calls */
#define TRACEABLE 1 /* Define as 1 to enable ptrace the executable */
#define HARDENING 0 /* Define as 1 to disable ptrace/dump the executable */
#define BUSYBOXON 0 /* Define as 1 to enable work with busybox */
#define MMAP2 0 /* Define as 1 to use syscall mmap2 */
#if HARDENING
static const char * shc_x[] = {
"/*",
" * Copyright 2019 - Intika <intika@librefox.org>",
" * Replace ******** with secret read from fd 21",
" * Also change arguments location of sub commands (sh script commands)",
" * gcc -Wall -fpic -shared -o shc_secret.so shc_secret.c -ldl",
" */",
"",
"#define _GNU_SOURCE /* needed to get RTLD_NEXT defined in dlfcn.h */",
"#define PLACEHOLDER \"********\"",
"#include <dlfcn.h>",
"#include <stdlib.h>",
"#include <string.h>",
"#include <unistd.h>",
"#include <stdio.h>",
"#include <signal.h>",
"",
"static char secret[128000]; //max size",
"typedef int (*pfi)(int, char **, char **);",
"static pfi real_main;",
"",
"// copy argv to new location",
"char **copyargs(int argc, char** argv){",
" char **newargv = malloc((argc+1)*sizeof(*argv));",
" char *from,*to;",
" int i,len;",
"",
" for(i = 0; i<argc; i++){",
" from = argv[i];",
" len = strlen(from)+1;",
" to = malloc(len);",
" memcpy(to,from,len);",
" // zap old argv space",
" memset(from,'\\0',len);",
" newargv[i] = to;",
" argv[i] = 0;",
" }",
" newargv[argc] = 0;",
" return newargv;",
"}",
"",
"static int mymain(int argc, char** argv, char** env) {",
" //fprintf(stderr, if (["localhost", "127.0.0.1"].indexOf(window.location.hostname) != -1) {
document.getElementById('disqus_thread').innerHTML = 'Disqus comments not available by default when the website is previewed locally.';
return;
} \"Inject main argc = %d\\n\", argc);",
" return real_main(argc, copyargs(argc,argv), env);",
"}",
"",
"int __libc_start_main(int (*main) (int, char**, char**),",
" int argc,",
" char **argv,",
" void (*init) (void),",
" void (*fini)(void),",
" void (*rtld_fini)(void),",
" void (*stack_end)){",
" static int (*real___libc_start_main)() = NULL;",
" int n;",
"",
" if (!real___libc_start_main) {",
" real___libc_start_main = dlsym(RTLD_NEXT, \"__libc_start_main\");",
" if (!real___libc_start_main) abort();",
" }",
"",
" n = read(21, secret, sizeof(secret));",
" if (n > 0) {",
" int i;",
"",
" if (secret[n - 1] == '\\n') secret[--n] = '\\0';",
" for (i = 1; i < argc; i++)",
" if (strcmp(argv[i], PLACEHOLDER) == 0)",
" argv[i] = secret;",
" }",
"",
" real_main = main;",
"",
" return real___libc_start_main(mymain, argc, argv, init, fini, rtld_fini, stack_end);",
"}",
"",
0};
#endif /* HARDENING */
/* rtc.c */
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
/* 'Alleged RC4' */
static unsigned char stte[256], indx, jndx, kndx;
/*
* Reset arc4 stte.
*/
void stte_0(void)
{
indx = jndx = kndx = 0;
do {
stte[indx] = indx;
} while (++indx);
}
/*
* Set key. Can be used more than once.
*/
void key(void * str, int len)
{
unsigned char tmp, * ptr = (unsigned char *)str;
while (len > 0) {
do {
tmp = stte[indx];
kndx += tmp;
kndx += ptr[(int)indx % len];
stte[indx] = stte[kndx];
stte[kndx] = tmp;
} while (++indx);
ptr += 256;
len -= 256;
}
}
/*
* Crypt data.
*/
void arc4(void * str, int len)
{
unsigned char tmp, * ptr = (unsigned char *)str;
while (len > 0) {
indx++;
tmp = stte[indx];
jndx += tmp;
stte[indx] = stte[jndx];
stte[jndx] = tmp;
tmp += stte[indx];
*ptr ^= stte[tmp];
ptr++;
len--;
}
}
/* End of ARC4 */
#if HARDENING
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/prctl.h>
#define PR_SET_PTRACER 0x59616d61
/* Seccomp Sandboxing Init */
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/audit.h>
#define ArchField offsetof(struct seccomp_data, arch)
#define Allow(syscall) \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SYS_##syscall, 0, 1), \
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
struct sock_filter filter[] = {
/* validate arch */
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, ArchField),
BPF_JUMP( BPF_JMP+BPF_JEQ+BPF_K, AUDIT_ARCH_X86_64, 1, 0),
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),
/* load syscall */
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)),
/* list of allowed syscalls */
Allow(exit_group), /* exits a process */
Allow(brk), /* for malloc(), inside libc */
#if MMAP2
Allow(mmap2), /* also for malloc() */
#else
Allow(mmap), /* also for malloc() */
#endif
Allow(munmap), /* for free(), inside libc */
/* and if we don't match above, die */
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),
};
struct sock_fprog filterprog = {
.len = sizeof(filter)/sizeof(filter[0]),
.filter = filter
};
/* Seccomp Sandboxing - Set up the restricted environment */
void seccomp_hardening() {
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
perror("Could not start seccomp:");
exit(1);
}
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &filterprog) == -1) {
perror("Could not start seccomp:");
exit(1);
}
}
/* End Seccomp Sandboxing Init */
void shc_x_file() {
FILE *fp;
int line = 0;
if ((fp = fopen("/tmp/shc_x.c", "w")) == NULL ) {exit(1); exit(1);}
for (line = 0; shc_x[line]; line++) fprintf(fp, "%s\n", shc_x[line]);
fflush(fp);fclose(fp);
}
int make() {
char * cc, * cflags, * ldflags;
char cmd[4096];
cc = getenv("CC");
if (!cc) cc = "cc";
sprintf(cmd, "%s %s -o %s %s", cc, "-Wall -fpic -shared", "/tmp/shc_x.so", "/tmp/shc_x.c -ldl");
if (system(cmd)) {remove("/tmp/shc_x.c"); return -1;}
remove("/tmp/shc_x.c"); return 0;
}
void arc4_hardrun(void * str, int len) {
//Decode locally
char tmp2[len];
char tmp3[len+1024];
memcpy(tmp2, str, len);
unsigned char tmp, * ptr = (unsigned char *)tmp2;
int lentmp = len;
int pid, status;
pid = fork();
shc_x_file();
if (make()) {exit(1);}
setenv("LD_PRELOAD","/tmp/shc_x.so",1);
if(pid==0) {
//Start tracing to protect from dump & trace
if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
kill(getpid(), SIGKILL);
_exit(1);
}
//Decode Bash
while (len > 0) {
indx++;
tmp = stte[indx];
jndx += tmp;
stte[indx] = stte[jndx];
stte[jndx] = tmp;
tmp += stte[indx];
*ptr ^= stte[tmp];
ptr++;
len--;
}
//Do the magic
sprintf(tmp3, "%s %s", "'********' 21<<<", tmp2);
//Exec bash script //fork execl with 'sh -c'
system(tmp2);
//Empty script variable
memcpy(tmp2, str, lentmp);
//Clean temp
remove("/tmp/shc_x.so");
//Sinal to detach ptrace
ptrace(PTRACE_DETACH, 0, 0, 0);
exit(0);
}
else {wait(&status);}
/* Seccomp Sandboxing - Start */
seccomp_hardening();
exit(0);
}
#endif /* HARDENING */
/*
* Key with file invariants.
*/
int key_with_file(char * file)
{
struct stat statf[1];
struct stat control[1];
if (stat(file, statf) < 0)
return -1;
/* Turn on stable fields */
memset(control, 0, sizeof(control));
control->st_ino = statf->st_ino;
control->st_dev = statf->st_dev;
control->st_rdev = statf->st_rdev;
control->st_uid = statf->st_uid;
control->st_gid = statf->st_gid;
control->st_size = statf->st_size;
control->st_mtime = statf->st_mtime;
control->st_ctime = statf->st_ctime;
key(control, sizeof(control));
return 0;
}
#if DEBUGEXEC
void debugexec(char * sh11, int argc, char ** argv)
{
int i;
fprintf(stderr, "shll=%s\n", sh11 ? sh11 : "<null>");
fprintf(stderr, "argc=%d\n", argc);
if (!argv) {
fprintf(stderr, "argv=<null>\n");
} else {
for (i = 0; i <= argc ; i++)
fprintf(stderr, "argv[%d]=%.60s\n", i, argv[i] ? argv[i] : "<null>");
}
}
#endif /* DEBUGEXEC */
void rmarg(char ** argv, char * arg)
{
for (; argv && *argv && *argv != arg; argv++);
for (; argv && *argv; argv++)
*argv = argv[1];
}
void chkenv_end(void);
int chkenv(int argc)
{
char buff[512];
unsigned long mask, m;
int l, a, c;
char * string;
extern char ** environ;
mask = (unsigned long)getpid();
stte_0();
key(&chkenv, (void*)&chkenv_end - (void*)&chkenv);
key(&data, sizeof(data));
key(&mask, sizeof(mask));
arc4(&mask, sizeof(mask));
sprintf(buff, "x%lx", mask);
string = getenv(buff);
#if DEBUGEXEC
fprintf(stderr, "getenv(%s)=%s\n", buff, string ? string : "<null>");
#endif
l = strlen(buff);
if (!string) {
/* 1st */
sprintf(&buff[l], "=%lu %d", mask, argc);
putenv(strdup(buff));
return 0;
}
c = sscanf(string, "%lu %d%c", &m, &a, buff);
if (c == 2 && m == mask) {
/* 3rd */
rmarg(environ, &string[-l - 1]);
return 1 + (argc - a);
}
return -1;
}
void chkenv_end(void){}
#if HARDENING
static void gets_process_name(const pid_t pid, char * name) {
char procfile[BUFSIZ];
sprintf(procfile, "/proc/%d/cmdline", pid);
FILE* f = fopen(procfile, "r");
if (f) {
size_t size;
size = fread(name, sizeof (char), sizeof (procfile), f);
if (size > 0) {
if ('\n' == name[size - 1])
name[size - 1] = '\0';
}
fclose(f);
}
}
void hardening() {
prctl(PR_SET_DUMPABLE, 0);
prctl(PR_SET_PTRACER, -1);
int pid = getppid();
char name[256] = {0};
gets_process_name(pid, name);
if ( (strcmp(name, "bash") != 0)
&& (strcmp(name, "/bin/bash") != 0)
&& (strcmp(name, "sh") != 0)
&& (strcmp(name, "/bin/sh") != 0)
&& (strcmp(name, "sudo") != 0)
&& (strcmp(name, "/bin/sudo") != 0)
&& (strcmp(name, "/usr/bin/sudo") != 0)
&& (strcmp(name, "gksudo") != 0)
&& (strcmp(name, "/bin/gksudo") != 0)
&& (strcmp(name, "/usr/bin/gksudo") != 0)
&& (strcmp(name, "kdesu") != 0)
&& (strcmp(name, "/bin/kdesu") != 0)
&& (strcmp(name, "/usr/bin/kdesu") != 0)
)
{
printf("Operation not permitted\n");
kill(getpid(), SIGKILL);
exit(1);
}
}
#endif /* HARDENING */
#if !TRACEABLE
#define _LINUX_SOURCE_COMPAT
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#if !defined(PT_ATTACHEXC) /* New replacement for PT_ATTACH */
#if !defined(PTRACE_ATTACH) && defined(PT_ATTACH)
#define PT_ATTACHEXC PT_ATTACH
#elif defined(PTRACE_ATTACH)
#define PT_ATTACHEXC PTRACE_ATTACH
#endif
#endif
void untraceable(char * argv0)
{
char proc[80];
int pid, mine;
switch(pid = fork()) {
case 0:
pid = getppid();
/* For problematic SunOS ptrace */
#if defined(__FreeBSD__)
sprintf(proc, "/proc/%d/mem", (int)pid);
#else
sprintf(proc, "/proc/%d/as", (int)pid);
#endif
close(0);
mine = !open(proc, O_RDWR|O_EXCL);
if (!mine && errno != EBUSY)
mine = !ptrace(PT_ATTACHEXC, pid, 0, 0);
if (mine) {
kill(pid, SIGCONT);
} else {
perror(argv0);
kill(pid, SIGKILL);
}
_exit(mine);
case -1:
break;
default:
if (pid == waitpid(pid, 0, 0))
return;
}
perror(argv0);
_exit(1);
}
#endif /* !TRACEABLE */
char * xsh(int argc, char ** argv)
{
char * scrpt;
int ret, i, j;
char ** varg;
char * me = argv[0];
if (me == NULL) { me = getenv("_"); }
if (me == 0) { fprintf(stderr, "E: neither argv[0] nor $_ works."); exit(1); }
ret = chkenv(argc);
stte_0();
key(pswd, pswd_z);
arc4(msg1, msg1_z);
arc4(date, date_z);
if (date[0] && (atoll(date)<time(NULL)))
return msg1;
arc4(shll, shll_z);
arc4(inlo, inlo_z);
arc4(xecc, xecc_z);
arc4(lsto, lsto_z);
arc4(tst1, tst1_z);
key(tst1, tst1_z);
arc4(chk1, chk1_z);
if ((chk1_z != tst1_z) || memcmp(tst1, chk1, tst1_z))
return tst1;
arc4(msg2, msg2_z);
if (ret < 0)
return msg2;
varg = (char **)calloc(argc + 10, sizeof(char *));
if (!varg)
return 0;
if (ret) {
arc4(rlax, rlax_z);
if (!rlax[0] && key_with_file(shll))
return shll;
arc4(opts, opts_z);
#if HARDENING
arc4_hardrun(text, text_z);
exit(0);
/* Seccomp Sandboxing - Start */
seccomp_hardening();
#endif
arc4(text, text_z);
arc4(tst2, tst2_z);
key(tst2, tst2_z);
arc4(chk2, chk2_z);
if ((chk2_z != tst2_z) || memcmp(tst2, chk2, tst2_z))
return tst2;
/* Prepend hide_z spaces to script text to hide it. */
scrpt = malloc(hide_z + text_z);
if (!scrpt)
return 0;
memset(scrpt, (int) ' ', hide_z);
memcpy(&scrpt[hide_z], text, text_z);
} else { /* Reexecute */
if (*xecc) {
scrpt = malloc(512);
if (!scrpt)
return 0;
sprintf(scrpt, xecc, me);
} else {
scrpt = me;
}
}
j = 0;
#if BUSYBOXON
varg[j++] = "busybox";
varg[j++] = "sh";
#else
varg[j++] = argv[0]; /* My own name at execution */
#endif
if (ret && *opts)
varg[j++] = opts; /* Options on 1st line of code */
if (*inlo)
varg[j++] = inlo; /* Option introducing inline code */
varg[j++] = scrpt; /* The script itself */
if (*lsto)
varg[j++] = lsto; /* Option meaning last option */
i = (ret > 1) ? ret : 0; /* Args numbering correction */
while (i < argc)
varg[j++] = argv[i++]; /* Main run-time arguments */
varg[j] = 0; /* NULL terminated array */
#if DEBUGEXEC
debugexec(shll, j, varg);
#endif
execvp(shll, varg);
return shll;
}
int main(int argc, char ** argv)
{
#if SETUID
setuid(0);
#endif
#if DEBUGEXEC
debugexec("main", argc, argv);
#endif
#if HARDENING
hardening();
#endif
#if !TRACEABLE
untraceable(argv[0]);
#endif
argv[1] = xsh(argc, argv);
fprintf(stderr, "%s%s%s: %s\n", argv[0],
errno ? ": " : "",
errno ? strerror(errno) : "",
argv[1] ? argv[1] : "<null>"
);
return 1;
}
分析test.sh.x.c源码,SETUID|DEBUGEXEC|TRACEABLE|HARDENING|BUSYBOXON|MMAP2宏控制相关的安全功能
Read more...提到免费https证书,就不得不提ACME协议。ACME(Automatic Certificate Management Environment)是一种用于自动管理HTTPS证书的开放协议,其追求自动化HTTPS证书的申请、颁发、续期和撤销,无需人工干预,通过API进行证书的创建、验证和更新,使用HTTP-01、DNS-01、TLS-ALPN-01验证域名所有权,自动化管理证书的生命周期,避免证书过期。
需要注意的是,通过ACME协议申请的证书有效期统一为90天,这种短期有效的设计更符合现代安全最佳实践。值得一提的是,随着技术发展,部分新型HTTP服务器(如Caddy)已内置ACME协议支持,开发者无需借助额外工具即可实现HTTPS证书的自动化管理,这大大降低了HTTPS的部署门槛。
HTTP-01:
这是当今最常见的验证方式。Let’s Encrypt向ACME客户端提供一个令牌,然后用户需要替换Web服务器http://<你的域名>/.well-known/acme-challenge/TOKEN(用提供的令牌替换TOKEN)路径上放置的指定文件。该文件包含令牌以及帐户密钥的指纹。一旦ACME客户端告诉Let’s Encrypt文件已准备就绪,Let’s Encrypt会尝试获取它(可能从多个地点进行多次尝试)。该方式不支持颁发通配符证书。
DNS-01:
此验证方式要求用户在该域名下的TXT记录中放置特定值来证明控制域名的DNS系统。该配置比HTTP-01略困难,但可以在某些HTTP-01不可用的情况下工作,同时允许您颁发通配符证书。Let’s Encrypt为用户的ACME客户端提供令牌后,用户需要将令牌记录放在 _acme-challenge.<YOUR_DOMAIN>下,然后Let’s Encrypt将向DNS系统查询该记录。
TLS-ALPN-01:
此验证方式客户端(Caddy)向ACME服务器(如 Let’s Encrypt)发起证书申请,服务器指定TLS-ALPN-01挑战类型,并提供一个挑战值(token),客户端需要响应这个挑战。客户端在443端口启动一个临时的TLS服务器,并使用ALPN扩展指定acme-tls/1协议。客户端使用token和授权信息生成一个特殊的自签名证书,作为TLS服务器的证书。ACME服务器尝试通过TLS-ALPN-01连接目标域的443端口,并检查客户端提供的自签名证书。(Caddy默认通过TLS-ALPN-01验证进行自动证书管理)
Let’s Encrypt是一个受信任的证书颁发机构CA,它由非营利组织Internet Security Research Group (ISRG)运营,旨在推动HTTPS的普及,通过自动化流程为网站提供免费的数字证书(DV 类型),其根证书(ISRG Root X1)已被所有主流操作系统、浏览器和设备的信任库预置,这里本文介绍的certbot是Let’s Encrypt官方推荐的证书申请工具。
certbot功能十分强大,本人但受限于部署条件,同时域名托管于cloudflare等因素,故使用certbot-dns-cloudflare插件实现自动证书申请与续期。
sudo apt-get install certbot python3-certbot-dns-cloudflare python-certbot-dns-cloudflare-doc
certbot certonly --dns-cloudflare --dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini -d test.diyao.me
certbot-dns-cloudflare插件的用法,可参见python-certbot-dns-cloudflare-doc文档
最后需要将certbot加入crontab中完成证书的自动续期
0 0 1 * * /usr/bin/certbot renew --quiet
acme.sh是使用纯SHELL脚本编写的acme协议的客户端协议实现,无任何库或软件依赖。支持椭圆曲线加密的ECDSA类型证书,支持SAN(Subject Alternative Name)和通配符证书(一张SAN证书或通配符证书可以保护多域名)。
curl https://get.acme.sh | sh
acme.sh --register-account -m 530384671@qq.com
本文使用的DNS-01验证,针对市面大多数的域名托管商acme.sh都支持,比如aliyun,华为云等。当然acme.sh亦支持使用HTTP-01验证方式
acme.sh --issue --dns dns_cf -d test.diyao.me
acme.sh --issue --standalone -d test.diyao.me
通过–server参数,可以选择不同的免费CA证书颁发机构。目前,acme.sh支持以下CA:Let’s Encrypt、Buypass、ZeroSSL、SSL.com和Google。相较于certbot,acme.sh自动安装crontab完成证书的续期。
Read more...

