https://angangmoddi.tistory.com/96

 

Buffer OverFlow 대응방안

Buffer OverFlow로 인한 프로그램의 비정상적인 실행을 막기 위한 대응 방안. 1. 스택 쉴드(Stack Shild)  함수 호출시 스택에 저장되는 return address를 Global RET이라는 특수 스택에 저장한다.  그리고 함수

angangmoddi.tistory.com

 

Buffer OverFlow로 인한 프로그램의 비정상적인 실행을 막기 위한 대응 방안.

1. 스택 쉴드(Stack Shild)

 함수 호출시 스택에 저장되는 return address를 Global RET이라는 특수 스택에 저장한다.
 그리고 함수 종료시에 이 Global RET에 저장되어 있는 값과 스택의 return address를 비교해서

 두 값이 다르면, 프로그램을 종료시키는 방법이다.

2. ASLR(Address Space Layout Randomization)

 메모리공격을 어렵게 하기 위해서 스택, 힙, 라이브러리 등 데이터영역 주소들을 랜덤으로 프로세스
 주소 공간에 배치하여 실행시마다 데이터 주소가 바뀌도록 하는 방법이다.

3. 스택 가드(Stack Guard)

 스택내에서 return address와 변수 사이에 Canary Word를 저장해두고, 해당 값이 변경되었을 경우

 프로그램을 종료하여 메모리 공격을 방지하는 방법이다.

4. NX-bit(Non-executable stack)

 프로세서 명령어나 코드 또는 데이터 저장을 위한 메모리 영역을 따로 분리하는 기술이다.

 스택을 NX특성으로 설정하면, 지정된 메모리 공간은 데이터 저장을 위해서만 사용되고, 명령어는 실행할 수 없게된다.

 만약 이를 위반하고 명령어를 실행하려고 하면 프로그램을 종료하여 공격을 방지하는 방법이다.

'시스템' 카테고리의 다른 글

Init 프로세스  (0) 2022.10.23
Radamsa Fuzzer  (0) 2022.10.22
ASL Persistent Mode  (0) 2022.10.22
AFL Fuzzer Error  (0) 2022.10.22
AFL fuzzer & Exploit  (0) 2022.10.22
블로그 이미지

wtdsoul

,

Init 프로세스

시스템 2022. 10. 23. 21:59

https://www.kernelpanic.kr/16

 

1. Init 시스템이란

같은 카테고리의 글 보기 www.kernelpanic.kr/category/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4%20%EC%9D%BD%EA%B8%B0/Init%20%EC%8B%9C%EC%8A%A4%ED%85%9C%20-%20Systemd '오픈소스 읽기/Init 시스템 - Systemd'..

www.kernelpanic.kr

 

1.1 Init이란

Init은 리눅스 커널 부팅이 완료된 뒤 실행되는 첫 번째 프로세스다. 또한 동시에 Init은 커널이 직접 실행하는 유일한 프로세스다. 따라서 Init은 부모 프로세스를 가지지 않는 유일한 프로세스인 동시에, Init을 제외한 나머지 모든 프로세스의 조상이 된다. 이러한 특징으로 인해 Init은 아래와 같은 작업들을 수행한다.

Init이 하는 일

위 그림과 같이 Init은 프로세스와 시스템의 초기화와 관리를 수행한다. 우선 Init은 등록된 서비스 혹은 initrc에 기록된 백그라운드 서비스와 시스템 서비스를 실행한다. 우선 우리가 리눅스 상에서 GUI를 사용할 수 있도록 GDM, LightDM 등의 디스플레이 매니저를 실행한다. 백그라운드 서비스로는 파일을 필요할 때 문맥에 맞게 가져올 수 있도록 분류, 그룹짓는 tracker-miner가 있으며, 시스템 서비스로는 네트워크 관리자인 NetworkManager, IPC 버스인 Dbus 등이 있다. 또한 Init은 시스템의 관리를 한다. 대표적으로 시스템 서비스, 커널 등에서 발생하는 로그를 한데 모아서 저널링 하는 기능이 있다. 마지막으로 Init은 데몬 프로세스나, 부모가 죽어서 고아가 된 프로세스의 부모가 된다. 이 덕분에 리눅스의 모든 프로세스는 (Init을 제외하고) 모두 부모 프로세스를 가지게 된다.

백그라운드 서비스와 시스템 서비스를 구분하는 명확한 기준은 없다. 여기서는 시스템 기능을 구성/관리하는 역할을 하는지, 어플리케이션 수준에서 동작하는지에 따라 임의 구분하였다.

단 이러한 작업들은 Init 프로세스 혼자서 수행하는 것은 아니고, 시스템 서비스들의 도움을 받아서 수행한다. 또한 위 그림은 Systemd를 기준으로 하였기 때문에 Upstart, SysV 등 다른 Init 시스템과 다른 부분이 있을 수 있다. (Init 시스템들에 대한 구분은 아래 "1.3 리눅스에서의 Init 시스템" 참조)

 

1.2 Init의 동작 플로우

Init의 동작 과정을 아주 단순화 하면 다음과 같다.

1. 파일 시스템 초기화 (파일 마운트, 스왑 디바이스 관리, 가상 파일 시스템 마운트 등)

2. 네트워크 및 기본적인 시스템 동작을 위한 시스템 서비스 실행

3. 백그라운드 서비스 실행

4. GUI 쉘 실행

위는 아주 개략화한 Init 프로그램의 동작이다. 리눅스 상에는 다양한 Init 프로그램이 있는데, 이들의 동작을 일반화 하기는 어렵다. 다음 포스트에서 대표적인 Init 프로세스인 Systemd를 다룰 때, Systemd의 동작 플로우에 대해 자세히 다루도록 하겠다.

 

1.3 리눅스에서의 Init 시스템

앞에서도 언급하였듯이 리눅스에는 다양한 Init 프로그램이 있다. 대표적으로는 오랜 역사를 가진 SysV, Ubuntu에서 사용되던 Upstart, 임베디드에서 종종 사용되는 runit, 그리고 가장 강력하며 오늘날 가장 널리 사용되는 Systemd가 있다. 각 Init 시스템은 아래와 같은 특징들을 가지고 있다.

init 시스템 특징 비고
SysV - initrc 파일을 통해서 시스템 서비스와 백그라운드 서비스를 실행
-
가장 오래된 Init 시스템
Upstart - 이벤트 기반의 서비스 관리
- 캐노니컬은 왜 자꾸 이런 갈라파고스같은 서비스를 만드냐...
Ubuntu에서 사용되던 Init 시스템
(현재는 Systemd로 변경)
Runit - 부팅 / 셧다운시 속도가 빠름
- 작은 코드베이스를 가졌으며, 포팅에 용이
가장 가벼운 Init 시스템, 임베디드 분야에서 사용
Systemd - 서비스를 "등록"하여 실행
- 이벤트 기반의 서비스 관리
- 저널링, 알람 등 자체적으로 다양한 기능 제공
- 다양한 시스템 관련 라이브러리(sdbus, sd-event 등) 제공
가장 많이 사용되며, 가장 기능이 많은 Init 시스템

이후 포스트에서는 가장 대중적이며, 많은 기능을 가지고 있는 Systemd에 대해서 좀 더 자세히 다루도록 하겠다.

'시스템' 카테고리의 다른 글

Buffer OverFlow 대응방안  (0) 2022.10.24
Radamsa Fuzzer  (0) 2022.10.22
ASL Persistent Mode  (0) 2022.10.22
AFL Fuzzer Error  (0) 2022.10.22
AFL fuzzer & Exploit  (0) 2022.10.22
블로그 이미지

wtdsoul

,

Radamsa Fuzzer

시스템 2022. 10. 22. 22:41

https://cpuu.postype.com/post/2069373

 

퍼즈 테스팅을 위한 Radamsa 소개

Radamsa는 Oulu University Secure Programming Group이 개발한 퍼즈 테스팅 도구이다. 이 도구를 활용하여 소프트웨어 제조사나 개인 개발자들이 보다 손쉽게 소프트웨어의 강건성(robustness) 테스트를 수행할

cpuu.postype.com

 

