공부/JUN STUDY

Fuzzotron 을 이용한 NASA cFS(Core Flight System) 퍼징하기

JUNFUTURE 2024. 5. 12. 17:23

1. 소개

Fuzzotron이라는 네트워크 퍼저를 이용해 UDP 패킷을 cFS 프로그램으로 전송하여 퍼징을 진행했다.
cFS를 설치하는 것은 아래 링크를 참고하면 좋다.
https://juntheworld.tistory.com/190

 

cFS 설치 및 실행하는방법

Part1 - Setup아래 깃허브 링크에서 cFS를 git clone 해준다.https://github.com/nasa/cFS GitHub - nasa/cFS: The Core Flight System (cFS)The Core Flight System (cFS). Contribute to nasa/cFS development by creating an account on GitHub.github.comgi

juntheworld.tistory.com

 

참고로 해당 게시글은 아래 DEF CON 발표에서 영감을 얻어 제작하게되었다.

DEF CON 29 Aerospace Village - Fuzzing NASA Core Flight System Software, Ronald Brobert

 

 

 

2. Fuzzer 설치 - Fuzzotron

fuzzotron은 TCP/UDP based network 퍼저이다.

실제 TCP/UDP 패킷을 테스트케이스로 넣어주면 이 패킷과 유사한 모양의 인풋들을 만드는 뮤테이션을 진행해 타겟 ip:port로 패킷들을 전달해준다.

 

https://github.com/denandz/fuzzotron

 

GitHub - denandz/fuzzotron: A TCP/UDP based network daemon fuzzer

A TCP/UDP based network daemon fuzzer. Contribute to denandz/fuzzotron development by creating an account on GitHub.

github.com


아래의 명령어들로 fuzztron을 설치 및 빌드해준다.

# git repository clone
git clone https://github.com/denandz/fuzzotron

# build dependency install
sudo apt install libssl-dev libpcre3-dev

# make fuzzotron
make

 

3. Fuzzotron 실행

실행은 아래 명령어로 간단히 해주면 된다.
자세한 실행 옵션들이 많지만 중요한 것은 아래의 명령어를 이해하는 것이다.

sudo ./fuzzotron --radamsa --directory testcasedirectory -h 127.0.0.1 -p 1234 -P udp -o output


타겟하는 프로그램이 127.0.0.1:1234 에서 실행되고있고
udp를 이용해 패킷을 받는 프로그램인 경우를 가정한다.

--radamsa : test case 생성할때 radamsa 를 이용하여 생성함
--directory : 맨처음 퍼징할때 참고할 original test cases를 담는 directory 지정
-h : 호스트이름. 타겟하는 ip주소나 도메인 네임을 입력
-p : 타겟 포트. 생성된 인풋들을 전송할 port 를 입력
-P : TCP/UDP등 프로토콜 종류 지정
-o : fuzzing 후 결과들을 담을 디렉터리 경로 지정

 

여기서 testcasedirectory 와 output은 디렉터리 이름이다.
적절히 디렉터리를 만들고 디렉터리 이름을 정해준 뒤 실행하면된다.

이때 --directory 로 지정해주는 디렉터리 안에 original test case를 넣어주면된다.

뒤에 이어지는 내용 (UDP packet capture)에서 자세히 살펴볼 예정이다.


지금은 테스트로 그냥 아무 파일이나 만들어서 아무 값이나 입력해주자.
ex) aaaaaaaaaaaaaaaaa

 

(참고) fuzzotron 실행옵션

FuzzoTron - A Fuzzing Harness built around OUSPG's Blab and Radamsa.

Usage (crash-detect mode - blab): ./fuzzotron --blab -g http_request -h 127.0.0.1 -p 80 -P tcp -o output
Usage (crash-detect mode - radamsa): ./fuzzotron --radamsa --directory testcases/ -h 127.0.0.1 -p 80 -P tcp -o output
Usage (crash-detect mode unix socket - radamsa): ./fuzzotron --radamsa --directory testcases/ -h /tmp/something.sock -P unix -o output
Usage (log-monitor mode): ./fuzzotron --blab -g http_request -h 127.0.0.1 -p 80 -P tcp -m /var/log/messages -r 'segfault' -o output
Usage (process-monitor mode): ./fuzzotron --radamsa --directory testcases/ -h 127.0.0.1 -p 80 -P tcp -c 23123 -o output

General Options:
	-k		Number of seconds before fuzzing stops
	-o		Output directory for crashes REQUIRED
	-t		Number of worker threads
	--trace		Use AFL style tracing. Single threaded only, see README.md

Generation Options:
	--blab		Use Blab for testcase generation
	-g		Blab grammar to use - eg /usr/share/blab/html.blab
	--radamsa	Use Radamsa for testcase generation
	--directory	Directory with original test cases

Connection Options:
	-h		IP of host to connect to or path to unix domain socket REQUIRED
	-p		Port to connect to REQUIRED for TCP and UDP
	-P		Protocol to use (tcp,udp,unix) REQUIRED
	--ssl		Use SSL for the connection
	--destroy	Use TCP_REPAIR mode to immediately destroy the connection, do not send FIN/RST.

