Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
efcf-framework — EF/CF - 초고속 스마트 계약 퍼징 | Kitploit
도구/GitHubGitHub/uni-due-syssec/efcf-framework
Vulnerability AnalysisExploitationFuzzingBinary Analysis
GitHubuni-due-syssec/efcf-framework

efcf-framework

EF/CF - 초고속 스마트 계약 퍼징

저장소 보기
70133년 전Kitploit 검토 완료

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

EF/CF - 극도로 빠른 (이더리움 스마트) 컨트랙트 퍼저

EF/CF는 스마트 컨트랙트 퍼징에 대한 새로운 접근 방식입니다. 새로 맞춤 제작된 퍼저를 사용하는 대신 C/C++ 코드용 기존 퍼징 인프라를 스마트 컨트랙트에 재사용합니다. 현재 AFL++가 기본 지원 퍼저이며, libfuzzer와 honggfuzz에 대한 매우 기초적인 지원도 일부 포함되어 있습니다.

왜 기존 퍼징 인프라를 사용하나요?

  • 속도. 더 빠르게 퍼징할 수 있습니다. 일반적으로 코어당 초당 약 20k execs를 달성합니다.
  • 네이티브 코드 퍼저는 잘 설계되고 최적화되어 있습니다.
  • 적절한 커버리지 가이던스, 큐 관리, 결정적 테스트 케이스 재생 등을 제공합니다.

그 과정에서 겪게 되는 문제들은 무엇일까요?

  • 퍼저에게 구조를 가르쳐야 합니다. 즉 트랜잭션이 무엇인지와 스마트 컨트랙트의 ABI가 무엇인지입니다. 이를 위해 커스텀 뮤테이터를 사용합니다: ./src/ethmutator/
  • 속도를 높이고 유용한 커버리지 피드백을 얻기 위해 커스텀 트랜스파일러를 사용하여 EVM 바이트코드를 C++로 변환합니다 ./src/evm2cpp/

이 저장소는 EF/CF 프로젝트의 기본 진입점입니다. 관련 코드는 모두 ./src/의 하위 프로젝트로 포함되어 있으며, 설치용 편의 스크립트, 퍼징 캠페인 실행용 스크립트, 그리고 퍼저를 테스트(및 다른 도구와 비교)하기 위한 다양한 데이터셋도 포함합니다.

  • ./src/ - EF/CF를 빌드하고 실행하는 데 필요한 모든 소스를 포함합니다. 재현성을 위해 모든 직접 의존성은 git 서브모듈로 추가됩니다.
  • ./data/ - 평가에 사용된 데이터셋을 포함합니다.
  • ./scripts - 실험 실행, 설치 등을 위한 스크립트를 포함합니다.
  • ./docker - 컨테이너 기반 워크플로우용 Dockerfile
    • 기본은 Ubuntu이지만, 원한다면 Fedora 또는 Arch Linux 기반 컨테이너도 사용할 수 있습니다.
    • ./docker/tools/에는 EF/CF를 평가할 때 비교 대상으로 사용한 도구들의 dockerfile이 들어 있습니다. 논문에서 평가한 버전을 dockerfile에 고정하기 위해 최선을 다했습니다.
  • ./EXPERIMENTS.md - 논문의 실험을 재현하기 위한 가이드를 포함합니다.
  • ./examples - EF/CF가 생성한 예제 출력물을 포함합니다.

EF/CF 논문

EF/CF의 아키텍처, 구현, 그리고 평가 결과 요약은 다음 논문에서 확인할 수 있습니다: arxiv.org 프리프린트

학술 연구에서의 인용

학술 연구에서 EF/CF를 인용할 때는 다음 bibtex 항목을 사용해 주십시오:```bibtex @InProceedings{efcf2023, author = "Michael Rodler and David Paaßen and Wenting Li and Lukas Bernhard and Thorsten Holz and Ghassan Karame and Lucas Davi", title = "EF/CF: High Performance Smart Contract Fuzzing for Exploit Generation", booktitle = "{IEEE} European Symposium on Security and Privacy ({EuroS&P})", publisher = "{IEEE}", year = "2023", }

root@kitploit:~
## Quickstart

권장 방법은 EF/CF를 대화형 docker 컨테이너로 실행하는 것입니다.

1. 셸로 컨테이너에 진입합니다.   ```
   docker run --rm -it ghcr.io/uni-due-syssec/efcf-framework

또는 복제된 저장소에서 컨테이너를 빌드하세요 ``` make gitmodules # to fetch the git submodules make container-enter

root@kitploit:~
1. solidity 계약을 컴파일한 다음 첫 번째 크래시/버그가 발견될
때까지 퍼즈합니다:   ```
efcfuzz --until-crash --out ./baby_bank_results/ --source ./data/examples/baby_bank.sol
  1. 식별된 크래시 검사 ``` cd /tmp/baby_bank_results/ ./r.sh crashes_min/default_id:000000*
    root@kitploit:~

설치 / 설정

Git 서브모듈

git이 없나요? tarball/docker 릴리스를 사용한다면 이 부분은 무시하세요.

이미 클론한 저장소에서 최신 서브모듈 커밋을 가져오려면 git submodule update --init을 실행하세요. 이 작업을 ./src/eEVM에서도 실행해야 합니다.``` git submodule update --init; cd src/eEVM/; git submodule update --init; cd ../../

