
*nix 시스템을 위한 아티팩트 수집 도구
fennec는 *nix 기반 시스템에서 사고 대응 시 사용되는 Rust로 작성된 아티팩트 수집 도구입니다. fennec은 아티팩트 수집 방법을 포함하는 구성 파일을 작성할 수 있습니다.
| OS 세부 정보 | 아키텍처 | 성공? | 세부 사항 |
|---|---|---|---|
| Ubuntu 20.04.3 LTS | x86_64 | ✅ | |
| Ubuntu 19.04 | x86_64 | ✅ | |
| Ubuntu 18.04.6 LTS | x86_64 | ✅ | |
| Ubuntu 17.04 | x86_64 | ✅ | |
| Ubuntu 16.04.7 LTS | x86_64 | ✅ | |
| Ubuntu 15.10 | x86_64 | ✅ | |
| Ubuntu 14.04.6 LTS | x86_64 | ✅ | |
| Ubuntu 13.04 | x86_64 | ✅ | |
| Ubuntu 12.04.5 LTS | x86_64 | ✅ | |
| CentOS 8.4.2105 | x86_64 | ✅ | |
| CentOS 7.9.2009 | x86_64 | ✅ | |
| CentOS 6.10 | x86_64 | ✅ | |
| CentOS 5.11 | x86_64 | ❌ | osquery는 libc >= 2.12 필요 |
| Ubuntu 20.04 | aarch64 | ✅ | |
| MacOS Monterey v12.0.1 | x86_64 | ✅ | 구성 튜닝이 필요합니다. MacOS 아티팩트에 경험이 있다면 기여해 주세요 |
| Oracle Linux Server 7.9 | x86_64 | ✅ |
fennec 0.4.1 AbdulRhman Alfaifi [email protected] Aritfact collection tool for *nix systems
USAGE: fennec [OPTIONS]
OPTIONS: -c, --config Sets a custom config file (Embedded : true)
-o, --output <FILE>
Sets output file name [default: ABDULRHMAN-PC.zip]
-l, --log-level <LEVEL>
Sets the log level [default: info] [possible values: trace, debug, info, error]
-f, --log-file <FILE>
Sets the log file name [default: fennec.log]
-u, --upload-artifact <CONFIG>...
Upload configuration string. Supported Protocols:
* s3 : Upload artifact package to S3 bucket (ex. minio)
* Format :
s3://<ACCESS_KEY>:<SECRET_ACCESS_KEY>@(http|https)://<HOSTNAME>:<PORT>/<BUCKET_NAME>:<PATH>
* Example (minio): s3://minioadmin:minioadmin@http://192.168.100.190:9000/fennec:/
* aws3 : Upload artifact package to AWS S3 bucket
* Format : aws3://<ACCESS_KEY>:<SECRET_ACCESS_KEY>@<AWS_REGOIN>.<BUCKET_NAME>:<PATH>
* Example: aws3://AKIAXXX:[email protected]:/
* scp : Upload artifact package to a server using SCP protocol
* Format : scp://<USERNAME>:<PASSWORD>@<HOSTNAME>:<PORT>:<PATH>
* Example: scp://testusername:[email protected]:22:/dev/shm
-q, --quiet
Do not print logs to stdout
-t, --timeout <SEC>
Sets osquery queries timeout in seconds [default: 60]
-h, --help
Print help information
--non-root
Run Fennec with non root permisions. This isn't recommended, most artifacts require root
permissions
--osquery-path <PATH>
Sets osquery path, if osquery is embedded it will be writen to this path otherwise the
path will be used to spawn osquery instance (Embedded : true) [default: ./osqueryd]
--output-format <FORMAT>
Sets output format [default: jsonl] [possible values: jsonl, csv, kjson]
--show-config
Show the embedded configuration file
--show-embedded
Show the embedded files metadata
-V, --version
Print version information
* `-c`, `--config` : 지정된 설정 파일을 기본 내장 설정 대신 사용합니다.
* `-f`, `--log-file` : 기본 로그 파일 이름을 변경합니다 (기본값: `fennec.log`)
* `-h`, `--help` : 도움말 메시지를 출력합니다.
* `-l`, `--log-level` : 기본 로그 수준을 변경합니다 (기본값: `info`)
* `-o`, `--output` : zip 파일의 기본 출력 파일 이름을 변경합니다 (기본값: `{HOSTNAME}.zip`, 호스트 이름은 런타임에 평가된 머신 호스트 이름입니다)
* `--osquery-path` : osquery 실행 파일의 경로입니다. 이 값은 다음 조건에 따라 사용됩니다:
* osquery 바이너리가 `fennec`에 내장되어 있으면 추출하여 `--osquery-path`에 덤프합니다.
* osquery가 `fennec`에 내장되어 있지 않으면 `--osquery-path` 경로의 osquery 바이너리를 사용합니다.
* `--output-format` : 출력 형식을 선택합니다. 지원되는 형식:
* jsonl : 개행으로 구분된 JSON 객체 (기본값)
* csv: 쉼표로 구분된 값
* kjson: 결과 파일을 [Kuiper](https://github.com/DFIRKuiper/Kuiper) 분석 플랫폼에 업로드하려면 이 형식을 사용하십시오.
* `-q`, `--quiet` : 로그를 `stdout`에 출력하지 않습니다.
* `--non-root`: 루트 권한 없이 Fennec을 실행합니다. 기본적으로 Fennec은 `root` 권한이 필요하며, 루트가 아니면 오류 메시지와 함께 종료됩니다.
* `--show-config` : 내장된 설정을 출력한 후 종료합니다.
* `--show-embedded` : 내장된 파일을 표시합니다.
* `-t`, `--timeout` : 각 osquery 쿼리 아티팩트 유형의 타임아웃(초)을 설정합니다.
* `-u`, `--upload-artifact` : 아티팩트 패키지를 원격 서버에 업로드합니다. 지원되는 프로토콜:
* `s3` : 아티팩트 패키지를 S3 버킷에 업로드합니다.
* `형식` : s3://<ACCESS_KEY>:<SECRET_ACCESS_KEY>@(http|https)://<HOSTNAME>:<PORT>/<BUCKET_NAME>:<PATH>
* `예`: s3://minioadmin:minioadmin@http://192.168.100.190:9000/fennec:/
* `aws3` : 아티팩트 패키지를 AWS S3 버킷에 업로드합니다.
* `형식` : aws3://<ACCESS_KEY>:<SECRET_ACCESS_KEY>@<AWS_REGOIN>.<BUCKET_NAME>:<PATH>
* `예`: aws3://AKIAXXXXXXXXXXXXXXXXX:[email protected]:/
* `scp` : SCP 프로토콜을 사용하여 아티팩트 패키지를 서버에 업로드합니다.
* `형식` : scp://<USERNAME>:<PASSWORD>@<HOSTNAME>:<PORT>:<PATH>
* `예`: scp://testusername:[email protected]:22:/dev/shm
* `-V`, `--version` : `fennec` 버전을 출력한 후 종료합니다.
## 종속성과 함께 컴파일하기 👨💻
fennec은 `query` 유형의 아티팩트를 실행하기 위해 `osquery`에 의존합니다. `deps`라는 디렉토리는 대상 OS 및 아키텍처에 따라 바이너리에 내장될 파일을 포함하고 있습니다. 컴파일하기 전에 다음 단계를 따르십시오:
* 필요에 따라 설정 파일 `deps/<TARGET_OS>/fennec.yaml`을 수정합니다.
* 아래 명령어 중 하나를 사용하여 바이너리를 빌드합니다:
* 동적으로 링크된 경우: ```bash
cargo build --release
릴리스 섹션에서 사전 컴파일된 바이너리를 사용할 수도 있습니다.
다음은 이 리포지토리의 동일한 구성으로 Ubuntu 20에서 실행된 예제입니다:

Kuiper 지원 형식으로 데이터를 출력하려면 다음 인수와 함께 Fennec을 실행하세요:```bash sudo ./fennec --output-format kjson
또는 구성의 `args` 섹션에 다음을 추가하세요:```yaml
args:
- "--output-format"
- "kjson"
다시 컴파일한 후 실행하십시오:```bash sudo ./fennec
그런 다음 생성된 zip 파일을 Kuiper에 업로드합니다. 다음은 예시입니다:

## Configuration🔨
기본적으로 `deps/<TARGET_OS>/fennec.yaml` 경로에 있는 설정은 컴파일 중에 실행 파일에 포함됩니다. 설정은 YAML 형식이며 두 개의 섹션이 있습니다:
### Args
실행 파일에 명령줄 인수로 전달될 인수 목록을 포함합니다. 다음은 `args` 섹션의 예시로, 출력 형식을 `jsonl`로 설정하고 로그 파일 이름을 `fennec.log`로 설정합니다:```yaml
args:
- "--output-format"
- "jsonl"
- "--log-file"
- "fennec.log"
...
명령줄 인수는 다음 우선순위로 사용됩니다:
수집할 아티팩트의 목록을 포함합니다. 각 아티팩트에는 다음 필드가 포함됩니다:
stdout을 구문 분석하기 위한 정규식을 포함합니다.osquery SQL 쿼리를 실행합니다. 다음은 시스템의 모든 사용자를 검색하는 예제 아티팩트입니다:```yaml artifacts:
#### 아티팩트 유형: 수집
이 아티팩트 유형은 **paths** 필드에 지정된 파일/폴더를 수집합니다. 다음은 시스템 로그를 수집하는 이 아티팩트 유형의 예시입니다:```yaml
artifacts:
- name: logs
type: collection
description: "Collect system logs"
paths:
- '/var/log/**/*'
...
다음 우선순위에 따라 셸 명령 해석기를 사용하여 시스템 명령을 실행합니다:
이것은 잘못된 로그인을 검색하는 이 아티팩트 유형의 예입니다:```yaml artifacts:
이 아티팩트 유형은 `commands` 목록에 있는 명령들을 실행하고, `regex` 필드에 지정된 정규 표현식을 사용하여 `stdout`을 파싱합니다. 정규 표현식은 `stdout` 스트림에서만 처리되며 `stderr`에서는 처리되지 않는다는 점에 유의하세요. 또한 `regex` 필드는 선택 사항입니다. 다음은 `regex` 필드를 사용한 경우와 사용하지 않은 경우의 결과 예시입니다.
##### `regex` 필드 없음```json
{
"line": 0,
"stdout": "root pts/1 2023-09-12T17:13:28+03:00 - 2023-09-12T17:13:28+03:00 (00:00)"
}
regex 필드와 함께```json{ "username": "root", "tty": "pts/1", "src_ip": null, "login_time": "2023-09-12 14:13:28", "logout_time": "2023-09-12T17:13:28+03:00", "duration": "00:00", "@timestamp": "2023-09-12 14:13:28" }
#### 아티팩트 유형: Parse
이 아티팩트 유형은 정규식을 사용하여 텍스트 파일을 파싱하고 데이터를 구조화된 형식으로 반환하는 기능을 제공합니다. 아래 예제는 nginx 액세스 로그를 파싱하고 결과를 구조화된 형식으로 반환합니다:```yaml
artifcats:
- name: nginx_access
type: parse
description: "Nginx access logs"
paths:
- /var/log/nginx/access.*
regex: '(?P<c_ip>[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) - (?P<remote_user>[^ ]+) \[(?P<time>[0-9]{2}/[a-zA-Z]{3}/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2} \+[0-9]{4})\] "(?P<method>[A-Z]+)?[ ]?(?P<uri>.*?)[ ]?(HTTP/(?P<http_prot>[0-9\.]+))?" (?P<status_code>[0-9]{3}) (?P<body_bytes_sent>[0-9]+) "(?P<referer>.*?)" "(?P<user_agent>.*?)"'
이 설정은 /var/log/nginx/access.* 경로의 파일들을 한 줄씩 읽고 정규식을 실행하여 필드를 추출합니다. 이 아티팩트는 또한 파일이 gzip 형식인지 확인하는데, gzip은 오래된 로그를 압축하여 공간을 절약하는 데 사용되며, 압축을 풀고 파싱합니다. 정규식은 rust regex 라이브러리에 문서화된 named captures 형식이어야 합니다. 다음은 파싱 전후의 nginx 액세스 레코드 예시입니다:
* 파싱된 레코드 ```json
{
"c_ip": "192.168.133.70",
"remote_user": "-",
"time": "23/Jan/2022:19:14:37 +0000",
"method": "GET",
"uri": "/blog/",
"http_prot": "1.1",
"status_code": "200",
"body_bytes_sent": "2497",
"referer": "https://u0041.co/",
"user_agent": "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0",
"full_path": "/var/log/nginx/access.log.9.gz"
}
이 선택적 필드는 결과 필드 이름을 변경하고 필드 값에 수정자(modifier)라는 후처리를 실행하는 데 사용할 수 있습니다. 아래 예제는 maps 없이 nginx 액세스 레코드를 파싱한 결과를 보여줍니다:
* 원본 기록```verilog
192.168.133.70 - - [23/Jan/2022:19:14:37 +0000] "GET /blog/ HTTP/1.1" 200 2497 "https://u0041.co/" "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0"
필드 이름 `time`을 `@timestamp`로 변경하려면 아티팩트 구성에 다음 맵 구성을 추가합니다:```yaml
artifcats:
- name: nginx_access
type: parse
description: "Nginx access logs"
paths:
- /var/log/nginx/access.*
regex: '(?P<c_ip>[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) - (?P<remote_user>[^ ]+) \[(?P<time>[0-9]{2}/[a-zA-Z]{3}/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2} \+[0-9]{4})\] "(?P<method>[A-Z]+)?[ ]?(?P<uri>.*?)[ ]?(HTTP/(?P<http_prot>[0-9\.]+))?" (?P<status_code>[0-9]{3}) (?P<body_bytes_sent>[0-9]+) "(?P<referer>.*?)" "(?P<user_agent>.*?)"'
maps:
- from: time #change field name from
to: '@timestamp' # to this name
동일한 nginx 액세스 로그에서 설정과 함께 수집 도구를 실행한 후, 다음 출력을 얻습니다.```json { "c_ip": "192.168.133.70", "remote_user": "-", "@timestamp": "23/Jan/2022:19:14:37 +0000", "method": "GET", "uri": "/blog/", "http_prot": "1.1", "status_code": "200", "body_bytes_sent": "2497", "referer": "https://u0041.co/", "user_agent": "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0", "full_path": "/var/log/nginx/access.log.9.gz" }
#### 수정자
`modifiers`는 아티팩트 결과의 필드 값에 대한 후처리를 제공합니다. 예를 들어 날짜와 시간 형식을 재구성합니다. 위의 예제를 계속해서, `@timestamp` 필드의 날짜와 시간 형식을 `%Y-%m-%d %H:%M:%S` 형식으로 변경할 수 있습니다. 이를 위해 아티팩트 구성에 다음을 추가할 수 있습니다:```yaml
artifacts:
- name: nginx_access
type: parse
description: "Nginx access logs"
paths:
- /var/log/nginx/access.*
regex: '(?P<c_ip>[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) - (?P<remote_user>[^ ]+) \[(?P<time>[0-9]{2}/[a-zA-Z]{3}/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2} \+[0-9]{4})\] "(?P<method>[A-Z]+)?[ ]?(?P<uri>.*?)[ ]?(HTTP/(?P<http_prot>[0-9\.]+))?" (?P<status_code>[0-9]{3}) (?P<body_bytes_sent>[0-9]+) "(?P<referer>.*?)" "(?P<user_agent>.*?)"'
maps:
- from: time
to: "@timestamp"
modifier:
name: datetime_to_iso
parameters:
input_time_format: '%d/%b/%Y:%H:%M:%S %z'
output_time_format: '%Y-%m-%d %H:%M:%S'
결과 레코드는 다음과 같습니다:```json { "c_ip": "192.168.133.70", "remote_user": "-", "@timestamp": "2022-01-23 19:14:37", "method": "GET", "uri": "/blog/", "http_prot": "1.1", "status_code": "200", "body_bytes_sent": "2497", "referer": "https://u0041.co/", "user_agent": "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0", "full_path": "/var/log/nginx/access.log.9.gz" }
The available modifiers are:
| Name | 세부 사항 | input_time_format | output_time_format |
| ------------------------ | ------------------------------------------------------------ | -------------------------------------- | ------------------------------------------------------------ |
| epoch_to_iso | 에포크 타임스탬프를 사용자 정의 날짜 및 시간 형식으로 변환합니다 | N/A | 출력 날짜 및 시간 형식을 지정합니다. 기본값은 `%Y-%m-%d %H:%M:%S`입니다. |
| datetime_to_iso | `input_time_format` 형식의 날짜와 시간을 `output_time_format` 형식으로 재구성합니다. | 입력 날짜 및 시간 형식을 지정합니다. | 출력 날짜 및 시간 형식을 지정합니다. 기본값은 `%Y-%m-%d %H:%M:%S`입니다. |
| time_without_year_to_iso | 연도 정보가 없는 날짜와 시간을 `input_time_format` 형식에서 `output_time_format` 형식으로 포맷합니다. | 입력 날짜 및 시간 형식을 지정합니다. | 출력 날짜 및 시간 형식을 지정합니다. 기본값은 `%Y-%m-%d %H:%M:%S`입니다. |
| to_int | 문자열 데이터(예: `command` 및 `parse` 아티팩트 유형)를 정수(`i64`, 즉 부호 있는 64비트 정수)로 변환합니다. 이는 파일 크기와 같은 필드에 유용하여 선택한 데이터 플랫폼을 사용해 `size < 1024`와 같은 검사를 수행할 수 있습니다. | N/A | N/A |
`time_without_year_to_iso` 수정자는 다음과 같이 작동합니다:
* 현재 연도를 추가한 후 파서 시간 < 현재 시간인지 확인합니다. 만약 그렇다면 올바른 시간입니다.
* 그렇지 않다면 전년도입니다.
이 수정자는 로그가 **오직** 한 해에 대한 것임을 가정하므로 주의해서 사용하십시오.