Monitoring Options:
	-c		PID to check - Fuzzotron will halt if this PID dissapears
	-m		Logfile to monitor
	-r		Regex to use with above logfile
	-z		Check script to execute. Should return 1 on server being okay and anything else otherwise.

 

 

성공적으로 실행되면 다음과 같은 화면을 확인할 수 있다.

4. UDP packet capture

fuzzotron testcase 생성을 위해 정상적인 cFS와 GroundStation 간의 통신환경을 갖추고 여러 예시 패킷들을 수집했다.

이렇게 생성한 packet 들을 fuzzotron의 초기 뮤테이션용 인풋(testcase)로 사용할 수 있다.

cFS와 GroundStation 정상패킷캡처

 

pcap packet parsing & reproduce

wireshark를 이용해서 패킷을 캡처하고 이 패킷을 그대로 cFS에 전달하여 reproduce해본다.

이때 두가지 도구를 이용할 수 있는데,

 

첫번째는 fuzzotron에 함께 내장되어있는 replay기능이고

두번째는 udpreplay라고하는 네트워크 패킷을 전송해주는 도구이다.

 

두가지 도구의 차이점은 아래와 같다.

  • fuzzotron의 replay는 UDP/TCP 패킷 데이터 영역에 있는 값만 읽어서 -h -p 로 지정해준 주소와 포트로 보내줌. 즉 헤더를 따로 붙여주고 새로운 패킷을 만들어서 보내주는 개념
  • udpreplay는 그냥 그 패킷 그대로 보내줌. 즉 인자로 받은 퍀시이 이미 udp 프로토콜 형태로 완성되어있다고 생각하고 바로 보내주는 개념

⇒ fuzzotron은 이 데이터를 읽고 헤더를 붙여서 보내는 식으로 fuzzing함.

  • 따라서 pcap를 이용해 udp 패킷을 캡쳐하고 별도로 파싱해주는 작업이 필요하다.

fuzzotron의 replay와 udpreplay를 이용한 udp 패킷 reproduce

 

Testcase 생성 및 fuzzing 시작

아래 코드를 이용해 pcap파일→여러개의 dat(fuzzotron testcase)로 파싱가능하다.

# oxagast
# use:
#   tcpdump -w ~/capture.pcap 'port 9000'
#   python3 pcap_gen_testcases.py ~/capture.pcap
# this will create a testcase directory of each destination port filled with files each
# the data section of the packets in the pcap file.  One packet is one file.  TCP header
# info is stripped.

import os
import sys
try:
    import scapy.all as scapy

except ImportError:
    import scapy
    
def prune_e(path):
    for (dirpath, dirnames, filenames) in os.walk(path):
        for filename in filenames:
            filef = dirpath + "/" + filename
            if os.path.getsize(filef) == 0:
                os.remove(filef)
def parse_pcap(pcap_path):
    print("Generating testcases based on " + sys.argv[1] + ".  This will take a while...")
    s = 0
    pcap_infos = list()
    packets = scapy.rdpcap(pcap_path)
    for p in packets:
        if p.haslayer("IP"):
            src_ip = p["IP"].src
            dst_ip = p["IP"].dst
        if p.haslayer("TCP"):
            raw_d = p["TCP"].payload.original
            sport = p["TCP"].sport
            dport = p["TCP"].dport
            dport_dir = str(dport)
            if not os.path.exists("testcases/" + dport_dir):
                os.mkdir("testcases/" + dport_dir)
            num = str(s)
            out = open("testcases/" + dport_dir + "/pcap.data_packet." + num + ".dat", "wb")
            out.write(raw_d) # yeah, i do hit it raw.
            s = s + 1
        if p.haslayer("UDP"):
            raw_d = p["UDP"].payload.original
            sport = p["UDP"].sport
            dport = p["UDP"].dport
            dport_dir = str(dport)
            if not os.path.exists("testcases/" + dport_dir):
                os.mkdir("testcases/" + dport_dir)
            num = str(s)
            out = open("testcases/" + dport_dir + "/pcap.data_packet_udp." + num + ".dat", "wb")
            out.write(raw_d) # yeah, i do hit it raw.
            s = s + 1

if len(sys.argv) != 2:
    print("You need to provide a .pcap file.")
    sys.exit(1)
if not os.path.exists(sys.argv[1]):
    print("The .pcap file does not exist.")
    sys.exit(1)
if not os.path.exists("testcases"):
    os.mkdir("testcases")

parse_pcap(sys.argv[1])
prune_e("testcases")
print("Done.")
sys.exit(0)

python pcap_gen_testcase.py를 실행한 모습

 

이제 생성된 testcases 디렉토리를 인자로 주고

cFS를 실행한 뒤 (./core-cpu1) 

아래 명령어로 퍼징을 할 수 있다!

sudo ./fuzzotron --radamsa --directory testcasedirectory -h 127.0.0.1 -p 1234 -P udp -o output

 

 

끝!