root@kitploit:~
*경고:* `git clone --recursive $repo`를 실행하거나 `git sumbodule (update|init)`에 `--recursive` 인수를 전달하면 git이 AFL++ 저장소의 서브모듈까지 재귀적으로 처리하게 되는데, 이 프로젝트에는 필요하지 않습니다. 따라서 공간을 절약하려면 재귀적 서브모듈 체크아웃을 피하는 것이 좋습니다.

### 컨테이너

컨테이너 기반 워크플로를 위해 다음과 같은 편리한 make 타겟을 제공합니다.```sh
make container-build  # build default efcf container
make container-enter  # enter default efcf container in current working dir

클린 빌드를 보장하려면 다음 명령을 사용할 수 있습니다.```sh make container-build CLEAN_CHECKOUT=1

root@kitploit:~
또는 다음 docker 명령으로 컨테이너를 빌드할 수 있습니다:```sh
docker build \
    -f docker/ubuntu.Dockerfile \
    -t efcf:latest \
    .

참고로 Archlinux 및 Fedora 기반 Dockerfile도 있습니다. 이들은 작동해야 하지만, 그만큼 잘 테스트되지는 않았습니다.

docker 이미지를 수동으로 배포하려면(예: 일부 로컬 변경 사항을 포함하는 경우), 다음을 사용하세요:``` make container-release docker load -i ./efcf*.tar

root@kitploit:~
We recommend the following docker options for launching:

* `--security-opt seccomp=unconfined` - better fuzzing perf
* `--net=host` - for easy access to a local ethereum node
* `--tmpfs "/tmp/efcf/":exec,size=6g` - put EF/CF's temporary files onto a ramdisk if possible (less disk wear)
* `--privileged` - to run `afl-system-config` or `efcfuzz --configure-system`
* `-v` - to persist the output data of EF/CF


### VM / 베어메탈

VM 또는 베어메탈 기반 워크플로우의 경우:```sh
make system-install   # install efcf to current system (requires root or sudo rights)

참고로 많은 스크립트가 어차피 상대 디렉터리 구조에서 동작하므로, 이는 주로 PATH에 두면 유용한 의존성 및 몇 가지 도구를 설치하는 것입니다. 우리는 다음 Linux 배포판에서 EF/CF를 테스트했습니다:

  • Ubuntu Jammy (또는 이후 버전)
  • Fedora ($ > 35 $)
  • Archlinux

(배포판은 그다지 중요하지 않습니다. 우리는 LLVM 13과 14를 테스트했으며 14를 선호합니다. LLVM 11 또는 12도 여전히 동작할 수 있지만, 언제나 그렇듯이 최신이 더 좋습니다. 중요한 것은 AFL++ 포크와 호환되는 LLVM이 있다는 것입니다.)

Mac OS / M1에서

우리는 Mac OS에서 EF/CF를 네이티브로 테스트하지 않았습니다. 아마도 동작하지 않을 가능성이 높습니다(예: Mac OS에서 afl-clang-lto는 동작하지 않는 것으로 보입니다). 가장 좋은 방법은 docker를 활용하는 것입니다.```sh

make sure that the submodules are initialized

make gitmodules

pull the linux/amd64 base image

docker pull ubuntu:jammy --platform linux/amd64

build the ef/cf image

docker build -t efcf:latest -f docker/ubuntu.Dockerfile --platform linux/amd64 .

launch the EF/CF container

docker run --tmpfs "/tmp/efcf/":exec,size=8g --platform linux/amd64 --rm -it -v $(pwd):$(pwd) -w $(pwd) efcf:latest

root@kitploit:~
docker desktop v4.21.1을 사용하여 테스트했으며 기본적인 EF/CF 사용이 작동합니다. 그러나 다음 사항을 고려하세요:
* 빌드 중 segfault가 발생하는 경우: Mac OS에서 docker가 사용하는 VM의 메모리 제한을 늘려 보세요.
* docker에서 rosetta를 사용하여 가속을 활성화해 보세요. 아마 조금 더 빨라질 것입니다.


### 개발 환경 설정

이 도구들은 일반적으로 설치할 필요가 없습니다. `system-install.sh` 스크립트나 Dockerfiles에 명시된 대로 필수
종속성을 설치하세요.

편의를 위해 `PATH`를 업데이트하는 몇 가지 스크립트가 있습니다:```sh
# POSIX-like shells (i.e., bash, ...)
source ./scripts/env.sh

# for the fish shell
source ./scripts/env.fish

Etherscan API 키

일부 스크립트는 Etherscan 서비스에서 메타데이터(예: ABI)를 가져오기 위해 API 키가 필요합니다. API 키가 있다면 이 키를 스크립트에 전달하기 위해 ETHERSCAN_API_KEY 환경 변수를 설정해야 합니다. docker 기반 워크플로우의 경우 --env 플래그로 docker 컨테이너를 실행하거나 .etherscan_api_key 파일에 API 키를 넣을 수 있으며, 그러면 API 키가 docker 컨테이너에 포함됩니다.

런처로 EF/CF 시작하기

편의를 위해, EF/CF 퍼저를 실행할 때 모든 세부 사항을 처리해 주는 래퍼 스크립트인 efcfuzz를 사용합니다.

빌드 및 퍼징 프로세스와 관련하여 퍼저의 동작을 구성하는 많은 명령줄 옵션을 설정할 수 있습니다. 옵션 목록은 efcfuzz --help를 참조하세요.

예제

Solidity 소스 코드를 EF/CF 네이티브 코드로 컴파일하고 5분(즉 300초) 동안 퍼징을 시작합니다.```bash efcfuzz --timeout 300 --source ./data/examples/baby_bank.sol

