최근 워게임을 풀다가 RootKit이라는 개념을 접하게 되었다.
RootKit...
이름은 많이 들어본것 같이 익숙한듯 낯설다.

그리하여, RootKit에 대해 알아보았다.

RootKit
- 특정 시스템을 해킹 한 후 시스템의 제어권을 획득할 목적으로 설치하는 악성 프로그램.
(Post Exploit에 해당한다.)

Rootkit의 성능 지표
1) 원하는 대로 제어권을 획득 할 수 있는지
2) 탐지 되지 않는지

이 조건을 높은 수준으로 충족시키기 위해서는 커널 모드에서 동작하는 RootKit을 작성하여야 한다.

...!
....!
RootKit을 한번 제작해보고 싶어졌다. (아주 간단한 옛날 버전의 리눅스더라도...!)
그래서 나의 프로젝트에 RootKit 제작이 추가되었다.

현재, 학교의 엄청난 과제양과 이미 벌려놓은 수많은 프로젝트들이 있지만,
그래도 정말 하고싶다.! 그래서 시작!!!

(성능좋은) RootKit을 제작하기 위해서는 커널 영역에서 놀 줄 알아야한다.

그러한 의미로 LKM을 공부해보았다.
LKM 입문? 정도다.

LKM은 Loadable Kernel Module 이다.

- 커널영역은 유저영역에서 직접 접근할 수 없다. 커널영역을 사용해야할 때는 system call을 이용하여 커널영역을 사용하게 된다.

- 커널 모듈은 커널에 올라가는 모듈이다. 운영체제 리눅스는 커널 모듈들의 모음이라고 할 수 있다. 다양한 커널모듈들이 있고 각각의 모듈들이 디바이스를 관리하는 역할을 한다. 우리는 LKM을 간단히 만들어 커널영역에 올리는 작업을 해볼 것이다.

그러기 위해 필요한 것이 있다. 바로 linux 헤더이다.


(linux-headers)


자기 운영체제 버전에 맞는 것을 설치해야한다.
먼저 search로 검색을 한 후,

알맞은 운영체제 버전을 선택해서 설치하면 된다.


(설치)


그렇게 되면 준비는 끝난다.

간단한 LKM 을 만들기 위해

hello.c 파일을 작성하였다.


(hello.c)


MODULE 로 시작한 코드들은 해당 모듈에 관한 정보를 적는 것이다.

그 아래 static으로 변수를 만들어 주었다.
주의할 점은 이러한 모듈을 만들 때 전역변수를 함부로 사용하면 안된다. 이유는 이 모듈은 커널에 올라가게 되는데, 전역변수를 사용하게 되면 이곳저곳의 프로세스에 영향을 줄 수 있기 때문이다. 그렇기에 static으로 하여 해당 모듈에서만 영향력있게 만든다. 전역변수를 썼다가 혹여나 다른 프로세스에서 겹치는 변수가 있게 되어 꼬여버릴 수 있기 때문이다.

그 아래 module_param 함수는 변수에 관한 설정을 해주는 것이다. 첫번째는 변수 이름이고, 두번째는 type이다. 여기서는 char pointer 이므로 charp 라고 적어준 것이다. 마지막은 접근 권한이다.

그 아래 init과 exit 함수가 있다. init 함수는 모듈이 올라갈 때 실행되며, exit 함수는 모듈이 해제될 때 실행되는 함수이다. 여기서는 printk를 이용하여 커널 로그에다가 print하는 코드를 넣었다.

맨 마지막에 module_init과 module_exit함수로 init함수와 exit 함수를 지정해준다.

이 파일을 컴파일 하기 위하여 Makefile을 만든다.


(Makefile)


현재 로컬 머신의 버전과 일치하는 컴파일을 하기 위하여 -C 옵션을 주어 경로를 변경한 후 M 모듈은 현재 경로에다가 컴파일 하도록 한다.

make 를 입력하여 컴파일한다.


(컴파일 완료)


그러면 hello.ko 가 생기게 된다. 이 커널 오브젝트 파일은 모듈인데 이 모듈을 올려보도록 할 것이다.


(커널 모듈 올리기)


insmod 명령어로 커널모듈을 올릴 수 있다.

올라간 모듈은 lsmod를 통하여 확인 할 수 있다.


(올라간 모듈 확인)


올라간 모듈을 해제하는 법은 rmmod 이다.


(모듈 해제)


모듈을 해제하고 확인해보니 올라간 모듈 리스트에 없는 것을 확인 할 수 있다.

커널 로그를 확인해보겠다.