Radamsa는 Oulu University Secure Programming Group이 개발한 퍼즈 테스팅 도구이다. 이 도구를 활용하여 소프트웨어 제조사나 개인 개발자들이 보다 손쉽게 소프트웨어의 강건성(robustness) 테스트를 수행할 수 있도록 돕기 위한 취지로 개발되었다. 기존에 출시되어있던 유사한 프로그램들은 설치 자체가 굉장히 복잡하고, 개별 프로젝트에 적용하기 위해서 수많은 추가작업이 필요하다는 단점이 있었다. 이러한 문제점에 착안하여 새롭게 개발한 도구가 바로 Radamsa이다.

Radamsa는 커맨드라인 기반으로 작동할 수 있으며, 샘플 파일을 주입하면 자동으로 변이(mutated)된 결과물을 생산해준다. 그러므로 이 도구는 mutation-based fuzzer라고 구분할 수 있겠다. 

이 도구는 심지어 TCP Client 또는 서버로써 사용할 수도 있다. 즉, TCP 연결을 수립한 상태에서 서버 또는 클라이언트의 입장에서 mutation된 입력값을 송수신하게 만드는 것이다.

Radamsa는 하나의 패키지 안에 포함된 다수의 fuzzer를 통칭한다. 단순히 bit를 임의로 몇번 바꾸는 것에 그치지 않고, 샘플 파일의 구조를 분석하여 보다 색다른 퍼징 기법에 적용할 방안을 찾는다. 이러한 기술의 성과는 실제로 효과적이어서, real world의 다수의 소프트웨어에서 흥미로운 취약점들을 도출해내었다.

Radamsa는 무료로 사용할 수 있는 응용 프로그램이며, 필요한 경우 원본 소스코드를 다운로드 받아 각자의 용도에 받게 개조하여 사용하는 것 또한 허용된다. 소스코드는 MIT License를 따라 관리되고 있다.

 

사용법

Radamsa는 Windows 및 리눅스 용으로 prebuilt된 바이너리 형태가 제공되며, 자신의 환경에 알맞도록 직접 컴파일하여 사용할 수 있도록 소스코드 방식으로도 지원된다. 코드는 아래의 방법으로 얻을 수 있다.