root@kitploit:~
또는, 퍼징 출력을 줄여서 실행할 수 있습니다 (`--quiet`는 기본 퍼저의 출력을
억제하고, `--print-progress`는 퍼징 진행 상황에 대한 간단한 요약을
출력합니다), 그리고 4개 코어에서 퍼저를 실행합니다.```bash
efcfuzz --quiet --print-progress --cores 4 --timeout 300 --source ./data/examples/baby_bank.sol

이미 컴파일된 바이트코드를 사용하고 해당 바이트코드를 EF/CF 네이티브 코드로 컴파일한 다음 퍼징을 시작합니다.```bash

efcfuzz can handle the combined.json output of the solidity compiler

pushd ./data/examples/; make baby_bank.combined.json; popd efcfuzz --timeout 300 --bin-runtime ./data/examples/baby_bank.combined.json

but you can also explicitely pass the runtime and deploy bytecode and the ABI

definition. This is useful if you want to fuzz contracts using other

compilers (e.g., vyper).

pushd ./data/examples/; make baby_bank; popd efcfuzz --timeout 300
--bin-runtime ./data/examples/baby_bank.bin-runtime
--bin-deploy ./data/examples/baby_bank.bin
--abi ./data/examples/baby_bank.abi

root@kitploit:~
래퍼는 go-ethereum/erigon 노드에서 컨트랙트의 상태를 내보내고
그 상태에서 퍼징을 시작할 수 있습니다.```bash
$ efcfuzz --timeout 300 --live-state 0xfffF8D17CB019E0825c478c666B251A7099df3FD

또한, --include-address-deps=y를 전달하면 내보낸 컨트랙트의 스토리지에 있는 다른 계정의 주소를 재귀적으로 검색하여 해당 주소들도 상태 내보내기에 포함할 수 있습니다. 하지만 이는 Solidity mapping 유형에 저장된 다른 컨트랙트는 포함하지 않습니다. 정말로 전체 상태를 재귀적으로 내보내려면 --include-mapping-deps=y 플래그도 함께 전달하세요.

하지만 주의하세요. 이 재귀적 조회는 컴파일 시간을 길게 만들고 퍼징 성능을 저하시킬 수 있습니다. 특히 자주 사용되는 컨트랙트는 내부 상태가 많을 수 있으며, 내보낸 상태를 사용하면 퍼징이 느려질 수 있습니다. 퍼저가 초당 1천 회 이상의 실행(execs/sec)을 달성할 수 있는지 확인하세요. 그렇지 않다면 인위적이고 더 작은 상태를 구성하는 것이 좋습니다. 로컬 go-ethereum 노드를 --dev 모드로 실행하고 컨트랙트를 배포해 보세요. 그런 다음 해당 노드에서 라이브 상태를 내보내세요.

래퍼는 빌드를 캐시하므로 두 번째 퍼징 실행은 훨씬 빨리 시작되어야 합니다. 초기 컴파일 시간이 더 이상 필요하지 않기 때문입니다. 빌드만 수행하고 캐시에 저장하려면 --build-only 인자를 전달하면 됩니다.

예제: 속성 기반 퍼징

EF/CF는 echidna 퍼저와 동일한 속성 정의를 사용하는 속성 기반 퍼징도 지원합니다. 속성(또는 불변 조건)은 퍼저에 대한 버그 오라클 역할을 하는 Solidity 함수로 표현됩니다. 예를 들어, 다음과 같은 Solidity 함수를 추가할 수 있습니다.```solidity function test_property_balance() public view returns (bool) { return total_balance < 1000; }

root@kitploit:~
이는 total_balance가 항상 1000 미만이어야 한다는 속성을 나타냅니다.
EF/CF는 어떤 트랜잭션 시퀀스를 사용하여 이 속성을 위반하는 경우,
즉, 오라클이 `false`를 반환하면 버그를 보고합니다.

EF/CF에 이것이 속성임을 알리려면 파일에 함수 시그니처 목록을
지정해야 합니다. EF/CF는 이를 퍼징 중에 확인할 속성 목록으로
사용합니다.

가장 쉬운 방법은 Solidity 컴파일러의 `--hashes` 플래그를 사용하여
관련 시그니처를 얻는 것입니다. 예를 들어,```
solc --hashes ./path/to/your.sol | grep test_property > property_list

이제 퍼저를 실행할 수 있습니다:``` efcfuzz --source ./path/to/your.sol --properties ./property_list -C

root@kitploit:~
`--disable-detectors`를 추가하여 내장된 이더 기반 버그 오라클을 비활성화할 수도 있습니다.

속성 기반 퍼징을 위해 다음 예제를 시도할 수 있습니다:```
efcfuzz \
    --properties ./data/examples/harvey_baz_properties.signatures
    --disable-detectors \
    --until-crash --timeout 120 \
    --source ./data/examples/harvey_baz.sol \

예: 이벤트 퍼징

EF/CF는 이벤트를 통해 표현되는 assertion 위반에 대한 퍼징을 지원합니다. 실제로 임의의 사용자 정의 이벤트를 버그 오라클로 사용하는 것도 지원합니다. 기본적으로 EF/CF는 대상 계약이 다음 이벤트 중 하나를 로그로 기록한 경우 버그로 식별합니다: AssertionFailed(), AssertionFailed(uint256), AssertionFailed(string), and Panic(uint256).``` efcfuzz --event-assertions
--timeout 120 --until-crash
--source ./data/properties-assertions-tests/verifyfunwithnumbers.sol