아까 우리가 init과 exit 에 printk를 넣었으니, 모듈이 올라갈 때 내려갈 때 로그가 찍혔을 것이다.


(로그 확인)


커널 로그의 위치는 /var/log/kern.log 이다.

이번에는 아까 만든 변수에 값을 넘겨주며 올려보겠다.


(인자 넘기기)


인자는 위와같이 넘길 수 있다.

이 올라간 모듈에 대한 정보는 어디있을까?
/sys/module에 들어가본다.

(모듈 확인)


올라간 모듈 hello에 들어가본다.


(모듈 정보)


모듈 정보들이 보인다.

여기서 우리는 parameters를 확인해 볼 것이다.



(변수 확인)


우리의 변수인 name이 보이고 안을 열어보니 Normaltic이라고 아까 우리가 입력한 값이 저장되어있는 것을 확인 할 수 있다.

이 값을 수정할 수 있을까 했더니


(파일 권한)


파일 권한은 읽기 권한으로만 되어있었다.

마지막으로 인자를 넘긴 모듈을 해제해보겠다.



(모듈 해제)


모듈 해제는 rmmod 로 같다.

그렇다면 kern.log는?


(커널 로그)


커널 로그에는 내가 넘겨준 인자와 함께 문구가 잘 찍힌 것을 확인 할 수 있었다.

LKM에 대해 간단히 이해를 해보았다.
RootKit을 만들고, 이해하는데 커널영역을 더 공부할 필요가 있을 것 같다.

참조
- http://derekmolloy.ie/writing-a-linux-kernel-module-part-1-introduction/

'Hacking > System Hacking' 카테고리의 다른 글

(RootKit) Simple Rootkit  (0) 2018.04.12
malloc에서 사용하는 syscall  (0) 2018.02.21
System Hacking - jmp (반복문)  (0) 2017.04.19
System Hacking - jmp (분기문)  (0) 2017.04.18
System Hacking - 어셈블리어(사칙연산)  (0) 2017.04.14


이번 문제는 소스코드가 주어져서 쉽게 해결할 수 있었다.
소스코드가 없었다면, 이 취약점을 찾기 쉽지 않았을 것 같다.

소스코드의 select_menu() 함수가 문제였다.


(문제 코드)



(문제코드)


문제가 되는 함수의 부분이 위의 빨간 부분이다.
select_menu() 함수는 안에서 recursive하게 자기 자신을 호출한다.

화면에 다시 메뉴를 출력하게 할 때, while 문이나 다른 코드로 처리를 하는게 보통인데
참 특이하게 자기자신을 다시 호출하므로써 메뉴를 다시 출력하였다.

재귀적 코드는 깔끔해 보이는 장점이 있지만,
재귀가 많이 반복될 수록 스택 메모리를 많이 잡아먹는 현상이 일어난다.

그래도 함수 마지막에 호출하게하여 Tail Call(Tail Recursion)을 유도했지만,
실제로 gdb로 분석해본 결과 스택메모리를 낭비하며 동작하고 있었다.

그렇다면 스택을 쭉 낮출 수 있다는 것이다.
그리고 랜덤하게 주소를 받는데 그 주소가 스택 주소가 아니란 법도 없다.
더군다나, 여기서 만든 메모리할당 함수를 보면 실행권한까지 떡하니 줘버린다.
쉘코드를 써달라고 애원하는 듯 하다.

내가 exploit을 짜기 전 계획이다.
1. 스택영역의 주소를 얻는다.(계속 반복하여서)
2. 스택을 차곡차곡 쌓아서 주소를 팍 낮춘다. 우리가 구한 스택영역의 주소보다 낮게.
3. 그 다음 메모리할당을 한번 더 받아서 그 안에 쉘코드를 넣어둔다.(이 때 받은 쉘코드의 주소를 기억)
4. 아까 받은 스택영역의 주소에다가 쉘코드주소를 쫙 넣어둔다. 4096바이트만큼 쓸수 있으므로 1024개의 주소로 덮어쓸 수 있다.
5. exit 을 실행하면 스택이 recursive하게 리턴되면서 그 중간에 덮어쓰여진 쉘코드 주소로 뛰게 되고 쉘코드 실행!

exploit을 짜면서 바뀐 계획인데

2번의 과정은 없어도 되었다. 스택영역의 주소를 얻기위해 반복하는 과정에서 자연스레 엄청 낮아졌다.



(스택 영역 주소 구하기)


반복은 받은 주소가 현재 ebp 주소보다 커진 경우까지 반복한다.