*원래 Radamsa가 제공되던 페이지는 github였는데(https://github.com/aoh/radamsa), 2018년 6월 16일부로 gitlab(https://gitlab.com/akihe/radamsa) 으로 이전되었다는 안내가 게재되어 있다. 알만한 사람들은 알테지만 최근 Microsoft가 Github를 인수하였기 때문에 이와 관련해서 IT업계가 시끄러운 상황이고, 실제로 많은 개발자들이 gitlab으로 옮겨가고 있다고 하는데 아마 그 일환이라고 보는 개인적 소견.. (참고 : MS가 깃허브를 인수했는데, IT 업계가 왜 시끄러울까?

설치는 아래와 같이 가능하다(Ubuntu Linux 16.04 기준)

$ sudo apt-get install gcc make git wget
$ git clone https://gitlab.com/akihe/radamsa.git
$ cd radamsa/
$ make
$ sudo make install
 

대략적인 사용법이 적혀있는 help 문서가 있다.

https://gitlab.com/akihe/radamsa/blob/master/README.md 

 

간단히 예제를 보면..

 
$ echo "aaa" | radamsa
 aaaa
$ echo "aaa" | radamsa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ▒▒
$ echo "aaa" | radamsa
aaa▒▒1▒3
$ echo "aaa" | radamsa
แbaa
$ echo "aaa" | radamsa
aaab
 

 위와 같이 echo "aaa" 명령어의 결과를 radamsa로 전달했더니 처음에는 a라는 문자가 하나 더 붙는 형태로 나타났지만 여러번 재수행할때마다 새로운 방식으로 결과물이 mutate되는 것을 확인할 수 있다. 기본적으로는 /dev/urandom을 사용해서 난수적 흐름을 주입하는 것으로 알려져 있다.

 

Radamsa는 퍼즈 테스팅을 자동화할 수 있도록 하기 위해 다양한 환경을 제공한다. 특히 command-line 파라미터를 이용해서 원하는 숫자값을 자동으로 계속 생성해낼 수 있다. 그리고 입력으로 주입되는 seed 값을 이용해서 지속적인 변화 흐름을 만들어낼 수 있다. 다만 target monitoring을 기본적으로 지원하지는 않는다. 개발진은 이를 도울 수 있는 shell script example을 readme 파일을 통해 간단히만 설명하고 있다. 이를 참고하여 사용자가 직접 관련 기능을 구현해야 한다.

Radamsa는 샘플파일을 이용해 mutation 기능을 수행한다. 따라서 mutation 기반이기 때문에 모델을 분석하여 data를 스스로 generating 하는 방식은 아니다.

 

더욱 자세한 설명은 아래의 논문을 참고하면 된다.

An Evaluation of Free Fuzzing Tools 에서 발췌한  Radamsa 도구 평과결과 요약

[Mikko, V. "An Evaluation of Free Fuzzing Tools." University of Oulu Department of Information Processing (2015).]

아래는 Radamsa의 사용 편리성을 언급한 대목이다.

Ease of use criterionPossible advantage

Getting the executable Yes. Source and binaries available.
Documentation of the tool Yes. Comprehensive documentation exists.
Support for the tool Yes. Support is available and the project is still alive.
Environmental requirements Yes. Radamsa runs under Windows, Mac OS X, Linux, and OpenBSD.

아래는 Radamsa를 통해 실용적으로 얼마나 많은 이득을 취할 수 있는지에 대한 평가이다

Practical features criterionPossible advantage

Automation support Maybe. Supports generating multiple mutations and also TCP connections.
Target monitoring support No. No monitoring support.
Customization options Yes. Can control the samples, seed, mutations, patterns and generators. Also source code is available.

아래는 Radamsa가 퍼징하는 방식에 대한 설계적 탁월성을 뜻한다

Intelligence of the fuzzing criterionPossible advantage

Is the data generated or mutated, or totally random Yes. The data is mutated from the input samples.
Does the fuzzer utilize fuzzing heuristics Yes. The mutations include both random and smart mutations.
Creating a model for data generation No.

 

결론

Radamsa는 아주 간단하면서도 꽤나 강력한 fuzzer이며, 개발자가 자신의 구미에 맞게 적절히 수정하여 사용할 수 있다는 장점을 가지고 있다. 스스로 적절한 퍼즈 테스팅 구조를 수립하고, 그에 알맞게 구현을 변경할 수 있는 개발자들에겐 굉장히 좋은 도구일 것이다. target application에 대한 monitoring 기능은 꼭 추가적으로 제작을 해서 사용하는 것이 좋을 것이다.

'시스템' 카테고리의 다른 글

Buffer OverFlow 대응방안  (0) 2022.10.24
Init 프로세스  (0) 2022.10.23
ASL Persistent Mode  (0) 2022.10.22
AFL Fuzzer Error  (0) 2022.10.22
AFL fuzzer & Exploit  (0) 2022.10.22
블로그 이미지

wtdsoul

,

ASL Persistent Mode

시스템 2022. 10. 22. 22:23

Persistent mode

두가지 방법이 있다.

  • app.c 파일을 수정하는 방법
  • AFL_LOOP 매크로를 for 반복 루프 내에 포함시킨다.

gint i;

for(i=0;filenames[i] != NULL; i++){
	if (run_loop){
		#ifdef __AFL_COMPILER
			while(__AFL_LOOP(1000)){
				file_open_from_command_line (gimp, filenames[i], as_new);
			}
			exit(0);

		#else
			file_open_from_command_line (gimp, filenames[i], as_new);
		#endif
		}
  • AFL_LOOP를 xcf_load_invoker 함수 내에 포함시킨다.
--- ../xcf.c	2014-08-20 08:27:58.000000000 -0700
+++ ./app/xcf/xcf.c	2021-10-11 13:02:42.800831192 -0700
@@ -277,6 +277,10 @@
 
   filename = g_value_get_string (&args->values[1]);
 
+#ifdef __AFL_COMPILER
+  while(__AFL_LOOP(10000)){
+#endif
+
   info.fp = g_fopen (filename, "rb");
 
   if (info.fp)
@@ -366,6 +370,12 @@
   if (success)
     gimp_value_set_image (&return_vals->values[1], image);
 
+#ifdef __AFL_COMPILER
+  }
+#endif
+
+  exit(0);
+
   gimp_unset_busy (gimp);
 
   return return_vals;

첫 번째 방법은 다른 입력 형식도 타겟으로 할 수 있지만 두 번째 방법은 xcf만 대상으로 하므로 더 빠르게 버그를 찾을 수 있다.

 

 

 

'시스템' 카테고리의 다른 글

Init 프로세스  (0) 2022.10.23
Radamsa Fuzzer  (0) 2022.10.22
AFL Fuzzer Error  (0) 2022.10.22
AFL fuzzer & Exploit  (0) 2022.10.22
AFL Fuzzer  (0) 2022.10.22
블로그 이미지

wtdsoul

,

AFL Fuzzer Error

시스템 2022. 10. 22. 20:44

https://ppiazi.tistory.com/entry/AFLAmerican-Fuzzy-Lop-Fuzzer-%EC%82%AC%EC%9A%A9%EA%B8%B0-binutils-%EB%8C%80%EC%83%81

 

AFL(American Fuzzy Lop) Fuzzer 사용기 - binutils 대상

참고 자료 FUZZING WITH AFL-FUZZ, A PRACTICAL EXAMPLE (AFL vs BINUTILS) AFL Github AFL Training Github American Fuzzy Lop Homepage AFL이란? SW 버그를 찾기위해 다양한 상황을 고려한 테스트 케이스를 만..

ppiazi.tistory.com

 

'시스템' 카테고리의 다른 글

Radamsa Fuzzer  (0) 2022.10.22
ASL Persistent Mode  (0) 2022.10.22
AFL fuzzer & Exploit  (0) 2022.10.22
AFL Fuzzer  (0) 2022.10.22
퍼징으로 1-day 취약점 분석하기(GIMP)  (0) 2022.10.22
블로그 이미지

wtdsoul

,

AFL fuzzer & Exploit

시스템 2022. 10. 22. 15:10

https://rond-o.tistory.com/214

 

Fuzz with AFL & Exploit dact (1) Fuzzing

Overview AFL dact Fuzzing Build dact Use dact Build dact with scan-build build with AFL Fuzz with AFL Conclusion Overview 비오비 교육 때 박세준 멘토님께서 내주셨던 과제를 정리할 겸 AFL을 이용해 1-da..

rond-o.tistory.com

AFL

afl은 honggfuzz, afl, libfuzzer와 함께 소프트웨어 퍼징을 위한 대표적인 gery-box fuzzer다.

자세한 내용은 따로 정리할 것이며 여기서는 간단하게만 다룬다.

현재 오픈소스로 제공하고 있으며 whitepaper도 확인할 수 있다.

[링크] : afl open source, afl whitepaper

아래 내용은 논문 [The Art, Science, and Engineering of Fuzzing: A Survey]를 기반으로 정리한 내용이다. 논문 리뷰는 이 링크에서 확인할 수 있다.

Summary
  • Grey-box Fuzzer
  • Open-sourced
  • In-memory Fuzzing
  • Seed Scheduling
  • Mutation
  • Coverage-baesd Crash Traige
  • Evolutionary Seed Pool Update
  • Seed Pool Culling

Instrumentation

AFL은 한 가지 이상의 instrumentation 방법을 사용하는 대표적인 퍼저다.

  • static instrumentation

afl 전용 컴파일러를 이용해 소스 코드 수준의 정적 instrument 코드를 삽입한다.

  • dynamic instrumentation

실행 가능한 코드를 PUT 내부에서 instrument 하거나(default 옵션) 실행 가능한 코드를 PUT나 외부 라이브러리에서 instrument 한다(AFL_INST_LIBS 옵션).

* AFL_INST_LIBS 옵션은 실행되는 코드를 모두 instrument 하는 것인데, 외부 라이브러리의 코드에 대한 커버리지 정보도 포함할 수 있어서, 더 완전한 커버리지 정보를 제공한다. 이는 AFL이 외부 라이브러리 함수에서 추가적인 경로를 퍼징하도록 도와준다.

AFL과 그 하위(AFL 계보 퍼저들)의 퍼저는 PUT의 모든 branch instruction을 instrument해서 branch coverage를 계산한다


In-memory fuzzing

GUI처럼 복잡한 프로그램은 프로세스를 생성하고 입력을 전달하기까지 수 초가 소요된다. 이런 프로그램을 퍼징하기 위해 GUI 초기화가 완료된 후에 메모리 스냅샷(snapshot)을 찍는 접근 방식이 존재한다. 새로운 테스트 케이스를 퍼징하기 위해, 테스트 케이스를 메모리에 집접 쓰고 실행하기 전에, 메모리 스냅샷을 복원할 수 있다. 클라이언트-서버간의 상호 작용이 많은 네트워크 응용프로그램을 퍼징할 때도 이런 접근 방식을 사용할 수 있다.

AFL은 초기 실행의 코스트를 줄이기 위해 fork server를 사용한다. fork server는 모든 fuzz iteration에 대해 새로운 프로세스를 fork 한다. 즉 각 fuzz iteration마다 이미 초기화 과정이 끝난 상태의 프로세스를 fork 하여 퍼징할 수 있다.


Execution Feedback

AFL의 minset은 각 branch에 지수적(:logarithm)인 카운터가 있는 branch coverage를 기반으로 한다. 그 이유는 branch 카운터가 몇 배 차이로 다를 때만 차이가 있다고 인지하도록 설정한 것이다.

[AFL whitepaper의 "2) Detecting new behaviors"에서 확인할 수 있다.]


seed trimming

AFL은 어떤 시드가 동일한 커버리지를 달성하도록 유지하면서, 그 시드의 일부분을 제거하는 코드 커버리지 instrumentation을 사용한다.


FCS Algorithms

AFL은 FCS problem에 EA(Evolutionary Algorithm)을 사용한다. 즉 fitness의 가치가 있는 configuration set을 유지한다.

AFL은 control-flow edge를 실행하는 configuration 중, 가장 빠르고 가장 작은 configuration 입력을 포함하는 configuration을 가장 효과적(AFL에서는 favorite라는 용어를 사용)이라고 판단한다. AFL은 config 큐를 원형 큐(circular queue) 형태로 유지하면서 다음으로 효과적인 configuration을 선택하고, 일정한 횟수만큼 실행한다.


Mutation

AFL은 다양한 mutation 기법을 사용한다. 대표적인 방식은 다음과 같다.

Bit-Flipping, Arithmetic Mutation, Block-based Mutation, Dictionary-based Mutation


Triage (Coverage-based Deduplication)

AFL은 PUT의 각 실행에 대한 edge coverage를 기록하고 각 edge에 대한 대략적인 hit count를 측정하기 위해 효율적인 소스 코드 instrumentation을 사용한다. 주로 이 edge 커버리지 정보를 사용해 새로운 시드 파일을 선택한다.

게다가 이 방식은 독특한 중복 제거 기법으로 이어진다. 이전에 볼 수 없었던 edge를 커버했거나, 모든 이전 크래시들에 존재하던 edge를 커버하지 않은 경우 이를 unique한 크래시라고 간주한다.


Test case minimization

AFL은 crashing input의 크기를 줄이는 기술을 test case minimization라고 부른다.

AFL의 test case minimizer는 바이트를 0으로 설정하여 테스트 케이스의 길이를 줄인다. 


Evolutionary Seed Pool Update

AFL은 branch에 hit 한 횟수를 기록하여 fitness function의 정의를 구체화했다.


dact

dact는 특히 출력 파일의 크기를 작게 만들기 위해 입력 데이터의 블록 크기를 고려하여 가장 적합한 알고리즘을 선택하는 압축 도구다.

dact의 manpage는 아래와 같다.

[dact manpage] https://manpages.debian.org/testing/dact/dact.1.en.html


Fuzzing

Build dact

먼저 취약점이 있는 dact-0.8.42 버전의 바이너리를 준비하자.

  • dact-0.8.42 binary
cd ~
wget https://fossies.org/linux/privat/dact-0.8.42.tar.gz
tar -xvf dact-0.8.42.tar.gz
mv dact-0.8.42  dact_dir

아래와 같이 빌드할 수 있다.

  • dact build
    cd ~/dact_dir/
    make clean
    ./configure
    make
    
    mv ~/dact_dir/dact ~/dact_original

Use dact

-help 옵션으로 여러 옵션을 살펴볼 수 있다.

  • dact usage
~/dact_original -help

몇 가지 주요 옵션만 살펴보자.

옵션 없이 입력 파일을 전달하면 압축해 준다.

-d 옵션과 함께 입력 파일을 전달하면 압축을 해제해 준다.

-c 옵션은 stdout으로 압축 또는 압축 해제 결과를 출력해 준다.

-f 옵션은 강제로 압축(해제)를 진행하는 옵션이다.

직접 사용해보자.

  • use dact
cd ~/
echo hello > hello.txt
~/dact_original ~/hello.txt
cat ~/hello.txt.dct

rm ~/hello.txt
~/dact_original -d ~/hello.txt.dct
cat ~/hello.txt

압축했는데 dct 파일 포맷으로 인해 크기가 오히려 증가한 것을 알 수 있다.

  • size
$ ls -alh ~/hello.txt*
-rw-r--r-- 1 topcue topcue  6 Feb 17 17:10 hello.txt
-rw-r--r-- 1 topcue topcue 67 Feb 17 17:08 hello.txt.dct
  • dct format
$ xxd ~/hello.txt.dct

00000000: 4443 54c3 0008 2a00 0000 0000 0000 0600  DCT...*.........
00000010: 0000 0100 0000 0b00 0000 001a 0400 0968  ...............h
00000020: 656c 6c6f 2e74 7874 0000 0412 e602 1f01  ello.txt........
00000030: 0004 12e6 021f 000b 6865 6c6c 6f0a 0000  ........hello...
00000040: 0000 00                                  ...

Build dact with scan-build

먼저 동적 분석 도구인 scan-view를 써보기 위해 scan-build를 이용해 빌드 하자.

  • scan-build: static analysis tool
sudo apt-get install -y clang clang-tools

cd ~/dact_dir/
scan-build ./configure
scan-build make

그러면 /tmp 디렉토리 하위에 분석 결과를 확인할 수 있는 html 파일이 생성된다.

  • scan-view
ls /tmp
scan-view /tmp/scan-build-*/
  • output

scan-view dact

의심 가는 버그 30가지를 알려주지만 심각한 취약점으로는 보이지 않는다.

build with AFL

AFL로 퍼징할 수 있도록 먼저 AFL을 빌드하자.

  • build AFL Fuzzer
cd ~
wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
tar -xvf afl-latest.tgz
mv afl-2*/ afl_dir/
cd ~/afl_dir/
make
sudo make install

아래와 같이 AFL 컴파일러를 이용해 dact를 빌드 하자.

  • compile dact w/ afl-compiler
cd ~/dact_dir/
make clean
CC=~/afl_dir/afl-gcc ./configure
make

mv ~/dact_dir/dact ~/

Fuzz with AFL

퍼징할 때 dact의 입력을 전달하기 위해 in이라는 디렉토리를 만들고 입력으로 쓸 hello.txt.dct를 전달하자.

  • set input directory
mkdir ~/in
mv ~/hello.txt.dct ~/in/

그리고 코어 덤프 생성을 위해 아래 명령어를 사용할 수 있다.

  • for core
sudo sysctl -w kernel.core_pattern=core

이제 직접 퍼징해보자.

  • fuzzing with afl
~/afl_dir/afl-fuzz -i ~/in -o ~/out -- ~/dact_afl -dcf

-i와 -o는 각각 input/output 디렉토리를 지정해 준 것이다. --은 타겟 프로그램의 이름과 인자를 전달하기 위함이다.

즉 -dcf 옵션으로 dact를 퍼징하겠다는 의미다.

퍼징을 시작하면 아래와 같이 초기화 작업 이후 깔끔한 모습으로 상황을 보여준다.

 

아래 사진은 11분간 퍼징을 했을 때 상황이다.

Fuzzing for 11 minutes

unique crash가 27개 발견되었다.

이제 퍼징을 멈추고 취약점 종류를 살펴보자.

  • see crash
ls ~/out/crashes/

퍼징에 사용한 dact_afl 바이너리도 reproduce에 사용할 수 있지만, 더 많은 정보를 제공하는 ASAN을 이용하겠다.

아래와 같이 ASAN instrumentation 코드를 삽입하여 dact를 빌드 하자.

  • build dact with ASAN
cd ~/dact_dir/
make clean
CC="clang -fsanitize=address" CXX="clang++ -fsanitize=address" ./configure
make

mv ~/dact_dir/dact ~/dact_asan

이렇게 빌드 한 dact_asan으로 crashing input을 실행할 경우 crash가 발생한 이유를 자세히 확인할 수 있다.

crash 원인만 간단히 살펴보기 위해 dact_asan을 이용해 crash log를 생성하자.

  • make crash log w/ asan
for file in ~/out/crashes/*; do;
echo Input: $file >> ~/crash.log;
~/dact_asan -dcf $file 2>> ~/crash.log;
done;

아래와 같이 grep을 이용해 ASAN이 분류해 준 취약점 원인을 확인할 수 있다.

  • grep ERR
grep ERROR ~/crash.log

crash log

이들 중 스택 버퍼 오버플로우만 필터링하자.

  • grep stack-buffer-overflow
$ grep ERROR ~/crash.log | grep stack

==1355737==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffc9c54a180 at pc 0x0000004c9a75 bp 0x7ffc9c549870 sp 0x7ffc9c549868
==1355760==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffdec592a20 at pc 0x0000004c9a75 bp 0x7ffdec592110 sp 0x7ffdec592108

스택 버퍼 오버플로우 취약점을 트리거 하는 crashing input 두 개가 있다.

이들이 각각 어떤 파일인지 확인하기 위해 crash.log 파일을 열고 id를 확인하자.

  • detail
vi ~/crash.log

# search string w/ command mode
/stack-buffer-overflow

crash log detail

==1355760==ERROR: 라인 위로 쭉 올라가면 파일명을 확인할 수 있다.

Input: /home/topcue/out/crashes/id:000026,sig:06,src:000027,op:havoc,rep:8
dact: read: No such file or directory
dact: read: No such file or directory
...
dact: read: No such file or directory
dact: read: No such file or directory
=================================================================
1721 ==1355760==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffdec592a20 at pc 0x0000004c9a75 bp 0x7ffdec592110 sp 0x7ffdec592108
1722 WRITE of size 8 at 0x7ffdec592a20 thread T0
1723     #0 0x4c9a74 in dact_process_file /home/topcue/dact-0.8.42/dact_common.c:478:40
1724     #1 0x4cdbf7 in main /home/topcue/dact-0.8.42/dact.c:689:8
1725     #2 0x7f38a1a330b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/../csu/libc-start.c:308:16
1726     #3 0x41c4ad in _start (/home/topcue/dact_asan+0x41c4ad)
  • crashing input filename
Input: /home/topcue/out/crashes/id:000026,sig:06,src:000027,op:havoc,rep:8
Input: /home/topcue/out/crashes/id:000017,sig:11,src:000000,op:havoc,rep:16

crash.log는 모든 crash의 정보가 포함되어 있기 때문에 보기 어렵다.

두 파일만 따로 가져와서 재현해보자.

$ ls ~/out/crashes/ | grep -e "id:000016" -e "id:000017"

id:000016,sig:06,src:000000,op:havoc,rep:4
id:000017,sig:11,src:000000,op:havoc,rep:16
  • rename
cp ~/out/crashes/id:000016* ~/crash_1 
cp ~/out/crashes/id:000017* ~/crash_2

crash_1과 crash_2로 스택 버퍼 오버플로우를 재현하면 아래와 같다.

  • reproduce crash_1
~/dact_asan -dcf ~/crash_1

crash_1 in crash log

  • reproduce crash_2
~/dact_asan -dcf ~/crash_2

crash_2 in crash log

/home/topcue/dact-0.8.42/dact_common.c:478:40에 취약점이 있는 crash_2를 이용해 exploit할 것이다.

따라서 crash_2의 이름을 crash로 바꿔주자.

  • rename crash
mv ~/crash_2 ~/crash

crash의 hex view는 다음과 같다.

  • xxd crash
$ xxd ~/crash
00000000: 4443 54c3 0b08 2a00 0000 0000 0000 0605  DCT...*.........
00000010: 0003 0100 0000 0aeb 0000 b8b8 0400 0968  ...............h
00000020: 0000 6865 6c6c 6f0a 0000 0000 0004 1107  ..hello.........
00000030: 6865 6c6c 656c 6c6f 2e74 7874 0000 1013  hellello.txt....
00000040: 0402 1fe6 ff03 12e6 021f 0e0b 684f 6c12  ............hOl.
00000050: e602 1f00 0000 09

이제 crash를 이용해 다시 취약점을 트리거하고 화면에 출력된 결과를 분석해보자.

  • reproduce crash
~/dact_asan -dcf ~/crash

결과는 아래와 같다.

  • output (crash)
    
    

하나씩 해석해보자.

dact: read: No such file or directory
dact: read: No such file or directory
		...skip...
dact: read: No such file or directory
dact: read: No such file or directory

dact가 실행하면서 남긴 오류 메시지들이다.

=================================================================

address sanitizer가 출력한 구분선이며 이후 아래 모든 내용은 address sanitizer가 출력한 내용이다.

==1356226==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffa42eed60 at pc 0x0000004c9a75 bp 0x7fffa42ee450 sp 0x7fffa42ee448

ASAN이 감지한 취약점이 stack-based buffer overflow라는 사실을 알 수 있다.

또한 pc, bp, sp와 같은 레지스터 값을 출력해 준다.

WRITE of size 8 at 0x7fffa42eed60 thread T0
    #0 0x4c9a74 in dact_process_file /home/topcue/dact-0.8.42/dact_common.c:478:40
    #1 0x4cdbf7 in main /home/topcue/dact-0.8.42/dact.c:689:8
    #2 ...skip...

어떤 thread에서 버그가 발생했는지 알려준다.

그 아래는 스택을 backtrace 해주며 취약점이 존재하는 정확한 라인 넘버를 제시한다.→ dact_common.c:478

Address 0x7fffa42eed60 is located in stack of thread T0 at offset 2304 in frame
    #0 0x4c50ff in dact_process_file /home/topcue/dact-0.8.42/dact_common.c:249

  This frame has 16 object(s):
    [32, 36) 'cipher.addr'
    [48, 192) 'filestats' (line 250)
    [256, 2304) 'file_extd_urls' (line 252) <== Memory access at offset 2304 overflows this variable
    [2432, 2433) 'algo' (line 253)
    
		...skip...

    [2640, 2644) 'x' (line 265)
    [2656, 2664) 'offset' (line 266)

stack frame 정보를 제공한다. frame에 16개의 object들이 있으며 [256, 2304) 'file_extd_urls'의 256바이트부터 2303바이트까지 범위를 의미한다.

그런데 offset 2304에 접근하려 해서 overflow로 분류되었다.

Shadow bytes of crash

스택에 guard page를 넣어뒀는데 [f2]에 접근하다가 다른 object를 침범했다.


Conclusion

AFL을 이용해 dact 바이너리를 퍼징하고 crash를 생성했다.

이제 crash가 발생한 원인을 살펴보고 이를 이용해 exploit 해보자.

'시스템' 카테고리의 다른 글

ASL Persistent Mode  (0) 2022.10.22
AFL Fuzzer Error  (0) 2022.10.22
AFL Fuzzer  (0) 2022.10.22
퍼징으로 1-day 취약점 분석하기(GIMP)  (0) 2022.10.22
Exploiting Null Byte Buffer Overflow for a $40,000 Bounty  (0) 2019.12.26
블로그 이미지

wtdsoul

,

AFL Fuzzer

시스템 2022. 10. 22. 14:58

https://wogh8732.tistory.com/272

 

AFL fuzzer 설명 및 사용방법

1. AFL 퍼저 원리 afl 퍼저는 instrumentation-guided genetic algorithm 과 결합된 퍼저이다. 브푸트포스로 입력을 받지만, 거기서 끝나는게 아니라, 커버리지를 넓혀가며 프로그램 제어 흐름에 대한 변경사항

wogh8732.tistory.com

 

afl 퍼저의 특징은 다음과 같다

  • 커버리지 기반 퍼저이기 때문에 매우 효율적인 퍼징이 가능함
  • 코드 커버리지 측정을 위한 코드를 컴파일 타임에 삽입함
  • QEMU를 이용해서 컴파일 타임이 아닌, 런타임시에 코드 삽입도 가능함. 여기서 말하는 코드는 코드가 어디가 실행됬고, 어디가 실행 안됬는지를 측정해주는 코드를 뜻한다.

 

코드삽입 : Instrumentation 이라고 함

 

즉, 코드 삽입을 어디에, 언제할꺼냐가 중요하다. 

⇒ 이건 아직 정확히 의미를 모르겠다. 내가 수동으로 삽입 시점과 위치를 지정할수 있는건가.. 

 

어쨋든 afl 퍼저의 간단한 동작과정은 다음과 같다 

 

1) afl 전체 로직

위 그림은 퍼징을 대략적으로 보여준다.  

  1. afl-fuzz에서 우리가 퍼징하려는 프로그램을 실행한다.
  1. 해당 프로그램이 처음으로 실행되면서 afl-fuzz와 pipe로 통신을 하면서 fork server를 만들고, 새로운 타겟 인스턴스를 fork call()로 실행한다.
  1. 표준 입력 or file로 들어온 입력이 fuzz 대상 프로그램으로 전달된다.
  1. 실행된 결과를 공유 메모리에 기록하여 실행을 완료한다.
  1. afl-fuzz는 공유메모리에서 fuzz 대상이 남긴 기록을 읽고 이전 항목을 변경하여 새로운 입력을 만든다.
  1. 새롭게 만든 입력은 다시 프로그램에 들어가서 실행된다.

 

앞서 말한

공유 메모리

를 통해 새로운 입력을 만든다고 했는데 이 부분이 바로 코드 커버리지를 높이기 위한 로직으로, 컴파일시 branch에 다음과 같은 코드가 삽입된다 

  cur_location = <COMPILE_TIME_RANDOM>;
  shared_mem[cur_location ^ prev_location]++; 
  prev_location = cur_location >> 1;

 

공유메모리를 이전 실행과 비교하여 새로운 path 발견시 해당 input data를 저장하고, 이를 다음 루틴의 입력으로 사용한다. 

 

 

2) 성능

afl에서는 퍼징의 성능을 높이기 위해 많은 노력이 들어갔다. 그 중하나가 fork server 이용한다. 타겟 프로그램을 컴파일할때, fork server 관련 코드가 삽입이 되고, 실행이 되면, 해당 프로그램에서 fork server를 만든다. 

 

일반적으로 퍼징 대상 프로그램이 실행되면, 프로그램이 실행되기 위해 execve () , 링커 및 모든 라이브러리 초기화 루틴을 거치게 된다. 우리가 퍼징을 하려는 위치가 프로그램의 특정 기능이라고 가정하면, 위 초기화 과정은 한번만 호출되는것이 제일 합리적이다. 

 

따라서 fork server에서 이러한 초기화 루틴이 한번 실행되고, fork를 통해 필요 리소스를 그대로 child에게 넘겨주고, child에서는 타겟 기능에 대해서만 뮤테이션된 입력으로 퍼징을 수행하는것이 바로 afl에서 성능을 높이기 위한 방법이다.  

 

결론적으로 fork를 통해 중복적으로 수행되는 부분을 제거를 통해 2배정도의 성능 향상이 있다고 한다. ( 추가적으로 fork server를 생성하는 위치를 조정하여 쫌더 성능을 높을수 있다는 글을 봄 ) 

 

만약 종료되지 않는 프로그램을 대상으로 퍼징할때는 Persistent mode를 이용해서 특정 루프로 지정해둔 곳만 퍼징을 돌릴수도 있다. 

 

 

 

 

 

2. AFL 퍼저 설치 및 사용법


그냥 진짜 디폴트로 퍼징돌리는건 간단하다. 

$ wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
$ tar -xvf afl-latest.tgz
$ cd afl-2.52b/
$ make
$ sudo make install

 

압축파일을 다운받아서, 압축풀고, 컴파일해주면 위 사진과 같이 짜자잔! 나온다. 그다음 이제 분석하려는 오프소스 프로그램을 하나 다운받는다. 나는 dact라는 압축 프로그램을 다운받았다. 

 

$ wget https://fossies.org/linux/privat/dact-0.8.42.tar.gz
$ tar -xvf dact-0.8.42.tar.gz
$ make clean => 맨처음 실행하면 요건 안해줘도 됨
$ CC=[afl folder]/afl-2.52b/afl-gcc ./configure
$ make

참고로 CC=의 의미는 dact 프로그램을 afl-gcc 컴파일러를 사용하겠다 라는 옵션이다. 이를 넣고 configure로 환경세팅을 하고, 컴파일을 진행하면 된다. (이렇게 afl-gcc로 dact를 컴파일해야, 코드 커버리지 기능을 위한 코드가 dact에 컴파일시 삽입되다.) 

 

요렇게 나옴. dact 사용방법은 옵션보면 된다. 

 

내가 타겟으로하는 dact의 기능은 압축이 아닌 압축해제할때이다. 따라서 -d옵션을 반드시 줘야하면 추가적으로 c,f도 주었다. 그럼이제 testcase를 하나 만들고 input 폴더를 만들자. 

 

$ echo hello > hello.txt
$ ./dact hello.txt => hello.txt.dct 생성됨

mkdir input
mv hello.txt.dct input/

 

1. Fix core file setting
sysctl -w kernel.core_pattern=core
	=>코어파일을 생성을 해놔야 afl이 얘가 뒤졌나 안뒤졌나 알 수 있음

2. Run AFL
[afl folder]/afl-fuzz -i input -o output -- ./dact -dcf
- i는 인풋
- o 는 아우풋 폴더
 -- 이후는 실제로 넣을 인자들
- dcf : 옵션들 

위 명령어대로 실행을 하면 퍼징이 시작된다. 

 

위 표에대한 설명들은 아래의 사이트에서 확인 가능하다 

 

자세히는 더 공부해야하고, 일단 퍼징이 돌아가고, finding in depth 탭을 보면, 현재 total crash가 279개 정도 나왔고, 그 중 우측 상단에 uniq crashes는 18개가 나온것을 확인할 수 있다. 

 

output/crashes 폴더에 가보면, 크래시가 터졌던, 변조된 파일들을 볼수 있다. dact 옵션에 -d를 줬으므로, 압축해제하는 기능을 기준으로 퍼징을 했고, 위 파일들은 변조된 압축 파일들이라고 보면 된다. 보통 sig 11이 세그폴트이므로 요거를 주로해서 파일을 dact로 압축해제 해보자. 

 

세그폴트가 뜬것을 볼수있다. 압축을 해제할때, 뭔가 문제가 있고, 그 문제 때문에 에러가 터졌을 것이다. 그럼 우리는 dact에서 해당 파일이 압축해제될때를 디버깅하면 된다. 

 

또한 afl을 쫌더 공부하다보면, crashes 폴더에 들어있는 크래시난 파일들이 전부다 유효하지 않다는 것을 알것이다. 중복되는 크래시도 있을 것이며, 등등의 이유가 있는데, afl 퍼저 기능을 활용하면 이러한 중복이나 등등.. testcase들을 최적화 시킬수 있다. 이러한 성능과 관련된 설명들은 아래의 사이트에서 확인하면 된다. (나중에 추가로 이것도 정리할 예정) 

 

 

 

퍼징에 익숙해지고, 어딘가 불편함을 느끼거나, 원하는 동작을 수행시키고 싶을때 저런것들을 참조하면서 퍼징을 하는게 공부에 나는 더 도움이 된다. 그래서 지금은 그냥 기본적인 것들만 설명하... 

 

 

 

3. ASAN 사용


위에서 나온 크래시 파일을 디버깅하면서 익스를 진행하면되는데, 여기서 ASAN이라는 기능을 이용하면 크래시에 대한 정보가 매우 자세하게 나온다. ASAN이 뭔지는 따로 정리를 할것이기 때문에 간단한 사용방법만 정리하겠다. 

 

그냥 afl 사용하려면 다운받고, 컴파일하고 사용하면 된다고 했다. 근데 좀더 성능적으로 속도를 증가시키려는 목적으로 afl안에서 llvm모드로 컴파일을 해주면 좋다. 

 

llvm_mode 디렉토리로 들어와서 여기서 컴파일을 진행해주면, clang-fast 컴파일러를 사용할수 있다. 그 전에 llvm_mode로 컴파일을 하려면, clang을 설치해줘야한다. 

 

  • clang 관련 패키지 설치
# vi /etc/apt/sources.list 에다가 아래 내용 추가
deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-4.0 main
deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-4.0 main

# wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -

# apt-get update


# apt-get install clang-4.0 clang-4.0-doc libclang-common-4.0-dev libclang-4.0-dev libclang1-4.0 libclang1-4.0-dbg libllvm-4.0-ocaml-dev libllvm4.0 libllvm4.0-dbg lldb-4.0 llvm-4.0 llvm-4.0-dev llvm-4.0-doc llvm-4.0-examples llvm-4.0-runtime clang-format-4.0 python-clang-4.0 liblldb-4.0-dev lld-4.0 libfuzzer-4.0-dev

 

다시 첨부터 afl 다운을 받는다 치고 설명하면, 

 

$ wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
$ tar -xvf afl-latest.tgz
$ cd afl-2.52b/
$ make
$ sudo make install
-------------------------위에꺼는 안해줘도 될꺼같은데 일단 저것도 해주자..
$ cd ./llvm_mode
$ sudo ln -s /usr/bin/llvm-config-4.0 /usr/local/bin/llvm-config
$ sudo ln -s /usr/bin/clang-4.0 /usr/local/bin/clang
$ sudo ln -s /usr/bin/clang++-4.0 /usr/local/bin/clang++

$ make
$ make install

export AFL_USE_ASAN=1 // ASAN을 사용하기 위해서 넣어저야함
export PATH=/usr/lib/llvm-4.0/bin:$PATH

 

저렇게 afl-clang-fast++ 를 사용할수 있다. 이걸 왜 사용하냐면 요 fast가 붙은 놈이 바로 바이너리 컴파일 단계에서 원하는 코드를 삽입시킬수 있는 컴파일러이다. (afl-clang은 어셈레벨에서 코드삽입함) 

 

그다음 

 

정리하자면 

  • llvm_mode로 퍼징을 하는 목적⇒ 성능향상 
  • afl-gcc가 아닌 afl-clang-fast++ 로 컴파일러를 사용하는 목적⇒ 컴파일단계에서 코드삽입(ASAN) 

 

그럼이제 ASAN을 사용하기위한 준비는 끝났고, dact를 컴파일 하면 된다 

$ cd dact-0.8.42
$ make clean
$ CC=[afl folder]/afl-clang-fast CXX=[afl folder]/afl-clang-fast++ CFLAGS="-fsanitize=address -g " CXXFLAGS="-fsanitize=address -g" LDFLAGS="-fsanitize=address -g" ./configure
$ make

아까는 CC에 afl-gcc를 사용했는데, AsAN을 이용하기 위해, CC를 afl-clang-fast로 변경했다. CXX는 c++ 컴파일러이다. (afl-clang-fast++도 afl-clang-fast에 링킹되어있어서 동일하다고 보면 될듯) 

 

그럼 이제 아까 크래시 파일을 dact로 압축해제 해보자. 아까는 그냥 이렇게  

 

에러만 나왔다. 근데 다시 해보면? 

 

왓더~~~~~~~~~~~~ 개쩐다. 어느 부분에서 어떠한 에러가 낫는지가 정말 상세하게 나와있다. 해당 크래시는 힙 bof이며, 주소는 뭐고, backstrace를 뭐고, 터지는 시점의 힙 메모리가 어딘지 나와있다. 이거를 가지고 익스를 진행하면 훨씬 편하게 조질수 있다.!!!! 

 

하지만 ASAN을 사용하게 되면 당연히 많은 정보가 나오는 대신 그만큼 퍼징시 성능이 저하가 된다. 그래서 llvm_mode로 빌드한게 바로 저런 성능저하를 어느정도 막기 위함이다. 

 

이러한 새니타이저에 대한 이론은 아직 공부가 완벽하게 된것은 아니다. 추후에 더 공부해서 따로 어떤원리인지 공부할것이다. 뵤비때 남겨줬던 자료가 지금 빛을 발한다.아런아럼ㄴ앎이나럼니알 

 

이제 저 dact를 asan을 이용해서 나온 정보를 토대로 익스를 진행해보자!는 다음에 정리.. 

 

 

4. 참고자료


블로그 이미지

wtdsoul

,

https://gomguk.tistory.com/m/116

 

[Fuzzing 101] 퍼징으로 1-day 취약점 분석하기(GIMP)

들어가며 GIMP라는 이미지에디터를 퍼징한다. GIMP 2.8.16에서 발생하는 CVE-2016-4994을 분석한다. 그리고 발견한 크래시에서 코드 커버리지를 분석한다. CVE-2017-9048은 Use-After-Free 취약점이다. 동적할당

gomguk.tistory.com

들어가며

GIMP라는 이미지에디터를 퍼징한다. GIMP 2.8.16에서 발생하는 CVE-2016-4994을 분석한다. 그리고 발견한 크래시에서 코드 커버리지를 분석한다.

CVE-2017-9048은 Use-After-Free 취약점이다. 동적할당된 heap을 free하고 다시 재사용할 때 취약점이 발생한다. GIMP에서는 조작된 XCF 파일에서 발생한다.

공격자는 유효한 데이터의 손상에서 임의 코드 실행까지 다양한 악의적인 행위를 할 수 있다.

준비

1-day 실습이므로 취약점이 발생했던 같은 환경을 준비한다. 또는 다음의 내용을 학습할 수 있다.

다음의 내용을 학습한다.

  • 퍼징 속도 향상을 위한 persist mode 사용
  • 상호작용바이너리 퍼징 / GUI 프로그램 퍼징

Persist mode

AFL 지속 모드(Persist mode)는 단일 프로세스를 사용하는 퍼저를 기반으로 한다. 퍼저는 하나의 타겟 프로세스에 코드를 주입하여 값을 변조하거나 삽입한다.

afl-fuzz는 프로세스 내 퍼징의 이점과 멀티 프로세스 도구인 지속 모드를 결합한 작업 방법을 지원한다.

지속 모드에서 AFL++는 각 퍼즈 단계 실행을 위한 새로운 프로세스를 생성하는 대신 단일 분기 프로세스에서 대상을 여러번 퍼즈한다. 이 모드는 퍼징 속도를 20배까지 향상시킬 수 있다.

기본 구조는 아래와 같다.

//Program initialization

  while (__AFL_LOOP(10000)) {

    /* Read input data. */
    /* Call library code to be fuzzed. */
    /* Reset state. */
  }
  
  //End of fuzzing

 

Download and Build

1. 대상 프로그램 다운 및 빌드

cd $HOME
mkdir Fuzzing_gimp && cd Fuzzing_gimp
sudo apt-get install build-essential libatk1.0-dev libfontconfig1-dev libcairo2-dev libgudev-1.0-0 libdbus-1-dev libdbus-glib-1-dev libexif-dev libxfixes-dev libgtk2.0-dev python2.7-dev libpango1.0-dev libglib2.0-dev zlib1g-dev intltool libbabl-dev

 

2. GEGL 0.2(Generic Graphics Library)의 설치가 필요하다.

wget https://download.gimp.org/pub/gegl/0.2/gegl-0.2.0.tar.bz2
tar xvf gegl-0.2.0.tar.bz2 && cd gegl-0.2.0
sed -i 's/CODEC_CAP_TRUNCATED/AV_CODEC_CAP_TRUNCATED/g' ./operations/external/ff-load.c
sed -i 's/CODEC_FLAG_TRUNCATED/AV_CODEC_FLAG_TRUNCATED/g' ./operations/external/ff-load.c

 

3. build

./configure --enable-debug --disable-glibtest  --without-vala --without-cairo --without-pango --without-pangocairo --without-gdk-pixbuf --without-lensfun --without-libjpeg --without-libpng --without-librsvg --without-openexr --without-sdl --without-libopenraw --without-jasper --without-graphviz --without-lua --without-libavformat --without-libv4l --without-libspiro --without-exiv2 --without-umfpack
make -j$(nproc)
sudo make install

 

4. GIMP 2.8.16 다운로드 및 압축 해제

cd ..
wget https://mirror.klaus-uwe.me/gimp/pub/gimp/v2.8/gimp-2.8.16.tar.bz2
tar xvf gimp-2.8.16.tar.bz2 && cd gimp-2.8.16/
CC=afl-clang-lto CXX=afl-clang-lto++ PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$HOME/Fuzzing_gimp/gegl-0.2.0/ CFLAGS="-fsanitize=address" CXXFLAGS="-fsanitize=address" LDFLAGS="-fsanitize=address" ./configure --disable-gtktest --disable-glibtest --disable-alsatest --disable-nls --without-libtiff --without-libjpeg --without-bzip2 --without-gs --without-libpng --without-libmng --without-libexif --without-aa --without-libxpm --without-webkit --without-librsvg --without-print --without-poppler --without-cairo-pdf --without-gvfs --without-libcurl --without-wmf --without-libjasper --without-alsa --without-gudev --disable-python --enable-gimp-console --without-mac-twain --without-script-fu --without-gudev --without-dbus --disable-mp --without-linux-input --without-xvfb-run --with-gif-compression=none --without-xmc --with-shm=none --enable-debug  --prefix="$HOME/Fuzzing_gimp/gimp-2.8.16/install"
make -j$(nproc)
make install

Persistent mode

두가지 방법이 있다.

  • app.c 파일을 수정하는 방법
  • AFL_LOOP 매크로를 for 반복 루프 내에 포함시킨다.
if ( filenames ){
gint i;

for(i=0;filenames[i] != NULL; i++){
	if (run_loop){
		#ifdef __AFL_COMPILER
			while(__AFL_LOOP(1000)){
				file_open_from_command_line (gimp, filenames[i], as_new);
			}
			exit(0);

		#else
			file_open_from_command_line (gimp, filenames[i], as_new);
		#endif
		}
  • AFL_LOOP를 xcf_load_invoker 함수 내에 포함시킨다.
--- ../xcf.c	2014-08-20 08:27:58.000000000 -0700
+++ ./app/xcf/xcf.c	2021-10-11 13:02:42.800831192 -0700
@@ -277,6 +277,10 @@
 
   filename = g_value_get_string (&args->values[1]);
 
+#ifdef __AFL_COMPILER
+  while(__AFL_LOOP(10000)){
+#endif
+
   info.fp = g_fopen (filename, "rb");
 
   if (info.fp)
@@ -366,6 +370,12 @@
   if (success)
     gimp_value_set_image (&return_vals->values[1], image);
 
+#ifdef __AFL_COMPILER
+  }
+#endif
+
+  exit(0);
+
   gimp_unset_busy (gimp);
 
   return return_vals;

첫 번째 방법은 다른 입력 형식도 타겟으로 할 수 있지만 두 번째 방법은 xcf만 대상으로 하므로 더 빠르게 버그를 찾을 수 있다.

Seed corpus creation

샘플 xcf 파일을 구해서 AFL input 폴더에 넣는다.

Fuzzing

GIMP 바이너리를 대상으로 한 퍼징이므로 불필요한 플러그인을 제거한다.

rm ./install/lib/gimp/2.0/plug-ins/*

 

Fuzzing!

ASAN_OPTIONS=detect_leaks=0,abort_on_error=1,symbolize=0 afl-fuzz -i './afl_in' -o './afl_out' -D -t 100 -- ./install/bin/gimp-console-2.8 --verbose -d -f @@
  • gimp-console-2.8은 GIMP의 CLI 버전이다.
  • 결정론적 변이 옵션을 허용했다. (-D)
  • 코드에 무한 루프 버그가 있으므로 시간 제한(-t 1000)을 설정해야 한다. 시스템 성능에 따라 다를 수 있기 때문에 적절히 조절한다.

Triage

ASan 트레이스를 지원하므로 크래시가 발생한 파일을 매개변수로 전달하여 실행하면 된다.

'시스템' 카테고리의 다른 글

AFL fuzzer & Exploit  (0) 2022.10.22
AFL Fuzzer  (0) 2022.10.22
Exploiting Null Byte Buffer Overflow for a $40,000 Bounty  (0) 2019.12.26
Pwning VMWare, Part 1: RWCTF 2018 Station-Escape  (0) 2019.12.24
x64 Stack 개요  (0) 2019.12.11
블로그 이미지

wtdsoul

,

https://samcurry.net/filling-in-the-blanks-exploiting-null-byte-buffer-overflow-for-a-40000-bounty/

 

Exploiting Null Byte Buffer Overflow for a $40,000 bounty | Sam Curry

November 1, 2019 samwcyo As a preface, when I originally found this bug I was unfamiliar the class of “null byte buffer overflow” even existed. I was simply fuzzing a standard web application’s input field and ran into a very interesting behavior that turn

samcurry.net

페북으로 정구홍님이 올려주셨던 글인걸로 기억합니다..

 

'시스템' 카테고리의 다른 글

AFL Fuzzer  (0) 2022.10.22
퍼징으로 1-day 취약점 분석하기(GIMP)  (0) 2022.10.22
Pwning VMWare, Part 1: RWCTF 2018 Station-Escape  (0) 2019.12.24
x64 Stack 개요  (0) 2019.12.11
pwnable.kr (uaf)  (0) 2019.12.11
블로그 이미지

wtdsoul

,

https://nafod.net/blog/2019/12/21/station-escape-vmware-pwn.html

 

Pwning VMWare, Part 1: RWCTF 2018 Station-Escape

Since December rolled around, I have been working on pwnables related to VMware breakouts as part of my advent calendar for 2019. Advent calendars are a fun way to get motivated to get familiar with a target you’re always putting off, and I had a lot of su

nafod.net

 

The bug

Examining the changes, we find that they’re all in request type 5, corresponding to GUESTRPC_FINALIZE. The user controls the argument which is & 0x21 and passed to guestrpc_close_backdoor.

void __fastcall guestrpc_close_backdoor(__int64 a1, unsigned __int16 a2, char a3) { __int64 v3; // rbx void *v4; // rdi v3 = a1; v4 = *(void **)(a1 + 8); if ( a3 & 0x20 ) { free(v4); } else if ( !(a3 & 0x10) ) { sub_176D90(v3, 0); if ( *(_BYTE *)(v3 + 0x20) ) { vmx_log("GuestRpc: Closing RPCI backdoor channel %u after send completion\n", a2); guestrpc_close_channel(a2); *(_BYTE *)(v3 + 32) = 0; } } }

Control of a3 allows us to go down the first branch in a previously inaccessible manner, letting us free the buffer at a1+0x8, which corresponds to the buffer used internally to store the reply data passed back to the user. However, this same buffer will also be freed with command type 6, GUESTRPC_CLOSE, resulting in a controlled double free which we can turn into use-after-free. (The other patch nop’d out code responsible for NULLing out the reply buffer, which would have prevented this codepath from being exploited.)

Given that the bug is very similar to a traditional CTF heap pwnable, we can already envision a rough path forward, for which we’ll fill in details shortly:

  • Obtain a leak, ideally of the vmware-vmx binary text section
  • Use tcache to allocate a chunk on top of a function pointer
  • Obtain rip and rdi control and invoke system("/usr/bin/xcalc &")

Heap internals and obtaining a leak

Firstly, it should be stated that the vmx heap appears to have little churn in a mostly idle VM, at least in the heap section used for guestrpc requests. This means that the exploit can relatively reliable even if the VM has been running for a bit or if the user was previously using the system.

In order to obtain a heap leak, we’ll perform the following series of operations

  1. Allocate three channels [A], [B], and [C]
  2. Send the info-set commmand to channel [A], which allows us to store arbitrary data of arbitrary size (up to a limit) in the host heap.
  3. Open channel [B] and issue a info-get to retrieve the data we just set
  4. Issue the reply length and reply read commands on channel [B]
  5. Invoke the buggy finalize command on channel [B], freeing the underlying reply buffer
  6. Invoke info-get on channel [C] and receive the reply length, which allocates a buffer at the same address we just
  7. Close channel [B], freeing the buffer again
  8. Read out the reply on channel [C] to leak our data

Each vmware-vmx process has a number of associated threads, including one thread per guest vCPU. This means that the underlying glibc heap has both the tcache mechanism active, as well as several different heap arenas. Although we can avoid mixing up our tcache chunks by pinning our cpu in the guest to a single core, we still cannot directly leak a libc pointer because only the main_arena in the glibc heap resides there. Instead, we can only leak a pointer to our individual thread arena, which is less useful in our case.

[#0] Id 1, Name: "vmware-vmx", stopped, reason: STOPPED [#1] Id 2, Name: "vmx-vthread-300", stopped, reason: STOPPED [#2] Id 3, Name: "vmx-vthread-301", stopped, reason: STOPPED [#3] Id 4, Name: "vmx-mks", stopped, reason: STOPPED [#4] Id 5, Name: "vmx-svga", stopped, reason: STOPPED [#5] Id 6, Name: "threaded-ml", stopped, reason: STOPPED [#6] Id 7, Name: "vmx-vcpu-0", stopped, reason: STOPPED <-- our vCPU thread [#7] Id 8, Name: "vmx-vcpu-1", stopped, reason: STOPPED [#8] Id 9, Name: "vmx-vcpu-2", stopped, reason: STOPPED [#9] Id 10, Name: "vmx-vcpu-3", stopped, reason: STOPPED [#10] Id 11, Name: "vmx-vthread-353", stopped, reason: STOPPED . . . .

To get around this, we’ll modify the above flow to spray some other object with a vtable pointer. I came across this writeup by Amat Cama which detailed his exploitation in 2017 using drag-n-drop and copy-paste structures, which are allocated when you send a guestrpc command in the host vCPU heap.

Therefore, I updated the above flow as follows to leak out a vtable/vmware-vmx-bss pointer

  1. Allocate four channels [A], [B], [C], and [D]
  2. Send the info-set commmand to channel [A], which allows us to store arbitrary data of arbitrary size (up to a limit) in the host heap.
  3. Open channel [B] and issue a info-get to retrieve the data we just set
  4. Issue the reply length and reply read commands on channel [B]
  5. Invoke the buggy finalize command on channel [B], freeing the underlying reply buffer
  6. Invoke info-get on channel [C] and receive the reply length, which allocates a buffer at the same address we just
  7. Close channel [B], freeing the buffer again
  8. Send vmx.capability.dnd_version on channel [D], which allocates an object with a vtable on top of the chunk referenced by [C]
  9. Read out the reply on channel [C] to leak the vtable pointer

One thing I did notice is that the copy-paste and drag-n-drop structures appear to only allocate their vtable-containing objects once per guest execution lifetime. This could complicate leaking pointers inside VMs where guest tools are installed and actively being used. In a more reliable exploit, we would hope to create a more repeatable arbitrary read and write primtive, maybe with these heap constructions alone. From there, we could trace backwards to leak our vmx binary.

 

'시스템' 카테고리의 다른 글

AFL Fuzzer  (0) 2022.10.22
퍼징으로 1-day 취약점 분석하기(GIMP)  (0) 2022.10.22
Exploiting Null Byte Buffer Overflow for a $40,000 Bounty  (0) 2019.12.26
x64 Stack 개요  (0) 2019.12.11
pwnable.kr (uaf)  (0) 2019.12.11
블로그 이미지

wtdsoul

,