无垠之码

深度剖析代码之道


简单网络管理协议SNMP

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确认,可靠的消息投递

1.核心概念


术语 解释
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入门

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)是一款强大的模式匹配工具(卧槽,老外的幽默是骨子里的),主要用于恶意软件分析、威胁检测和文件分类。它允许研究人员和安全分析师基于文本或二进制模式(如字符串、正则表达式、字节码等)创建规则,从而识别和分类文件或进程。

  1. 包管理工具安装

    sudo apt-get install yara yara-doc libyara8 libyara-dev python3-yara python3-yaramod libyaramod0 libyaramod-dev
    
  2. 源码安装

    git clone https://github.com/VirusTotal/yara.git
    ./bootstrap.sh && ./configure --enable-cuckoo --enable-magic --enable-dotnet
    make && make install
    

1. 核心功能


  • 基于规则的检测,使用自定义规则匹配文件、内存数据或网络流量中的特征
  • 支持多种数据类型,可检测字符串、十六进制模式、正则表达式、文件哈希等
  • 模块化设计,支持扩展模块,如PE、ELF、Mach-O、PDF、ZIP等文件格式解析
  • 跨平台,支持Windows、Linux、macOS,并可以集成到多种安全工具中,如VirusTotal、Cuckoo沙箱
  • 高性能,适用于大规模文件扫描和实时监控

2. 语法规则


yara语法规则十分简单,下面是入门文档中介绍的一则简单的样例。

Read more...

gcc部分参数说明

1.GCC -M 参数


大型工程,如果必需清楚每一个源文件都包含了哪些头文件,并且在加入或删除某些头文件时,也需要一并修改Makefile,这将是一个繁重琐碎维护性极差的工作。

  • -M 输出用于make系统的规则,该规则描述了源文件的依赖关系
  • -MM 生成源文件依赖关系,和-M类似,但不包含标准库的头文件
  • -MG 要求把缺失的头文件按存在对待,并且假定他们和源文件在同一目录下,必须和-M选项或-MM选项一起用
  • -MF 将依赖关系写入指定文件
  • -MD 等同于-M -MF,若给定-o选项,则输出文件名是-o指定的文件名,并添加.d后缀,若没有给定,则输入的文件名作为输出的文件名,并添加.d后缀。例gcc -E -o tmp.i -MD main.c,将生成tmp.d tmp.i
  • -MMD 类似于-MD,但是输出的依赖文件中,不包含标准头文件
  • -MP 生成的依赖文件里面,依赖规则中的所有.h依赖项都会在该文件中生成一个伪目标,其不依赖任何其他依赖项。防止在头文件被删除时构建失败
  • -MT 在生成的依赖文件中,指定依赖规则中的目标
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

Breakpad是一个由Google开发的开源跨平台崩溃报告收集系统,主要作用是在程序崩溃时生成崩溃转储minidump文件,供开发者进行后续分析和调试。

breakpad

上图摘自官方文档,可以看出在开发者在构建系统中将调试信息从可执行文件中分离,分发给用户运行的是一个包含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的使用方法和两种集成模式。

1.In-process崩溃收集


示例函数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...

docker命令小技巧

1.runlike工具


工作中总是难免遇到没有使用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

2.下一代构建引擎buildkit


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...

开源威胁情报系统

1.opencti


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(结构化威胁信息表达格式),并与多个情报源无缝集成。

1.1安装

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

注意事项:

  1. 使用docker安装需下载相关镜像,配置科学上网连接docker仓库
  2. 使用docker安装方式需要将rabbimtmq的端口映射至宿主机,同时需要在connecters中增加extra_hosts参数,rabbitmq:host-gateway
  3. 安装uuid,针对每一个connector使用uuid生成唯一的id

1.2概念

  1. Observable(可观察对象): 网络或系统中可以直接观测到的技术性数据元素

    例子:

    Read more...

加密shell脚本

neurobin-shc作为一款通用的SHELL脚本编译器,将换用户输入的SHELL脚本转换为C语言代码,编译链接形成一份剥离符号的可执行程序。shc并不是类似与cc的编译器工具,它只是简单的编码和加密SHELL脚本,生成C语言代码,同时增加比如脚本过期检测,等其他特性。其使用系统的编译器和相关工具生成剥离符号的可执行程序,程序与脚本执行效果相同。

需要注意的是其编译的二进制文件取决于SHELL解释器,shc产生的并非完全独立与系统的二进制程序,其处理后的可执行程序只能在编译主机相同的环境中运行

0.使用


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;
}

1.原理


分析test.sh.x.c源码,SETUID|DEBUGEXEC|TRACEABLE|HARDENING|BUSYBOXON|MMAP2宏控制相关的安全功能

Read more...

免费https证书的申请

提到免费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验证进行自动证书管理)

1.certbot


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

2.acme.sh


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...
Previous Page 2 of 4 Next Page