无垠之码

深度剖析代码之道


frida零基础入门

It’s Greasemonkey for native apps, or, put in more technical terms, it’s a dynamic code instrumentation toolkit. It lets you inject snippets of JavaScript or your own library into native apps on Windows, macOS, GNU/Linux, iOS, watchOS, tvOS, Android, FreeBSD, and QNX. Frida also provides you with some simple tools built on top of the Frida API. These can be used as-is, tweaked to your needs, or serve as examples of how to use the API.

Read more...

cowrie蜜罐

蜜罐(Honeypot)是指一种故意暴露的系统、服务或数据,用来引诱攻击者进行入侵、探测或攻击,然后监控、记录他们行为的信息化系统。其主要目的是迷惑攻击者,监控攻击行为,收集威胁情报。本文介绍的cowrie是一种模拟SSH服务器,诱捕爆破和远程登录攻击行为的中交互蜜罐。

cowrie作为一种交互式蜜罐具有以下优点:

  • 可以输出丰富的日志格式:如文本、JSON格式,方便后续处理
  • 能够直接接入SIEM系统(比如 Splunk、ELK Stack)、安全告警系统,实现实时监控
  • 轻松介入misp开源威胁情报平台
  • 虚拟文件系统,伪装度高,不容易被识破,攻击者虽然可以登录和操作,但其实运行在受控的模拟环境里,不能真正破坏宿主机
  • 记录登录行为,命令执行,收集恶意样本
  • 社区活跃,持续修复Bug、更新新功能,定制化程度高

1. 部署蜜罐


cowrie的安装官网已介绍的是否详细,这里不再赘述,为使得模拟环境更加真实,下面使用qemu虚拟机+官方的docker镜像快速搭建蜜罐环境

sudo apt-get install u-boot-qemu opensbi qemu-system qemu guestfs-tools
wget https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/cloud/generic_alpine-3.21.2-x86_64-bios-cloudinit-r0.qcow2
mv generic_alpine-3.21.2-x86_64-bios-cloudinit-r0.qcow2 alpine.qcow2
qemu-img resize ./alpine.qcow2 +10G
sudo virt-customize -a alpine.qcow2 --root-password password:123456

# 由于虚拟机内存实在太小,只能分配给qemu实例256M内存
qemu-system-x86_64 \
  --nographic \
  -m 256M \
  -smp 1 \
  -net nic -net user,hostfwd=tcp::9090-:22 \
  -drive file=alpine.qcow2,format=qcow2 

# 增加用户
useradd -m peter

# ubuntu云主机镜像需要执行下列操作实际扩容
growpart /dev/sda 1
resize2fs /dev/sda1

下面运行cowrie实例,配置关闭其关闭telnet,简单的将流量转发至后端主机9090端口,此刻攻击者通过连接8822端口,即可访问qemu虚拟机

docker run --name=cowrie \
	--user=cowrie \
	--env COWRIE_TELNET_ENABLES=no \
	--env COWRIE_SSH_LISTEN_ENDPOINTS=tcp:8822:interface=0.0.0.0 \
	--env COWRIE_HONEYPOT_BACKEND=proxy \
	--env COWRIE_PROXY_BACKEND=simple \
	--env COWRIE_PROXY_BACKEND_SSH_HOST=103.79.76.198 \
	--env COWRIE_PROXY_BACKEND_SSH_PORT=9090 \
	--env COWRIE_PROXY_BACKEND_USER=peter \
	--env COWRIE_PROXY_BACKEND_PASS=topsec \
	--volume /cowrie/cowrie-git/etc \
	--volume /cowrie/cowrie-git/var \
	--cap-drop=ALL \
	--workdir=/cowrie/cowrie-git \
	-p 8822:8822 \
	--detach=true \
	cowrie/cowrie:114c918e 

可以看到攻击者117.151.88.32使用root/1234账户登录主机,执行date命令后退出,整个会话持续96秒

Read more...

简单网络管理协议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;
}

下面命令行展示如何编译breakpad程序,导出符号信息以及如何分析minidump文件将其转为coredump文件

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