root@kitploit:~
또한 `--event-assertions-list ./path/to/eventslist.txt`를 사용하여 파일에서
확인하고자 하는 추가 사용자 지정 이벤트 토픽/해시를 지정할 수 있습니다.
앞서의 속성 목록과 마찬가지로 `solc --hashes`를 사용하여 형식을 얻은 후
이벤트 해시와 이름을 이벤트 목록 파일에 복사하면 됩니다.

기본적으로 EF/CF는 대상 계약에서 발생하지 않은 이벤트를 무시합니다.
이를 변경하려면 `--event-assertions-target-only=n`을 사용하세요.

(참고: `--assertions`를 사용하면 이벤트 및 Solidity assertion 확인을
모두 활성화할 수 있습니다)

**예: Solidity ^0.8 Assertions 퍼징**

현재 0.8 미만의 Solidity 버전에서는 Solidity 코드의 임의 assertion에 대한
퍼징을 지원하지 않습니다. 이전에는 Solidity assertion이 단순히 `invalid`
opcode를 트리거하여 상당히 강제적인 revert를 발생시켰습니다. Solidity
버전 0.8에서는 트랜잭션을 되돌리기 위해 `invalid` opcode를 사용하는 대신
`revert` 메커니즘을 사용하여 호출자에게 오류를 신호로 보냅니다.
EF/CF에서는 이러한 오류 전파 유형을 버그 오라클로 활용할 수 있습니다.
현재 EF/CF는 Solidity 오류 유형 `Panic(uint256)`에 대한 확인을 지원합니다.
[Solidity 오류에 대한 자세한
정보.](https://docs.soliditylang.org/en/v0.8.0/control-structures.html?highlight=assert#panic-via-assert-and-error-via-require)```
efcfuzz --sol-assertions \
    --timeout 120 --until-crash \
    --source ./data/assertions-tests/overflow.sol

(참고: --assertions를 사용하여 이벤트 및 solidity 어서션 검사를 모두 활성화할 수 있습니다)

시스템 사양 및 구성

코어당 4~16개 코어와 약 1GB 메모리를 할당하는 것을 권장합니다. 시스템을 고속 퍼징용으로 구성하려면 --configure-system 플래그를 사용하거나 직접 구성할 수 있습니다. Docker 컨테이너에서는 최상의 성능을 얻으려면 호스트도 구성해야 합니다. 중요하지 않은 호스트라면 컨테이너를 --privileged로 실행하고 /usr/local/bin/afl-system-config를 사용하여 고속 퍼징용으로 시스템을 구성할 수 있습니다 (이 경우 본질적으로 컨테이너를 root로 실행하게 됩니다).```

configure system

docker run --rm -it --privileged efcf afl-system-config

run fuzzer (somewhat sandboxed and using a tmpfs for less SSD wear)

docker run --rm -it
--security-opt seccomp=unconfined
--tmpfs "/tmp/efcf/":exec,size=6g
efcf

root@kitploit:~
## 퍼징 실험 실행하기

`data/tests/` 데이터셋에서 실험을 실행하려면 다음 명령을 사용하여
컨트랙트와 해당 퍼징 하네스를 빌드한 다음, 다양한 설정과 다중 반복
등으로 퍼저를 실행할 수 있습니다. 이 작업은 꽤 오래 걸리므로
실험을 병렬로 실행할 수 있습니다. 퍼징 실험을 빌드 단계와
퍼즈 단계로 나눕니다. 빌드 단계는 모든 스마트 컨트랙트를 순차적으로
빌드합니다(빌드 자체는 여러 코어를 사용하지만). 그런 다음 백그라운드에서
8개의 퍼저 인스턴스를 실행하며, 이들은 빌드 단계의 산출물을 가져와
퍼징 실행을 시작합니다. Makefile은 `docker` 또는 `podman`이
사용 가능한 경우 적절한 컨테이너에서 모든 것을 자동으로 실행하려고 시도합니다.```bash
make build-tests
make fuzz-tests CONTAINER_BACKGROUND=1 FUZZER_INSTANCES=8

백그라운드에서 컨테이너를 시작할 때 seccomp 및 네트워크 샌드박싱을 비활성화합니다. seccomp 샌드박싱을 비활성화하면 퍼징 성능이 향상됩니다. 호스트 네트워크를 사용하면 추가 구성 없이 EF/CF가 로컬 네트워크의 Ethereum 노드에 액세스할 수 있습니다.

이 데이터셋에서 다른 도구를 docker 컨테이너 내부에서 실행하기 위해 ./scripts/run-tools-on-dataset.py 스크립트를 사용했습니다. 예를 들어 멀티 데이터셋의 경우 다음 명령을 사용합니다:```bash python3 ./scripts/run-tools-on-dataset.py ./data/multi/ cd ./results/tools-multi/ python3 ../../scripts/get-tools-on-dataset-stats.py head stats.csv

root@kitploit:~
도구와 실행 횟수를 구성하려면 스크립트를 조정해야 합니다.

### 퍼징 실험 설정하기

여기서는 예시로 `tests` 실험을 사용합니다. 다음 단계에서 문자열
`tests`를 실험 이름으로 간단히 바꾸면 됩니다:

1. `./data/`에 데이터셋을 모으세요(예: 테스트 컨트랙트가 있는 `./data/tests` 데이터셋).
   Solidity 컨트랙트의 경우 컨트랙트를 빌드하기 위한 일반적인 `Makefile`인
   `sol.Makefile`이 있습니다. 원한다면 이를 재사용할 수 있습니다. 예시는
   `./data/tests/Makefile`을 참조하세요.
2. 필요한 전처리/스크래핑 단계를 포함하여 빌드 산출물을 생성하는 스크립트를
   작성하세요. 예를 들어 `tests` 데이터셋에는 `./scripts/build-tests.sh` 스크립트가
   있습니다. 빌드 산출물은
   `./builds/tests/${contract}.build.tar.xz`에 저장해야 합니다.
3. 퍼징 캠페인을 시작하는 스크립트를 작성하세요(예: `tests` 데이터셋의 경우
   `./scripts/fuzz-tests.sh`라는 스크립트를 만드세요). 일반적으로
   `./scripts/common.sh`의 공통 퍼징 캠페인 함수를 사용할 수 있습니다. 템플릿은
   `fuzz-tests.sh`를 참조하세요.
4. `fuzz-tests.sh`의 결과는 `./results/run-fuzz-tests/`에 저장됩니다.
5. 결과를 요약하기 위해 bash 기반 런처 스크립트용 `./scripts/summarize.py`와
   python 런처 스크립트(`efcfuzz` 도구)용 `./scripts/summarize_l.py`를 제공합니다.  
   단계 3에 따라 이 스크립트를 조정해야 할 수도 있습니다.



### 기존 퍼징 실험

#### 벤치마크

* <a href="./data/multi/">`./data/multi`</a>는 확장성 벤치마크를 포함합니다.
  더 긴 트랜잭션 시퀀스에서 분석 도구가 얼마나 잘 확장되는지 평가하는 데 사용했습니다.
  이 벤치마크는 세 가지 유형의 컨트랙트로 구성됩니다:
    * `multi_gen_*.sol` - 자동으로 합성된 컨트랙트로, 일련의
      `require(input <= MAGIC)`를 수행한 다음 내부 상태 변수를 설정합니다. 모든
      상태 변수가 설정되면 `selfdestruct`(또는 echidna 오라클)를
      트리거할 수 있습니다.
    * `multi_man_complex_*.sol` - 수동으로 만든 변형으로, `multi_gen`
      유형의 컨트랙트와 유사하게 동작하지만 좀 더 까다로운 제약 조건(예: 매직
      값과의 같음/다름 이외의 조건)을 특징으로
      합니다.
    * `justlen_*.sol` - [echidna-parade 예제](https://github.com/crytic/echidna-parade/blob/main/examples/justlen.sol)에서 가져온 것입니다.
    * `multi_simple_*.sol` - 퍼저/도구가 이론적으로 9개 또는 10개의 트랜잭션이 필요한
      버그를 찾을 수 있는지 확인하는 기본 검증입니다. 여기서 분석기는
      인자 없이 10개의 함수를 올바른 순서로 호출하기만 하면 됩니다.
      이는 대부분의 분석 도구에게 매우 쉽습니다.
* <a href="./data/throughput/">`./data/throughput`</a>는 처리량 평가에 사용한
  컨트랙트를 포함합니다. 이는 다양한 크기의 컨트랙트로 구성된 선별 세트입니다.
  이 컨트랙트의 모든 취약점을 패치했으며, 따라서 발견된 취약점이 처리량
  측정에 영향을 미치지 않습니다.
* <a href="./data/cov-max-testset">`./data/cov-max-testset`</a>는 코드 커버리지
  기반 퍼저 비교에 사용한 컨트랙트를 포함합니다.

#### 버그 탐지
  
* <a href="./data/ethbmc-vuln">`./data/ethbmc-vuln`</a>은 EthBMC가 취약하다고
  탐지한 컨트랙트 목록입니다.
* <a href="./data/ethbmc-timeouts">`./data/ethbmc-timeouts`</a>는 시간 초과로
  인해 EthBMC가 분석을 중단한 컨트랙트 목록입니다.
* <a href="./data/reentrancy">`./data/reentrancy`</a>는 재진입 공격에 취약한
  컨트랙트 집합입니다.
* <a href="./data/sailfish-dao-tp">`./data/sailfish-dao-tp`</a>는
  [sailfish 연구](https://github.com/ucsb-seclab/sailfish/tree/master/data/ground-truth)의
  일부로 재진입 버그를 포함하는 것으로 검증된 컨트랙트 집합입니다.
* <a href="./data/sailfish-dao">`./data/sailfish-dao`</a>는
  [sailfish](https://github.com/ucsb-seclab/sailfish/tree/master/data/bugs)가
  재진입 버그를 발견한 모든 컨트랙트 목록입니다.
* <a href="./data/sereum">`./data/sereum`</a>는
  [Sereum](https://github.com/uni-due-syssec/sereum-results)에 따르면 재진입 공격에 취약한 컨트랙트 목록입니다.
* <a href="./data/smartbugs-curated-accesscontrol">`./data/smartbugs-curated-accesscontrol`</a>
  는 선별된 smartbugs에서 "access control" 버그로 분류된 컨트랙트입니다.
  ([smartbugs github](https://github.com/smartbugs/smartbugs/tree/master/dataset/access_control))
* <a href="./data/smartbugs-curated-reentrancy">`./data/smartbugs-curated-reentrancy`</a>
  는 선별된 smartbugs에서 "reentrancy" 버그로 분류된 컨트랙트입니다.
  ([smartbugs github](https://github.com/smartbugs/smartbugs/tree/master/dataset/reentrancy))

#### 테스트

다음 데이터셋은 퍼저의 기능을 테스트하기 위한 기본적인 합성 테스트 컨트랙트를
포함합니다:
  
* <a href="./data/tests">`./data/tests`</a> 퍼저의 기본 기능을 확인하는 여러 소스에서
  수집한 기본 테스트입니다. 모두 selfdestruct
  오라클을 사용합니다.
* <a href="./data/tests-not-vuln">`./data/tests-not-vuln`</a> tests와 동일하지만
  취약한 것으로 탐지되지 않아야 합니다.
* <a href="./data/properties-tests">`./data/properties-tests`</a> 속성 기반 퍼징을 위한
  테스트 
* <a href="./data/assertions-tests">`./data/assertions-tests`</a> assertion을 위한
  퍼징 테스트입니다.


## 퍼징에 대한 자세한 내용

실제 퍼저(이 경우 AFL++)를 실행하기 위해 래퍼 스크립트를 사용합니다.
`efcfuzz` 런처를 사용할 때 이 작업은 자동으로 수행됩니다.```bash
$ cd data/tests
$ make SimpleDAO.evm2cpp
$ cd ../../src/eEVM/
$ env AFL_BENCH_UNTIL_CRASH=1 ./fuzz/launch-aflfuzz.sh SimpleDAO

tmux와 tmuxp가 설치되어 있다면 개발 및 검사를 위해 스크립트의 대화형 버전이 유용할 수 있습니다:```bash $ ./fuzz/interactive-aflfuzz.sh -b SimpleDAO

root@kitploit:~
그러면 꽤 오랫동안 컴파일 및 퍼징이 진행됩니다. 그런 다음
`cd ./fuzz/out/SimpleDAO*`를 실행하여 퍼징 결과를 확인할 수 있습니다. 우리의 래퍼 스크립트는
`afl-fuzz` 프로그램 실행 외에 추가 작업, 즉 주로
결과 후처리를 수행합니다. 또한 생성된 테스트 케이스를 분석하기 위한 몇 가지 편의
스크립트를 생성합니다.

* `./a.sh` - 테스트 케이스의 사람이 읽을 수 있는 형태를 출력합니다.
  `efuzzcaseanalyzer`의 래퍼입니다.
* `./r.sh` - 퍼저가 실행되었을 때와 동일한 설정으로 테스트 케이스를 실행합니다.
* `./m.sh` - 퍼저가 실행되었을 때와 동일한 설정으로 테스트 케이스를
  최소화합니다.
* `./c.sh` - 주어진 테스트 케이스로 이어지는 테스트 케이스의 "체인"을
  분석합니다. 퍼저를 분석/최적화하는 데 유용합니다. 어떤 큐 항목에 대한
  어떤 변이 체인에 의해 어떤 테스트 케이스가 생성되었는지 빠르게 확인할 수 있습니다. 
  `fzf`가 필요합니다.

다른 편의 보고서도 있습니다. 예를 들어,

* `./bugs` 및 `./bugtypes`는 식별된 모든 버그를 요약합니다.
* `./crashes_min`, 모든 `afl-fuzz`
  인스턴스의 최소화된 크래시를 포함합니다.

**EVM 기본 블록 코드 커버리지 보기**```bash
$ cat coverage-percent-all.evmcov
70.73170731707317

fuzz/evm-bb-coverage.sh 스크립트는 AFL 출력 디렉터리가 주어지면 기본 블록 커버리지를 계산합니다. 하네스는 선택적으로 기본 블록의 추적(trace)을 덤프할 수 있으며, 그런 다음 이는 evm2cpp가 출력하는 기본 블록 목록(즉, eEVM/contracts/의 .bb_list 파일)과 비교됩니다.

기본적으로 우리는 기본 제네릭 시드(참고: eEVM/fuzz/generic_seeds)가 생성하는 커버리지도 계산합니다:```bash $ cat coverage-percent-seeds.evmcov 10.5890

root@kitploit:~
커버된 기본 블록 목록은 `all.evmcov` 파일에 저장됩니다.


**생성된 테스트케이스 요약 보기**

`efuzzcaseanalyzer`를 사용하여 생성된 테스트케이스를 조회/요약할 수 있습니다,
예를 들어,```
$ efuzzcaseanalyzer -a ./contract.abi -s ./crashes_min/
Transactions Sequences:
--------------------------------------------------------------
TX [🪙]
    deposit()[🪙];
    withdraw(uint256)[↕️ ↩️ ];
    withdraw(uint256)[];
--------------------------------------------------------------
Number of fuzzcases: 1
Average number of TXs: 3
Number of unique TX sequences: 1
Number of unique TX sequences (consecutive deduplicated): 1

요약본은 일반적으로 crashes_tx_summary 및 queue_tx_summary 파일에 저장되지만, 후자는 다소 장황할 수 있습니다.

단일 크래시 테스트케이스 분석``` $ ./a.sh default/crashes/id:000000,...

roughly equivalent to running

$ efuzzcaseanalyzer -a ./contract.abi default/crashes/id:000000,... Block header: number: 0 difficulty: 0 gas_limit: 0 timestamp: 0 initial_ether: 0

TX with tx_sender: 54 (selector); call_value: 0x0; length: 36; block+=1; #returns=0 func: withdraw(uint256) input: { Uint(80), } TX with tx_sender: 238 (selector); call_value: 0x246ddf979; length: 4; block+=1; #returns=0 func: deposit() input: { } TX with tx_sender: 153 (selector); call_value: 0x3860e6373; length: 4; block+=1; #returns=0 func: deposit() input: { } TX with tx_sender: 166 (selector); call_value: 0x0; length: 36; block+=1; #returns=1 func: withdraw(uint256) input: { Uint(37000000000000000000), } returns: return val: 1; allows reenter: 2; data: 0x0000000000000000000000000000000000000000000000000000000000000001 TX with tx_sender: 166 (selector); call_value: 0x0; length: 36; block+=1; #returns=0 func: withdraw(uint256) input: { Uint(37000000000000000000), }

root@kitploit:~
그리고 fuzz target의 실제 결과를 얻으려면 다음을 실행할 수 있습니다:```
$ ./r.sh default/crashes/id:000000,sig:06,src:000000+000010,time:1584,EM-________SAO_______AD
# roughly equivalent to running
$ env EVM_DEBUG_PRINT=1 ./build/fuzz_multitx default/crashes/id:000000,sig:06,src:000000+000010,time:1584,EM-________SAO_______AD

[...]

account 0xc4b803ea8bc30894cc4672a9159ca000d377d9a3 has balance 0x100000000000000000000000000000001bc16d67562e80000( > 0x1000000000000000000000000000000000000000000000000)
Aborted (core dumped)

이로 인해 계약의 실행 추적 일부와 하네스가 수행하는 잔액 확인 결과를 포함한 많은 장황한 출력이 생성됩니다.

크래시 입력 최소화

크래시 입력에는 무작위 테스트 방식 때문에 관련 없는 트랜잭션이 포함되는 경우가 많습니다. 이는 크래시 입력에 대한 최소화를 수행하여 완화할 수 있습니다(즉, 여전히 크래시를 유발하는 한 입력을 줄이는 것). 크래시하지 않는 입력을 최소화하려면 -M 플래그를 사용하여 최소화 기준으로 커버리지에 따른 최소화를 활성화할 수 있습니다.

다음 명령은 테스트케이스를 줄이고 파일을 덮어씁니다:``` $ efuzzcaseminimizer -oa ./contract.abi ./build/fuzz_multitx ./default/crashes/id:000000,sig:06,src:000000+000010,time:1584,EM-________SAO_______AD

[..]

=== Before minimizing: === Block header: number: 0 difficulty: 0 gas_limit: 0 timestamp: 0 initial_ether: 0

TX with tx_sender: 54 (selector); call_value: 0x0; length: 36; block+=1; #returns=0 func: withdraw(uint256) input: { Uint(80), } TX with tx_sender: 238 (selector); call_value: 0x246ddf979; length: 4; block+=1; #returns=0 func: deposit() input: { } TX with tx_sender: 153 (selector); call_value: 0x3860e6373; length: 4; block+=1; #returns=0 func: deposit() input: { } TX with tx_sender: 166 (selector); call_value: 0x0; length: 36; block+=1; #returns=1 func: withdraw(uint256) input: { Uint(37000000000000000000), } returns: return val: 1; allows reenter: 2; data: 0x0000000000000000000000000000000000000000000000000000000000000001 TX with tx_sender: 166 (selector); call_value: 0x0; length: 36; block+=1; #returns=0 func: withdraw(uint256) input: { Uint(37000000000000000000), }

=== After minimizing: === Block header: number: 0 difficulty: 0 gas_limit: 0 timestamp: 0 initial_ether: 15133991795

TX with tx_sender: 4 (selector); call_value: 0x246ddf979; length: 4; block+=0; #returns=0 func: deposit() input: { } TX with tx_sender: 4 (selector); call_value: 0x0; length: 36; block+=1; #returns=1 func: withdraw(uint256) input: { Uint(37000000000000000000), } returns: return val: 1; allows reenter: 2; data: 0x TX with tx_sender: 0 (selector); call_value: 0x0; length: 36; block+=1; #returns=0 func: withdraw(uint256) input: { Uint(37000000000000000000), }

root@kitploit:~
## 테스트 케이스 형식 읽기

테스트 케이스 형식은 퍼징을 대상으로 하며 읽기가 그리 직관적이지 않다. 알아두어야 할 몇 가지 미묘한 점이 있다.

* 테스트 케이스 형식은 실행될 수 있는 트랜잭션들의 "큐"로 간주된다. 어떤 문제라도 발생하면 테스트 케이스 처리는 중단된다. 여기에는 다음이 포함된다:
    * 트랜잭션이 revert되는 경우.
    * 하네싱 코드에서 오류가 발생하는 경우.
    * 버그가 트리거되어 감지되는 경우.
  결과적으로, 출력된 테스트 케이스가 실제로 실행되는 것과 반드시 일치하지는 않는다 - 끝에 실행되지 않은 트랜잭션이 있을 수 있다. 자세한 출력을 확인하고 최소화 도구를 사용하여 그러한 부분을 제거하라!
* 마찬가지로, 너무 많은 `returns` 또는 잘못된 `reenter` 플래그가 있을 수 있다. 테스트 케이스 최소화 도구를 사용하여 이를 제거하라.
* 계약은 재진입을 수행해야 하는 트랜잭션 뒤에 목록에 다른 트랜잭션이 있을 때만 재진입된다 (즉, 큐에 다음 항목이 있을 때).
* `reenter` 플래그가 어떤 값으로 설정되어 있더라도 반드시 계약이 재진입된다는 의미는 아니며, 단지 하네스 코드가 가능하면 이를 시도할 것이라는 뜻이다. 예를 들어, 계약이 호출을 수행하지 않으면 재진입 가능성이 없으므로 `reenter` 플래그는 무시된다. 대개 최소화 도구는 잘못된 `reenter` 플래그를 제거한다.

일반적으로 이러한 문제 중 상당수는 테스트 케이스 최소화 도구를 사용하면 사라지므로, 생성된 테스트 케이스를 분석하기 전에 항상 최소화 도구를 사용하는 것이 좋다.

## 알려진 오탐(False Alarms)

EF/CF로 계약을 퍼징할 때 반복적으로 나타나는 것으로 보이는 몇 가지 유형의 오탐을 관찰했다.

* 설계상 Ether를 지불하는 계약. EF/CF의 Ether 이득 버그 오라클은 이러한 계약을 설계대로 작동함에도 불구하고 취약한 것으로 감지한다:
    * 도박 계약: 많은 도박 계약은 어떤 형태의 무작위성을 사용하는데, 이는 이미 Ethereum에서 좋지 않은 관행이다. 그러나 일부 도박 계약은 다음 블록해시의 마지막 두 자리 또는 이와 유사한 것을 추측하도록 강제하는 방식으로 구현된다. 이는 커밋먼트 스킴을 사용하여 가능해진다. 즉, 첫 번째 트랜잭션이 사용자를 특정 값에 커밋하고, 두 번째 트랜잭션이 추측을 트리거하고 이기면 지불한다. 이러한 계약은 실제 블록체인에서는 일반적으로 악용될 수 없다. 그러나 EF/CF의 시뮬레이션된 블록체인에서 퍼저는 두 번째 트랜잭션의 값이 관찰된 후 커밋먼트를 조정할 수 있다. 이 사실은 EF/CF가 더 나은 코드 커버리지에 도달하는 데 중요하다. 그러나 이로 인해 EF/CF가 도박 계약에서 퍼저가 결정적으로 이길 수 있는 TX 시퀀스를 식별하는 것도 쉬워진다.
    * 이자를 지불하는 계약: Ether를 투자한 다음 매 `N` 블록마다 일정 비율의 이자를 지불하는 작은 계약이 많이 있다. EF/CF의 시뮬레이션된 공격자는 `N` 블록을 기다린 후 이자 지급을 받을 수 있으며, 이는 다시 Ether 이득 버그 오라클에 의해 감지된다.
    * 에어드랍: 일부 토큰 계약은 에어드랍을 활성화한다. 즉, 특정 한도에 도달할 때까지 요청하는 모든 사람에게 토큰을 그냥 나눠준다. 예를 들어, 에어드랍은 종종 짧은 기간 동안만 활성화된다. 이러한 계약이 EF/CF 내에 배포되면 시간 제한이 에어드랍이 여전히 활성화되도록 설정되었을 가능성이 높다. EF/CF는 에어드랍된 토큰을 다시 판매할 수 있으면 Ether 이득으로 감지한다.
* 제어 가능한 `DELEGATECALL`의 즉시 보고: 현재 우리는 제어 가능한 delegatecall이 호출되는 즉시 보고한다. 그러나 호출자가 임의 주소에 대한 delegatecall을 의도적으로 수행하도록 허용하는 함수를 가진 여러 계약이 있다. 그러나 이러한 함수는 delegatecall 직후 트랜잭션을 무조건 revert시킨다. 이는 상태 업데이트나 Ether 전송이 유지되는 것을 방지한다. 일반적으로 이러한 함수는 함수 이름에 "simulate"와 같은 단어가 있어 쉽게 찾을 수 있다.
    * 이는 실행이 끝날 때까지 보고를 지연함으로써 EF/CF에서 수정될 수 있다. 그러나 이는 버그 오라클을 상당히 복잡하게 만든다.
    * 현재 이 문제를 수정할 계획은 없다.
* 초기화 함수 호출 가능: 블록체인에서 내보낸 계약을 퍼징할 때, EF/CF가 계약이 이미 초기화되었음에도 불구하고 초기화 함수를 호출할 수 있는 경우가 있음을 관찰했다. 일반적으로 이는 revert를 유발해야 하지만 EF/CF의 EVM 환경에서는 그렇게 되지 않는다. 초기화 함수를 다시 호출하면 예를 들어 초기화 함수가 *소유자* 변수나 유사한 것을 설정하기 때문에 종종 사소한 Ether 이득으로 이어진다.
    * 이 문제의 근본 원인이 무엇인지 아직 확실하지 않다. 그러나 일반적으로 초기화 함수가 `initializer`, `init` 등으로 호출되기 때문에 쉽게 찾을 수 있다.

## 일반적인 함정

우리는 이것을 어느 정도 사용 가능하게 만들기 위해 최선을 다했지만, 여전히 연구용 프로토타입이다. 문제가 발생할 것으로 예상하라. 다음은 우리가 관찰한 몇 가지 일반적인 문제이다:

* *Q: `TOKENPASTE` 매크로 때문에 이상한 컴파일 오류가 발생합니다.*  
  A: 이는 종종 `efcfuzz`가 잘못된 계약 이름을 추측할 때(즉, 추상 계약을 추측할 때) 발생한다. 대상 계약을 지정하려면 `--name YourContract`를 전달해 보라.
도구 다운로드