그렇게 되면 ret,leave 하면서 쭉 내려갈 테니까!



(clear 함수)


반복하다보면 255개의 배열을 다 쓴다..
그런 경우 255개를 다시 다 지워주는 함수를 만들었다.
(계속 반복 할 수 있도록)

쉘코드 올리기


(쉘코드 올리기)


쉘코드를 올리는 부분이다.



(스택 영역 주소 덮기)


그 후 쉘코드 주소를 스택영역 주소에 덮어쓴다.

그리고 

exploit!



(스택 주소 받기)


엄청난 양을 받는다.

옆에 Stacking 옆의 값은 현재 ebp 값이다.


(clear 중)


clear 도 몇번 실행되었다.



(exploit)


끝내 만족하는 스택영역 주소를 받고
ret 주소를 덮어썼다.

여기서 5를 눌러 exit를 실행하면 최종적으로 shell을 획득할 수 있다.


(쉘 획득)


문제 풀이 끝!

'WarGame > 500 Project' 카테고리의 다른 글

(66/500) pwnable.kr - rsa_calculator  (1) 2018.03.10
(65/500) pwnable.kr - echo2  (0) 2018.03.08
(64/500) backdoor - Enter the matrix  (0) 2017.10.31
(63/500) pwnable.kr - syscall  (2) 2017.10.22
(62/500) NOE.systems - BURYBURY  (0) 2017.10.01


지난 프로젝트 일지에서의 계획
1. Framework의 update 기능 및 info 명령시 각 명령어에 대한 설명 추가.
2. 선택한 인터페이스의 IP대역에 있는 호스트 스캐닝 구현

Framework의 update 기능 구현은 스캐너 툴을 완성시킨 후, github페이지에 올린 후에 구현할 것이다.!
info 명령시 각 명령어에 대한 설명 추가! scanner 툴에 대한 설명을 추가하였다.
그리고 선택한 인터페이스의 IP대역에 있는 호스트 스캔을 구현하였다.

info 명령 추가이다.

각 명령어의 사용법을 추가하였다.


(명령어 사용법 추가)


그리고 각 도구에 대한 설명을 추가하였다.


(스캐너 툴 설명 추가)


이번에 구현한 기능은 호스트 스캔 기능으로
해당 네트워크 대역에 살아있는 호스트들을 확인하는 기능이다.
나는 패킷을 직접 만들어 구현하지 않고, nmap의 기능을 이용했다.
python3의 nmap 모듈을 설치하여 사용했다.

nmap 모듈에 대한 설명이 많지 않았다...
그렇기에 python3 nmap 파일을 직접 열어서 확인해봐야했다.

nmap에는 PortScanner 클래스가 있고 이 클래스를 생성해 nmap 스캔 작업을 한다.

PortScanner 뿐 아니라 다른 기능의 클래스들이 있다. 비동기화스캐너라고 콜백함수를 이용하는 스캐너가 있었는데, 이거 잘쓰면 재밌는 기능을 구현할 수 있을 듯했다.



(포트스캐너 생성)


생성한 nmap 클래스 nm을 scan_menu에 전달하여 구현하였다.



(호스트 스캔)


호스트 스캔을 하고 아래에는 각 정보를 Tool 클래스의 hosts 사전형 변수에 저장한다.
기능을 구현하고 보니 지저분하다는 생각이 들었다.
scan_menu 함수 안에 두기 지저분하니 이걸 함수로 옮겨야겠다.(다음에 옮길 것이다.)

그리고 가지고 있는 hosts 정보를 출력할 함수도 scan_func에 구현하였다.
scanner 툴에 스캔한 호스트 정보를 출력해주는 기능이 있는데, 그 때 이 함수를 호출할 것이다.

(정보는 더 많게!)


(정보 출력 함수)


실행 모습이다.


(스캔 중인 모습)


스캔 결과 아주 흡족하게 나왔다.



(스캔결과)


이 정보 만 가지고는 부족하다.
이 호스트 정보를 가지고 디테일한 스캐닝을 하는 기능을 구현 할 것이다.


오늘 구현한 기능
- scanner의 host scan 기능
- Framwork의 info 명령 추가

현재까지 구현한 기능
1. Framework 내 tool import 등록, 연동
2. 각 Framework의 기능 구현(list, use, info, exit)
3. scanner의 인터페이스 설정 부분, 설정된 인터페이스의 IP정보 구하기.
4. scanner의 host scan 기능

다음 계획
1. 호스트 리스트를 가지고 디테일 스캐닝 기능 구현

2. Framework의 update 기능 구현

+ Recent posts